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

extcon-usbc-tusb320.c (8519B)


      1// SPDX-License-Identifier: GPL-2.0
      2/**
      3 * drivers/extcon/extcon-tusb320.c - TUSB320 extcon driver
      4 *
      5 * Copyright (C) 2020 National Instruments Corporation
      6 * Author: Michael Auchter <michael.auchter@ni.com>
      7 */
      8
      9#include <linux/extcon-provider.h>
     10#include <linux/i2c.h>
     11#include <linux/init.h>
     12#include <linux/interrupt.h>
     13#include <linux/kernel.h>
     14#include <linux/module.h>
     15#include <linux/regmap.h>
     16
     17#define TUSB320_REG9				0x9
     18#define TUSB320_REG9_ATTACHED_STATE_SHIFT	6
     19#define TUSB320_REG9_ATTACHED_STATE_MASK	0x3
     20#define TUSB320_REG9_CABLE_DIRECTION		BIT(5)
     21#define TUSB320_REG9_INTERRUPT_STATUS		BIT(4)
     22
     23#define TUSB320_REGA				0xa
     24#define TUSB320L_REGA_DISABLE_TERM		BIT(0)
     25#define TUSB320_REGA_I2C_SOFT_RESET		BIT(3)
     26#define TUSB320_REGA_MODE_SELECT_SHIFT		4
     27#define TUSB320_REGA_MODE_SELECT_MASK		0x3
     28
     29#define TUSB320L_REGA0_REVISION			0xa0
     30
     31enum tusb320_attached_state {
     32	TUSB320_ATTACHED_STATE_NONE,
     33	TUSB320_ATTACHED_STATE_DFP,
     34	TUSB320_ATTACHED_STATE_UFP,
     35	TUSB320_ATTACHED_STATE_ACC,
     36};
     37
     38enum tusb320_mode {
     39	TUSB320_MODE_PORT,
     40	TUSB320_MODE_UFP,
     41	TUSB320_MODE_DFP,
     42	TUSB320_MODE_DRP,
     43};
     44
     45struct tusb320_priv;
     46
     47struct tusb320_ops {
     48	int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode);
     49	int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision);
     50};
     51
     52struct tusb320_priv {
     53	struct device *dev;
     54	struct regmap *regmap;
     55	struct extcon_dev *edev;
     56	struct tusb320_ops *ops;
     57	enum tusb320_attached_state state;
     58};
     59
     60static const char * const tusb_attached_states[] = {
     61	[TUSB320_ATTACHED_STATE_NONE] = "not attached",
     62	[TUSB320_ATTACHED_STATE_DFP]  = "downstream facing port",
     63	[TUSB320_ATTACHED_STATE_UFP]  = "upstream facing port",
     64	[TUSB320_ATTACHED_STATE_ACC]  = "accessory",
     65};
     66
     67static const unsigned int tusb320_extcon_cable[] = {
     68	EXTCON_USB,
     69	EXTCON_USB_HOST,
     70	EXTCON_NONE,
     71};
     72
     73static int tusb320_check_signature(struct tusb320_priv *priv)
     74{
     75	static const char sig[] = { '\0', 'T', 'U', 'S', 'B', '3', '2', '0' };
     76	unsigned val;
     77	int i, ret;
     78
     79	for (i = 0; i < sizeof(sig); i++) {
     80		ret = regmap_read(priv->regmap, sizeof(sig) - 1 - i, &val);
     81		if (ret < 0)
     82			return ret;
     83		if (val != sig[i]) {
     84			dev_err(priv->dev, "signature mismatch!\n");
     85			return -ENODEV;
     86		}
     87	}
     88
     89	return 0;
     90}
     91
     92static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
     93{
     94	int ret;
     95
     96	/* Mode cannot be changed while cable is attached */
     97	if (priv->state != TUSB320_ATTACHED_STATE_NONE)
     98		return -EBUSY;
     99
    100	/* Write mode */
    101	ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
    102		TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
    103		mode << TUSB320_REGA_MODE_SELECT_SHIFT);
    104	if (ret) {
    105		dev_err(priv->dev, "failed to write mode: %d\n", ret);
    106		return ret;
    107	}
    108
    109	return 0;
    110}
    111
    112static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
    113{
    114	int ret;
    115
    116	/* Disable CC state machine */
    117	ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
    118		TUSB320L_REGA_DISABLE_TERM, 1);
    119	if (ret) {
    120		dev_err(priv->dev,
    121			"failed to disable CC state machine: %d\n", ret);
    122		return ret;
    123	}
    124
    125	/* Write mode */
    126	ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
    127		TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
    128		mode << TUSB320_REGA_MODE_SELECT_SHIFT);
    129	if (ret) {
    130		dev_err(priv->dev, "failed to write mode: %d\n", ret);
    131		goto err;
    132	}
    133
    134	msleep(5);
    135err:
    136	/* Re-enable CC state machine */
    137	ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
    138		TUSB320L_REGA_DISABLE_TERM, 0);
    139	if (ret)
    140		dev_err(priv->dev,
    141			"failed to re-enable CC state machine: %d\n", ret);
    142
    143	return ret;
    144}
    145
    146static int tusb320_reset(struct tusb320_priv *priv)
    147{
    148	int ret;
    149
    150	/* Set mode to default (follow PORT pin) */
    151	ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT);
    152	if (ret && ret != -EBUSY) {
    153		dev_err(priv->dev,
    154			"failed to set mode to PORT: %d\n", ret);
    155		return ret;
    156	}
    157
    158	/* Perform soft reset */
    159	ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
    160			TUSB320_REGA_I2C_SOFT_RESET, 1);
    161	if (ret) {
    162		dev_err(priv->dev,
    163			"failed to write soft reset bit: %d\n", ret);
    164		return ret;
    165	}
    166
    167	/* Wait for chip to go through reset */
    168	msleep(95);
    169
    170	return 0;
    171}
    172
    173static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision)
    174{
    175	return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision);
    176}
    177
    178static struct tusb320_ops tusb320_ops = {
    179	.set_mode = tusb320_set_mode,
    180};
    181
    182static struct tusb320_ops tusb320l_ops = {
    183	.set_mode = tusb320l_set_mode,
    184	.get_revision = tusb320l_get_revision,
    185};
    186
    187static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
    188{
    189	struct tusb320_priv *priv = dev_id;
    190	int state, polarity;
    191	unsigned reg;
    192
    193	if (regmap_read(priv->regmap, TUSB320_REG9, &reg)) {
    194		dev_err(priv->dev, "error during i2c read!\n");
    195		return IRQ_NONE;
    196	}
    197
    198	if (!(reg & TUSB320_REG9_INTERRUPT_STATUS))
    199		return IRQ_NONE;
    200
    201	state = (reg >> TUSB320_REG9_ATTACHED_STATE_SHIFT) &
    202		TUSB320_REG9_ATTACHED_STATE_MASK;
    203	polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION);
    204
    205	dev_dbg(priv->dev, "attached state: %s, polarity: %d\n",
    206		tusb_attached_states[state], polarity);
    207
    208	extcon_set_state(priv->edev, EXTCON_USB,
    209			 state == TUSB320_ATTACHED_STATE_UFP);
    210	extcon_set_state(priv->edev, EXTCON_USB_HOST,
    211			 state == TUSB320_ATTACHED_STATE_DFP);
    212	extcon_set_property(priv->edev, EXTCON_USB,
    213			    EXTCON_PROP_USB_TYPEC_POLARITY,
    214			    (union extcon_property_value)polarity);
    215	extcon_set_property(priv->edev, EXTCON_USB_HOST,
    216			    EXTCON_PROP_USB_TYPEC_POLARITY,
    217			    (union extcon_property_value)polarity);
    218	extcon_sync(priv->edev, EXTCON_USB);
    219	extcon_sync(priv->edev, EXTCON_USB_HOST);
    220
    221	priv->state = state;
    222
    223	regmap_write(priv->regmap, TUSB320_REG9, reg);
    224
    225	return IRQ_HANDLED;
    226}
    227
    228static const struct regmap_config tusb320_regmap_config = {
    229	.reg_bits = 8,
    230	.val_bits = 8,
    231};
    232
    233static int tusb320_extcon_probe(struct i2c_client *client,
    234				const struct i2c_device_id *id)
    235{
    236	struct tusb320_priv *priv;
    237	const void *match_data;
    238	unsigned int revision;
    239	int ret;
    240
    241	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    242	if (!priv)
    243		return -ENOMEM;
    244	priv->dev = &client->dev;
    245
    246	priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config);
    247	if (IS_ERR(priv->regmap))
    248		return PTR_ERR(priv->regmap);
    249
    250	ret = tusb320_check_signature(priv);
    251	if (ret)
    252		return ret;
    253
    254	match_data = device_get_match_data(&client->dev);
    255	if (!match_data)
    256		return -EINVAL;
    257
    258	priv->ops = (struct tusb320_ops*)match_data;
    259
    260	priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable);
    261	if (IS_ERR(priv->edev)) {
    262		dev_err(priv->dev, "failed to allocate extcon device\n");
    263		return PTR_ERR(priv->edev);
    264	}
    265
    266	if (priv->ops->get_revision) {
    267		ret = priv->ops->get_revision(priv, &revision);
    268		if (ret)
    269			dev_warn(priv->dev,
    270				"failed to read revision register: %d\n", ret);
    271		else
    272			dev_info(priv->dev, "chip revision %d\n", revision);
    273	}
    274
    275	ret = devm_extcon_dev_register(priv->dev, priv->edev);
    276	if (ret < 0) {
    277		dev_err(priv->dev, "failed to register extcon device\n");
    278		return ret;
    279	}
    280
    281	extcon_set_property_capability(priv->edev, EXTCON_USB,
    282				       EXTCON_PROP_USB_TYPEC_POLARITY);
    283	extcon_set_property_capability(priv->edev, EXTCON_USB_HOST,
    284				       EXTCON_PROP_USB_TYPEC_POLARITY);
    285
    286	/* update initial state */
    287	tusb320_irq_handler(client->irq, priv);
    288
    289	/* Reset chip to its default state */
    290	ret = tusb320_reset(priv);
    291	if (ret)
    292		dev_warn(priv->dev, "failed to reset chip: %d\n", ret);
    293	else
    294		/*
    295		 * State and polarity might change after a reset, so update
    296		 * them again and make sure the interrupt status bit is cleared.
    297		 */
    298		tusb320_irq_handler(client->irq, priv);
    299
    300	ret = devm_request_threaded_irq(priv->dev, client->irq, NULL,
    301					tusb320_irq_handler,
    302					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
    303					client->name, priv);
    304
    305	return ret;
    306}
    307
    308static const struct of_device_id tusb320_extcon_dt_match[] = {
    309	{ .compatible = "ti,tusb320", .data = &tusb320_ops, },
    310	{ .compatible = "ti,tusb320l", .data = &tusb320l_ops, },
    311	{ }
    312};
    313MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match);
    314
    315static struct i2c_driver tusb320_extcon_driver = {
    316	.probe		= tusb320_extcon_probe,
    317	.driver		= {
    318		.name	= "extcon-tusb320",
    319		.of_match_table = tusb320_extcon_dt_match,
    320	},
    321};
    322
    323static int __init tusb320_init(void)
    324{
    325	return i2c_add_driver(&tusb320_extcon_driver);
    326}
    327subsys_initcall(tusb320_init);
    328
    329static void __exit tusb320_exit(void)
    330{
    331	i2c_del_driver(&tusb320_extcon_driver);
    332}
    333module_exit(tusb320_exit);
    334
    335MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>");
    336MODULE_DESCRIPTION("TI TUSB320 extcon driver");
    337MODULE_LICENSE("GPL v2");