Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Mar 24-27, 2025, special US time zones
Register
Loading...
Note: File does not exist in v5.9.
  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#define TUSB320_ATTACHED_STATE_NONE		0x0
 23#define TUSB320_ATTACHED_STATE_DFP		0x1
 24#define TUSB320_ATTACHED_STATE_UFP		0x2
 25#define TUSB320_ATTACHED_STATE_ACC		0x3
 26
 27struct tusb320_priv {
 28	struct device *dev;
 29	struct regmap *regmap;
 30	struct extcon_dev *edev;
 31};
 32
 33static const char * const tusb_attached_states[] = {
 34	[TUSB320_ATTACHED_STATE_NONE] = "not attached",
 35	[TUSB320_ATTACHED_STATE_DFP]  = "downstream facing port",
 36	[TUSB320_ATTACHED_STATE_UFP]  = "upstream facing port",
 37	[TUSB320_ATTACHED_STATE_ACC]  = "accessory",
 38};
 39
 40static const unsigned int tusb320_extcon_cable[] = {
 41	EXTCON_USB,
 42	EXTCON_USB_HOST,
 43	EXTCON_NONE,
 44};
 45
 46static int tusb320_check_signature(struct tusb320_priv *priv)
 47{
 48	static const char sig[] = { '\0', 'T', 'U', 'S', 'B', '3', '2', '0' };
 49	unsigned val;
 50	int i, ret;
 51
 52	for (i = 0; i < sizeof(sig); i++) {
 53		ret = regmap_read(priv->regmap, sizeof(sig) - 1 - i, &val);
 54		if (ret < 0)
 55			return ret;
 56		if (val != sig[i]) {
 57			dev_err(priv->dev, "signature mismatch!\n");
 58			return -ENODEV;
 59		}
 60	}
 61
 62	return 0;
 63}
 64
 65static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
 66{
 67	struct tusb320_priv *priv = dev_id;
 68	int state, polarity;
 69	unsigned reg;
 70
 71	if (regmap_read(priv->regmap, TUSB320_REG9, &reg)) {
 72		dev_err(priv->dev, "error during i2c read!\n");
 73		return IRQ_NONE;
 74	}
 75
 76	if (!(reg & TUSB320_REG9_INTERRUPT_STATUS))
 77		return IRQ_NONE;
 78
 79	state = (reg >> TUSB320_REG9_ATTACHED_STATE_SHIFT) &
 80		TUSB320_REG9_ATTACHED_STATE_MASK;
 81	polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION);
 82
 83	dev_dbg(priv->dev, "attached state: %s, polarity: %d\n",
 84		tusb_attached_states[state], polarity);
 85
 86	extcon_set_state(priv->edev, EXTCON_USB,
 87			 state == TUSB320_ATTACHED_STATE_UFP);
 88	extcon_set_state(priv->edev, EXTCON_USB_HOST,
 89			 state == TUSB320_ATTACHED_STATE_DFP);
 90	extcon_set_property(priv->edev, EXTCON_USB,
 91			    EXTCON_PROP_USB_TYPEC_POLARITY,
 92			    (union extcon_property_value)polarity);
 93	extcon_set_property(priv->edev, EXTCON_USB_HOST,
 94			    EXTCON_PROP_USB_TYPEC_POLARITY,
 95			    (union extcon_property_value)polarity);
 96	extcon_sync(priv->edev, EXTCON_USB);
 97	extcon_sync(priv->edev, EXTCON_USB_HOST);
 98
 99	regmap_write(priv->regmap, TUSB320_REG9, reg);
100
101	return IRQ_HANDLED;
102}
103
104static const struct regmap_config tusb320_regmap_config = {
105	.reg_bits = 8,
106	.val_bits = 8,
107};
108
109static int tusb320_extcon_probe(struct i2c_client *client,
110				const struct i2c_device_id *id)
111{
112	struct tusb320_priv *priv;
113	int ret;
114
115	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
116	if (!priv)
117		return -ENOMEM;
118	priv->dev = &client->dev;
119
120	priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config);
121	if (IS_ERR(priv->regmap))
122		return PTR_ERR(priv->regmap);
123
124	ret = tusb320_check_signature(priv);
125	if (ret)
126		return ret;
127
128	priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable);
129	if (IS_ERR(priv->edev)) {
130		dev_err(priv->dev, "failed to allocate extcon device\n");
131		return PTR_ERR(priv->edev);
132	}
133
134	ret = devm_extcon_dev_register(priv->dev, priv->edev);
135	if (ret < 0) {
136		dev_err(priv->dev, "failed to register extcon device\n");
137		return ret;
138	}
139
140	extcon_set_property_capability(priv->edev, EXTCON_USB,
141				       EXTCON_PROP_USB_TYPEC_POLARITY);
142	extcon_set_property_capability(priv->edev, EXTCON_USB_HOST,
143				       EXTCON_PROP_USB_TYPEC_POLARITY);
144
145	/* update initial state */
146	tusb320_irq_handler(client->irq, priv);
147
148	ret = devm_request_threaded_irq(priv->dev, client->irq, NULL,
149					tusb320_irq_handler,
150					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
151					client->name, priv);
152
153	return ret;
154}
155
156static const struct of_device_id tusb320_extcon_dt_match[] = {
157	{ .compatible = "ti,tusb320", },
158	{ }
159};
160MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match);
161
162static struct i2c_driver tusb320_extcon_driver = {
163	.probe		= tusb320_extcon_probe,
164	.driver		= {
165		.name	= "extcon-tusb320",
166		.of_match_table = tusb320_extcon_dt_match,
167	},
168};
169
170static int __init tusb320_init(void)
171{
172	return i2c_add_driver(&tusb320_extcon_driver);
173}
174subsys_initcall(tusb320_init);
175
176static void __exit tusb320_exit(void)
177{
178	i2c_del_driver(&tusb320_extcon_driver);
179}
180module_exit(tusb320_exit);
181
182MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>");
183MODULE_DESCRIPTION("TI TUSB320 extcon driver");
184MODULE_LICENSE("GPL v2");