Skip to content

Commit bac7b12

Browse files
committed
usb: typec: tipd: Add cd321x specific control commands
CD321x support USB-PD commands to control connected devices which use CD321x as well. The most useful commands can reboot the connected device and route a debug UART over SBU pins. The linked tuxvdmtool exists as simple tool to trigger these actions. Link: https://asahilinux.org/docs/hw/soc/usb-pd/ Link: https://github.com/AsahiLinux/tuxvdmtool Co-developed-by: Martin R <[email protected]> Signed-off-by: Martin R <[email protected]> Signed-off-by: Janne Grunau <[email protected]>
1 parent 343ca1f commit bac7b12

1 file changed

Lines changed: 190 additions & 0 deletions

File tree

drivers/usb/typec/tipd/core.c

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include <linux/usb/role.h>
2020
#include <linux/workqueue.h>
2121
#include <linux/firmware.h>
22+
#include <linux/sysfs.h>
23+
#include <linux/string.h>
2224

2325
#include "tps6598x.h"
2426
#include "trace.h"
@@ -43,6 +45,7 @@
4345
#define TPS_REG_POWER_STATUS 0x3f
4446
#define TPS_REG_PD_STATUS 0x40
4547
#define TPS_REG_RX_IDENTITY_SOP 0x48
48+
#define TPS_REG_TX_VDM 0x4d
4649
#define TPS_REG_DATA_STATUS 0x5f
4750
#define TPS_REG_SLEEP_CONF 0x70
4851

@@ -95,6 +98,7 @@ enum {
9598
TPS_MODE_BIST,
9699
TPS_MODE_DISC,
97100
TPS_MODE_PTCH,
101+
CD_MODE_DBMA,
98102
};
99103

