]>
Commit | Line | Data |
---|---|---|
03acaa9b JP |
1 | From 8149051c34bc3d4c55adc56d04ffb7f7a04c2fd9 Mon Sep 17 00:00:00 2001 |
2 | From: Dan Johansen <strit@manjaro.org> | |
3 | Date: Sun, 2 Jan 2022 16:45:28 +0100 | |
4 | Subject: [PATCH 1/2] Add megis extcon changes to fusb302 | |
8c9054af | 5 | |
03acaa9b | 6 | Signed-off-by: Dan Johansen <strit@manjaro.org> |
8c9054af | 7 | --- |
03acaa9b JP |
8 | drivers/phy/rockchip/phy-rockchip-typec.c | 5 +++ |
9 | drivers/usb/typec/Kconfig | 7 ++++ | |
10 | drivers/usb/typec/Makefile | 1 + | |
11 | drivers/usb/typec/tcpm/fusb302.c | 47 ++++++++++++++++------- | |
12 | drivers/usb/typec/tcpm/fusb302_reg.h | 16 ++++---- | |
13 | 5 files changed, 53 insertions(+), 23 deletions(-) | |
8c9054af | 14 | |
03acaa9b JP |
15 | diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c |
16 | index d2bbdc96a167..fa10ee9a5794 100644 | |
17 | --- a/drivers/phy/rockchip/phy-rockchip-typec.c | |
18 | +++ b/drivers/phy/rockchip/phy-rockchip-typec.c | |
19 | @@ -350,6 +350,7 @@ struct usb3phy_reg { | |
20 | * struct rockchip_usb3phy_port_cfg - usb3-phy port configuration. | |
21 | * @reg: the base address for usb3-phy config. | |
22 | * @typec_conn_dir: the register of type-c connector direction. | |
23 | + * @typec_conn_dir_sel: the register of type-c connector direction source. | |
24 | * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. | |
25 | * @external_psm: the register of type-c phy external psm clock. | |
26 | * @pipe_status: the register of type-c phy pipe status. | |
27 | @@ -360,6 +361,7 @@ struct usb3phy_reg { | |
28 | struct rockchip_usb3phy_port_cfg { | |
29 | unsigned int reg; | |
30 | struct usb3phy_reg typec_conn_dir; | |
31 | + struct usb3phy_reg typec_conn_dir_sel; | |
32 | struct usb3phy_reg usb3tousb2_en; | |
33 | struct usb3phy_reg external_psm; | |
34 | struct usb3phy_reg pipe_status; | |
35 | @@ -434,6 +436,7 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { | |
36 | { | |
37 | .reg = 0xff7c0000, | |
38 | .typec_conn_dir = { 0xe580, 0, 16 }, | |
39 | + .typec_conn_dir_sel = { 0xe580, 8, 16+8 }, | |
40 | .usb3tousb2_en = { 0xe580, 3, 19 }, | |
41 | .external_psm = { 0xe588, 14, 30 }, | |
42 | .pipe_status = { 0xe5c0, 0, 0 }, | |
43 | @@ -444,6 +447,7 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { | |
44 | { | |
45 | .reg = 0xff800000, | |
46 | .typec_conn_dir = { 0xe58c, 0, 16 }, | |
47 | + .typec_conn_dir_sel = { 0xe58c, 8, 16+8 }, | |
48 | .usb3tousb2_en = { 0xe58c, 3, 19 }, | |
49 | .external_psm = { 0xe594, 14, 30 }, | |
50 | .pipe_status = { 0xe5c0, 16, 16 }, | |
51 | @@ -739,6 +743,7 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) | |
8c9054af | 52 | |
03acaa9b | 53 | reset_control_deassert(tcphy->tcphy_rst); |
8c9054af | 54 | |
03acaa9b JP |
55 | + property_enable(tcphy, &cfg->typec_conn_dir_sel, 0); |
56 | property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip); | |
57 | tcphy_dp_aux_set_flip(tcphy); | |
8c9054af | 58 | |
03acaa9b JP |
59 | diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig |
60 | index ab480f38523a..01ecc5e590f1 100644 | |
61 | --- a/drivers/usb/typec/Kconfig | |
62 | +++ b/drivers/usb/typec/Kconfig | |
63 | @@ -88,6 +88,13 @@ config TYPEC_QCOM_PMIC | |
74b60f45 JP |
64 | If you choose to build this driver as a dynamically linked module, the |
65 | module will be called wusb3801.ko. | |
03acaa9b JP |
66 | |
67 | +config TYPEC_EXTCON | |
68 | + tristate "Type-C switch/mux -> extcon interface bridge driver" | |
69 | + depends on USB_ROLE_SWITCH | |
70 | + help | |
71 | + Say Y or M here if your system needs bridging between typec class | |
72 | + and extcon interfaces. | |
8c9054af | 73 | + |
03acaa9b JP |
74 | source "drivers/usb/typec/mux/Kconfig" |
75 | ||
76 | source "drivers/usb/typec/altmodes/Kconfig" | |
77 | diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile | |
78 | index a0adb8947a30..d9d829386b73 100644 | |
79 | --- a/drivers/usb/typec/Makefile | |
80 | +++ b/drivers/usb/typec/Makefile | |
81 | @@ -8,4 +8,5 @@ obj-$(CONFIG_TYPEC_TPS6598X) += tipd/ | |
03acaa9b | 82 | obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o |
74b60f45 JP |
83 | obj-$(CONFIG_TYPEC_RT1719) += rt1719.o |
84 | obj-$(CONFIG_TYPEC_WUSB3801) += wusb3801.o | |
03acaa9b JP |
85 | +obj-$(CONFIG_TYPEC_EXTCON) += typec-extcon.o |
86 | obj-$(CONFIG_TYPEC) += mux/ | |
87 | diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c | |
88 | index 72f9001b0792..cb26793f90f8 100644 | |
89 | --- a/drivers/usb/typec/tcpm/fusb302.c | |
90 | +++ b/drivers/usb/typec/tcpm/fusb302.c | |
91 | @@ -440,6 +440,16 @@ static int tcpm_get_current_limit(struct tcpc_dev *dev) | |
92 | int current_limit = 0; | |
93 | unsigned long timeout; | |
94 | ||
95 | + /* | |
96 | + * To avoid cycles in OF dependencies, we get extcon when necessary | |
97 | + * outside of probe function. | |
98 | + */ | |
99 | + if (of_property_read_bool(chip->dev->of_node, "extcon") && !chip->extcon) { | |
100 | + chip->extcon = extcon_get_edev_by_phandle(chip->dev, 0); | |
101 | + if (IS_ERR(chip->extcon)) | |
102 | + chip->extcon = NULL; | |
8c9054af JP |
103 | + } |
104 | + | |
03acaa9b JP |
105 | if (!chip->extcon) |
106 | return 0; | |
107 | ||
108 | @@ -498,6 +508,7 @@ static int fusb302_set_toggling(struct fusb302_chip *chip, | |
109 | enum toggling_mode mode) | |
110 | { | |
111 | int ret = 0; | |
112 | + u8 reg; | |
113 | ||
114 | /* first disable toggling */ | |
115 | ret = fusb302_i2c_clear_bits(chip, FUSB_REG_CONTROL2, | |
116 | @@ -556,6 +567,12 @@ static int fusb302_set_toggling(struct fusb302_chip *chip, | |
117 | } else { | |
118 | /* Datasheet says vconn MUST be off when toggling */ | |
119 | WARN(chip->vconn_on, "Vconn is on during toggle start"); | |
8c9054af | 120 | + |
03acaa9b JP |
121 | + /* clear interrupts */ |
122 | + ret = fusb302_i2c_read(chip, FUSB_REG_INTERRUPT, ®); | |
123 | + if (ret < 0) | |
124 | + return ret; | |
8c9054af | 125 | + |
03acaa9b JP |
126 | /* unmask TOGDONE interrupt */ |
127 | ret = fusb302_i2c_clear_bits(chip, FUSB_REG_MASKA, | |
128 | FUSB_REG_MASKA_TOGDONE); | |
129 | @@ -635,6 +652,14 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) | |
130 | goto done; | |
131 | } | |
132 | ||
133 | + /* adjust current for SRC */ | |
134 | + ret = fusb302_set_src_current(chip, cc_src_current[cc]); | |
135 | + if (ret < 0) { | |
136 | + fusb302_log(chip, "cannot set src current %s, ret=%d", | |
137 | + typec_cc_status_name[cc], ret); | |
138 | + goto done; | |
8c9054af | 139 | + } |
8c9054af | 140 | + |
03acaa9b JP |
141 | ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES0, |
142 | switches0_mask, switches0_data); | |
143 | if (ret < 0) { | |
144 | @@ -645,14 +670,6 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) | |
145 | chip->cc1 = TYPEC_CC_OPEN; | |
146 | chip->cc2 = TYPEC_CC_OPEN; | |
8c9054af | 147 | |
03acaa9b JP |
148 | - /* adjust current for SRC */ |
149 | - ret = fusb302_set_src_current(chip, cc_src_current[cc]); | |
150 | - if (ret < 0) { | |
151 | - fusb302_log(chip, "cannot set src current %s, ret=%d", | |
152 | - typec_cc_status_name[cc], ret); | |
153 | - goto done; | |
8c9054af | 154 | - } |
03acaa9b JP |
155 | - |
156 | /* enable/disable interrupts, BC_LVL for SNK and COMP_CHNG for SRC */ | |
157 | switch (cc) { | |
158 | case TYPEC_CC_RP_DEF: | |
159 | @@ -1528,14 +1545,16 @@ static void fusb302_irq_work(struct work_struct *work) | |
160 | "IRQ: 0x%02x, a: 0x%02x, b: 0x%02x, status0: 0x%02x", | |
161 | interrupt, interrupta, interruptb, status0); | |
8c9054af | 162 | |
03acaa9b JP |
163 | - if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) { |
164 | - vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK); | |
165 | + vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK); | |
166 | + if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) | |
167 | fusb302_log(chip, "IRQ: VBUS_OK, vbus=%s", | |
168 | vbus_present ? "On" : "Off"); | |
169 | - if (vbus_present != chip->vbus_present) { | |
170 | - chip->vbus_present = vbus_present; | |
171 | - tcpm_vbus_change(chip->tcpm_port); | |
8c9054af | 172 | - } |
03acaa9b JP |
173 | + if (vbus_present != chip->vbus_present) { |
174 | + chip->vbus_present = vbus_present; | |
175 | + if (!(interrupt & FUSB_REG_INTERRUPT_VBUSOK)) | |
176 | + fusb302_log(chip, "IRQ: VBUS changed without interrupt, vbus=%s", | |
177 | + vbus_present ? "On" : "Off"); | |
178 | + tcpm_vbus_change(chip->tcpm_port); | |
8c9054af | 179 | } |
8c9054af | 180 | |
03acaa9b JP |
181 | if ((interrupta & FUSB_REG_INTERRUPTA_TOGDONE) && intr_togdone) { |
182 | diff --git a/drivers/usb/typec/tcpm/fusb302_reg.h b/drivers/usb/typec/tcpm/fusb302_reg.h | |
183 | index edc0e4b0f1e6..f37d226c5027 100644 | |
184 | --- a/drivers/usb/typec/tcpm/fusb302_reg.h | |
185 | +++ b/drivers/usb/typec/tcpm/fusb302_reg.h | |
186 | @@ -27,14 +27,13 @@ | |
187 | #define FUSB_REG_SWITCHES1_TXCC2_EN BIT(1) | |
188 | #define FUSB_REG_SWITCHES1_TXCC1_EN BIT(0) | |
189 | #define FUSB_REG_MEASURE 0x04 | |
190 | -#define FUSB_REG_MEASURE_MDAC5 BIT(7) | |
191 | -#define FUSB_REG_MEASURE_MDAC4 BIT(6) | |
192 | -#define FUSB_REG_MEASURE_MDAC3 BIT(5) | |
193 | -#define FUSB_REG_MEASURE_MDAC2 BIT(4) | |
194 | -#define FUSB_REG_MEASURE_MDAC1 BIT(3) | |
195 | -#define FUSB_REG_MEASURE_MDAC0 BIT(2) | |
196 | -#define FUSB_REG_MEASURE_VBUS BIT(1) | |
197 | -#define FUSB_REG_MEASURE_XXXX5 BIT(0) | |
198 | +#define FUSB_REG_MEASURE_VBUS BIT(6) | |
199 | +#define FUSB_REG_MEASURE_MDAC5 BIT(5) | |
200 | +#define FUSB_REG_MEASURE_MDAC4 BIT(4) | |
201 | +#define FUSB_REG_MEASURE_MDAC3 BIT(3) | |
202 | +#define FUSB_REG_MEASURE_MDAC2 BIT(2) | |
203 | +#define FUSB_REG_MEASURE_MDAC1 BIT(1) | |
204 | +#define FUSB_REG_MEASURE_MDAC0 BIT(0) | |
205 | #define FUSB_REG_CONTROL0 0x06 | |
206 | #define FUSB_REG_CONTROL0_TX_FLUSH BIT(6) | |
207 | #define FUSB_REG_CONTROL0_INT_MASK BIT(5) | |
208 | @@ -105,7 +104,6 @@ | |
209 | #define FUSB_REG_STATUS0A_RX_SOFT_RESET BIT(1) | |
210 | #define FUSB_REG_STATUS0A_RX_HARD_RESET BIT(0) | |
211 | #define FUSB_REG_STATUS1A 0x3D | |
212 | -#define FUSB_REG_STATUS1A_TOGSS BIT(3) | |
213 | #define FUSB_REG_STATUS1A_TOGSS_RUNNING 0x0 | |
214 | #define FUSB_REG_STATUS1A_TOGSS_SRC1 0x1 | |
215 | #define FUSB_REG_STATUS1A_TOGSS_SRC2 0x2 | |
8c9054af | 216 | -- |
03acaa9b | 217 | 2.34.1 |
8c9054af | 218 | |
03acaa9b JP |
219 | From 6af2e6a2d59bd755234e5e15a47dfa669788143c Mon Sep 17 00:00:00 2001 |
220 | From: Dan Johansen <strit@manjaro.org> | |
221 | Date: Sun, 2 Jan 2022 16:47:40 +0100 | |
222 | Subject: [PATCH 2/2] usb: typec: Add megis typex to extcon bridge driver | |
8c9054af | 223 | |
03acaa9b | 224 | Signed-off-by: Dan Johansen <strit@manjaro.org> |
8c9054af | 225 | --- |
03acaa9b JP |
226 | drivers/usb/typec/typec-extcon.c | 337 +++++++++++++++++++++++++++++++ |
227 | 1 file changed, 337 insertions(+) | |
228 | create mode 100644 drivers/usb/typec/typec-extcon.c | |
8c9054af | 229 | |
03acaa9b JP |
230 | diff --git a/drivers/usb/typec/typec-extcon.c b/drivers/usb/typec/typec-extcon.c |
231 | new file mode 100644 | |
232 | index 000000000000..143ff2486f2f | |
233 | --- /dev/null | |
234 | +++ b/drivers/usb/typec/typec-extcon.c | |
235 | @@ -0,0 +1,337 @@ | |
236 | +/* | |
237 | + * typec -> extcon bridge | |
238 | + * Copyright (c) 2021 Ondřej Jirman <megi@xff.cz> | |
239 | + * | |
240 | + * This driver bridges standard type-c interfaces to drivers that | |
241 | + * expect extcon interface. | |
242 | + */ | |
243 | + | |
244 | +#include <linux/delay.h> | |
245 | +#include <linux/kernel.h> | |
246 | +#include <linux/module.h> | |
247 | +#include <linux/power_supply.h> | |
248 | +#include <linux/platform_device.h> | |
249 | +#include <linux/usb/pd.h> | |
250 | +#include <linux/usb/role.h> | |
251 | +#include <linux/usb/typec.h> | |
252 | +#include <linux/usb/typec_dp.h> | |
253 | +#include <linux/usb/typec_mux.h> | |
254 | +#include <linux/extcon-provider.h> | |
255 | + | |
256 | +struct typec_extcon { | |
257 | + struct device *dev; | |
258 | + | |
259 | + /* consumers */ | |
260 | + struct usb_role_switch *role_sw; | |
0592a12c JP |
261 | + struct typec_switch_dev *sw; |
262 | + struct typec_mux_dev *mux; | |
03acaa9b JP |
263 | + |
264 | + /* providers */ | |
265 | + struct extcon_dev *extcon; | |
266 | + struct notifier_block extcon_nb; | |
267 | + | |
268 | + /* cached state from typec controller */ | |
269 | + enum usb_role role; | |
270 | + enum typec_orientation orientation; | |
271 | + struct typec_altmode alt; | |
272 | + unsigned long mode; | |
273 | + bool has_alt; | |
274 | + struct mutex lock; | |
275 | +}; | |
276 | + | |
277 | +static const unsigned int typec_extcon_cable[] = { | |
278 | + EXTCON_DISP_DP, | |
279 | + | |
280 | + EXTCON_USB, | |
281 | + EXTCON_USB_HOST, | |
282 | + | |
283 | + EXTCON_CHG_USB_SDP, | |
284 | + EXTCON_CHG_USB_CDP, | |
285 | + EXTCON_CHG_USB_DCP, | |
286 | + EXTCON_CHG_USB_ACA, | |
287 | + | |
288 | + EXTCON_NONE, | |
289 | +}; | |
290 | + | |
291 | +static void typec_extcon_set_cable(struct typec_extcon *tce, int id, bool on, | |
292 | + union extcon_property_value prop_ss, | |
293 | + union extcon_property_value prop_or) | |
8c9054af | 294 | +{ |
03acaa9b JP |
295 | + union extcon_property_value cur_ss, cur_or; |
296 | + bool prop_diff = false; | |
297 | + int ret; | |
298 | + | |
299 | + ret = extcon_get_property(tce->extcon, id, | |
300 | + EXTCON_PROP_USB_SS, &cur_ss); | |
301 | + if (ret || cur_ss.intval != prop_ss.intval) | |
302 | + prop_diff = true; | |
303 | + | |
304 | + ret = extcon_get_property(tce->extcon, id, | |
305 | + EXTCON_PROP_USB_TYPEC_POLARITY, &cur_or); | |
306 | + if (ret || cur_or.intval != prop_or.intval) | |
307 | + prop_diff = true; | |
308 | + | |
309 | + if (!on && extcon_get_state(tce->extcon, id)) { | |
310 | + extcon_set_state_sync(tce->extcon, id, false); | |
311 | + } else if (on && (!extcon_get_state(tce->extcon, id) || prop_diff)) { | |
312 | + extcon_set_state(tce->extcon, id, true); | |
313 | + extcon_set_property(tce->extcon, id, | |
314 | + EXTCON_PROP_USB_SS, prop_ss); | |
315 | + extcon_set_property(tce->extcon, id, | |
316 | + EXTCON_PROP_USB_TYPEC_POLARITY, prop_or); | |
317 | + extcon_sync(tce->extcon, id); | |
8c9054af JP |
318 | + } |
319 | +} | |
320 | + | |
03acaa9b | 321 | +static int typec_extcon_sync_extcon(struct typec_extcon *tce) |
8c9054af | 322 | +{ |
03acaa9b JP |
323 | + union extcon_property_value prop_ss, prop_or; |
324 | + bool has_dp = false; | |
8c9054af | 325 | + |
03acaa9b JP |
326 | + mutex_lock(&tce->lock); |
327 | + | |
328 | + /* connector is disconnected */ | |
329 | + if (tce->orientation == TYPEC_ORIENTATION_NONE) { | |
330 | + typec_extcon_set_cable(tce, EXTCON_USB, false, prop_ss, prop_or); | |
331 | + typec_extcon_set_cable(tce, EXTCON_USB_HOST, false, prop_ss, prop_or); | |
332 | + typec_extcon_set_cable(tce, EXTCON_DISP_DP, false, prop_ss, prop_or); | |
333 | + | |
334 | + extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_SDP, false); | |
335 | + extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_DCP, false); | |
336 | + extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_CDP, false); | |
337 | + extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_ACA, false); | |
338 | + | |
339 | + goto out_unlock; | |
8c9054af | 340 | + } |
03acaa9b JP |
341 | + |
342 | + prop_or.intval = tce->orientation == TYPEC_ORIENTATION_NORMAL ? 0 : 1; | |
343 | + prop_ss.intval = 0; | |
344 | + | |
345 | + if (tce->has_alt && tce->alt.svid == USB_TYPEC_DP_SID) { | |
346 | + switch (tce->mode) { | |
347 | + case TYPEC_STATE_SAFE: | |
348 | + break; | |
349 | + case TYPEC_DP_STATE_C: | |
350 | + case TYPEC_DP_STATE_E: | |
351 | + has_dp = true; | |
352 | + break; | |
353 | + case TYPEC_DP_STATE_D: | |
354 | + has_dp = true; | |
355 | + fallthrough; | |
356 | + case TYPEC_STATE_USB: | |
357 | + prop_ss.intval = 1; | |
358 | + break; | |
359 | + default: | |
360 | + dev_err(tce->dev, "unhandled mux mode=%lu\n", tce->mode); | |
361 | + break; | |
362 | + } | |
8c9054af | 363 | + } |
8c9054af | 364 | + |
03acaa9b JP |
365 | + typec_extcon_set_cable(tce, EXTCON_USB, |
366 | + tce->role == USB_ROLE_DEVICE, prop_ss, prop_or); | |
367 | + typec_extcon_set_cable(tce, EXTCON_USB_HOST, | |
368 | + tce->role == USB_ROLE_HOST, prop_ss, prop_or); | |
8c9054af | 369 | + |
03acaa9b JP |
370 | + typec_extcon_set_cable(tce, EXTCON_DISP_DP, has_dp, prop_ss, prop_or); |
371 | + | |
372 | +out_unlock: | |
373 | + mutex_unlock(&tce->lock); | |
374 | + return 0; | |
375 | +} | |
376 | + | |
0592a12c | 377 | +static int typec_extcon_sw_set(struct typec_switch_dev *sw, |
03acaa9b | 378 | + enum typec_orientation orientation) |
8c9054af | 379 | +{ |
03acaa9b JP |
380 | + struct typec_extcon *tce = typec_switch_get_drvdata(sw); |
381 | + | |
382 | + dev_dbg(tce->dev, "SW SET: orientation=%d\n", orientation); | |
383 | + | |
384 | + mutex_lock(&tce->lock); | |
385 | + tce->orientation = orientation; | |
386 | + mutex_unlock(&tce->lock); | |
387 | + | |
388 | + typec_extcon_sync_extcon(tce); | |
389 | + | |
390 | + return 0; | |
8c9054af JP |
391 | +} |
392 | + | |
0592a12c | 393 | +static int typec_extcon_mux_set(struct typec_mux_dev *mux, |
03acaa9b | 394 | + struct typec_mux_state *state) |
8c9054af | 395 | +{ |
03acaa9b JP |
396 | + struct typec_extcon *tce = typec_mux_get_drvdata(mux); |
397 | + struct typec_altmode *alt = state->alt; | |
8c9054af | 398 | + |
03acaa9b JP |
399 | + dev_dbg(tce->dev, "MUX SET: state->mode=%lu\n", state->mode); |
400 | + if (alt) | |
401 | + dev_dbg(tce->dev, " ...alt: svid=%04hx mode=%d vdo=%08x active=%u\n", | |
402 | + alt->svid, alt->mode, alt->vdo, alt->active); | |
403 | + | |
404 | + mutex_lock(&tce->lock); | |
405 | + tce->mode = state->mode; | |
406 | + tce->has_alt = alt != NULL; | |
407 | + if (alt) | |
408 | + tce->alt = *alt; | |
409 | + mutex_unlock(&tce->lock); | |
410 | + | |
411 | + typec_extcon_sync_extcon(tce); | |
412 | + | |
413 | + return 0; | |
8c9054af JP |
414 | +} |
415 | + | |
03acaa9b JP |
416 | +static int typec_extcon_usb_set_role(struct usb_role_switch *sw, |
417 | + enum usb_role role) | |
418 | +{ | |
419 | + struct typec_extcon *tce = usb_role_switch_get_drvdata(sw); | |
420 | + | |
421 | + dev_dbg(tce->dev, "ROLE SET: role=%d\n", role); | |
422 | + | |
423 | + mutex_lock(&tce->lock); | |
424 | + tce->role = role; | |
425 | + mutex_unlock(&tce->lock); | |
426 | + | |
427 | + typec_extcon_sync_extcon(tce); | |
428 | + | |
429 | + return 0; | |
8c9054af JP |
430 | +} |
431 | + | |
03acaa9b JP |
432 | +static int typec_extcon_notifier(struct notifier_block *nb, |
433 | + unsigned long action, void *data) | |
434 | +{ | |
435 | + struct typec_extcon *tce = container_of(nb, struct typec_extcon, extcon_nb); | |
8c9054af | 436 | + |
03acaa9b JP |
437 | + bool sdp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_SDP); |
438 | + bool cdp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_CDP); | |
439 | + bool dcp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_DCP); | |
440 | + bool usb = extcon_get_state(tce->extcon, EXTCON_USB); | |
441 | + bool usb_host = extcon_get_state(tce->extcon, EXTCON_USB_HOST); | |
442 | + bool dp = extcon_get_state(tce->extcon, EXTCON_DISP_DP); | |
8c9054af | 443 | + |
03acaa9b JP |
444 | + dev_info(tce->dev, "extcon changed sdp=%d cdp=%d dcp=%d usb=%d usb_host=%d dp=%d\n", |
445 | + sdp, cdp, dcp, usb, usb_host, dp); | |
8c9054af | 446 | + |
03acaa9b JP |
447 | + return NOTIFY_OK; |
448 | +} | |
8c9054af | 449 | + |
03acaa9b | 450 | +static int typec_extcon_probe(struct platform_device *pdev) |
8c9054af | 451 | +{ |
03acaa9b JP |
452 | + struct typec_switch_desc sw_desc = { }; |
453 | + struct typec_mux_desc mux_desc = { }; | |
454 | + struct usb_role_switch_desc role_desc = { }; | |
455 | + struct device *dev = &pdev->dev; | |
456 | + struct typec_extcon *tce; | |
457 | + int ret = 0; | |
8c9054af | 458 | + |
03acaa9b JP |
459 | + tce = devm_kzalloc(dev, sizeof(*tce), GFP_KERNEL); |
460 | + if (!tce) | |
461 | + return -ENOMEM; | |
8c9054af | 462 | + |
03acaa9b JP |
463 | + tce->dev = &pdev->dev; |
464 | + mutex_init(&tce->lock); | |
465 | + tce->mode = TYPEC_STATE_SAFE; | |
8c9054af | 466 | + |
03acaa9b JP |
467 | + sw_desc.drvdata = tce; |
468 | + sw_desc.fwnode = dev->fwnode; | |
469 | + sw_desc.set = typec_extcon_sw_set; | |
8c9054af | 470 | + |
03acaa9b JP |
471 | + tce->sw = typec_switch_register(dev, &sw_desc); |
472 | + if (IS_ERR(tce->sw)) | |
473 | + return dev_err_probe(dev, PTR_ERR(tce->sw), | |
474 | + "Error registering typec switch\n"); | |
8c9054af | 475 | + |
03acaa9b JP |
476 | + mux_desc.drvdata = tce; |
477 | + mux_desc.fwnode = dev->fwnode; | |
478 | + mux_desc.set = typec_extcon_mux_set; | |
479 | + | |
480 | + tce->mux = typec_mux_register(dev, &mux_desc); | |
481 | + if (IS_ERR(tce->mux)) { | |
482 | + ret = dev_err_probe(dev, PTR_ERR(tce->mux), | |
483 | + "Error registering typec mux\n"); | |
484 | + goto err_sw; | |
8c9054af | 485 | + } |
8c9054af | 486 | + |
03acaa9b JP |
487 | + role_desc.driver_data = tce; |
488 | + role_desc.fwnode = dev->fwnode; | |
489 | + role_desc.name = fwnode_get_name(dev->fwnode); | |
490 | + role_desc.set = typec_extcon_usb_set_role; | |
491 | + | |
492 | + tce->role_sw = usb_role_switch_register(dev, &role_desc); | |
493 | + if (IS_ERR(tce->role_sw)) { | |
494 | + ret = dev_err_probe(dev, PTR_ERR(tce->role_sw), | |
495 | + "Error registering USB role switch\n"); | |
496 | + goto err_mux; | |
8c9054af | 497 | + } |
03acaa9b JP |
498 | + |
499 | + tce->extcon = devm_extcon_dev_allocate(dev, typec_extcon_cable); | |
500 | + if (IS_ERR(tce->extcon)) { | |
501 | + ret = PTR_ERR(tce->extcon); | |
502 | + goto err_role; | |
8c9054af | 503 | + } |
03acaa9b JP |
504 | + |
505 | + ret = devm_extcon_dev_register(dev, tce->extcon); | |
506 | + if (ret) { | |
507 | + ret = dev_err_probe(dev, ret, "failed to register extcon device\n"); | |
508 | + goto err_role; | |
8c9054af | 509 | + } |
03acaa9b JP |
510 | + |
511 | + extcon_set_property_capability(tce->extcon, EXTCON_USB, | |
512 | + EXTCON_PROP_USB_SS); | |
513 | + extcon_set_property_capability(tce->extcon, EXTCON_USB, | |
514 | + EXTCON_PROP_USB_TYPEC_POLARITY); | |
515 | + extcon_set_property_capability(tce->extcon, EXTCON_USB_HOST, | |
516 | + EXTCON_PROP_USB_SS); | |
517 | + extcon_set_property_capability(tce->extcon, EXTCON_USB_HOST, | |
518 | + EXTCON_PROP_USB_TYPEC_POLARITY); | |
519 | + extcon_set_property_capability(tce->extcon, EXTCON_DISP_DP, | |
520 | + EXTCON_PROP_USB_SS); | |
521 | + extcon_set_property_capability(tce->extcon, EXTCON_DISP_DP, | |
522 | + EXTCON_PROP_USB_TYPEC_POLARITY); | |
523 | + | |
524 | + tce->extcon_nb.notifier_call = typec_extcon_notifier; | |
525 | + ret = devm_extcon_register_notifier_all(dev, tce->extcon, &tce->extcon_nb); | |
526 | + if (ret) { | |
527 | + dev_err_probe(dev, ret, "Failed to register extcon notifier\n"); | |
528 | + goto err_role; | |
8c9054af JP |
529 | + } |
530 | + | |
03acaa9b JP |
531 | + return 0; |
532 | + | |
533 | +err_role: | |
534 | + usb_role_switch_unregister(tce->role_sw); | |
535 | +err_mux: | |
536 | + typec_mux_unregister(tce->mux); | |
537 | +err_sw: | |
538 | + typec_switch_unregister(tce->sw); | |
539 | + return ret; | |
540 | +} | |
541 | + | |
542 | +static int typec_extcon_remove(struct platform_device *pdev) | |
543 | +{ | |
544 | + struct typec_extcon *tce = platform_get_drvdata(pdev); | |
545 | + | |
546 | + usb_role_switch_unregister(tce->role_sw); | |
547 | + typec_mux_unregister(tce->mux); | |
548 | + typec_switch_unregister(tce->sw); | |
549 | + | |
550 | + return 0; | |
551 | +} | |
552 | + | |
553 | +static struct of_device_id typec_extcon_of_match_table[] = { | |
554 | + { .compatible = "linux,typec-extcon-bridge" }, | |
555 | + { }, | |
556 | +}; | |
557 | +MODULE_DEVICE_TABLE(of, typec_extcon_of_match_table); | |
558 | + | |
559 | +static struct platform_driver typec_extcon_driver = { | |
560 | + .driver = { | |
561 | + .name = "typec-extcon", | |
562 | + .of_match_table = typec_extcon_of_match_table, | |
563 | + }, | |
564 | + .probe = typec_extcon_probe, | |
565 | + .remove = typec_extcon_remove, | |
566 | +}; | |
567 | + | |
568 | +module_platform_driver(typec_extcon_driver); | |
569 | + | |
570 | +MODULE_LICENSE("GPL"); | |
571 | +MODULE_AUTHOR("Ondrej Jirman <megous@megous.com>"); | |
572 | +MODULE_DESCRIPTION("typec -> extcon bridge driver"); | |
8c9054af | 573 | -- |
03acaa9b | 574 | 2.34.1 |
8c9054af | 575 | |
03acaa9b JP |
576 | From 4c839ce95766910235ff558b2959589c9068917c Mon Sep 17 00:00:00 2001 |
577 | From: Dan Johansen <strit@manjaro.org> | |
578 | Date: Sun, 2 Jan 2022 19:15:39 +0100 | |
579 | Subject: [PATCH] arm64: dts: rockchip: add typec extcon hack | |
8c9054af | 580 | |
03acaa9b | 581 | Signed-off-by: Dan Johansen <strit@manjaro.org> |
8c9054af | 582 | --- |
03acaa9b JP |
583 | .../boot/dts/rockchip/rk3399-pinebook-pro.dts | 31 +++++++++++++++++-- |
584 | 1 file changed, 29 insertions(+), 2 deletions(-) | |
8c9054af | 585 | |
03acaa9b JP |
586 | diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts |
587 | index c2f021a1a18f..fc33e111bbee 100644 | |
588 | --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |
589 | +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |
590 | @@ -384,6 +384,20 @@ mains_charger: dc-charger { | |
591 | pinctrl-names = "default"; | |
592 | pinctrl-0 = <&dc_det_pin>; | |
593 | }; | |
594 | + | |
595 | + typec_extcon_bridge: typec-extcon { | |
596 | + compatible = "linux,typec-extcon-bridge"; | |
597 | + usb-role-switch; | |
598 | + orientation-switch; | |
599 | + mode-switch; | |
600 | + svid = /bits/ 16 <0xff01>; | |
601 | + }; | |
602 | +}; | |
603 | + | |
604 | +&cdn_dp { | |
605 | + status = "okay"; | |
606 | + extcon = <&typec_extcon_bridge>; | |
607 | + phys = <&tcphy0_dp>; | |
608 | }; | |
609 | ||
610 | &cpu_b0 { | |
611 | @@ -705,6 +719,8 @@ fusb0: fusb30x@22 { | |
612 | pinctrl-names = "default"; | |
613 | pinctrl-0 = <&fusb0_int_pin>; | |
614 | vbus-supply = <&vbus_typec>; | |
615 | + extcon = <&typec_extcon_bridge>; | |
616 | + usb-role-switch = <&typec_extcon_bridge>; | |
617 | ||
618 | connector { | |
619 | compatible = "usb-c-connector"; | |
620 | @@ -713,10 +729,20 @@ connector { | |
621 | op-sink-microwatt = <1000000>; | |
622 | power-role = "dual"; | |
623 | sink-pdos = | |
624 | - <PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM)>; | |
625 | + <PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>; | |
626 | source-pdos = | |
627 | - <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>; | |
628 | + <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>; | |
629 | try-power-role = "sink"; | |
630 | + | |
631 | + mode-switch = <&typec_extcon_bridge>; | |
632 | + orientation-switch = <&typec_extcon_bridge>; | |
633 | + | |
634 | + altmodes { | |
635 | + dp { | |
636 | + svid = <0xff01>; | |
ace9f2bf | 637 | + vdo = <0x0c0006>; |
03acaa9b JP |
638 | + }; |
639 | + }; | |
640 | ||
641 | ports { | |
642 | #address-cells = <1>; | |
643 | @@ -984,6 +1010,7 @@ spiflash: flash@0 { | |
644 | }; | |
645 | ||
646 | &tcphy0 { | |
647 | + extcon = <&typec_extcon_bridge>; | |
648 | status = "okay"; | |
649 | }; | |
8c9054af | 650 | |
8c9054af | 651 | -- |
03acaa9b | 652 | 2.34.1 |
8c9054af | 653 |