Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
0e08d2a7 FW |
2 | /* |
3 | * Rockchip USB2.0 PHY with Innosilicon IP block driver | |
4 | * | |
5 | * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd | |
0e08d2a7 FW |
6 | */ |
7 | ||
8 | #include <linux/clk.h> | |
9 | #include <linux/clk-provider.h> | |
10 | #include <linux/delay.h> | |
176aa360 | 11 | #include <linux/extcon-provider.h> |
0e08d2a7 FW |
12 | #include <linux/interrupt.h> |
13 | #include <linux/io.h> | |
14 | #include <linux/gpio/consumer.h> | |
15 | #include <linux/jiffies.h> | |
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/mutex.h> | |
19 | #include <linux/of.h> | |
20 | #include <linux/of_address.h> | |
21 | #include <linux/of_irq.h> | |
22 | #include <linux/of_platform.h> | |
23 | #include <linux/phy/phy.h> | |
24 | #include <linux/platform_device.h> | |
98898f3b | 25 | #include <linux/power_supply.h> |
0e08d2a7 FW |
26 | #include <linux/regmap.h> |
27 | #include <linux/mfd/syscon.h> | |
98898f3b WW |
28 | #include <linux/usb/of.h> |
29 | #include <linux/usb/otg.h> | |
0e08d2a7 FW |
30 | |
31 | #define BIT_WRITEABLE_SHIFT 16 | |
98898f3b WW |
32 | #define SCHEDULE_DELAY (60 * HZ) |
33 | #define OTG_SCHEDULE_DELAY (2 * HZ) | |
0e08d2a7 FW |
34 | |
35 | enum rockchip_usb2phy_port_id { | |
36 | USB2PHY_PORT_OTG, | |
37 | USB2PHY_PORT_HOST, | |
38 | USB2PHY_NUM_PORTS, | |
39 | }; | |
40 | ||
41 | enum rockchip_usb2phy_host_state { | |
42 | PHY_STATE_HS_ONLINE = 0, | |
43 | PHY_STATE_DISCONNECT = 1, | |
44 | PHY_STATE_CONNECT = 2, | |
45 | PHY_STATE_FS_LS_ONLINE = 4, | |
46 | }; | |
47 | ||
98898f3b | 48 | /** |
8fa0402b EBS |
49 | * enum usb_chg_state - Different states involved in USB charger detection. |
50 | * @USB_CHG_STATE_UNDEFINED: USB charger is not connected or detection | |
98898f3b | 51 | * process is not yet started. |
8fa0402b EBS |
52 | * @USB_CHG_STATE_WAIT_FOR_DCD: Waiting for Data pins contact. |
53 | * @USB_CHG_STATE_DCD_DONE: Data pin contact is detected. | |
54 | * @USB_CHG_STATE_PRIMARY_DONE: Primary detection is completed (Detects | |
98898f3b | 55 | * between SDP and DCP/CDP). |
8fa0402b EBS |
56 | * @USB_CHG_STATE_SECONDARY_DONE: Secondary detection is completed (Detects |
57 | * between DCP and CDP). | |
58 | * @USB_CHG_STATE_DETECTED: USB charger type is determined. | |
98898f3b WW |
59 | */ |
60 | enum usb_chg_state { | |
61 | USB_CHG_STATE_UNDEFINED = 0, | |
62 | USB_CHG_STATE_WAIT_FOR_DCD, | |
63 | USB_CHG_STATE_DCD_DONE, | |
64 | USB_CHG_STATE_PRIMARY_DONE, | |
65 | USB_CHG_STATE_SECONDARY_DONE, | |
66 | USB_CHG_STATE_DETECTED, | |
67 | }; | |
68 | ||
69 | static const unsigned int rockchip_usb2phy_extcon_cable[] = { | |
70 | EXTCON_USB, | |
71 | EXTCON_USB_HOST, | |
72 | EXTCON_CHG_USB_SDP, | |
73 | EXTCON_CHG_USB_CDP, | |
74 | EXTCON_CHG_USB_DCP, | |
75 | EXTCON_CHG_USB_SLOW, | |
76 | EXTCON_NONE, | |
77 | }; | |
78 | ||
0e08d2a7 FW |
79 | struct usb2phy_reg { |
80 | unsigned int offset; | |
81 | unsigned int bitend; | |
82 | unsigned int bitstart; | |
83 | unsigned int disable; | |
84 | unsigned int enable; | |
85 | }; | |
86 | ||
98898f3b | 87 | /** |
8fa0402b | 88 | * struct rockchip_chg_det_reg - usb charger detect registers |
98898f3b WW |
89 | * @cp_det: charging port detected successfully. |
90 | * @dcp_det: dedicated charging port detected successfully. | |
91 | * @dp_det: assert data pin connect successfully. | |
92 | * @idm_sink_en: open dm sink curren. | |
93 | * @idp_sink_en: open dp sink current. | |
94 | * @idp_src_en: open dm source current. | |
95 | * @rdm_pdwn_en: open dm pull down resistor. | |
96 | * @vdm_src_en: open dm voltage source. | |
97 | * @vdp_src_en: open dp voltage source. | |
98 | * @opmode: utmi operational mode. | |
99 | */ | |
100 | struct rockchip_chg_det_reg { | |
101 | struct usb2phy_reg cp_det; | |
102 | struct usb2phy_reg dcp_det; | |
103 | struct usb2phy_reg dp_det; | |
104 | struct usb2phy_reg idm_sink_en; | |
105 | struct usb2phy_reg idp_sink_en; | |
106 | struct usb2phy_reg idp_src_en; | |
107 | struct usb2phy_reg rdm_pdwn_en; | |
108 | struct usb2phy_reg vdm_src_en; | |
109 | struct usb2phy_reg vdp_src_en; | |
110 | struct usb2phy_reg opmode; | |
111 | }; | |
112 | ||
0e08d2a7 | 113 | /** |
8fa0402b | 114 | * struct rockchip_usb2phy_port_cfg - usb-phy port configuration. |
0e08d2a7 | 115 | * @phy_sus: phy suspend register. |
98898f3b WW |
116 | * @bvalid_det_en: vbus valid rise detection enable register. |
117 | * @bvalid_det_st: vbus valid rise detection status register. | |
118 | * @bvalid_det_clr: vbus valid rise detection clear register. | |
0e08d2a7 FW |
119 | * @ls_det_en: linestate detection enable register. |
120 | * @ls_det_st: linestate detection state register. | |
121 | * @ls_det_clr: linestate detection clear register. | |
98898f3b WW |
122 | * @utmi_avalid: utmi vbus avalid status register. |
123 | * @utmi_bvalid: utmi vbus bvalid status register. | |
0e08d2a7 FW |
124 | * @utmi_ls: utmi linestate state register. |
125 | * @utmi_hstdet: utmi host disconnect register. | |
126 | */ | |
127 | struct rockchip_usb2phy_port_cfg { | |
128 | struct usb2phy_reg phy_sus; | |
98898f3b WW |
129 | struct usb2phy_reg bvalid_det_en; |
130 | struct usb2phy_reg bvalid_det_st; | |
131 | struct usb2phy_reg bvalid_det_clr; | |
0e08d2a7 FW |
132 | struct usb2phy_reg ls_det_en; |
133 | struct usb2phy_reg ls_det_st; | |
134 | struct usb2phy_reg ls_det_clr; | |
98898f3b WW |
135 | struct usb2phy_reg utmi_avalid; |
136 | struct usb2phy_reg utmi_bvalid; | |
0e08d2a7 FW |
137 | struct usb2phy_reg utmi_ls; |
138 | struct usb2phy_reg utmi_hstdet; | |
139 | }; | |
140 | ||
141 | /** | |
8fa0402b | 142 | * struct rockchip_usb2phy_cfg - usb-phy configuration. |
0e08d2a7 FW |
143 | * @reg: the address offset of grf for usb-phy config. |
144 | * @num_ports: specify how many ports that the phy has. | |
145 | * @clkout_ctl: keep on/turn off output clk of phy. | |
8fa0402b | 146 | * @port_cfgs: usb-phy port configurations. |
98898f3b | 147 | * @chg_det: charger detection registers. |
0e08d2a7 FW |
148 | */ |
149 | struct rockchip_usb2phy_cfg { | |
150 | unsigned int reg; | |
151 | unsigned int num_ports; | |
152 | struct usb2phy_reg clkout_ctl; | |
153 | const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; | |
98898f3b | 154 | const struct rockchip_chg_det_reg chg_det; |
0e08d2a7 FW |
155 | }; |
156 | ||
157 | /** | |
8fa0402b EBS |
158 | * struct rockchip_usb2phy_port - usb-phy port data. |
159 | * @phy: generic phy. | |
0e08d2a7 FW |
160 | * @port_id: flag for otg port or host port. |
161 | * @suspended: phy suspended flag. | |
98898f3b WW |
162 | * @vbus_attached: otg device vbus status. |
163 | * @bvalid_irq: IRQ number assigned for vbus valid rise detection. | |
0e08d2a7 | 164 | * @ls_irq: IRQ number assigned for linestate detection. |
0983e2ab FW |
165 | * @otg_mux_irq: IRQ number which multiplex otg-id/otg-bvalid/linestate |
166 | * irqs to one irq in otg-port. | |
0e08d2a7 | 167 | * @mutex: for register updating in sm_work. |
98898f3b WW |
168 | * @chg_work: charge detect work. |
169 | * @otg_sm_work: OTG state machine work. | |
170 | * @sm_work: HOST state machine work. | |
8fa0402b | 171 | * @port_cfg: port register configuration, assigned by driver data. |
98898f3b WW |
172 | * @event_nb: hold event notification callback. |
173 | * @state: define OTG enumeration states before device reset. | |
174 | * @mode: the dr_mode of the controller. | |
0e08d2a7 FW |
175 | */ |
176 | struct rockchip_usb2phy_port { | |
177 | struct phy *phy; | |
178 | unsigned int port_id; | |
179 | bool suspended; | |
98898f3b WW |
180 | bool vbus_attached; |
181 | int bvalid_irq; | |
0e08d2a7 | 182 | int ls_irq; |
0983e2ab | 183 | int otg_mux_irq; |
0e08d2a7 | 184 | struct mutex mutex; |
98898f3b WW |
185 | struct delayed_work chg_work; |
186 | struct delayed_work otg_sm_work; | |
0e08d2a7 FW |
187 | struct delayed_work sm_work; |
188 | const struct rockchip_usb2phy_port_cfg *port_cfg; | |
98898f3b WW |
189 | struct notifier_block event_nb; |
190 | enum usb_otg_state state; | |
191 | enum usb_dr_mode mode; | |
0e08d2a7 FW |
192 | }; |
193 | ||
194 | /** | |
8fa0402b EBS |
195 | * struct rockchip_usb2phy - usb2.0 phy driver data. |
196 | * @dev: pointer to device. | |
0e08d2a7 | 197 | * @grf: General Register Files regmap. |
1543645c | 198 | * @usbgrf: USB General Register Files regmap. |
0e08d2a7 FW |
199 | * @clk: clock struct of phy input clk. |
200 | * @clk480m: clock struct of phy output clk. | |
8fa0402b | 201 | * @clk480m_hw: clock struct of phy output clk management. |
98898f3b WW |
202 | * @chg_state: states involved in USB charger detection. |
203 | * @chg_type: USB charger types. | |
204 | * @dcd_retries: The retry count used to track Data contact | |
205 | * detection process. | |
206 | * @edev: extcon device for notification registration | |
0e08d2a7 FW |
207 | * @phy_cfg: phy register configuration, assigned by driver data. |
208 | * @ports: phy port instance. | |
209 | */ | |
210 | struct rockchip_usb2phy { | |
211 | struct device *dev; | |
212 | struct regmap *grf; | |
1543645c | 213 | struct regmap *usbgrf; |
0e08d2a7 FW |
214 | struct clk *clk; |
215 | struct clk *clk480m; | |
216 | struct clk_hw clk480m_hw; | |
98898f3b WW |
217 | enum usb_chg_state chg_state; |
218 | enum power_supply_type chg_type; | |
219 | u8 dcd_retries; | |
220 | struct extcon_dev *edev; | |
0e08d2a7 FW |
221 | const struct rockchip_usb2phy_cfg *phy_cfg; |
222 | struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; | |
223 | }; | |
224 | ||
1543645c FW |
225 | static inline struct regmap *get_reg_base(struct rockchip_usb2phy *rphy) |
226 | { | |
227 | return rphy->usbgrf == NULL ? rphy->grf : rphy->usbgrf; | |
228 | } | |
229 | ||
230 | static inline int property_enable(struct regmap *base, | |
0e08d2a7 FW |
231 | const struct usb2phy_reg *reg, bool en) |
232 | { | |
233 | unsigned int val, mask, tmp; | |
234 | ||
235 | tmp = en ? reg->enable : reg->disable; | |
236 | mask = GENMASK(reg->bitend, reg->bitstart); | |
237 | val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); | |
238 | ||
1543645c | 239 | return regmap_write(base, reg->offset, val); |
0e08d2a7 FW |
240 | } |
241 | ||
1543645c | 242 | static inline bool property_enabled(struct regmap *base, |
0e08d2a7 FW |
243 | const struct usb2phy_reg *reg) |
244 | { | |
245 | int ret; | |
246 | unsigned int tmp, orig; | |
247 | unsigned int mask = GENMASK(reg->bitend, reg->bitstart); | |
248 | ||
1543645c | 249 | ret = regmap_read(base, reg->offset, &orig); |
0e08d2a7 FW |
250 | if (ret) |
251 | return false; | |
252 | ||
253 | tmp = (orig & mask) >> reg->bitstart; | |
254 | return tmp == reg->enable; | |
255 | } | |
256 | ||
ae9fc711 | 257 | static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) |
0e08d2a7 FW |
258 | { |
259 | struct rockchip_usb2phy *rphy = | |
260 | container_of(hw, struct rockchip_usb2phy, clk480m_hw); | |
1543645c | 261 | struct regmap *base = get_reg_base(rphy); |
0e08d2a7 FW |
262 | int ret; |
263 | ||
264 | /* turn on 480m clk output if it is off */ | |
1543645c FW |
265 | if (!property_enabled(base, &rphy->phy_cfg->clkout_ctl)) { |
266 | ret = property_enable(base, &rphy->phy_cfg->clkout_ctl, true); | |
0e08d2a7 FW |
267 | if (ret) |
268 | return ret; | |
269 | ||
882e1492 WW |
270 | /* waiting for the clk become stable */ |
271 | usleep_range(1200, 1300); | |
0e08d2a7 FW |
272 | } |
273 | ||
274 | return 0; | |
275 | } | |
276 | ||
ae9fc711 | 277 | static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) |
0e08d2a7 FW |
278 | { |
279 | struct rockchip_usb2phy *rphy = | |
280 | container_of(hw, struct rockchip_usb2phy, clk480m_hw); | |
1543645c | 281 | struct regmap *base = get_reg_base(rphy); |
0e08d2a7 FW |
282 | |
283 | /* turn off 480m clk output */ | |
1543645c | 284 | property_enable(base, &rphy->phy_cfg->clkout_ctl, false); |
0e08d2a7 FW |
285 | } |
286 | ||
ae9fc711 | 287 | static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) |
0e08d2a7 FW |
288 | { |
289 | struct rockchip_usb2phy *rphy = | |
290 | container_of(hw, struct rockchip_usb2phy, clk480m_hw); | |
1543645c | 291 | struct regmap *base = get_reg_base(rphy); |
0e08d2a7 | 292 | |
1543645c | 293 | return property_enabled(base, &rphy->phy_cfg->clkout_ctl); |
0e08d2a7 FW |
294 | } |
295 | ||
296 | static unsigned long | |
297 | rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw, | |
298 | unsigned long parent_rate) | |
299 | { | |
300 | return 480000000; | |
301 | } | |
302 | ||
303 | static const struct clk_ops rockchip_usb2phy_clkout_ops = { | |
ae9fc711 WW |
304 | .prepare = rockchip_usb2phy_clk480m_prepare, |
305 | .unprepare = rockchip_usb2phy_clk480m_unprepare, | |
306 | .is_prepared = rockchip_usb2phy_clk480m_prepared, | |
0e08d2a7 FW |
307 | .recalc_rate = rockchip_usb2phy_clk480m_recalc_rate, |
308 | }; | |
309 | ||
310 | static void rockchip_usb2phy_clk480m_unregister(void *data) | |
311 | { | |
312 | struct rockchip_usb2phy *rphy = data; | |
313 | ||
314 | of_clk_del_provider(rphy->dev->of_node); | |
315 | clk_unregister(rphy->clk480m); | |
316 | } | |
317 | ||
318 | static int | |
319 | rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) | |
320 | { | |
321 | struct device_node *node = rphy->dev->of_node; | |
322 | struct clk_init_data init; | |
323 | const char *clk_name; | |
324 | int ret; | |
325 | ||
326 | init.flags = 0; | |
327 | init.name = "clk_usbphy_480m"; | |
328 | init.ops = &rockchip_usb2phy_clkout_ops; | |
329 | ||
330 | /* optional override of the clockname */ | |
331 | of_property_read_string(node, "clock-output-names", &init.name); | |
332 | ||
333 | if (rphy->clk) { | |
334 | clk_name = __clk_get_name(rphy->clk); | |
335 | init.parent_names = &clk_name; | |
336 | init.num_parents = 1; | |
337 | } else { | |
338 | init.parent_names = NULL; | |
339 | init.num_parents = 0; | |
340 | } | |
341 | ||
342 | rphy->clk480m_hw.init = &init; | |
343 | ||
344 | /* register the clock */ | |
345 | rphy->clk480m = clk_register(rphy->dev, &rphy->clk480m_hw); | |
346 | if (IS_ERR(rphy->clk480m)) { | |
347 | ret = PTR_ERR(rphy->clk480m); | |
348 | goto err_ret; | |
349 | } | |
350 | ||
351 | ret = of_clk_add_provider(node, of_clk_src_simple_get, rphy->clk480m); | |
352 | if (ret < 0) | |
353 | goto err_clk_provider; | |
354 | ||
355 | ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister, | |
356 | rphy); | |
357 | if (ret < 0) | |
358 | goto err_unreg_action; | |
359 | ||
360 | return 0; | |
361 | ||
362 | err_unreg_action: | |
363 | of_clk_del_provider(node); | |
364 | err_clk_provider: | |
365 | clk_unregister(rphy->clk480m); | |
366 | err_ret: | |
367 | return ret; | |
368 | } | |
369 | ||
98898f3b | 370 | static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) |
0e08d2a7 | 371 | { |
0e08d2a7 | 372 | int ret; |
98898f3b WW |
373 | struct device_node *node = rphy->dev->of_node; |
374 | struct extcon_dev *edev; | |
375 | ||
376 | if (of_property_read_bool(node, "extcon")) { | |
377 | edev = extcon_get_edev_by_phandle(rphy->dev, 0); | |
378 | if (IS_ERR(edev)) { | |
379 | if (PTR_ERR(edev) != -EPROBE_DEFER) | |
380 | dev_err(rphy->dev, "Invalid or missing extcon\n"); | |
381 | return PTR_ERR(edev); | |
382 | } | |
383 | } else { | |
384 | /* Initialize extcon device */ | |
385 | edev = devm_extcon_dev_allocate(rphy->dev, | |
386 | rockchip_usb2phy_extcon_cable); | |
0e08d2a7 | 387 | |
98898f3b WW |
388 | if (IS_ERR(edev)) |
389 | return -ENOMEM; | |
0e08d2a7 | 390 | |
98898f3b | 391 | ret = devm_extcon_dev_register(rphy->dev, edev); |
0e08d2a7 | 392 | if (ret) { |
98898f3b | 393 | dev_err(rphy->dev, "failed to register extcon device\n"); |
0e08d2a7 FW |
394 | return ret; |
395 | } | |
98898f3b | 396 | } |
0e08d2a7 | 397 | |
98898f3b WW |
398 | rphy->edev = edev; |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
403 | static int rockchip_usb2phy_init(struct phy *phy) | |
404 | { | |
405 | struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); | |
406 | struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); | |
407 | int ret = 0; | |
408 | ||
409 | mutex_lock(&rport->mutex); | |
410 | ||
411 | if (rport->port_id == USB2PHY_PORT_OTG) { | |
96327811 WW |
412 | if (rport->mode != USB_DR_MODE_HOST && |
413 | rport->mode != USB_DR_MODE_UNKNOWN) { | |
98898f3b | 414 | /* clear bvalid status and enable bvalid detect irq */ |
1543645c | 415 | ret = property_enable(rphy->grf, |
98898f3b WW |
416 | &rport->port_cfg->bvalid_det_clr, |
417 | true); | |
418 | if (ret) | |
419 | goto out; | |
420 | ||
1543645c | 421 | ret = property_enable(rphy->grf, |
98898f3b WW |
422 | &rport->port_cfg->bvalid_det_en, |
423 | true); | |
424 | if (ret) | |
425 | goto out; | |
426 | ||
427 | schedule_delayed_work(&rport->otg_sm_work, | |
5a74a8b7 | 428 | OTG_SCHEDULE_DELAY * 3); |
98898f3b WW |
429 | } else { |
430 | /* If OTG works in host only mode, do nothing. */ | |
431 | dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode); | |
0e08d2a7 | 432 | } |
98898f3b WW |
433 | } else if (rport->port_id == USB2PHY_PORT_HOST) { |
434 | /* clear linestate and enable linestate detect irq */ | |
1543645c FW |
435 | ret = property_enable(rphy->grf, |
436 | &rport->port_cfg->ls_det_clr, true); | |
98898f3b WW |
437 | if (ret) |
438 | goto out; | |
439 | ||
1543645c FW |
440 | ret = property_enable(rphy->grf, |
441 | &rport->port_cfg->ls_det_en, true); | |
98898f3b WW |
442 | if (ret) |
443 | goto out; | |
0e08d2a7 | 444 | |
0e08d2a7 FW |
445 | schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); |
446 | } | |
447 | ||
98898f3b WW |
448 | out: |
449 | mutex_unlock(&rport->mutex); | |
450 | return ret; | |
0e08d2a7 FW |
451 | } |
452 | ||
453 | static int rockchip_usb2phy_power_on(struct phy *phy) | |
454 | { | |
455 | struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); | |
456 | struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); | |
1543645c | 457 | struct regmap *base = get_reg_base(rphy); |
0e08d2a7 FW |
458 | int ret; |
459 | ||
460 | dev_dbg(&rport->phy->dev, "port power on\n"); | |
461 | ||
462 | if (!rport->suspended) | |
463 | return 0; | |
464 | ||
465 | ret = clk_prepare_enable(rphy->clk480m); | |
466 | if (ret) | |
467 | return ret; | |
468 | ||
1543645c | 469 | ret = property_enable(base, &rport->port_cfg->phy_sus, false); |
0e08d2a7 FW |
470 | if (ret) |
471 | return ret; | |
472 | ||
fbbe98cd WW |
473 | /* waiting for the utmi_clk to become stable */ |
474 | usleep_range(1500, 2000); | |
475 | ||
0e08d2a7 FW |
476 | rport->suspended = false; |
477 | return 0; | |
478 | } | |
479 | ||
480 | static int rockchip_usb2phy_power_off(struct phy *phy) | |
481 | { | |
482 | struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); | |
483 | struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); | |
1543645c | 484 | struct regmap *base = get_reg_base(rphy); |
0e08d2a7 FW |
485 | int ret; |
486 | ||
487 | dev_dbg(&rport->phy->dev, "port power off\n"); | |
488 | ||
489 | if (rport->suspended) | |
490 | return 0; | |
491 | ||
1543645c | 492 | ret = property_enable(base, &rport->port_cfg->phy_sus, true); |
0e08d2a7 FW |
493 | if (ret) |
494 | return ret; | |
495 | ||
496 | rport->suspended = true; | |
497 | clk_disable_unprepare(rphy->clk480m); | |
498 | ||
499 | return 0; | |
500 | } | |
501 | ||
502 | static int rockchip_usb2phy_exit(struct phy *phy) | |
503 | { | |
504 | struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); | |
505 | ||
98898f3b | 506 | if (rport->port_id == USB2PHY_PORT_OTG && |
96327811 WW |
507 | rport->mode != USB_DR_MODE_HOST && |
508 | rport->mode != USB_DR_MODE_UNKNOWN) { | |
98898f3b WW |
509 | cancel_delayed_work_sync(&rport->otg_sm_work); |
510 | cancel_delayed_work_sync(&rport->chg_work); | |
511 | } else if (rport->port_id == USB2PHY_PORT_HOST) | |
0e08d2a7 FW |
512 | cancel_delayed_work_sync(&rport->sm_work); |
513 | ||
514 | return 0; | |
515 | } | |
516 | ||
517 | static const struct phy_ops rockchip_usb2phy_ops = { | |
518 | .init = rockchip_usb2phy_init, | |
519 | .exit = rockchip_usb2phy_exit, | |
520 | .power_on = rockchip_usb2phy_power_on, | |
521 | .power_off = rockchip_usb2phy_power_off, | |
522 | .owner = THIS_MODULE, | |
523 | }; | |
524 | ||
98898f3b WW |
525 | static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) |
526 | { | |
527 | struct rockchip_usb2phy_port *rport = | |
528 | container_of(work, struct rockchip_usb2phy_port, | |
529 | otg_sm_work.work); | |
530 | struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); | |
531 | static unsigned int cable; | |
532 | unsigned long delay; | |
533 | bool vbus_attach, sch_work, notify_charger; | |
534 | ||
31926c21 EBS |
535 | vbus_attach = property_enabled(rphy->grf, |
536 | &rport->port_cfg->utmi_bvalid); | |
98898f3b WW |
537 | |
538 | sch_work = false; | |
539 | notify_charger = false; | |
540 | delay = OTG_SCHEDULE_DELAY; | |
541 | dev_dbg(&rport->phy->dev, "%s otg sm work\n", | |
542 | usb_otg_state_string(rport->state)); | |
543 | ||
544 | switch (rport->state) { | |
545 | case OTG_STATE_UNDEFINED: | |
546 | rport->state = OTG_STATE_B_IDLE; | |
547 | if (!vbus_attach) | |
548 | rockchip_usb2phy_power_off(rport->phy); | |
549 | /* fall through */ | |
550 | case OTG_STATE_B_IDLE: | |
86f44c88 | 551 | if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0) { |
98898f3b WW |
552 | dev_dbg(&rport->phy->dev, "usb otg host connect\n"); |
553 | rport->state = OTG_STATE_A_HOST; | |
554 | rockchip_usb2phy_power_on(rport->phy); | |
555 | return; | |
556 | } else if (vbus_attach) { | |
557 | dev_dbg(&rport->phy->dev, "vbus_attach\n"); | |
558 | switch (rphy->chg_state) { | |
559 | case USB_CHG_STATE_UNDEFINED: | |
560 | schedule_delayed_work(&rport->chg_work, 0); | |
561 | return; | |
562 | case USB_CHG_STATE_DETECTED: | |
563 | switch (rphy->chg_type) { | |
564 | case POWER_SUPPLY_TYPE_USB: | |
7dfa3026 | 565 | dev_dbg(&rport->phy->dev, "sdp cable is connected\n"); |
98898f3b WW |
566 | rockchip_usb2phy_power_on(rport->phy); |
567 | rport->state = OTG_STATE_B_PERIPHERAL; | |
568 | notify_charger = true; | |
569 | sch_work = true; | |
570 | cable = EXTCON_CHG_USB_SDP; | |
571 | break; | |
572 | case POWER_SUPPLY_TYPE_USB_DCP: | |
7dfa3026 | 573 | dev_dbg(&rport->phy->dev, "dcp cable is connected\n"); |
98898f3b WW |
574 | rockchip_usb2phy_power_off(rport->phy); |
575 | notify_charger = true; | |
576 | sch_work = true; | |
577 | cable = EXTCON_CHG_USB_DCP; | |
578 | break; | |
579 | case POWER_SUPPLY_TYPE_USB_CDP: | |
7dfa3026 | 580 | dev_dbg(&rport->phy->dev, "cdp cable is connected\n"); |
98898f3b WW |
581 | rockchip_usb2phy_power_on(rport->phy); |
582 | rport->state = OTG_STATE_B_PERIPHERAL; | |
583 | notify_charger = true; | |
584 | sch_work = true; | |
585 | cable = EXTCON_CHG_USB_CDP; | |
586 | break; | |
587 | default: | |
588 | break; | |
589 | } | |
590 | break; | |
591 | default: | |
592 | break; | |
593 | } | |
594 | } else { | |
595 | notify_charger = true; | |
596 | rphy->chg_state = USB_CHG_STATE_UNDEFINED; | |
597 | rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; | |
598 | } | |
599 | ||
600 | if (rport->vbus_attached != vbus_attach) { | |
601 | rport->vbus_attached = vbus_attach; | |
602 | ||
3445be59 | 603 | if (notify_charger && rphy->edev) { |
86f44c88 | 604 | extcon_set_state_sync(rphy->edev, |
98898f3b | 605 | cable, vbus_attach); |
3445be59 BW |
606 | if (cable == EXTCON_CHG_USB_SDP) |
607 | extcon_set_state_sync(rphy->edev, | |
608 | EXTCON_USB, | |
609 | vbus_attach); | |
610 | } | |
98898f3b WW |
611 | } |
612 | break; | |
613 | case OTG_STATE_B_PERIPHERAL: | |
614 | if (!vbus_attach) { | |
615 | dev_dbg(&rport->phy->dev, "usb disconnect\n"); | |
616 | rphy->chg_state = USB_CHG_STATE_UNDEFINED; | |
617 | rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; | |
618 | rport->state = OTG_STATE_B_IDLE; | |
619 | delay = 0; | |
620 | rockchip_usb2phy_power_off(rport->phy); | |
621 | } | |
622 | sch_work = true; | |
623 | break; | |
624 | case OTG_STATE_A_HOST: | |
86f44c88 | 625 | if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) == 0) { |
98898f3b WW |
626 | dev_dbg(&rport->phy->dev, "usb otg host disconnect\n"); |
627 | rport->state = OTG_STATE_B_IDLE; | |
628 | rockchip_usb2phy_power_off(rport->phy); | |
629 | } | |
630 | break; | |
631 | default: | |
632 | break; | |
633 | } | |
634 | ||
635 | if (sch_work) | |
636 | schedule_delayed_work(&rport->otg_sm_work, delay); | |
637 | } | |
638 | ||
639 | static const char *chg_to_string(enum power_supply_type chg_type) | |
640 | { | |
641 | switch (chg_type) { | |
642 | case POWER_SUPPLY_TYPE_USB: | |
643 | return "USB_SDP_CHARGER"; | |
644 | case POWER_SUPPLY_TYPE_USB_DCP: | |
645 | return "USB_DCP_CHARGER"; | |
646 | case POWER_SUPPLY_TYPE_USB_CDP: | |
647 | return "USB_CDP_CHARGER"; | |
648 | default: | |
649 | return "INVALID_CHARGER"; | |
650 | } | |
651 | } | |
652 | ||
653 | static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy, | |
654 | bool en) | |
655 | { | |
1543645c FW |
656 | struct regmap *base = get_reg_base(rphy); |
657 | ||
658 | property_enable(base, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); | |
659 | property_enable(base, &rphy->phy_cfg->chg_det.idp_src_en, en); | |
98898f3b WW |
660 | } |
661 | ||
662 | static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy, | |
663 | bool en) | |
664 | { | |
1543645c FW |
665 | struct regmap *base = get_reg_base(rphy); |
666 | ||
667 | property_enable(base, &rphy->phy_cfg->chg_det.vdp_src_en, en); | |
668 | property_enable(base, &rphy->phy_cfg->chg_det.idm_sink_en, en); | |
98898f3b WW |
669 | } |
670 | ||
671 | static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, | |
672 | bool en) | |
673 | { | |
1543645c FW |
674 | struct regmap *base = get_reg_base(rphy); |
675 | ||
676 | property_enable(base, &rphy->phy_cfg->chg_det.vdm_src_en, en); | |
677 | property_enable(base, &rphy->phy_cfg->chg_det.idp_sink_en, en); | |
98898f3b WW |
678 | } |
679 | ||
680 | #define CHG_DCD_POLL_TIME (100 * HZ / 1000) | |
681 | #define CHG_DCD_MAX_RETRIES 6 | |
682 | #define CHG_PRIMARY_DET_TIME (40 * HZ / 1000) | |
683 | #define CHG_SECONDARY_DET_TIME (40 * HZ / 1000) | |
684 | static void rockchip_chg_detect_work(struct work_struct *work) | |
685 | { | |
686 | struct rockchip_usb2phy_port *rport = | |
687 | container_of(work, struct rockchip_usb2phy_port, chg_work.work); | |
688 | struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); | |
1543645c | 689 | struct regmap *base = get_reg_base(rphy); |
98898f3b WW |
690 | bool is_dcd, tmout, vout; |
691 | unsigned long delay; | |
692 | ||
693 | dev_dbg(&rport->phy->dev, "chg detection work state = %d\n", | |
694 | rphy->chg_state); | |
695 | switch (rphy->chg_state) { | |
696 | case USB_CHG_STATE_UNDEFINED: | |
697 | if (!rport->suspended) | |
698 | rockchip_usb2phy_power_off(rport->phy); | |
699 | /* put the controller in non-driving mode */ | |
1543645c | 700 | property_enable(base, &rphy->phy_cfg->chg_det.opmode, false); |
98898f3b WW |
701 | /* Start DCD processing stage 1 */ |
702 | rockchip_chg_enable_dcd(rphy, true); | |
703 | rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; | |
704 | rphy->dcd_retries = 0; | |
705 | delay = CHG_DCD_POLL_TIME; | |
706 | break; | |
707 | case USB_CHG_STATE_WAIT_FOR_DCD: | |
708 | /* get data contact detection status */ | |
1543645c FW |
709 | is_dcd = property_enabled(rphy->grf, |
710 | &rphy->phy_cfg->chg_det.dp_det); | |
98898f3b WW |
711 | tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES; |
712 | /* stage 2 */ | |
713 | if (is_dcd || tmout) { | |
714 | /* stage 4 */ | |
715 | /* Turn off DCD circuitry */ | |
716 | rockchip_chg_enable_dcd(rphy, false); | |
717 | /* Voltage Source on DP, Probe on DM */ | |
718 | rockchip_chg_enable_primary_det(rphy, true); | |
719 | delay = CHG_PRIMARY_DET_TIME; | |
720 | rphy->chg_state = USB_CHG_STATE_DCD_DONE; | |
721 | } else { | |
722 | /* stage 3 */ | |
723 | delay = CHG_DCD_POLL_TIME; | |
724 | } | |
725 | break; | |
726 | case USB_CHG_STATE_DCD_DONE: | |
1543645c FW |
727 | vout = property_enabled(rphy->grf, |
728 | &rphy->phy_cfg->chg_det.cp_det); | |
98898f3b WW |
729 | rockchip_chg_enable_primary_det(rphy, false); |
730 | if (vout) { | |
731 | /* Voltage Source on DM, Probe on DP */ | |
732 | rockchip_chg_enable_secondary_det(rphy, true); | |
733 | delay = CHG_SECONDARY_DET_TIME; | |
734 | rphy->chg_state = USB_CHG_STATE_PRIMARY_DONE; | |
735 | } else { | |
dd796e92 | 736 | if (rphy->dcd_retries == CHG_DCD_MAX_RETRIES) { |
98898f3b WW |
737 | /* floating charger found */ |
738 | rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; | |
739 | rphy->chg_state = USB_CHG_STATE_DETECTED; | |
740 | delay = 0; | |
741 | } else { | |
742 | rphy->chg_type = POWER_SUPPLY_TYPE_USB; | |
743 | rphy->chg_state = USB_CHG_STATE_DETECTED; | |
744 | delay = 0; | |
745 | } | |
746 | } | |
747 | break; | |
748 | case USB_CHG_STATE_PRIMARY_DONE: | |
1543645c FW |
749 | vout = property_enabled(rphy->grf, |
750 | &rphy->phy_cfg->chg_det.dcp_det); | |
98898f3b WW |
751 | /* Turn off voltage source */ |
752 | rockchip_chg_enable_secondary_det(rphy, false); | |
753 | if (vout) | |
754 | rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; | |
755 | else | |
756 | rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP; | |
757 | /* fall through */ | |
758 | case USB_CHG_STATE_SECONDARY_DONE: | |
759 | rphy->chg_state = USB_CHG_STATE_DETECTED; | |
760 | delay = 0; | |
761 | /* fall through */ | |
762 | case USB_CHG_STATE_DETECTED: | |
763 | /* put the controller in normal mode */ | |
1543645c | 764 | property_enable(base, &rphy->phy_cfg->chg_det.opmode, true); |
98898f3b WW |
765 | rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); |
766 | dev_info(&rport->phy->dev, "charger = %s\n", | |
767 | chg_to_string(rphy->chg_type)); | |
768 | return; | |
769 | default: | |
770 | return; | |
771 | } | |
772 | ||
773 | schedule_delayed_work(&rport->chg_work, delay); | |
774 | } | |
775 | ||
0e08d2a7 FW |
776 | /* |
777 | * The function manage host-phy port state and suspend/resume phy port | |
778 | * to save power. | |
779 | * | |
780 | * we rely on utmi_linestate and utmi_hostdisconnect to identify whether | |
781 | * devices is disconnect or not. Besides, we do not need care it is FS/LS | |
782 | * disconnected or HS disconnected, actually, we just only need get the | |
783 | * device is disconnected at last through rearm the delayed work, | |
784 | * to suspend the phy port in _PHY_STATE_DISCONNECT_ case. | |
785 | * | |
786 | * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke | |
787 | * some clk related APIs, so do not invoke it from interrupt context directly. | |
788 | */ | |
789 | static void rockchip_usb2phy_sm_work(struct work_struct *work) | |
790 | { | |
791 | struct rockchip_usb2phy_port *rport = | |
792 | container_of(work, struct rockchip_usb2phy_port, sm_work.work); | |
793 | struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); | |
794 | unsigned int sh = rport->port_cfg->utmi_hstdet.bitend - | |
795 | rport->port_cfg->utmi_hstdet.bitstart + 1; | |
796 | unsigned int ul, uhd, state; | |
797 | unsigned int ul_mask, uhd_mask; | |
798 | int ret; | |
799 | ||
800 | mutex_lock(&rport->mutex); | |
801 | ||
802 | ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul); | |
803 | if (ret < 0) | |
804 | goto next_schedule; | |
805 | ||
1543645c | 806 | ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, &uhd); |
0e08d2a7 FW |
807 | if (ret < 0) |
808 | goto next_schedule; | |
809 | ||
810 | uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend, | |
811 | rport->port_cfg->utmi_hstdet.bitstart); | |
812 | ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend, | |
813 | rport->port_cfg->utmi_ls.bitstart); | |
814 | ||
815 | /* stitch on utmi_ls and utmi_hstdet as phy state */ | |
816 | state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) | | |
817 | (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh); | |
818 | ||
819 | switch (state) { | |
820 | case PHY_STATE_HS_ONLINE: | |
821 | dev_dbg(&rport->phy->dev, "HS online\n"); | |
822 | break; | |
823 | case PHY_STATE_FS_LS_ONLINE: | |
824 | /* | |
825 | * For FS/LS device, the online state share with connect state | |
826 | * from utmi_ls and utmi_hstdet register, so we distinguish | |
827 | * them via suspended flag. | |
828 | * | |
829 | * Plus, there are two cases, one is D- Line pull-up, and D+ | |
830 | * line pull-down, the state is 4; another is D+ line pull-up, | |
831 | * and D- line pull-down, the state is 2. | |
832 | */ | |
833 | if (!rport->suspended) { | |
834 | /* D- line pull-up, D+ line pull-down */ | |
835 | dev_dbg(&rport->phy->dev, "FS/LS online\n"); | |
836 | break; | |
837 | } | |
838 | /* fall through */ | |
839 | case PHY_STATE_CONNECT: | |
840 | if (rport->suspended) { | |
841 | dev_dbg(&rport->phy->dev, "Connected\n"); | |
842 | rockchip_usb2phy_power_on(rport->phy); | |
843 | rport->suspended = false; | |
844 | } else { | |
845 | /* D+ line pull-up, D- line pull-down */ | |
846 | dev_dbg(&rport->phy->dev, "FS/LS online\n"); | |
847 | } | |
848 | break; | |
849 | case PHY_STATE_DISCONNECT: | |
850 | if (!rport->suspended) { | |
851 | dev_dbg(&rport->phy->dev, "Disconnected\n"); | |
852 | rockchip_usb2phy_power_off(rport->phy); | |
853 | rport->suspended = true; | |
854 | } | |
855 | ||
856 | /* | |
857 | * activate the linestate detection to get the next device | |
858 | * plug-in irq. | |
859 | */ | |
1543645c FW |
860 | property_enable(rphy->grf, &rport->port_cfg->ls_det_clr, true); |
861 | property_enable(rphy->grf, &rport->port_cfg->ls_det_en, true); | |
0e08d2a7 FW |
862 | |
863 | /* | |
864 | * we don't need to rearm the delayed work when the phy port | |
865 | * is suspended. | |
866 | */ | |
867 | mutex_unlock(&rport->mutex); | |
868 | return; | |
869 | default: | |
870 | dev_dbg(&rport->phy->dev, "unknown phy state\n"); | |
871 | break; | |
872 | } | |
873 | ||
874 | next_schedule: | |
875 | mutex_unlock(&rport->mutex); | |
876 | schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); | |
877 | } | |
878 | ||
879 | static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data) | |
880 | { | |
881 | struct rockchip_usb2phy_port *rport = data; | |
882 | struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); | |
883 | ||
1543645c | 884 | if (!property_enabled(rphy->grf, &rport->port_cfg->ls_det_st)) |
0e08d2a7 FW |
885 | return IRQ_NONE; |
886 | ||
887 | mutex_lock(&rport->mutex); | |
888 | ||
889 | /* disable linestate detect irq and clear its status */ | |
1543645c FW |
890 | property_enable(rphy->grf, &rport->port_cfg->ls_det_en, false); |
891 | property_enable(rphy->grf, &rport->port_cfg->ls_det_clr, true); | |
0e08d2a7 FW |
892 | |
893 | mutex_unlock(&rport->mutex); | |
894 | ||
895 | /* | |
896 | * In this case for host phy port, a new device is plugged in, | |
897 | * meanwhile, if the phy port is suspended, we need rearm the work to | |
898 | * resume it and mange its states; otherwise, we do nothing about that. | |
899 | */ | |
900 | if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST) | |
901 | rockchip_usb2phy_sm_work(&rport->sm_work.work); | |
902 | ||
903 | return IRQ_HANDLED; | |
904 | } | |
905 | ||
98898f3b WW |
906 | static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) |
907 | { | |
908 | struct rockchip_usb2phy_port *rport = data; | |
909 | struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); | |
910 | ||
1543645c | 911 | if (!property_enabled(rphy->grf, &rport->port_cfg->bvalid_det_st)) |
98898f3b WW |
912 | return IRQ_NONE; |
913 | ||
914 | mutex_lock(&rport->mutex); | |
915 | ||
916 | /* clear bvalid detect irq pending status */ | |
1543645c | 917 | property_enable(rphy->grf, &rport->port_cfg->bvalid_det_clr, true); |
98898f3b WW |
918 | |
919 | mutex_unlock(&rport->mutex); | |
920 | ||
921 | rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); | |
922 | ||
923 | return IRQ_HANDLED; | |
924 | } | |
925 | ||
0983e2ab FW |
926 | static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data) |
927 | { | |
928 | struct rockchip_usb2phy_port *rport = data; | |
929 | struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); | |
930 | ||
931 | if (property_enabled(rphy->grf, &rport->port_cfg->bvalid_det_st)) | |
932 | return rockchip_usb2phy_bvalid_irq(irq, data); | |
933 | else | |
934 | return IRQ_NONE; | |
935 | } | |
936 | ||
0e08d2a7 FW |
937 | static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, |
938 | struct rockchip_usb2phy_port *rport, | |
939 | struct device_node *child_np) | |
940 | { | |
941 | int ret; | |
942 | ||
943 | rport->port_id = USB2PHY_PORT_HOST; | |
944 | rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST]; | |
945 | rport->suspended = true; | |
946 | ||
947 | mutex_init(&rport->mutex); | |
948 | INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work); | |
949 | ||
950 | rport->ls_irq = of_irq_get_byname(child_np, "linestate"); | |
951 | if (rport->ls_irq < 0) { | |
952 | dev_err(rphy->dev, "no linestate irq provided\n"); | |
953 | return rport->ls_irq; | |
954 | } | |
955 | ||
956 | ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL, | |
957 | rockchip_usb2phy_linestate_irq, | |
958 | IRQF_ONESHOT, | |
959 | "rockchip_usb2phy", rport); | |
960 | if (ret) { | |
98898f3b | 961 | dev_err(rphy->dev, "failed to request linestate irq handle\n"); |
0e08d2a7 FW |
962 | return ret; |
963 | } | |
964 | ||
965 | return 0; | |
966 | } | |
967 | ||
98898f3b WW |
968 | static int rockchip_otg_event(struct notifier_block *nb, |
969 | unsigned long event, void *ptr) | |
970 | { | |
971 | struct rockchip_usb2phy_port *rport = | |
972 | container_of(nb, struct rockchip_usb2phy_port, event_nb); | |
973 | ||
974 | schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY); | |
975 | ||
976 | return NOTIFY_DONE; | |
977 | } | |
978 | ||
979 | static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, | |
980 | struct rockchip_usb2phy_port *rport, | |
981 | struct device_node *child_np) | |
982 | { | |
983 | int ret; | |
984 | ||
985 | rport->port_id = USB2PHY_PORT_OTG; | |
986 | rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG]; | |
987 | rport->state = OTG_STATE_UNDEFINED; | |
988 | ||
989 | /* | |
990 | * set suspended flag to true, but actually don't | |
991 | * put phy in suspend mode, it aims to enable usb | |
992 | * phy and clock in power_on() called by usb controller | |
993 | * driver during probe. | |
994 | */ | |
995 | rport->suspended = true; | |
996 | rport->vbus_attached = false; | |
997 | ||
998 | mutex_init(&rport->mutex); | |
999 | ||
1000 | rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1); | |
96327811 WW |
1001 | if (rport->mode == USB_DR_MODE_HOST || |
1002 | rport->mode == USB_DR_MODE_UNKNOWN) { | |
98898f3b WW |
1003 | ret = 0; |
1004 | goto out; | |
1005 | } | |
1006 | ||
1007 | INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); | |
1008 | INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); | |
1009 | ||
0983e2ab FW |
1010 | /* |
1011 | * Some SoCs use one interrupt with otg-id/otg-bvalid/linestate | |
1012 | * interrupts muxed together, so probe the otg-mux interrupt first, | |
1013 | * if not found, then look for the regular interrupts one by one. | |
1014 | */ | |
1015 | rport->otg_mux_irq = of_irq_get_byname(child_np, "otg-mux"); | |
1016 | if (rport->otg_mux_irq > 0) { | |
1017 | ret = devm_request_threaded_irq(rphy->dev, rport->otg_mux_irq, | |
1018 | NULL, | |
1019 | rockchip_usb2phy_otg_mux_irq, | |
1020 | IRQF_ONESHOT, | |
1021 | "rockchip_usb2phy_otg", | |
1022 | rport); | |
1023 | if (ret) { | |
1024 | dev_err(rphy->dev, | |
1025 | "failed to request otg-mux irq handle\n"); | |
1026 | goto out; | |
1027 | } | |
1028 | } else { | |
1029 | rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); | |
1030 | if (rport->bvalid_irq < 0) { | |
1031 | dev_err(rphy->dev, "no vbus valid irq provided\n"); | |
1032 | ret = rport->bvalid_irq; | |
1033 | goto out; | |
1034 | } | |
98898f3b | 1035 | |
0983e2ab FW |
1036 | ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, |
1037 | NULL, | |
1038 | rockchip_usb2phy_bvalid_irq, | |
1039 | IRQF_ONESHOT, | |
1040 | "rockchip_usb2phy_bvalid", | |
1041 | rport); | |
1042 | if (ret) { | |
1043 | dev_err(rphy->dev, | |
1044 | "failed to request otg-bvalid irq handle\n"); | |
1045 | goto out; | |
1046 | } | |
98898f3b WW |
1047 | } |
1048 | ||
1049 | if (!IS_ERR(rphy->edev)) { | |
1050 | rport->event_nb.notifier_call = rockchip_otg_event; | |
1051 | ||
86f44c88 CC |
1052 | ret = devm_extcon_register_notifier(rphy->dev, rphy->edev, |
1053 | EXTCON_USB_HOST, &rport->event_nb); | |
98898f3b WW |
1054 | if (ret) |
1055 | dev_err(rphy->dev, "register USB HOST notifier failed\n"); | |
1056 | } | |
1057 | ||
1058 | out: | |
1059 | return ret; | |
1060 | } | |
1061 | ||
0e08d2a7 FW |
1062 | static int rockchip_usb2phy_probe(struct platform_device *pdev) |
1063 | { | |
1064 | struct device *dev = &pdev->dev; | |
1065 | struct device_node *np = dev->of_node; | |
1066 | struct device_node *child_np; | |
1067 | struct phy_provider *provider; | |
1068 | struct rockchip_usb2phy *rphy; | |
1069 | const struct rockchip_usb2phy_cfg *phy_cfgs; | |
1070 | const struct of_device_id *match; | |
1071 | unsigned int reg; | |
1072 | int index, ret; | |
1073 | ||
1074 | rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL); | |
1075 | if (!rphy) | |
1076 | return -ENOMEM; | |
1077 | ||
1078 | match = of_match_device(dev->driver->of_match_table, dev); | |
1079 | if (!match || !match->data) { | |
1080 | dev_err(dev, "phy configs are not assigned!\n"); | |
1081 | return -EINVAL; | |
1082 | } | |
1083 | ||
1084 | if (!dev->parent || !dev->parent->of_node) | |
1085 | return -EINVAL; | |
1086 | ||
1087 | rphy->grf = syscon_node_to_regmap(dev->parent->of_node); | |
1088 | if (IS_ERR(rphy->grf)) | |
1089 | return PTR_ERR(rphy->grf); | |
1090 | ||
1543645c FW |
1091 | if (of_device_is_compatible(np, "rockchip,rv1108-usb2phy")) { |
1092 | rphy->usbgrf = | |
1093 | syscon_regmap_lookup_by_phandle(dev->of_node, | |
1094 | "rockchip,usbgrf"); | |
1095 | if (IS_ERR(rphy->usbgrf)) | |
1096 | return PTR_ERR(rphy->usbgrf); | |
1097 | } else { | |
1098 | rphy->usbgrf = NULL; | |
1099 | } | |
1100 | ||
0e08d2a7 | 1101 | if (of_property_read_u32(np, "reg", ®)) { |
ac9ba7dc RH |
1102 | dev_err(dev, "the reg property is not assigned in %pOFn node\n", |
1103 | np); | |
0e08d2a7 FW |
1104 | return -EINVAL; |
1105 | } | |
1106 | ||
1107 | rphy->dev = dev; | |
1108 | phy_cfgs = match->data; | |
98898f3b WW |
1109 | rphy->chg_state = USB_CHG_STATE_UNDEFINED; |
1110 | rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; | |
0e08d2a7 FW |
1111 | platform_set_drvdata(pdev, rphy); |
1112 | ||
98898f3b WW |
1113 | ret = rockchip_usb2phy_extcon_register(rphy); |
1114 | if (ret) | |
1115 | return ret; | |
1116 | ||
0e08d2a7 FW |
1117 | /* find out a proper config which can be matched with dt. */ |
1118 | index = 0; | |
1119 | while (phy_cfgs[index].reg) { | |
1120 | if (phy_cfgs[index].reg == reg) { | |
1121 | rphy->phy_cfg = &phy_cfgs[index]; | |
1122 | break; | |
1123 | } | |
1124 | ||
1125 | ++index; | |
1126 | } | |
1127 | ||
1128 | if (!rphy->phy_cfg) { | |
ac9ba7dc RH |
1129 | dev_err(dev, "no phy-config can be matched with %pOFn node\n", |
1130 | np); | |
0e08d2a7 FW |
1131 | return -EINVAL; |
1132 | } | |
1133 | ||
1134 | rphy->clk = of_clk_get_by_name(np, "phyclk"); | |
1135 | if (!IS_ERR(rphy->clk)) { | |
1136 | clk_prepare_enable(rphy->clk); | |
1137 | } else { | |
1138 | dev_info(&pdev->dev, "no phyclk specified\n"); | |
1139 | rphy->clk = NULL; | |
1140 | } | |
1141 | ||
1142 | ret = rockchip_usb2phy_clk480m_register(rphy); | |
1143 | if (ret) { | |
1144 | dev_err(dev, "failed to register 480m output clock\n"); | |
1145 | goto disable_clks; | |
1146 | } | |
1147 | ||
1148 | index = 0; | |
1149 | for_each_available_child_of_node(np, child_np) { | |
1150 | struct rockchip_usb2phy_port *rport = &rphy->ports[index]; | |
1151 | struct phy *phy; | |
1152 | ||
98898f3b | 1153 | /* This driver aims to support both otg-port and host-port */ |
03e7d002 RH |
1154 | if (!of_node_name_eq(child_np, "host-port") && |
1155 | !of_node_name_eq(child_np, "otg-port")) | |
0e08d2a7 FW |
1156 | goto next_child; |
1157 | ||
1158 | phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); | |
1159 | if (IS_ERR(phy)) { | |
1160 | dev_err(dev, "failed to create phy\n"); | |
1161 | ret = PTR_ERR(phy); | |
1162 | goto put_child; | |
1163 | } | |
1164 | ||
1165 | rport->phy = phy; | |
1166 | phy_set_drvdata(rport->phy, rport); | |
1167 | ||
98898f3b | 1168 | /* initialize otg/host port separately */ |
03e7d002 | 1169 | if (of_node_name_eq(child_np, "host-port")) { |
98898f3b WW |
1170 | ret = rockchip_usb2phy_host_port_init(rphy, rport, |
1171 | child_np); | |
1172 | if (ret) | |
1173 | goto put_child; | |
1174 | } else { | |
1175 | ret = rockchip_usb2phy_otg_port_init(rphy, rport, | |
1176 | child_np); | |
1177 | if (ret) | |
1178 | goto put_child; | |
1179 | } | |
0e08d2a7 FW |
1180 | |
1181 | next_child: | |
1182 | /* to prevent out of boundary */ | |
1183 | if (++index >= rphy->phy_cfg->num_ports) | |
1184 | break; | |
1185 | } | |
1186 | ||
1187 | provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | |
1188 | return PTR_ERR_OR_ZERO(provider); | |
1189 | ||
1190 | put_child: | |
1191 | of_node_put(child_np); | |
1192 | disable_clks: | |
1193 | if (rphy->clk) { | |
1194 | clk_disable_unprepare(rphy->clk); | |
1195 | clk_put(rphy->clk); | |
1196 | } | |
1197 | return ret; | |
1198 | } | |
1199 | ||
b59b1d39 FW |
1200 | static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { |
1201 | { | |
1202 | .reg = 0x760, | |
1203 | .num_ports = 2, | |
1204 | .clkout_ctl = { 0x0768, 4, 4, 1, 0 }, | |
1205 | .port_cfgs = { | |
1206 | [USB2PHY_PORT_OTG] = { | |
1207 | .phy_sus = { 0x0760, 15, 0, 0, 0x1d1 }, | |
1208 | .bvalid_det_en = { 0x0680, 3, 3, 0, 1 }, | |
1209 | .bvalid_det_st = { 0x0690, 3, 3, 0, 1 }, | |
1210 | .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 }, | |
1211 | .ls_det_en = { 0x0680, 2, 2, 0, 1 }, | |
1212 | .ls_det_st = { 0x0690, 2, 2, 0, 1 }, | |
1213 | .ls_det_clr = { 0x06a0, 2, 2, 0, 1 }, | |
1214 | .utmi_bvalid = { 0x0480, 4, 4, 0, 1 }, | |
1215 | .utmi_ls = { 0x0480, 3, 2, 0, 1 }, | |
1216 | }, | |
1217 | [USB2PHY_PORT_HOST] = { | |
1218 | .phy_sus = { 0x0764, 15, 0, 0, 0x1d1 }, | |
1219 | .ls_det_en = { 0x0680, 4, 4, 0, 1 }, | |
1220 | .ls_det_st = { 0x0690, 4, 4, 0, 1 }, | |
1221 | .ls_det_clr = { 0x06a0, 4, 4, 0, 1 } | |
1222 | } | |
1223 | }, | |
1224 | .chg_det = { | |
1225 | .opmode = { 0x0760, 3, 0, 5, 1 }, | |
1226 | .cp_det = { 0x0884, 4, 4, 0, 1 }, | |
1227 | .dcp_det = { 0x0884, 3, 3, 0, 1 }, | |
1228 | .dp_det = { 0x0884, 5, 5, 0, 1 }, | |
1229 | .idm_sink_en = { 0x0768, 8, 8, 0, 1 }, | |
1230 | .idp_sink_en = { 0x0768, 7, 7, 0, 1 }, | |
1231 | .idp_src_en = { 0x0768, 9, 9, 0, 1 }, | |
1232 | .rdm_pdwn_en = { 0x0768, 10, 10, 0, 1 }, | |
1233 | .vdm_src_en = { 0x0768, 12, 12, 0, 1 }, | |
1234 | .vdp_src_en = { 0x0768, 11, 11, 0, 1 }, | |
1235 | }, | |
1236 | }, | |
1237 | { | |
1238 | .reg = 0x800, | |
1239 | .num_ports = 2, | |
1240 | .clkout_ctl = { 0x0808, 4, 4, 1, 0 }, | |
1241 | .port_cfgs = { | |
1242 | [USB2PHY_PORT_OTG] = { | |
1243 | .phy_sus = { 0x800, 15, 0, 0, 0x1d1 }, | |
1244 | .ls_det_en = { 0x0684, 0, 0, 0, 1 }, | |
1245 | .ls_det_st = { 0x0694, 0, 0, 0, 1 }, | |
1246 | .ls_det_clr = { 0x06a4, 0, 0, 0, 1 } | |
1247 | }, | |
1248 | [USB2PHY_PORT_HOST] = { | |
1249 | .phy_sus = { 0x804, 15, 0, 0, 0x1d1 }, | |
1250 | .ls_det_en = { 0x0684, 1, 1, 0, 1 }, | |
1251 | .ls_det_st = { 0x0694, 1, 1, 0, 1 }, | |
1252 | .ls_det_clr = { 0x06a4, 1, 1, 0, 1 } | |
1253 | } | |
1254 | }, | |
1255 | }, | |
1256 | { /* sentinel */ } | |
1257 | }; | |
1258 | ||
d99b1ab3 MD |
1259 | static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { |
1260 | { | |
1261 | .reg = 0x100, | |
1262 | .num_ports = 2, | |
1263 | .clkout_ctl = { 0x108, 4, 4, 1, 0 }, | |
1264 | .port_cfgs = { | |
1265 | [USB2PHY_PORT_OTG] = { | |
1266 | .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, | |
1267 | .bvalid_det_en = { 0x0110, 2, 2, 0, 1 }, | |
1268 | .bvalid_det_st = { 0x0114, 2, 2, 0, 1 }, | |
1269 | .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 }, | |
1270 | .ls_det_en = { 0x0110, 0, 0, 0, 1 }, | |
1271 | .ls_det_st = { 0x0114, 0, 0, 0, 1 }, | |
1272 | .ls_det_clr = { 0x0118, 0, 0, 0, 1 }, | |
1273 | .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, | |
1274 | .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, | |
1275 | .utmi_ls = { 0x0120, 5, 4, 0, 1 }, | |
1276 | }, | |
1277 | [USB2PHY_PORT_HOST] = { | |
1278 | .phy_sus = { 0x104, 15, 0, 0, 0x1d1 }, | |
1279 | .ls_det_en = { 0x110, 1, 1, 0, 1 }, | |
1280 | .ls_det_st = { 0x114, 1, 1, 0, 1 }, | |
1281 | .ls_det_clr = { 0x118, 1, 1, 0, 1 }, | |
1282 | .utmi_ls = { 0x120, 17, 16, 0, 1 }, | |
1283 | .utmi_hstdet = { 0x120, 19, 19, 0, 1 } | |
1284 | } | |
1285 | }, | |
1286 | .chg_det = { | |
1287 | .opmode = { 0x0100, 3, 0, 5, 1 }, | |
1288 | .cp_det = { 0x0120, 24, 24, 0, 1 }, | |
1289 | .dcp_det = { 0x0120, 23, 23, 0, 1 }, | |
1290 | .dp_det = { 0x0120, 25, 25, 0, 1 }, | |
1291 | .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, | |
1292 | .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, | |
1293 | .idp_src_en = { 0x0108, 9, 9, 0, 1 }, | |
1294 | .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, | |
1295 | .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, | |
1296 | .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, | |
1297 | }, | |
1298 | }, | |
1299 | { /* sentinel */ } | |
1300 | }; | |
1301 | ||
0e08d2a7 FW |
1302 | static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = { |
1303 | { | |
1304 | .reg = 0x700, | |
1305 | .num_ports = 2, | |
1306 | .clkout_ctl = { 0x0724, 15, 15, 1, 0 }, | |
1307 | .port_cfgs = { | |
1308 | [USB2PHY_PORT_HOST] = { | |
1309 | .phy_sus = { 0x0728, 15, 0, 0, 0x1d1 }, | |
1310 | .ls_det_en = { 0x0680, 4, 4, 0, 1 }, | |
1311 | .ls_det_st = { 0x0690, 4, 4, 0, 1 }, | |
1312 | .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }, | |
1313 | .utmi_ls = { 0x049c, 14, 13, 0, 1 }, | |
1314 | .utmi_hstdet = { 0x049c, 12, 12, 0, 1 } | |
1315 | } | |
1316 | }, | |
1317 | }, | |
1318 | { /* sentinel */ } | |
1319 | }; | |
1320 | ||
1321 | static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { | |
1322 | { | |
98898f3b | 1323 | .reg = 0xe450, |
0e08d2a7 FW |
1324 | .num_ports = 2, |
1325 | .clkout_ctl = { 0xe450, 4, 4, 1, 0 }, | |
1326 | .port_cfgs = { | |
98898f3b WW |
1327 | [USB2PHY_PORT_OTG] = { |
1328 | .phy_sus = { 0xe454, 1, 0, 2, 1 }, | |
1329 | .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, | |
1330 | .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, | |
1331 | .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, | |
1332 | .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 }, | |
1333 | .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 }, | |
1334 | }, | |
0e08d2a7 FW |
1335 | [USB2PHY_PORT_HOST] = { |
1336 | .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 }, | |
1337 | .ls_det_en = { 0xe3c0, 6, 6, 0, 1 }, | |
1338 | .ls_det_st = { 0xe3e0, 6, 6, 0, 1 }, | |
1339 | .ls_det_clr = { 0xe3d0, 6, 6, 0, 1 }, | |
1340 | .utmi_ls = { 0xe2ac, 22, 21, 0, 1 }, | |
1341 | .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 } | |
1342 | } | |
1343 | }, | |
98898f3b WW |
1344 | .chg_det = { |
1345 | .opmode = { 0xe454, 3, 0, 5, 1 }, | |
1346 | .cp_det = { 0xe2ac, 2, 2, 0, 1 }, | |
1347 | .dcp_det = { 0xe2ac, 1, 1, 0, 1 }, | |
1348 | .dp_det = { 0xe2ac, 0, 0, 0, 1 }, | |
1349 | .idm_sink_en = { 0xe450, 8, 8, 0, 1 }, | |
1350 | .idp_sink_en = { 0xe450, 7, 7, 0, 1 }, | |
1351 | .idp_src_en = { 0xe450, 9, 9, 0, 1 }, | |
1352 | .rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 }, | |
1353 | .vdm_src_en = { 0xe450, 12, 12, 0, 1 }, | |
1354 | .vdp_src_en = { 0xe450, 11, 11, 0, 1 }, | |
1355 | }, | |
0e08d2a7 FW |
1356 | }, |
1357 | { | |
98898f3b | 1358 | .reg = 0xe460, |
0e08d2a7 FW |
1359 | .num_ports = 2, |
1360 | .clkout_ctl = { 0xe460, 4, 4, 1, 0 }, | |
1361 | .port_cfgs = { | |
98898f3b WW |
1362 | [USB2PHY_PORT_OTG] = { |
1363 | .phy_sus = { 0xe464, 1, 0, 2, 1 }, | |
1364 | .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, | |
1365 | .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, | |
1366 | .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 }, | |
1367 | .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 }, | |
1368 | .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 }, | |
1369 | }, | |
0e08d2a7 FW |
1370 | [USB2PHY_PORT_HOST] = { |
1371 | .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 }, | |
1372 | .ls_det_en = { 0xe3c0, 11, 11, 0, 1 }, | |
1373 | .ls_det_st = { 0xe3e0, 11, 11, 0, 1 }, | |
1374 | .ls_det_clr = { 0xe3d0, 11, 11, 0, 1 }, | |
1375 | .utmi_ls = { 0xe2ac, 26, 25, 0, 1 }, | |
1376 | .utmi_hstdet = { 0xe2ac, 27, 27, 0, 1 } | |
1377 | } | |
1378 | }, | |
1379 | }, | |
1380 | { /* sentinel */ } | |
1381 | }; | |
1382 | ||
fc938810 FW |
1383 | static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { |
1384 | { | |
1385 | .reg = 0x100, | |
1386 | .num_ports = 2, | |
1387 | .clkout_ctl = { 0x108, 4, 4, 1, 0 }, | |
1388 | .port_cfgs = { | |
1389 | [USB2PHY_PORT_OTG] = { | |
1390 | .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, | |
1391 | .bvalid_det_en = { 0x0680, 3, 3, 0, 1 }, | |
1392 | .bvalid_det_st = { 0x0690, 3, 3, 0, 1 }, | |
1393 | .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 }, | |
1394 | .ls_det_en = { 0x0680, 2, 2, 0, 1 }, | |
1395 | .ls_det_st = { 0x0690, 2, 2, 0, 1 }, | |
1396 | .ls_det_clr = { 0x06a0, 2, 2, 0, 1 }, | |
1397 | .utmi_bvalid = { 0x0804, 10, 10, 0, 1 }, | |
1398 | .utmi_ls = { 0x0804, 13, 12, 0, 1 }, | |
1399 | }, | |
1400 | [USB2PHY_PORT_HOST] = { | |
1401 | .phy_sus = { 0x0104, 15, 0, 0, 0x1d1 }, | |
1402 | .ls_det_en = { 0x0680, 4, 4, 0, 1 }, | |
1403 | .ls_det_st = { 0x0690, 4, 4, 0, 1 }, | |
1404 | .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }, | |
1405 | .utmi_ls = { 0x0804, 9, 8, 0, 1 }, | |
1406 | .utmi_hstdet = { 0x0804, 7, 7, 0, 1 } | |
1407 | } | |
1408 | }, | |
1409 | .chg_det = { | |
1410 | .opmode = { 0x0100, 3, 0, 5, 1 }, | |
1411 | .cp_det = { 0x0804, 1, 1, 0, 1 }, | |
1412 | .dcp_det = { 0x0804, 0, 0, 0, 1 }, | |
1413 | .dp_det = { 0x0804, 2, 2, 0, 1 }, | |
1414 | .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, | |
1415 | .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, | |
1416 | .idp_src_en = { 0x0108, 9, 9, 0, 1 }, | |
1417 | .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, | |
1418 | .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, | |
1419 | .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, | |
1420 | }, | |
1421 | }, | |
1422 | { /* sentinel */ } | |
1423 | }; | |
1424 | ||
0e08d2a7 | 1425 | static const struct of_device_id rockchip_usb2phy_dt_match[] = { |
b59b1d39 | 1426 | { .compatible = "rockchip,rk3228-usb2phy", .data = &rk3228_phy_cfgs }, |
d99b1ab3 | 1427 | { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, |
0e08d2a7 FW |
1428 | { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, |
1429 | { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, | |
fc938810 | 1430 | { .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs }, |
0e08d2a7 FW |
1431 | {} |
1432 | }; | |
1433 | MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match); | |
1434 | ||
1435 | static struct platform_driver rockchip_usb2phy_driver = { | |
1436 | .probe = rockchip_usb2phy_probe, | |
1437 | .driver = { | |
1438 | .name = "rockchip-usb2phy", | |
1439 | .of_match_table = rockchip_usb2phy_dt_match, | |
1440 | }, | |
1441 | }; | |
1442 | module_platform_driver(rockchip_usb2phy_driver); | |
1443 | ||
1444 | MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>"); | |
1445 | MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver"); | |
1446 | MODULE_LICENSE("GPL v2"); |