100104
static const char *const modes[] = {
@@ -103,11 +107,14 @@ static const char *const modes[] = {
103107
[TPS_MODE_BIST] = "BIST",
104108
[TPS_MODE_DISC] = "DISC",
105109
[TPS_MODE_PTCH] = "PTCH",
110+
[CD_MODE_DBMA] = "DBMa",
106111
};
107112

108113
/* Unrecognized commands will be replaced with "!CMD" */
109114
#define INVALID_CMD(_cmd_) (_cmd_ == 0x444d4321)
110115

116+
#define TPS_VDMS_MAX_LEN (7 * 4 + 1)
117+
111118
struct tps6598x;
112119

113120
struct tipd_data {
@@ -125,6 +132,7 @@ struct tps6598x {
125132
struct regmap *regmap;
126133
struct mutex lock; /* device lock */
127134
u8 i2c_protocol:1;
135+
u8 cd321x_unlocked:1;
128136

129137
struct gpio_desc *reset;
130138
struct typec_port *port;
@@ -726,6 +734,8 @@ static int tps6598x_check_mode(struct tps6598x *tps)
726734
return ret;
727735
case TPS_MODE_BIST:
728736
case TPS_MODE_DISC:
737+
case CD_MODE_DBMA:
738+
return ret;
729739
default:
730740
dev_err(tps->dev, "controller in unsupported mode \"%s\"\n",
731741
mode);
@@ -1193,6 +1203,175 @@ static int tps6598x_apply_patch(struct tps6598x *tps)
11931203
return ret;
11941204
}
11951205

1206+
1207+
static ssize_t lock_show(struct device *dev, struct device_attribute *attr,
1208+
char *buf)
1209+
{
1210+
struct i2c_client *client = to_i2c_client(dev);
1211+
struct tps6598x *tps = i2c_get_clientdata(client);
1212+
1213+
if (tps->cd321x_unlocked)
1214+
return sysfs_emit(buf, "unlocked\n");
1215+
else
1216+
return sysfs_emit(buf, "locked\n");
1217+
}
1218+
static DEVICE_ATTR_RO(lock);
1219+
1220+
static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
1221+
char *buf)
1222+
{
1223+
struct i2c_client *client = to_i2c_client(dev);
1224+
struct tps6598x *tps = i2c_get_clientdata(client);
1225+
1226+
int mode = tps6598x_check_mode(tps);
1227+
switch (mode) {
1228+
case TPS_MODE_APP ... CD_MODE_DBMA:
1229+
return sysfs_emit(buf, "%s\n", modes[mode]);
1230+
default:
1231+
return sysfs_emit(buf, "unkown\n");
1232+
}
1233+
}
1234+
static DEVICE_ATTR_RO(mode);
1235+
1236+
static ssize_t power_status_show(struct device *dev,
1237+
struct device_attribute *attr, char *buf)
1238+
{
1239+
struct i2c_client *client = to_i2c_client(dev);
1240+
struct tps6598x *tps = i2c_get_clientdata(client);
1241+
1242+
return sysfs_emit(buf, "0x%04hx\n", tps->pwr_status);
1243+
}
1244+
static DEVICE_ATTR_RO(power_status);
1245+
1246+
/* this assumes cd321x has a customized UnlockCode */
1247+
static ssize_t commad_lock(struct tps6598x *tps, const char *buf, size_t count)
1248+
{
1249+
const char default_code[4] = { 0, 0, 0, 0 };
1250+
int ret;
1251+
1252+
if (count < 4)
1253+
return -EINVAL;
1254+
1255+
ret = tps6598x_exec_cmd(tps, "LOCK", 4, buf, 0, NULL);
1256+
if (ret)
1257+
dev_err(tps->dev, "Unlock command failed: %d\n", ret);
1258+
else
1259+
/* Key 0 locks cd321x when UnlockCode is customized */
1260+
tps->cd321x_unlocked = !!memcmp(buf, default_code, 4);
1261+
1262+
return count;
1263+
}
1264+
1265+
static ssize_t commad_dbma(struct tps6598x *tps, const char *buf, size_t count)
1266+
{
1267+
int ret, mode;
1268+
bool enable;
1269+
1270+
if (count < 1)
1271+
return -EINVAL;
1272+
1273+
enable = buf[0] != 0;
1274+
1275+
if (enable && !tps->cd321x_unlocked)
1276+
return -EINVAL;
1277+
1278+
ret = tps6598x_exec_cmd(tps, "DBMa", 1, buf, 0, NULL);
1279+
if (ret) {
1280+
dev_err(tps->dev, "Failed to exec 'DBMa' command: %d\n", ret);
1281+
return ret;
1282+
}
1283+
1284+
mode = tps6598x_check_mode(tps);
1285+
if (enable && mode != CD_MODE_DBMA) {
1286+
dev_err(tps->dev, "CD321x failed to enter \"DBMa\" mode\n");
1287+
return -EIO;
1288+
} else if (!enable && mode != TPS_MODE_APP) {
1289+
dev_err(tps->dev, "CD321x failed to exit \"DBMa\" mode\n");
1290+
return -EIO;
1291+
}
1292+
1293+
return count;
1294+
}
1295+
1296+
static ssize_t commad_vdms(struct tps6598x *tps, const char *buf, size_t count)
1297+
{
1298+
int ret;
1299+
1300+
if (count < 5 || ((count -1) % 4) != 0 || count > TPS_VDMS_MAX_LEN)
1301+
return -EINVAL;
1302+
1303+
if (tps6598x_check_mode(tps) != CD_MODE_DBMA)
1304+
return -EIO;
1305+
1306+
ret = tps6598x_exec_cmd_tmo(tps, "VDMs", count, buf, 0, NULL, 200, 200);
1307+
if (ret) {
1308+
dev_err(tps->dev, "Sending VDM failed: %d\n", ret);
1309+
return ret;
1310+
}
1311+
1312+
return count;
1313+
}
1314+
1315+
static ssize_t commad_devn(struct tps6598x *tps, const char *buf, size_t count)
1316+
{
1317+
int ret;
1318+
1319+
if (count < 4)
1320+
return -EINVAL;
1321+
1322+
if (tps6598x_check_mode(tps) != CD_MODE_DBMA)
1323+
return -EIO;
1324+
1325+
ret = tps6598x_exec_cmd(tps, "DVEn", 4, buf, 0, NULL);
1326+
if (ret)
1327+
dev_err(tps->dev, "Could not enter local serial mode: %d\n",
1328+
ret);
1329+
1330+
return count;
1331+
}
1332+
1333+
#define CMD_LEN 4
1334+
static ssize_t command_store(struct device *dev, struct device_attribute *attr,
1335+
const char *buf, size_t count)
1336+
{
1337+
struct i2c_client *client = to_i2c_client(dev);
1338+
struct tps6598x *tps = i2c_get_clientdata(client);
1339+
int ret;
1340+
1341+
if (count < CMD_LEN)
1342+
return -EINVAL;
1343+
1344+
if (memcmp(buf, "LOCK", CMD_LEN) == 0)
1345+
ret = commad_lock(tps, buf + 4, count - 4);
1346+
else if (memcmp(buf, "DBMa", CMD_LEN) == 0)
1347+
ret = commad_dbma(tps, buf + 4, count - 4);
1348+
else if (memcmp(buf, "VDMs", CMD_LEN) == 0)
1349+
ret = commad_vdms(tps, buf + 4, count - 4);
1350+
else if (memcmp(buf, "DEVn", CMD_LEN) == 0)
1351+
ret = commad_devn(tps, buf + 4, count - 4);
1352+
else
1353+
return -EINVAL;
1354+
1355+
if (ret < 0)
1356+
return ret;
1357+
1358+
return ret + CMD_LEN;
1359+
}
1360+
static DEVICE_ATTR_WO(command);
1361+
1362+
static struct attribute *vdm_attrs[] = {
1363+
&dev_attr_lock.attr,
1364+
&dev_attr_mode.attr,
1365+
&dev_attr_power_status.attr,
1366+
&dev_attr_command.attr,
1367+
NULL,
1368+
};
1369+
1370+
static const struct attribute_group vdm_group = {
1371+
.name = "cd321x_vdm",
1372+
.attrs = vdm_attrs,
1373+
};
1374+
11961375
static int cd321x_init(struct tps6598x *tps)
11971376
{
11981377
return 0;
@@ -1433,6 +1612,14 @@ static int tps6598x_probe(struct i2c_client *client)
14331612
enable_irq_wake(client->irq);
14341613
}
14351614

1615+
if (device_is_compatible(tps->dev, "apple,cd321x")) {
1616+
int err;
1617+
err = sysfs_create_group(&client->dev.kobj, &vdm_group);
1618+
if (err < 0)
1619+
dev_err(tps->dev, "Couldn't register sysfs group for "
1620+
"CD321x VDMs\n");
1621+
}
1622+
14361623
return 0;
14371624

14381625
err_disconnect:
@@ -1456,6 +1643,9 @@ static void tps6598x_remove(struct i2c_client *client)
14561643
{
14571644
struct tps6598x *tps = i2c_get_clientdata(client);
14581645

1646+
if (device_is_compatible(tps->dev, "apple,cd321x"))
1647+
sysfs_remove_group(&client->dev.kobj, &vdm_group);
1648+
14591649
if (!client->irq)
14601650
cancel_delayed_work_sync(&tps->wq_poll);
14611651
else

0 commit comments

Comments
 (0)