cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wusb3801.c (11772B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Willsemi WUSB3801 Type-C port controller driver
      4 *
      5 * Copyright (C) 2022 Samuel Holland <samuel@sholland.org>
      6 */
      7
      8#include <linux/i2c.h>
      9#include <linux/module.h>
     10#include <linux/regmap.h>
     11#include <linux/regulator/consumer.h>
     12#include <linux/usb/typec.h>
     13
     14#define WUSB3801_REG_DEVICE_ID		0x01
     15#define WUSB3801_REG_CTRL0		0x02
     16#define WUSB3801_REG_INT		0x03
     17#define WUSB3801_REG_STAT		0x04
     18#define WUSB3801_REG_CTRL1		0x05
     19#define WUSB3801_REG_TEST00		0x06
     20#define WUSB3801_REG_TEST01		0x07
     21#define WUSB3801_REG_TEST02		0x08
     22#define WUSB3801_REG_TEST03		0x09
     23#define WUSB3801_REG_TEST04		0x0a
     24#define WUSB3801_REG_TEST05		0x0b
     25#define WUSB3801_REG_TEST06		0x0c
     26#define WUSB3801_REG_TEST07		0x0d
     27#define WUSB3801_REG_TEST08		0x0e
     28#define WUSB3801_REG_TEST09		0x0f
     29#define WUSB3801_REG_TEST0A		0x10
     30#define WUSB3801_REG_TEST0B		0x11
     31#define WUSB3801_REG_TEST0C		0x12
     32#define WUSB3801_REG_TEST0D		0x13
     33#define WUSB3801_REG_TEST0E		0x14
     34#define WUSB3801_REG_TEST0F		0x15
     35#define WUSB3801_REG_TEST10		0x16
     36#define WUSB3801_REG_TEST11		0x17
     37#define WUSB3801_REG_TEST12		0x18
     38
     39#define WUSB3801_DEVICE_ID_VERSION_ID	GENMASK(7, 3)
     40#define WUSB3801_DEVICE_ID_VENDOR_ID	GENMASK(2, 0)
     41
     42#define WUSB3801_CTRL0_DIS_ACC_SUPPORT	BIT(7)
     43#define WUSB3801_CTRL0_TRY		GENMASK(6, 5)
     44#define WUSB3801_CTRL0_TRY_NONE		(0x0 << 5)
     45#define WUSB3801_CTRL0_TRY_SNK		(0x1 << 5)
     46#define WUSB3801_CTRL0_TRY_SRC		(0x2 << 5)
     47#define WUSB3801_CTRL0_CURRENT		GENMASK(4, 3) /* SRC */
     48#define WUSB3801_CTRL0_CURRENT_DEFAULT	(0x0 << 3)
     49#define WUSB3801_CTRL0_CURRENT_1_5A	(0x1 << 3)
     50#define WUSB3801_CTRL0_CURRENT_3_0A	(0x2 << 3)
     51#define WUSB3801_CTRL0_ROLE		GENMASK(2, 1)
     52#define WUSB3801_CTRL0_ROLE_SNK		(0x0 << 1)
     53#define WUSB3801_CTRL0_ROLE_SRC		(0x1 << 1)
     54#define WUSB3801_CTRL0_ROLE_DRP		(0x2 << 1)
     55#define WUSB3801_CTRL0_INT_MASK		BIT(0)
     56
     57#define WUSB3801_INT_ATTACHED		BIT(0)
     58#define WUSB3801_INT_DETACHED		BIT(1)
     59
     60#define WUSB3801_STAT_VBUS_DETECTED	BIT(7)
     61#define WUSB3801_STAT_CURRENT		GENMASK(6, 5) /* SNK */
     62#define WUSB3801_STAT_CURRENT_STANDBY	(0x0 << 5)
     63#define WUSB3801_STAT_CURRENT_DEFAULT	(0x1 << 5)
     64#define WUSB3801_STAT_CURRENT_1_5A	(0x2 << 5)
     65#define WUSB3801_STAT_CURRENT_3_0A	(0x3 << 5)
     66#define WUSB3801_STAT_PARTNER		GENMASK(4, 2)
     67#define WUSB3801_STAT_PARTNER_STANDBY	(0x0 << 2)
     68#define WUSB3801_STAT_PARTNER_SNK	(0x1 << 2)
     69#define WUSB3801_STAT_PARTNER_SRC	(0x2 << 2)
     70#define WUSB3801_STAT_PARTNER_AUDIO	(0x3 << 2)
     71#define WUSB3801_STAT_PARTNER_DEBUG	(0x4 << 2)
     72#define WUSB3801_STAT_ORIENTATION	GENMASK(1, 0)
     73#define WUSB3801_STAT_ORIENTATION_NONE	(0x0 << 0)
     74#define WUSB3801_STAT_ORIENTATION_CC1	(0x1 << 0)
     75#define WUSB3801_STAT_ORIENTATION_CC2	(0x2 << 0)
     76#define WUSB3801_STAT_ORIENTATION_BOTH	(0x3 << 0)
     77
     78#define WUSB3801_CTRL1_SM_RESET		BIT(0)
     79
     80#define WUSB3801_TEST01_VENDOR_SUB_ID	(BIT(8) | BIT(6))
     81
     82#define WUSB3801_TEST02_FORCE_ERR_RCY	BIT(8)
     83
     84#define WUSB3801_TEST0A_WAIT_VBUS	BIT(5)
     85
     86struct wusb3801 {
     87	struct typec_capability	cap;
     88	struct device		*dev;
     89	struct typec_partner	*partner;
     90	struct typec_port	*port;
     91	struct regmap		*regmap;
     92	struct regulator	*vbus_supply;
     93	unsigned int		partner_type;
     94	enum typec_port_type	port_type;
     95	enum typec_pwr_opmode	pwr_opmode;
     96	bool			vbus_on;
     97};
     98
     99static enum typec_role wusb3801_get_default_role(struct wusb3801 *wusb3801)
    100{
    101	switch (wusb3801->port_type) {
    102	case TYPEC_PORT_SRC:
    103		return TYPEC_SOURCE;
    104	case TYPEC_PORT_SNK:
    105		return TYPEC_SINK;
    106	case TYPEC_PORT_DRP:
    107	default:
    108		if (wusb3801->cap.prefer_role == TYPEC_SOURCE)
    109			return TYPEC_SOURCE;
    110		return TYPEC_SINK;
    111	}
    112}
    113
    114static int wusb3801_map_port_type(enum typec_port_type type)
    115{
    116	switch (type) {
    117	case TYPEC_PORT_SRC:
    118		return WUSB3801_CTRL0_ROLE_SRC;
    119	case TYPEC_PORT_SNK:
    120		return WUSB3801_CTRL0_ROLE_SNK;
    121	case TYPEC_PORT_DRP:
    122	default:
    123		return WUSB3801_CTRL0_ROLE_DRP;
    124	}
    125}
    126
    127static int wusb3801_map_pwr_opmode(enum typec_pwr_opmode mode)
    128{
    129	switch (mode) {
    130	case TYPEC_PWR_MODE_USB:
    131	default:
    132		return WUSB3801_CTRL0_CURRENT_DEFAULT;
    133	case TYPEC_PWR_MODE_1_5A:
    134		return WUSB3801_CTRL0_CURRENT_1_5A;
    135	case TYPEC_PWR_MODE_3_0A:
    136		return WUSB3801_CTRL0_CURRENT_3_0A;
    137	}
    138}
    139
    140static unsigned int wusb3801_map_try_role(int role)
    141{
    142	switch (role) {
    143	case TYPEC_NO_PREFERRED_ROLE:
    144	default:
    145		return WUSB3801_CTRL0_TRY_NONE;
    146	case TYPEC_SINK:
    147		return WUSB3801_CTRL0_TRY_SNK;
    148	case TYPEC_SOURCE:
    149		return WUSB3801_CTRL0_TRY_SRC;
    150	}
    151}
    152
    153static enum typec_orientation wusb3801_unmap_orientation(unsigned int status)
    154{
    155	switch (status & WUSB3801_STAT_ORIENTATION) {
    156	case WUSB3801_STAT_ORIENTATION_NONE:
    157	case WUSB3801_STAT_ORIENTATION_BOTH:
    158	default:
    159		return TYPEC_ORIENTATION_NONE;
    160	case WUSB3801_STAT_ORIENTATION_CC1:
    161		return TYPEC_ORIENTATION_NORMAL;
    162	case WUSB3801_STAT_ORIENTATION_CC2:
    163		return TYPEC_ORIENTATION_REVERSE;
    164	}
    165}
    166
    167static enum typec_pwr_opmode wusb3801_unmap_pwr_opmode(unsigned int status)
    168{
    169	switch (status & WUSB3801_STAT_CURRENT) {
    170	case WUSB3801_STAT_CURRENT_STANDBY:
    171	case WUSB3801_STAT_CURRENT_DEFAULT:
    172	default:
    173		return TYPEC_PWR_MODE_USB;
    174	case WUSB3801_STAT_CURRENT_1_5A:
    175		return TYPEC_PWR_MODE_1_5A;
    176	case WUSB3801_STAT_CURRENT_3_0A:
    177		return TYPEC_PWR_MODE_3_0A;
    178	}
    179}
    180
    181static int wusb3801_try_role(struct typec_port *port, int role)
    182{
    183	struct wusb3801 *wusb3801 = typec_get_drvdata(port);
    184
    185	return regmap_update_bits(wusb3801->regmap, WUSB3801_REG_CTRL0,
    186				  WUSB3801_CTRL0_TRY,
    187				  wusb3801_map_try_role(role));
    188}
    189
    190static int wusb3801_port_type_set(struct typec_port *port,
    191				  enum typec_port_type type)
    192{
    193	struct wusb3801 *wusb3801 = typec_get_drvdata(port);
    194	int ret;
    195
    196	ret = regmap_update_bits(wusb3801->regmap, WUSB3801_REG_CTRL0,
    197				 WUSB3801_CTRL0_ROLE,
    198				 wusb3801_map_port_type(type));
    199	if (ret)
    200		return ret;
    201
    202	wusb3801->port_type = type;
    203
    204	return 0;
    205}
    206
    207static const struct typec_operations wusb3801_typec_ops = {
    208	.try_role	= wusb3801_try_role,
    209	.port_type_set	= wusb3801_port_type_set,
    210};
    211
    212static int wusb3801_hw_init(struct wusb3801 *wusb3801)
    213{
    214	return regmap_write(wusb3801->regmap, WUSB3801_REG_CTRL0,
    215			    wusb3801_map_try_role(wusb3801->cap.prefer_role) |
    216			    wusb3801_map_pwr_opmode(wusb3801->pwr_opmode) |
    217			    wusb3801_map_port_type(wusb3801->port_type));
    218}
    219
    220static void wusb3801_hw_update(struct wusb3801 *wusb3801)
    221{
    222	struct typec_port *port = wusb3801->port;
    223	struct device *dev = wusb3801->dev;
    224	unsigned int partner_type, status;
    225	int ret;
    226
    227	ret = regmap_read(wusb3801->regmap, WUSB3801_REG_STAT, &status);
    228	if (ret) {
    229		dev_warn(dev, "Failed to read port status: %d\n", ret);
    230		status = 0;
    231	}
    232	dev_dbg(dev, "status = 0x%02x\n", status);
    233
    234	partner_type = status & WUSB3801_STAT_PARTNER;
    235
    236	if (partner_type == WUSB3801_STAT_PARTNER_SNK) {
    237		if (!wusb3801->vbus_on) {
    238			ret = regulator_enable(wusb3801->vbus_supply);
    239			if (ret)
    240				dev_warn(dev, "Failed to enable VBUS: %d\n", ret);
    241			wusb3801->vbus_on = true;
    242		}
    243	} else {
    244		if (wusb3801->vbus_on) {
    245			regulator_disable(wusb3801->vbus_supply);
    246			wusb3801->vbus_on = false;
    247		}
    248	}
    249
    250	if (partner_type != wusb3801->partner_type) {
    251		struct typec_partner_desc desc = {};
    252		enum typec_data_role data_role;
    253		enum typec_role pwr_role = wusb3801_get_default_role(wusb3801);
    254
    255		switch (partner_type) {
    256		case WUSB3801_STAT_PARTNER_STANDBY:
    257			break;
    258		case WUSB3801_STAT_PARTNER_SNK:
    259			pwr_role = TYPEC_SOURCE;
    260			break;
    261		case WUSB3801_STAT_PARTNER_SRC:
    262			pwr_role = TYPEC_SINK;
    263			break;
    264		case WUSB3801_STAT_PARTNER_AUDIO:
    265			desc.accessory = TYPEC_ACCESSORY_AUDIO;
    266			break;
    267		case WUSB3801_STAT_PARTNER_DEBUG:
    268			desc.accessory = TYPEC_ACCESSORY_DEBUG;
    269			break;
    270		}
    271
    272		if (wusb3801->partner) {
    273			typec_unregister_partner(wusb3801->partner);
    274			wusb3801->partner = NULL;
    275		}
    276
    277		if (partner_type != WUSB3801_STAT_PARTNER_STANDBY) {
    278			wusb3801->partner = typec_register_partner(port, &desc);
    279			if (IS_ERR(wusb3801->partner))
    280				dev_err(dev, "Failed to register partner: %ld\n",
    281					PTR_ERR(wusb3801->partner));
    282		}
    283
    284		data_role = pwr_role == TYPEC_SOURCE ? TYPEC_HOST : TYPEC_DEVICE;
    285		typec_set_data_role(port, data_role);
    286		typec_set_pwr_role(port, pwr_role);
    287		typec_set_vconn_role(port, pwr_role);
    288	}
    289
    290	typec_set_pwr_opmode(wusb3801->port,
    291			     partner_type == WUSB3801_STAT_PARTNER_SRC
    292				? wusb3801_unmap_pwr_opmode(status)
    293				: wusb3801->pwr_opmode);
    294	typec_set_orientation(wusb3801->port,
    295			      wusb3801_unmap_orientation(status));
    296
    297	wusb3801->partner_type = partner_type;
    298}
    299
    300static irqreturn_t wusb3801_irq(int irq, void *data)
    301{
    302	struct wusb3801 *wusb3801 = data;
    303	unsigned int dummy;
    304
    305	/*
    306	 * The interrupt register must be read in order to clear the IRQ,
    307	 * but all of the useful information is in the status register.
    308	 */
    309	regmap_read(wusb3801->regmap, WUSB3801_REG_INT, &dummy);
    310
    311	wusb3801_hw_update(wusb3801);
    312
    313	return IRQ_HANDLED;
    314}
    315
    316static const struct regmap_config config = {
    317	.reg_bits	= 8,
    318	.val_bits	= 8,
    319	.max_register	= WUSB3801_REG_TEST12,
    320};
    321
    322static int wusb3801_probe(struct i2c_client *client)
    323{
    324	struct device *dev = &client->dev;
    325	struct fwnode_handle *connector;
    326	struct wusb3801 *wusb3801;
    327	const char *cap_str;
    328	int ret;
    329
    330	wusb3801 = devm_kzalloc(dev, sizeof(*wusb3801), GFP_KERNEL);
    331	if (!wusb3801)
    332		return -ENOMEM;
    333
    334	i2c_set_clientdata(client, wusb3801);
    335
    336	wusb3801->dev = dev;
    337
    338	wusb3801->regmap = devm_regmap_init_i2c(client, &config);
    339	if (IS_ERR(wusb3801->regmap))
    340		return PTR_ERR(wusb3801->regmap);
    341
    342	wusb3801->vbus_supply = devm_regulator_get(dev, "vbus");
    343	if (IS_ERR(wusb3801->vbus_supply))
    344		return PTR_ERR(wusb3801->vbus_supply);
    345
    346	connector = device_get_named_child_node(dev, "connector");
    347	if (!connector)
    348		return -ENODEV;
    349
    350	ret = typec_get_fw_cap(&wusb3801->cap, connector);
    351	if (ret)
    352		goto err_put_connector;
    353	wusb3801->port_type = wusb3801->cap.type;
    354
    355	ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str);
    356	if (ret)
    357		goto err_put_connector;
    358
    359	ret = typec_find_pwr_opmode(cap_str);
    360	if (ret < 0 || ret == TYPEC_PWR_MODE_PD)
    361		goto err_put_connector;
    362	wusb3801->pwr_opmode = ret;
    363
    364	/* Initialize the hardware with the devicetree settings. */
    365	ret = wusb3801_hw_init(wusb3801);
    366	if (ret)
    367		return ret;
    368
    369	wusb3801->cap.revision		= USB_TYPEC_REV_1_2;
    370	wusb3801->cap.accessory[0]	= TYPEC_ACCESSORY_AUDIO;
    371	wusb3801->cap.accessory[1]	= TYPEC_ACCESSORY_DEBUG;
    372	wusb3801->cap.orientation_aware	= true;
    373	wusb3801->cap.driver_data	= wusb3801;
    374	wusb3801->cap.ops		= &wusb3801_typec_ops;
    375
    376	wusb3801->port = typec_register_port(dev, &wusb3801->cap);
    377	if (IS_ERR(wusb3801->port)) {
    378		ret = PTR_ERR(wusb3801->port);
    379		goto err_put_connector;
    380	}
    381
    382	/* Initialize the port attributes from the hardware state. */
    383	wusb3801_hw_update(wusb3801);
    384
    385	ret = request_threaded_irq(client->irq, NULL, wusb3801_irq,
    386				   IRQF_ONESHOT, dev_name(dev), wusb3801);
    387	if (ret)
    388		goto err_unregister_port;
    389
    390	fwnode_handle_put(connector);
    391
    392	return 0;
    393
    394err_unregister_port:
    395	typec_unregister_port(wusb3801->port);
    396err_put_connector:
    397	fwnode_handle_put(connector);
    398
    399	return ret;
    400}
    401
    402static int wusb3801_remove(struct i2c_client *client)
    403{
    404	struct wusb3801 *wusb3801 = i2c_get_clientdata(client);
    405
    406	free_irq(client->irq, wusb3801);
    407
    408	if (wusb3801->partner)
    409		typec_unregister_partner(wusb3801->partner);
    410	typec_unregister_port(wusb3801->port);
    411
    412	if (wusb3801->vbus_on)
    413		regulator_disable(wusb3801->vbus_supply);
    414
    415	return 0;
    416}
    417
    418static const struct of_device_id wusb3801_of_match[] = {
    419	{ .compatible = "willsemi,wusb3801" },
    420	{}
    421};
    422MODULE_DEVICE_TABLE(of, wusb3801_of_match);
    423
    424static struct i2c_driver wusb3801_driver = {
    425	.probe_new	= wusb3801_probe,
    426	.remove		= wusb3801_remove,
    427	.driver		= {
    428		.name		= "wusb3801",
    429		.of_match_table	= wusb3801_of_match,
    430	},
    431};
    432
    433module_i2c_driver(wusb3801_driver);
    434
    435MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
    436MODULE_DESCRIPTION("Willsemi WUSB3801 Type-C port controller driver");
    437MODULE_LICENSE("GPL");