Skip to content

Commit 99231e1

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 <me@martinyr.com> Signed-off-by: Martin R <me@martinyr.com> Signed-off-by: Janne Grunau <j@jannau.net>
1 parent 934763c commit 99231e1

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
@@ -22,6 +22,8 @@
2222
#include <linux/usb/role.h>
2323
#include <linux/workqueue.h>
2424
#include <linux/firmware.h>
25+
#include <linux/sysfs.h>
26+
#include <linux/string.h>
2527

2628
#include "tps6598x.h"
2729
#include "trace.h"
@@ -47,6 +49,7 @@
4749
#define TPS_REG_POWER_STATUS 0x3f
4850
#define TPS_REG_PD_STATUS 0x40
4951
#define TPS_REG_RX_IDENTITY_SOP 0x48
52+
#define TPS_REG_TX_VDM 0x4d
5053
#define TPS_REG_CF_VID_STATUS 0x5e
5154
#define TPS_REG_DP_SID_STATUS 0x58
5255
#define TPS_REG_INTEL_VID_STATUS 0x59
@@ -130,6 +133,7 @@ enum {
130133
TPS_MODE_BIST,
131134
TPS_MODE_DISC,
132135
TPS_MODE_PTCH,
136+
CD_MODE_DBMA,
133137
};
134138

