brintos

brintos / linux-shallow public Read only

0
0
Text · 16.1 KiB · 2eab341 Raw
626 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * drivers/extcon/extcon-tusb320.c - TUSB320 extcon driver4 *5 * Copyright (C) 2020 National Instruments Corporation6 * Author: Michael Auchter <michael.auchter@ni.com>7 */8 9#include <linux/bitfield.h>10#include <linux/extcon-provider.h>11#include <linux/i2c.h>12#include <linux/init.h>13#include <linux/interrupt.h>14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/regmap.h>17#include <linux/usb/typec.h>18#include <linux/usb/typec_altmode.h>19#include <linux/usb/role.h>20#include <linux/irq.h>21 22#define TUSB320_REG8				0x823#define TUSB320_REG8_CURRENT_MODE_ADVERTISE	GENMASK(7, 6)24#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB	0x025#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A	0x126#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A	0x227#define TUSB320_REG8_CURRENT_MODE_DETECT	GENMASK(5, 4)28#define TUSB320_REG8_CURRENT_MODE_DETECT_DEF	0x029#define TUSB320_REG8_CURRENT_MODE_DETECT_MED	0x130#define TUSB320_REG8_CURRENT_MODE_DETECT_ACC	0x231#define TUSB320_REG8_CURRENT_MODE_DETECT_HI	0x332#define TUSB320_REG8_ACCESSORY_CONNECTED	GENMASK(3, 1)33#define TUSB320_REG8_ACCESSORY_CONNECTED_NONE	0x034#define TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO	0x435#define TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG	0x536#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP	0x637#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP	0x738#define TUSB320_REG8_ACTIVE_CABLE_DETECTION	BIT(0)39 40#define TUSB320_REG9				0x941#define TUSB320_REG9_ATTACHED_STATE		GENMASK(7, 6)42#define TUSB320_REG9_CABLE_DIRECTION		BIT(5)43#define TUSB320_REG9_INTERRUPT_STATUS		BIT(4)44 45#define TUSB320_REGA				0xa46#define TUSB320L_REGA_DISABLE_TERM		BIT(0)47#define TUSB320_REGA_I2C_SOFT_RESET		BIT(3)48#define TUSB320_REGA_MODE_SELECT_SHIFT		449#define TUSB320_REGA_MODE_SELECT_MASK		0x350 51#define TUSB320L_REGA0_REVISION			0xa052 53enum tusb320_attached_state {54	TUSB320_ATTACHED_STATE_NONE,55	TUSB320_ATTACHED_STATE_DFP,56	TUSB320_ATTACHED_STATE_UFP,57	TUSB320_ATTACHED_STATE_ACC,58};59 60enum tusb320_mode {61	TUSB320_MODE_PORT,62	TUSB320_MODE_UFP,63	TUSB320_MODE_DFP,64	TUSB320_MODE_DRP,65};66 67struct tusb320_priv;68 69struct tusb320_ops {70	int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode);71	int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision);72};73 74struct tusb320_priv {75	struct device *dev;76	struct regmap *regmap;77	struct extcon_dev *edev;78	struct tusb320_ops *ops;79	enum tusb320_attached_state state;80	struct typec_port *port;81	struct typec_capability	cap;82	enum typec_port_type port_type;83	enum typec_pwr_opmode pwr_opmode;84	struct fwnode_handle *connector_fwnode;85	struct usb_role_switch *role_sw;86};87 88static const char * const tusb_attached_states[] = {89	[TUSB320_ATTACHED_STATE_NONE] = "not attached",90	[TUSB320_ATTACHED_STATE_DFP]  = "downstream facing port",91	[TUSB320_ATTACHED_STATE_UFP]  = "upstream facing port",92	[TUSB320_ATTACHED_STATE_ACC]  = "accessory",93};94 95static const unsigned int tusb320_extcon_cable[] = {96	EXTCON_USB,97	EXTCON_USB_HOST,98	EXTCON_NONE,99};100 101static int tusb320_check_signature(struct tusb320_priv *priv)102{103	static const char sig[] = { '\0', 'T', 'U', 'S', 'B', '3', '2', '0' };104	unsigned val;105	int i, ret;106 107	for (i = 0; i < sizeof(sig); i++) {108		ret = regmap_read(priv->regmap, sizeof(sig) - 1 - i, &val);109		if (ret < 0)110			return ret;111		if (val != sig[i]) {112			dev_err(priv->dev, "signature mismatch!\n");113			return -ENODEV;114		}115	}116 117	return 0;118}119 120static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)121{122	int ret;123 124	/* Mode cannot be changed while cable is attached */125	if (priv->state != TUSB320_ATTACHED_STATE_NONE)126		return -EBUSY;127 128	/* Write mode */129	ret = regmap_write_bits(priv->regmap, TUSB320_REGA,130		TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,131		mode << TUSB320_REGA_MODE_SELECT_SHIFT);132	if (ret) {133		dev_err(priv->dev, "failed to write mode: %d\n", ret);134		return ret;135	}136 137	return 0;138}139 140static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)141{142	int ret;143 144	/* Disable CC state machine */145	ret = regmap_write_bits(priv->regmap, TUSB320_REGA,146		TUSB320L_REGA_DISABLE_TERM, 1);147	if (ret) {148		dev_err(priv->dev,149			"failed to disable CC state machine: %d\n", ret);150		return ret;151	}152 153	/* Write mode */154	ret = regmap_write_bits(priv->regmap, TUSB320_REGA,155		TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,156		mode << TUSB320_REGA_MODE_SELECT_SHIFT);157	if (ret) {158		dev_err(priv->dev, "failed to write mode: %d\n", ret);159		goto err;160	}161 162	msleep(5);163err:164	/* Re-enable CC state machine */165	ret = regmap_write_bits(priv->regmap, TUSB320_REGA,166		TUSB320L_REGA_DISABLE_TERM, 0);167	if (ret)168		dev_err(priv->dev,169			"failed to re-enable CC state machine: %d\n", ret);170 171	return ret;172}173 174static int tusb320_reset(struct tusb320_priv *priv)175{176	int ret;177 178	/* Set mode to default (follow PORT pin) */179	ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT);180	if (ret && ret != -EBUSY) {181		dev_err(priv->dev,182			"failed to set mode to PORT: %d\n", ret);183		return ret;184	}185 186	/* Perform soft reset */187	ret = regmap_write_bits(priv->regmap, TUSB320_REGA,188			TUSB320_REGA_I2C_SOFT_RESET, 1);189	if (ret) {190		dev_err(priv->dev,191			"failed to write soft reset bit: %d\n", ret);192		return ret;193	}194 195	/* Wait for chip to go through reset */196	msleep(95);197 198	return 0;199}200 201static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision)202{203	return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision);204}205 206static struct tusb320_ops tusb320_ops = {207	.set_mode = tusb320_set_mode,208};209 210static struct tusb320_ops tusb320l_ops = {211	.set_mode = tusb320l_set_mode,212	.get_revision = tusb320l_get_revision,213};214 215static int tusb320_set_adv_pwr_mode(struct tusb320_priv *priv)216{217	u8 mode;218 219	if (priv->pwr_opmode == TYPEC_PWR_MODE_USB)220		mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB;221	else if (priv->pwr_opmode == TYPEC_PWR_MODE_1_5A)222		mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A;223	else if (priv->pwr_opmode == TYPEC_PWR_MODE_3_0A)224		mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A;225	else	/* No other mode is supported. */226		return -EINVAL;227 228	return regmap_write_bits(priv->regmap, TUSB320_REG8,229				 TUSB320_REG8_CURRENT_MODE_ADVERTISE,230				 FIELD_PREP(TUSB320_REG8_CURRENT_MODE_ADVERTISE,231					    mode));232}233 234static int tusb320_port_type_set(struct typec_port *port,235				 enum typec_port_type type)236{237	struct tusb320_priv *priv = typec_get_drvdata(port);238 239	if (type == TYPEC_PORT_SRC)240		return priv->ops->set_mode(priv, TUSB320_MODE_DFP);241	else if (type == TYPEC_PORT_SNK)242		return priv->ops->set_mode(priv, TUSB320_MODE_UFP);243	else if (type == TYPEC_PORT_DRP)244		return priv->ops->set_mode(priv, TUSB320_MODE_DRP);245	else246		return priv->ops->set_mode(priv, TUSB320_MODE_PORT);247}248 249static const struct typec_operations tusb320_typec_ops = {250	.port_type_set	= tusb320_port_type_set,251};252 253static void tusb320_extcon_irq_handler(struct tusb320_priv *priv, u8 reg)254{255	int state, polarity;256 257	state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg);258	polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION);259 260	dev_dbg(priv->dev, "attached state: %s, polarity: %d\n",261		tusb_attached_states[state], polarity);262 263	extcon_set_state(priv->edev, EXTCON_USB,264			 state == TUSB320_ATTACHED_STATE_UFP);265	extcon_set_state(priv->edev, EXTCON_USB_HOST,266			 state == TUSB320_ATTACHED_STATE_DFP);267	extcon_set_property(priv->edev, EXTCON_USB,268			    EXTCON_PROP_USB_TYPEC_POLARITY,269			    (union extcon_property_value)polarity);270	extcon_set_property(priv->edev, EXTCON_USB_HOST,271			    EXTCON_PROP_USB_TYPEC_POLARITY,272			    (union extcon_property_value)polarity);273	extcon_sync(priv->edev, EXTCON_USB);274	extcon_sync(priv->edev, EXTCON_USB_HOST);275 276	priv->state = state;277}278 279static void tusb320_typec_irq_handler(struct tusb320_priv *priv, u8 reg9)280{281	struct typec_port *port = priv->port;282	struct device *dev = priv->dev;283	int typec_mode;284	enum usb_role usb_role;285	enum typec_role pwr_role;286	enum typec_data_role data_role;287	u8 state, mode, accessory;288	int ret, reg8;289	bool ori;290 291	ret = regmap_read(priv->regmap, TUSB320_REG8, &reg8);292	if (ret) {293		dev_err(dev, "error during reg8 i2c read, ret=%d!\n", ret);294		return;295	}296 297	ori = reg9 & TUSB320_REG9_CABLE_DIRECTION;298	typec_set_orientation(port, ori ? TYPEC_ORIENTATION_REVERSE :299					  TYPEC_ORIENTATION_NORMAL);300 301	state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg9);302	accessory = FIELD_GET(TUSB320_REG8_ACCESSORY_CONNECTED, reg8);303 304	switch (state) {305	case TUSB320_ATTACHED_STATE_DFP:306		typec_mode = TYPEC_MODE_USB2;307		usb_role = USB_ROLE_HOST;308		pwr_role = TYPEC_SOURCE;309		data_role = TYPEC_HOST;310		break;311	case TUSB320_ATTACHED_STATE_UFP:312		typec_mode = TYPEC_MODE_USB2;313		usb_role = USB_ROLE_DEVICE;314		pwr_role = TYPEC_SINK;315		data_role = TYPEC_DEVICE;316		break;317	case TUSB320_ATTACHED_STATE_ACC:318		/*319		 * Accessory detected. For debug accessories, just make some320		 * qualified guesses as to the role for lack of a better option.321		 */322		if (accessory == TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO ||323		    accessory == TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG) {324			typec_mode = TYPEC_MODE_AUDIO;325			usb_role = USB_ROLE_NONE;326			pwr_role = TYPEC_SINK;327			data_role = TYPEC_DEVICE;328			break;329		} else if (accessory ==330			   TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP) {331			typec_mode = TYPEC_MODE_DEBUG;332			pwr_role = TYPEC_SOURCE;333			usb_role = USB_ROLE_HOST;334			data_role = TYPEC_HOST;335			break;336		} else if (accessory ==337			   TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP) {338			typec_mode = TYPEC_MODE_DEBUG;339			pwr_role = TYPEC_SINK;340			usb_role = USB_ROLE_DEVICE;341			data_role = TYPEC_DEVICE;342			break;343		}344 345		dev_warn(priv->dev, "unexpected ACCESSORY_CONNECTED state %d\n",346			 accessory);347 348		fallthrough;349	default:350		typec_mode = TYPEC_MODE_USB2;351		usb_role = USB_ROLE_NONE;352		pwr_role = TYPEC_SINK;353		data_role = TYPEC_DEVICE;354		break;355	}356 357	typec_set_vconn_role(port, pwr_role);358	typec_set_pwr_role(port, pwr_role);359	typec_set_data_role(port, data_role);360	typec_set_mode(port, typec_mode);361	usb_role_switch_set_role(priv->role_sw, usb_role);362 363	mode = FIELD_GET(TUSB320_REG8_CURRENT_MODE_DETECT, reg8);364	if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_DEF)365		typec_set_pwr_opmode(port, TYPEC_PWR_MODE_USB);366	else if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_MED)367		typec_set_pwr_opmode(port, TYPEC_PWR_MODE_1_5A);368	else if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_HI)369		typec_set_pwr_opmode(port, TYPEC_PWR_MODE_3_0A);370	else	/* Charge through accessory */371		typec_set_pwr_opmode(port, TYPEC_PWR_MODE_USB);372}373 374static irqreturn_t tusb320_state_update_handler(struct tusb320_priv *priv,375						bool force_update)376{377	unsigned int reg;378 379	if (regmap_read(priv->regmap, TUSB320_REG9, &reg)) {380		dev_err(priv->dev, "error during i2c read!\n");381		return IRQ_NONE;382	}383 384	if (!force_update && !(reg & TUSB320_REG9_INTERRUPT_STATUS))385		return IRQ_NONE;386 387	tusb320_extcon_irq_handler(priv, reg);388 389	/*390	 * Type-C support is optional. Only call the Type-C handler if a391	 * port had been registered previously.392	 */393	if (priv->port)394		tusb320_typec_irq_handler(priv, reg);395 396	regmap_write(priv->regmap, TUSB320_REG9, reg);397 398	return IRQ_HANDLED;399}400 401static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)402{403	struct tusb320_priv *priv = dev_id;404 405	return tusb320_state_update_handler(priv, false);406}407 408static const struct regmap_config tusb320_regmap_config = {409	.reg_bits = 8,410	.val_bits = 8,411};412 413static int tusb320_extcon_probe(struct tusb320_priv *priv)414{415	int ret;416 417	priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable);418	if (IS_ERR(priv->edev)) {419		dev_err(priv->dev, "failed to allocate extcon device\n");420		return PTR_ERR(priv->edev);421	}422 423	ret = devm_extcon_dev_register(priv->dev, priv->edev);424	if (ret < 0) {425		dev_err(priv->dev, "failed to register extcon device\n");426		return ret;427	}428 429	extcon_set_property_capability(priv->edev, EXTCON_USB,430				       EXTCON_PROP_USB_TYPEC_POLARITY);431	extcon_set_property_capability(priv->edev, EXTCON_USB_HOST,432				       EXTCON_PROP_USB_TYPEC_POLARITY);433 434	return 0;435}436 437static int tusb320_typec_probe(struct i2c_client *client,438			       struct tusb320_priv *priv)439{440	struct fwnode_handle *connector;441	const char *cap_str;442	int ret;443 444	/* The Type-C connector is optional, for backward compatibility. */445	connector = device_get_named_child_node(&client->dev, "connector");446	if (!connector)447		return 0;448 449	/* Type-C connector found. */450	ret = typec_get_fw_cap(&priv->cap, connector);451	if (ret)452		goto err_put;453 454	priv->port_type = priv->cap.type;455 456	/* This goes into register 0x8 field CURRENT_MODE_ADVERTISE */457	ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str);458	if (ret)459		goto err_put;460 461	ret = typec_find_pwr_opmode(cap_str);462	if (ret < 0)463		goto err_put;464 465	priv->pwr_opmode = ret;466 467	/* Initialize the hardware with the devicetree settings. */468	ret = tusb320_set_adv_pwr_mode(priv);469	if (ret)470		goto err_put;471 472	priv->cap.revision		= USB_TYPEC_REV_1_1;473	priv->cap.accessory[0]		= TYPEC_ACCESSORY_AUDIO;474	priv->cap.accessory[1]		= TYPEC_ACCESSORY_DEBUG;475	priv->cap.orientation_aware	= true;476	priv->cap.driver_data		= priv;477	priv->cap.ops			= &tusb320_typec_ops;478	priv->cap.fwnode		= connector;479 480	priv->port = typec_register_port(&client->dev, &priv->cap);481	if (IS_ERR(priv->port)) {482		ret = PTR_ERR(priv->port);483		goto err_put;484	}485 486	/* Find any optional USB role switch that needs reporting to */487	priv->role_sw = fwnode_usb_role_switch_get(connector);488	if (IS_ERR(priv->role_sw)) {489		ret = PTR_ERR(priv->role_sw);490		goto err_unreg;491	}492 493	priv->connector_fwnode = connector;494 495	return 0;496 497err_unreg:498	typec_unregister_port(priv->port);499 500err_put:501	fwnode_handle_put(connector);502 503	return ret;504}505 506static void tusb320_typec_remove(struct tusb320_priv *priv)507{508	usb_role_switch_put(priv->role_sw);509	typec_unregister_port(priv->port);510	fwnode_handle_put(priv->connector_fwnode);511}512 513static int tusb320_probe(struct i2c_client *client)514{515	struct tusb320_priv *priv;516	const void *match_data;517	unsigned int revision;518	int ret;519	u32 irq_trigger_type = IRQF_TRIGGER_FALLING;520	struct irq_data *irq_d;521 522	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);523	if (!priv)524		return -ENOMEM;525 526	priv->dev = &client->dev;527	i2c_set_clientdata(client, priv);528 529	priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config);530	if (IS_ERR(priv->regmap))531		return PTR_ERR(priv->regmap);532 533	ret = tusb320_check_signature(priv);534	if (ret)535		return ret;536 537	match_data = device_get_match_data(&client->dev);538	if (!match_data)539		return -EINVAL;540 541	priv->ops = (struct tusb320_ops*)match_data;542 543	if (priv->ops->get_revision) {544		ret = priv->ops->get_revision(priv, &revision);545		if (ret)546			dev_warn(priv->dev,547				"failed to read revision register: %d\n", ret);548		else549			dev_info(priv->dev, "chip revision %d\n", revision);550	}551 552	ret = tusb320_extcon_probe(priv);553	if (ret)554		return ret;555 556	ret = tusb320_typec_probe(client, priv);557	if (ret)558		return ret;559 560	/* update initial state */561	tusb320_state_update_handler(priv, true);562 563	/* Reset chip to its default state */564	ret = tusb320_reset(priv);565	if (ret)566		dev_warn(priv->dev, "failed to reset chip: %d\n", ret);567	else568		/*569		 * State and polarity might change after a reset, so update570		 * them again and make sure the interrupt status bit is cleared.571		 */572		tusb320_state_update_handler(priv, true);573 574	irq_d = irq_get_irq_data(client->irq);575	if (irq_d)576		irq_trigger_type = irqd_get_trigger_type(irq_d);577 578	ret = devm_request_threaded_irq(priv->dev, client->irq, NULL,579					tusb320_irq_handler,580					IRQF_ONESHOT | irq_trigger_type,581					client->name, priv);582	if (ret)583		tusb320_typec_remove(priv);584 585	return ret;586}587 588static void tusb320_remove(struct i2c_client *client)589{590	struct tusb320_priv *priv = i2c_get_clientdata(client);591 592	tusb320_typec_remove(priv);593}594 595static const struct of_device_id tusb320_extcon_dt_match[] = {596	{ .compatible = "ti,tusb320", .data = &tusb320_ops, },597	{ .compatible = "ti,tusb320l", .data = &tusb320l_ops, },598	{ }599};600MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match);601 602static struct i2c_driver tusb320_extcon_driver = {603	.probe		= tusb320_probe,604	.remove		= tusb320_remove,605	.driver		= {606		.name	= "extcon-tusb320",607		.of_match_table = tusb320_extcon_dt_match,608	},609};610 611static int __init tusb320_init(void)612{613	return i2c_add_driver(&tusb320_extcon_driver);614}615subsys_initcall(tusb320_init);616 617static void __exit tusb320_exit(void)618{619	i2c_del_driver(&tusb320_extcon_driver);620}621module_exit(tusb320_exit);622 623MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>");624MODULE_DESCRIPTION("TI TUSB320 extcon driver");625MODULE_LICENSE("GPL v2");626