xref: /linux/drivers/usb/typec/mux/tusb1046.c (revision 60675d4ca1ef0857e44eba5849b74a3a998d0c0f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Driver for the TUSB1046-DCI USB Type-C crosspoint switch
4  *
5  * Copyright (C) 2024 Bootlin
6  */
7 
8 #include <linux/bits.h>
9 #include <linux/i2c.h>
10 #include <linux/usb/typec_mux.h>
11 #include <linux/usb/typec_dp.h>
12 #include <linux/usb/typec_altmode.h>
13 #include <linux/module.h>
14 #include <linux/mod_devicetable.h>
15 #include <linux/err.h>
16 #include <linux/of_device.h>
17 #include <linux/device.h>
18 #include <linux/mutex.h>
19 
20 #define TUSB1046_REG_GENERAL 0xa
21 
22 /* General register bits */
23 #define TUSB1046_GENERAL_FLIPSEL BIT(2)
24 #define TUSB1046_GENERAL_CTLSEL  GENMASK(1, 0)
25 
26 /* Mux modes */
27 #define TUSB1046_CTLSEL_DISABLED          0x0
28 #define TUSB1046_CTLSEL_USB3              0x1
29 #define TUSB1046_CTLSEL_4LANE_DP          0x2
30 #define TUSB1046_CTLSEL_USB3_AND_2LANE_DP 0x3
31 
32 struct tusb1046_priv {
33 	struct i2c_client *client;
34 	struct typec_switch_dev *sw;
35 	struct typec_mux_dev *mux;
36 
37 	/* Lock General register during accesses */
38 	struct mutex general_reg_lock;
39 };
40 
41 static int tusb1046_mux_set(struct typec_mux_dev *mux,
42 			    struct typec_mux_state *state)
43 {
44 	struct tusb1046_priv *priv = typec_mux_get_drvdata(mux);
45 	struct i2c_client *client = priv->client;
46 	struct device *dev = &client->dev;
47 	int mode, val, ret = 0;
48 
49 	if (state->mode >= TYPEC_STATE_MODAL &&
50 	    state->alt->svid != USB_TYPEC_DP_SID)
51 		return -EINVAL;
52 
53 	dev_dbg(dev, "mux mode requested: %lu\n", state->mode);
54 
55 	mutex_lock(&priv->general_reg_lock);
56 
57 	val = i2c_smbus_read_byte_data(client, TUSB1046_REG_GENERAL);
58 	if (val < 0) {
59 		dev_err(dev, "failed to read ctlsel status, err %d\n", val);
60 		ret = val;
61 		goto out_unlock;
62 	}
63 
64 	switch (state->mode) {
65 	case TYPEC_STATE_USB:
66 		mode = TUSB1046_CTLSEL_USB3;
67 		break;
68 	case TYPEC_DP_STATE_C:
69 	case TYPEC_DP_STATE_E:
70 		mode = TUSB1046_CTLSEL_4LANE_DP;
71 		break;
72 	case TYPEC_DP_STATE_D:
73 		mode = TUSB1046_CTLSEL_USB3_AND_2LANE_DP;
74 		break;
75 	case TYPEC_STATE_SAFE:
76 	default:
77 		mode = TUSB1046_CTLSEL_DISABLED;
78 		break;
79 	}
80 
81 	val &= ~TUSB1046_GENERAL_CTLSEL;
82 	val |= mode;
83 
84 	ret = i2c_smbus_write_byte_data(client, TUSB1046_REG_GENERAL, val);
85 
86 out_unlock:
87 	mutex_unlock(&priv->general_reg_lock);
88 	return ret;
89 }
90 
91 static int tusb1046_switch_set(struct typec_switch_dev *sw,
92 			       enum typec_orientation orientation)
93 {
94 	struct tusb1046_priv *priv = typec_switch_get_drvdata(sw);
95 	struct i2c_client *client = priv->client;
96 	struct device *dev = &client->dev;
97 	int val, ret = 0;
98 
99 	dev_dbg(dev, "setting USB3.0 lane flip for orientation %d\n", orientation);
100 
101 	mutex_lock(&priv->general_reg_lock);
102 
103 	val = i2c_smbus_read_byte_data(client, TUSB1046_REG_GENERAL);
104 	if (val < 0) {
105 		dev_err(dev, "failed to read flipsel status, err %d\n", val);
106 		ret = val;
107 		goto out_unlock;
108 	}
109 
110 	if (orientation == TYPEC_ORIENTATION_REVERSE)
111 		val |= TUSB1046_GENERAL_FLIPSEL;
112 	else
113 		val &= ~TUSB1046_GENERAL_FLIPSEL;
114 
115 	ret = i2c_smbus_write_byte_data(client, TUSB1046_REG_GENERAL, val);
116 
117 out_unlock:
118 	mutex_unlock(&priv->general_reg_lock);
119 	return ret;
120 }
121 
122 static int tusb1046_i2c_probe(struct i2c_client *client)
123 {
124 	struct typec_switch_desc sw_desc = { };
125 	struct typec_mux_desc mux_desc = { };
126 	struct device *dev = &client->dev;
127 	struct tusb1046_priv *priv;
128 	int ret = 0;
129 
130 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
131 	if (!priv)
132 		return dev_err_probe(dev, -ENOMEM, "failed to allocate driver data\n");
133 
134 	priv->client = client;
135 
136 	mutex_init(&priv->general_reg_lock);
137 
138 	sw_desc.drvdata = priv;
139 	sw_desc.fwnode = dev_fwnode(dev);
140 	sw_desc.set = tusb1046_switch_set;
141 
142 	priv->sw = typec_switch_register(dev, &sw_desc);
143 	if (IS_ERR(priv->sw)) {
144 		ret = dev_err_probe(dev, PTR_ERR(priv->sw), "failed to register type-c switch\n");
145 		goto err_destroy_mutex;
146 	}
147 
148 	mux_desc.drvdata = priv;
149 	mux_desc.fwnode = dev_fwnode(dev);
150 	mux_desc.set = tusb1046_mux_set;
151 
152 	priv->mux = typec_mux_register(dev, &mux_desc);
153 	if (IS_ERR(priv->mux)) {
154 		ret = dev_err_probe(dev, PTR_ERR(priv->mux), "failed to register type-c mux\n");
155 		goto err_unregister_switch;
156 	}
157 
158 	i2c_set_clientdata(client, priv);
159 
160 	return 0;
161 
162 err_unregister_switch:
163 	typec_switch_unregister(priv->sw);
164 err_destroy_mutex:
165 	mutex_destroy(&priv->general_reg_lock);
166 	return ret;
167 }
168 
169 static void tusb1046_i2c_remove(struct i2c_client *client)
170 {
171 	struct tusb1046_priv *priv = i2c_get_clientdata(client);
172 
173 	typec_switch_unregister(priv->sw);
174 	typec_mux_unregister(priv->mux);
175 	mutex_destroy(&priv->general_reg_lock);
176 }
177 
178 static const struct of_device_id tusb1046_match_table[] = {
179 	{.compatible = "ti,tusb1046"},
180 	{},
181 };
182 
183 static struct i2c_driver tusb1046_driver = {
184 	.driver = {
185 		.name = "tusb1046",
186 		.of_match_table = tusb1046_match_table,
187 	},
188 	.probe = tusb1046_i2c_probe,
189 	.remove = tusb1046_i2c_remove,
190 };
191 
192 module_i2c_driver(tusb1046_driver);
193 
194 MODULE_DESCRIPTION("TUSB1046 USB Type-C switch driver");
195 MODULE_AUTHOR("Romain Gantois <romain.gantois@bootlin.com>");
196 MODULE_LICENSE("GPL");
197