135139
static const char *const modes[] = {
@@ -138,11 +142,14 @@ static const char *const modes[] = {
138142
[TPS_MODE_BIST] = "BIST",
139143
[TPS_MODE_DISC] = "DISC",
140144
[TPS_MODE_PTCH] = "PTCH",
145+
[CD_MODE_DBMA] = "DBMa",
141146
};
142147

143148
/* Unrecognized commands will be replaced with "!CMD" */
144149
#define INVALID_CMD(_cmd_) (_cmd_ == 0x444d4321)
145150

151+
#define TPS_VDMS_MAX_LEN (7 * 4 + 1)
152+
146153
struct tps6598x;
147154

148155
struct tipd_data {
@@ -168,6 +175,7 @@ struct tps6598x {
168175
struct regmap *regmap;
169176
struct mutex lock; /* device lock */
170177
u8 i2c_protocol:1;
178+
u8 cd321x_unlocked:1;
171179

172180
struct gpio_desc *reset;
173181
struct typec_port *port;
@@ -1076,6 +1084,8 @@ static int tps6598x_check_mode(struct tps6598x *tps)
10761084
return ret;
10771085
case TPS_MODE_BIST:
10781086
case TPS_MODE_DISC:
1087+
case CD_MODE_DBMA:
1088+
return ret;
10791089
default:
10801090
dev_err(tps->dev, "controller in unsupported mode \"%s\"\n",
10811091
mode);
@@ -1631,6 +1641,175 @@ static int tps6598x_apply_patch(struct tps6598x *tps)
16311641
return ret;
16321642
}
16331643

1644+
1645+
static ssize_t lock_show(struct device *dev, struct device_attribute *attr,
1646+
char *buf)
1647+
{
1648+
struct i2c_client *client = to_i2c_client(dev);
1649+
struct tps6598x *tps = i2c_get_clientdata(client);
1650+
1651+
if (tps->cd321x_unlocked)
1652+
return sysfs_emit(buf, "unlocked\n");
1653+
else
1654+
return sysfs_emit(buf, "locked\n");
1655+
}
1656+
static DEVICE_ATTR_RO(lock);
1657+
1658+
static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
1659+
char *buf)
1660+
{
1661+
struct i2c_client *client = to_i2c_client(dev);
1662+
struct tps6598x *tps = i2c_get_clientdata(client);
1663+
1664+
int mode = tps6598x_check_mode(tps);
1665+
switch (mode) {
1666+
case TPS_MODE_APP ... CD_MODE_DBMA:
1667+
return sysfs_emit(buf, "%s\n", modes[mode]);
1668+
default:
1669+
return sysfs_emit(buf, "unkown\n");
1670+
}
1671+
}
1672+
static DEVICE_ATTR_RO(mode);
1673+
1674+
static ssize_t power_status_show(struct device *dev,
1675+
struct device_attribute *attr, char *buf)
1676+
{
1677+
struct i2c_client *client = to_i2c_client(dev);
1678+
struct tps6598x *tps = i2c_get_clientdata(client);
1679+
1680+
return sysfs_emit(buf, "0x%04hx\n", tps->pwr_status);
1681+
}
1682+
static DEVICE_ATTR_RO(power_status);
1683+
1684+
/* this assumes cd321x has a customized UnlockCode */
1685+
static ssize_t commad_lock(struct tps6598x *tps, const char *buf, size_t count)
1686+
{
1687+
const char default_code[4] = { 0, 0, 0, 0 };
1688+
int ret;
1689+
1690+
if (count < 4)
1691+
return -EINVAL;
1692+
1693+
ret = tps6598x_exec_cmd(tps, "LOCK", 4, buf, 0, NULL);
1694+
if (ret)
1695+
dev_err(tps->dev, "Unlock command failed: %d\n", ret);
1696+
else
1697+
/* Key 0 locks cd321x when UnlockCode is customized */
1698+
tps->cd321x_unlocked = !!memcmp(buf, default_code, 4);
1699+
1700+
return count;
1701+
}
1702+
1703+
static ssize_t commad_dbma(struct tps6598x *tps, const char *buf, size_t count)
1704+
{
1705+
int ret, mode;
1706+
bool enable;
1707+
1708+
if (count < 1)
1709+
return -EINVAL;
1710+
1711+
enable = buf[0] != 0;
1712+
1713+
if (enable && !tps->cd321x_unlocked)
1714+
return -EINVAL;
1715+
1716+
ret = tps6598x_exec_cmd(tps, "DBMa", 1, buf, 0, NULL);
1717+
if (ret) {
1718+
dev_err(tps->dev, "Failed to exec 'DBMa' command: %d\n", ret);
1719+
return ret;
1720+
}
1721+
1722+
mode = tps6598x_check_mode(tps);
1723+
if (enable && mode != CD_MODE_DBMA) {
1724+
dev_err(tps->dev, "CD321x failed to enter \"DBMa\" mode\n");
1725+
return -EIO;
1726+
} else if (!enable && mode != TPS_MODE_APP) {
1727+
dev_err(tps->dev, "CD321x failed to exit \"DBMa\" mode\n");
1728+
return -EIO;
1729+
}
1730+
1731+
return count;
1732+
}
1733+
1734+
static ssize_t commad_vdms(struct tps6598x *tps, const char *buf, size_t count)
1735+
{
1736+
int ret;
1737+
1738+
if (count < 5 || ((count -1) % 4) != 0 || count > TPS_VDMS_MAX_LEN)
1739+
return -EINVAL;
1740+
1741+
if (tps6598x_check_mode(tps) != CD_MODE_DBMA)
1742+
return -EIO;
1743+
1744+
ret = tps6598x_exec_cmd_tmo(tps, "VDMs", count, buf, 0, NULL, 200, 200);
1745+
if (ret) {
1746+
dev_err(tps->dev, "Sending VDM failed: %d\n", ret);
1747+
return ret;
1748+
}
1749+
1750+
return count;
1751+
}
1752+
1753+
static ssize_t commad_devn(struct tps6598x *tps, const char *buf, size_t count)
1754+
{
1755+
int ret;
1756+
1757+
if (count < 4)
1758+
return -EINVAL;
1759+
1760+
if (tps6598x_check_mode(tps) != CD_MODE_DBMA)
1761+
return -EIO;
1762+
1763+
ret = tps6598x_exec_cmd(tps, "DVEn", 4, buf, 0, NULL);
1764+
if (ret)
1765+
dev_err(tps->dev, "Could not enter local serial mode: %d\n",
1766+
ret);
1767+
1768+
return count;
1769+
}
1770+
1771+
#define CMD_LEN 4
1772+
static ssize_t command_store(struct device *dev, struct device_attribute *attr,
1773+
const char *buf, size_t count)
1774+
{
1775+
struct i2c_client *client = to_i2c_client(dev);
1776+
struct tps6598x *tps = i2c_get_clientdata(client);
1777+
int ret;
1778+
1779+
if (count < CMD_LEN)
1780+
return -EINVAL;
1781+
1782+
if (memcmp(buf, "LOCK", CMD_LEN) == 0)
1783+
ret = commad_lock(tps, buf + 4, count - 4);
1784+
else if (memcmp(buf, "DBMa", CMD_LEN) == 0)
1785+
ret = commad_dbma(tps, buf + 4, count - 4);
1786+
else if (memcmp(buf, "VDMs", CMD_LEN) == 0)
1787+
ret = commad_vdms(tps, buf + 4, count - 4);
1788+
else if (memcmp(buf, "DEVn", CMD_LEN) == 0)
1789+
ret = commad_devn(tps, buf + 4, count - 4);
1790+
else
1791+
return -EINVAL;
1792+
1793+
if (ret < 0)
1794+
return ret;
1795+
1796+
return ret + CMD_LEN;
1797+
}
1798+
static DEVICE_ATTR_WO(command);
1799+
1800+
static struct attribute *vdm_attrs[] = {
1801+
&dev_attr_lock.attr,
1802+
&dev_attr_mode.attr,
1803+
&dev_attr_power_status.attr,
1804+
&dev_attr_command.attr,
1805+
NULL,
1806+
};
1807+
1808+
static const struct attribute_group vdm_group = {
1809+
.name = "cd321x_vdm",
1810+
.attrs = vdm_attrs,
1811+
};
1812+
16341813
static int cd321x_init(struct tps6598x *tps)
16351814
{
16361815
return 0;
@@ -1866,6 +2045,14 @@ static int tps6598x_probe(struct i2c_client *client)
18662045
enable_irq_wake(client->irq);
18672046
}
18682047

2048+
if (device_is_compatible(tps->dev, "apple,cd321x")) {
2049+
int err;
2050+
err = sysfs_create_group(&client->dev.kobj, &vdm_group);
2051+
if (err < 0)
2052+
dev_err(tps->dev, "Couldn't register sysfs group for "
2053+
"CD321x VDMs\n");
2054+
}
2055+
18692056
return 0;
18702057

18712058
err_disconnect:
@@ -1889,6 +2076,9 @@ static void tps6598x_remove(struct i2c_client *client)
18892076
{
18902077
struct tps6598x *tps = i2c_get_clientdata(client);
18912078

2079+
if (device_is_compatible(tps->dev, "apple,cd321x"))
2080+
sysfs_remove_group(&client->dev.kobj, &vdm_group);
2081+
18922082
if (!client->irq)
18932083
cancel_delayed_work_sync(&tps->wq_poll);
18942084
else

0 commit comments

Comments
 (0)