1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Rockchip USB2.0 PHY with Innosilicon IP block driver |
4 | * |
5 | * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/extcon-provider.h> |
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_irq.h> |
21 | #include <linux/phy/phy.h> |
22 | #include <linux/platform_device.h> |
23 | #include <linux/power_supply.h> |
24 | #include <linux/regmap.h> |
25 | #include <linux/reset.h> |
26 | #include <linux/mfd/syscon.h> |
27 | #include <linux/usb/of.h> |
28 | #include <linux/usb/otg.h> |
29 | |
30 | #define BIT_WRITEABLE_SHIFT 16 |
31 | #define SCHEDULE_DELAY (60 * HZ) |
32 | #define OTG_SCHEDULE_DELAY (2 * HZ) |
33 | |
34 | struct rockchip_usb2phy; |
35 | |
36 | enum rockchip_usb2phy_port_id { |
37 | USB2PHY_PORT_OTG, |
38 | USB2PHY_PORT_HOST, |
39 | USB2PHY_NUM_PORTS, |
40 | }; |
41 | |
42 | enum rockchip_usb2phy_host_state { |
43 | PHY_STATE_HS_ONLINE = 0, |
44 | PHY_STATE_DISCONNECT = 1, |
45 | PHY_STATE_CONNECT = 2, |
46 | PHY_STATE_FS_LS_ONLINE = 4, |
47 | }; |
48 | |
49 | /** |
50 | * enum usb_chg_state - Different states involved in USB charger detection. |
51 | * @USB_CHG_STATE_UNDEFINED: USB charger is not connected or detection |
52 | * process is not yet started. |
53 | * @USB_CHG_STATE_WAIT_FOR_DCD: Waiting for Data pins contact. |
54 | * @USB_CHG_STATE_DCD_DONE: Data pin contact is detected. |
55 | * @USB_CHG_STATE_PRIMARY_DONE: Primary detection is completed (Detects |
56 | * between SDP and DCP/CDP). |
57 | * @USB_CHG_STATE_SECONDARY_DONE: Secondary detection is completed (Detects |
58 | * between DCP and CDP). |
59 | * @USB_CHG_STATE_DETECTED: USB charger type is determined. |
60 | */ |
61 | enum usb_chg_state { |
62 | USB_CHG_STATE_UNDEFINED = 0, |
63 | USB_CHG_STATE_WAIT_FOR_DCD, |
64 | USB_CHG_STATE_DCD_DONE, |
65 | USB_CHG_STATE_PRIMARY_DONE, |
66 | USB_CHG_STATE_SECONDARY_DONE, |
67 | USB_CHG_STATE_DETECTED, |
68 | }; |
69 | |
70 | static const unsigned int rockchip_usb2phy_extcon_cable[] = { |
71 | EXTCON_USB, |
72 | EXTCON_USB_HOST, |
73 | EXTCON_CHG_USB_SDP, |
74 | EXTCON_CHG_USB_CDP, |
75 | EXTCON_CHG_USB_DCP, |
76 | EXTCON_CHG_USB_SLOW, |
77 | EXTCON_NONE, |
78 | }; |
79 | |
80 | struct usb2phy_reg { |
81 | unsigned int offset; |
82 | unsigned int bitend; |
83 | unsigned int bitstart; |
84 | unsigned int disable; |
85 | unsigned int enable; |
86 | }; |
87 | |
88 | /** |
89 | * struct rockchip_chg_det_reg - usb charger detect registers |
90 | * @cp_det: charging port detected successfully. |
91 | * @dcp_det: dedicated charging port detected successfully. |
92 | * @dp_det: assert data pin connect successfully. |
93 | * @idm_sink_en: open dm sink curren. |
94 | * @idp_sink_en: open dp sink current. |
95 | * @idp_src_en: open dm source current. |
96 | * @rdm_pdwn_en: open dm pull down resistor. |
97 | * @vdm_src_en: open dm voltage source. |
98 | * @vdp_src_en: open dp voltage source. |
99 | * @opmode: utmi operational mode. |
100 | */ |
101 | struct rockchip_chg_det_reg { |
102 | struct usb2phy_reg cp_det; |
103 | struct usb2phy_reg dcp_det; |
104 | struct usb2phy_reg dp_det; |
105 | struct usb2phy_reg idm_sink_en; |
106 | struct usb2phy_reg idp_sink_en; |
107 | struct usb2phy_reg idp_src_en; |
108 | struct usb2phy_reg rdm_pdwn_en; |
109 | struct usb2phy_reg vdm_src_en; |
110 | struct usb2phy_reg vdp_src_en; |
111 | struct usb2phy_reg opmode; |
112 | }; |
113 | |
114 | /** |
115 | * struct rockchip_usb2phy_port_cfg - usb-phy port configuration. |
116 | * @phy_sus: phy suspend register. |
117 | * @bvalid_det_en: vbus valid rise detection enable register. |
118 | * @bvalid_det_st: vbus valid rise detection status register. |
119 | * @bvalid_det_clr: vbus valid rise detection clear register. |
120 | * @disfall_en: host disconnect fall edge detection enable. |
121 | * @disfall_st: host disconnect fall edge detection state. |
122 | * @disfall_clr: host disconnect fall edge detection clear. |
123 | * @disrise_en: host disconnect rise edge detection enable. |
124 | * @disrise_st: host disconnect rise edge detection state. |
125 | * @disrise_clr: host disconnect rise edge detection clear. |
126 | * @idfall_det_en: id detection enable register, falling edge |
127 | * @idfall_det_st: id detection state register, falling edge |
128 | * @idfall_det_clr: id detection clear register, falling edge |
129 | * @idrise_det_en: id detection enable register, rising edge |
130 | * @idrise_det_st: id detection state register, rising edge |
131 | * @idrise_det_clr: id detection clear register, rising edge |
132 | * @ls_det_en: linestate detection enable register. |
133 | * @ls_det_st: linestate detection state register. |
134 | * @ls_det_clr: linestate detection clear register. |
135 | * @utmi_avalid: utmi vbus avalid status register. |
136 | * @utmi_bvalid: utmi vbus bvalid status register. |
137 | * @utmi_id: utmi id state register. |
138 | * @utmi_ls: utmi linestate state register. |
139 | * @utmi_hstdet: utmi host disconnect register. |
140 | */ |
141 | struct rockchip_usb2phy_port_cfg { |
142 | struct usb2phy_reg phy_sus; |
143 | struct usb2phy_reg bvalid_det_en; |
144 | struct usb2phy_reg bvalid_det_st; |
145 | struct usb2phy_reg bvalid_det_clr; |
146 | struct usb2phy_reg disfall_en; |
147 | struct usb2phy_reg disfall_st; |
148 | struct usb2phy_reg disfall_clr; |
149 | struct usb2phy_reg disrise_en; |
150 | struct usb2phy_reg disrise_st; |
151 | struct usb2phy_reg disrise_clr; |
152 | struct usb2phy_reg idfall_det_en; |
153 | struct usb2phy_reg idfall_det_st; |
154 | struct usb2phy_reg idfall_det_clr; |
155 | struct usb2phy_reg idrise_det_en; |
156 | struct usb2phy_reg idrise_det_st; |
157 | struct usb2phy_reg idrise_det_clr; |
158 | struct usb2phy_reg ls_det_en; |
159 | struct usb2phy_reg ls_det_st; |
160 | struct usb2phy_reg ls_det_clr; |
161 | struct usb2phy_reg utmi_avalid; |
162 | struct usb2phy_reg utmi_bvalid; |
163 | struct usb2phy_reg utmi_id; |
164 | struct usb2phy_reg utmi_ls; |
165 | struct usb2phy_reg utmi_hstdet; |
166 | }; |
167 | |
168 | /** |
169 | * struct rockchip_usb2phy_cfg - usb-phy configuration. |
170 | * @reg: the address offset of grf for usb-phy config. |
171 | * @num_ports: specify how many ports that the phy has. |
172 | * @phy_tuning: phy default parameters tuning. |
173 | * @clkout_ctl: keep on/turn off output clk of phy. |
174 | * @port_cfgs: usb-phy port configurations. |
175 | * @chg_det: charger detection registers. |
176 | */ |
177 | struct rockchip_usb2phy_cfg { |
178 | unsigned int reg; |
179 | unsigned int num_ports; |
180 | int (*phy_tuning)(struct rockchip_usb2phy *rphy); |
181 | struct usb2phy_reg clkout_ctl; |
182 | const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; |
183 | const struct rockchip_chg_det_reg chg_det; |
184 | }; |
185 | |
186 | /** |
187 | * struct rockchip_usb2phy_port - usb-phy port data. |
188 | * @phy: generic phy. |
189 | * @port_id: flag for otg port or host port. |
190 | * @suspended: phy suspended flag. |
191 | * @vbus_attached: otg device vbus status. |
192 | * @host_disconnect: usb host disconnect status. |
193 | * @bvalid_irq: IRQ number assigned for vbus valid rise detection. |
194 | * @id_irq: IRQ number assigned for ID pin detection. |
195 | * @ls_irq: IRQ number assigned for linestate detection. |
196 | * @otg_mux_irq: IRQ number which multiplex otg-id/otg-bvalid/linestate |
197 | * irqs to one irq in otg-port. |
198 | * @mutex: for register updating in sm_work. |
199 | * @chg_work: charge detect work. |
200 | * @otg_sm_work: OTG state machine work. |
201 | * @sm_work: HOST state machine work. |
202 | * @port_cfg: port register configuration, assigned by driver data. |
203 | * @event_nb: hold event notification callback. |
204 | * @state: define OTG enumeration states before device reset. |
205 | * @mode: the dr_mode of the controller. |
206 | */ |
207 | struct rockchip_usb2phy_port { |
208 | struct phy *phy; |
209 | unsigned int port_id; |
210 | bool suspended; |
211 | bool vbus_attached; |
212 | bool host_disconnect; |
213 | int bvalid_irq; |
214 | int id_irq; |
215 | int ls_irq; |
216 | int otg_mux_irq; |
217 | struct mutex mutex; |
218 | struct delayed_work chg_work; |
219 | struct delayed_work otg_sm_work; |
220 | struct delayed_work sm_work; |
221 | const struct rockchip_usb2phy_port_cfg *port_cfg; |
222 | struct notifier_block event_nb; |
223 | enum usb_otg_state state; |
224 | enum usb_dr_mode mode; |
225 | }; |
226 | |
227 | /** |
228 | * struct rockchip_usb2phy - usb2.0 phy driver data. |
229 | * @dev: pointer to device. |
230 | * @grf: General Register Files regmap. |
231 | * @usbgrf: USB General Register Files regmap. |
232 | * @clk: clock struct of phy input clk. |
233 | * @clk480m: clock struct of phy output clk. |
234 | * @clk480m_hw: clock struct of phy output clk management. |
235 | * @phy_reset: phy reset control. |
236 | * @chg_state: states involved in USB charger detection. |
237 | * @chg_type: USB charger types. |
238 | * @dcd_retries: The retry count used to track Data contact |
239 | * detection process. |
240 | * @edev: extcon device for notification registration |
241 | * @irq: muxed interrupt for single irq configuration |
242 | * @phy_cfg: phy register configuration, assigned by driver data. |
243 | * @ports: phy port instance. |
244 | */ |
245 | struct rockchip_usb2phy { |
246 | struct device *dev; |
247 | struct regmap *grf; |
248 | struct regmap *usbgrf; |
249 | struct clk *clk; |
250 | struct clk *clk480m; |
251 | struct clk_hw clk480m_hw; |
252 | struct reset_control *phy_reset; |
253 | enum usb_chg_state chg_state; |
254 | enum power_supply_type chg_type; |
255 | u8 dcd_retries; |
256 | struct extcon_dev *edev; |
257 | int irq; |
258 | const struct rockchip_usb2phy_cfg *phy_cfg; |
259 | struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; |
260 | }; |
261 | |
262 | static inline struct regmap *get_reg_base(struct rockchip_usb2phy *rphy) |
263 | { |
264 | return rphy->usbgrf == NULL ? rphy->grf : rphy->usbgrf; |
265 | } |
266 | |
267 | static inline int property_enable(struct regmap *base, |
268 | const struct usb2phy_reg *reg, bool en) |
269 | { |
270 | unsigned int val, mask, tmp; |
271 | |
272 | tmp = en ? reg->enable : reg->disable; |
273 | mask = GENMASK(reg->bitend, reg->bitstart); |
274 | val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); |
275 | |
276 | return regmap_write(map: base, reg: reg->offset, val); |
277 | } |
278 | |
279 | static inline bool property_enabled(struct regmap *base, |
280 | const struct usb2phy_reg *reg) |
281 | { |
282 | int ret; |
283 | unsigned int tmp, orig; |
284 | unsigned int mask = GENMASK(reg->bitend, reg->bitstart); |
285 | |
286 | ret = regmap_read(map: base, reg: reg->offset, val: &orig); |
287 | if (ret) |
288 | return false; |
289 | |
290 | tmp = (orig & mask) >> reg->bitstart; |
291 | return tmp != reg->disable; |
292 | } |
293 | |
294 | static int rockchip_usb2phy_reset(struct rockchip_usb2phy *rphy) |
295 | { |
296 | int ret; |
297 | |
298 | ret = reset_control_assert(rstc: rphy->phy_reset); |
299 | if (ret) |
300 | return ret; |
301 | |
302 | udelay(10); |
303 | |
304 | ret = reset_control_deassert(rstc: rphy->phy_reset); |
305 | if (ret) |
306 | return ret; |
307 | |
308 | usleep_range(min: 100, max: 200); |
309 | |
310 | return 0; |
311 | } |
312 | |
313 | static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) |
314 | { |
315 | struct rockchip_usb2phy *rphy = |
316 | container_of(hw, struct rockchip_usb2phy, clk480m_hw); |
317 | struct regmap *base = get_reg_base(rphy); |
318 | int ret; |
319 | |
320 | /* turn on 480m clk output if it is off */ |
321 | if (!property_enabled(base, reg: &rphy->phy_cfg->clkout_ctl)) { |
322 | ret = property_enable(base, reg: &rphy->phy_cfg->clkout_ctl, en: true); |
323 | if (ret) |
324 | return ret; |
325 | |
326 | /* waiting for the clk become stable */ |
327 | usleep_range(min: 1200, max: 1300); |
328 | } |
329 | |
330 | return 0; |
331 | } |
332 | |
333 | static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) |
334 | { |
335 | struct rockchip_usb2phy *rphy = |
336 | container_of(hw, struct rockchip_usb2phy, clk480m_hw); |
337 | struct regmap *base = get_reg_base(rphy); |
338 | |
339 | /* turn off 480m clk output */ |
340 | property_enable(base, reg: &rphy->phy_cfg->clkout_ctl, en: false); |
341 | } |
342 | |
343 | static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) |
344 | { |
345 | struct rockchip_usb2phy *rphy = |
346 | container_of(hw, struct rockchip_usb2phy, clk480m_hw); |
347 | struct regmap *base = get_reg_base(rphy); |
348 | |
349 | return property_enabled(base, reg: &rphy->phy_cfg->clkout_ctl); |
350 | } |
351 | |
352 | static unsigned long |
353 | rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw, |
354 | unsigned long parent_rate) |
355 | { |
356 | return 480000000; |
357 | } |
358 | |
359 | static const struct clk_ops rockchip_usb2phy_clkout_ops = { |
360 | .prepare = rockchip_usb2phy_clk480m_prepare, |
361 | .unprepare = rockchip_usb2phy_clk480m_unprepare, |
362 | .is_prepared = rockchip_usb2phy_clk480m_prepared, |
363 | .recalc_rate = rockchip_usb2phy_clk480m_recalc_rate, |
364 | }; |
365 | |
366 | static void rockchip_usb2phy_clk480m_unregister(void *data) |
367 | { |
368 | struct rockchip_usb2phy *rphy = data; |
369 | |
370 | of_clk_del_provider(np: rphy->dev->of_node); |
371 | clk_unregister(clk: rphy->clk480m); |
372 | } |
373 | |
374 | static int |
375 | rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) |
376 | { |
377 | struct device_node *node = rphy->dev->of_node; |
378 | struct clk_init_data init; |
379 | const char *clk_name; |
380 | int ret = 0; |
381 | |
382 | init.flags = 0; |
383 | init.name = "clk_usbphy_480m" ; |
384 | init.ops = &rockchip_usb2phy_clkout_ops; |
385 | |
386 | /* optional override of the clockname */ |
387 | of_property_read_string(np: node, propname: "clock-output-names" , out_string: &init.name); |
388 | |
389 | if (rphy->clk) { |
390 | clk_name = __clk_get_name(clk: rphy->clk); |
391 | init.parent_names = &clk_name; |
392 | init.num_parents = 1; |
393 | } else { |
394 | init.parent_names = NULL; |
395 | init.num_parents = 0; |
396 | } |
397 | |
398 | rphy->clk480m_hw.init = &init; |
399 | |
400 | /* register the clock */ |
401 | rphy->clk480m = clk_register(dev: rphy->dev, hw: &rphy->clk480m_hw); |
402 | if (IS_ERR(ptr: rphy->clk480m)) { |
403 | ret = PTR_ERR(ptr: rphy->clk480m); |
404 | goto err_ret; |
405 | } |
406 | |
407 | ret = of_clk_add_provider(np: node, clk_src_get: of_clk_src_simple_get, data: rphy->clk480m); |
408 | if (ret < 0) |
409 | goto err_clk_provider; |
410 | |
411 | return devm_add_action_or_reset(rphy->dev, rockchip_usb2phy_clk480m_unregister, rphy); |
412 | |
413 | err_clk_provider: |
414 | clk_unregister(clk: rphy->clk480m); |
415 | err_ret: |
416 | return ret; |
417 | } |
418 | |
419 | static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) |
420 | { |
421 | int ret; |
422 | struct device_node *node = rphy->dev->of_node; |
423 | struct extcon_dev *edev; |
424 | |
425 | if (of_property_read_bool(np: node, propname: "extcon" )) { |
426 | edev = extcon_get_edev_by_phandle(dev: rphy->dev, index: 0); |
427 | if (IS_ERR(ptr: edev)) { |
428 | if (PTR_ERR(ptr: edev) != -EPROBE_DEFER) |
429 | dev_err(rphy->dev, "Invalid or missing extcon\n" ); |
430 | return PTR_ERR(ptr: edev); |
431 | } |
432 | } else { |
433 | /* Initialize extcon device */ |
434 | edev = devm_extcon_dev_allocate(dev: rphy->dev, |
435 | cable: rockchip_usb2phy_extcon_cable); |
436 | |
437 | if (IS_ERR(ptr: edev)) |
438 | return -ENOMEM; |
439 | |
440 | ret = devm_extcon_dev_register(dev: rphy->dev, edev); |
441 | if (ret) { |
442 | dev_err(rphy->dev, "failed to register extcon device\n" ); |
443 | return ret; |
444 | } |
445 | } |
446 | |
447 | rphy->edev = edev; |
448 | |
449 | return 0; |
450 | } |
451 | |
452 | static int rockchip_usb2phy_enable_host_disc_irq(struct rockchip_usb2phy *rphy, |
453 | struct rockchip_usb2phy_port *rport, |
454 | bool en) |
455 | { |
456 | int ret; |
457 | |
458 | ret = property_enable(base: rphy->grf, reg: &rport->port_cfg->disfall_clr, en: true); |
459 | if (ret) |
460 | return ret; |
461 | |
462 | ret = property_enable(base: rphy->grf, reg: &rport->port_cfg->disfall_en, en); |
463 | if (ret) |
464 | return ret; |
465 | |
466 | ret = property_enable(base: rphy->grf, reg: &rport->port_cfg->disrise_clr, en: true); |
467 | if (ret) |
468 | return ret; |
469 | |
470 | return property_enable(base: rphy->grf, reg: &rport->port_cfg->disrise_en, en); |
471 | } |
472 | |
473 | static int rockchip_usb2phy_init(struct phy *phy) |
474 | { |
475 | struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); |
476 | struct rockchip_usb2phy *rphy = dev_get_drvdata(dev: phy->dev.parent); |
477 | int ret = 0; |
478 | |
479 | mutex_lock(&rport->mutex); |
480 | |
481 | if (rport->port_id == USB2PHY_PORT_OTG) { |
482 | if (rport->mode != USB_DR_MODE_HOST && |
483 | rport->mode != USB_DR_MODE_UNKNOWN) { |
484 | /* clear bvalid status and enable bvalid detect irq */ |
485 | ret = property_enable(base: rphy->grf, |
486 | reg: &rport->port_cfg->bvalid_det_clr, |
487 | en: true); |
488 | if (ret) |
489 | goto out; |
490 | |
491 | ret = property_enable(base: rphy->grf, |
492 | reg: &rport->port_cfg->bvalid_det_en, |
493 | en: true); |
494 | if (ret) |
495 | goto out; |
496 | |
497 | /* clear id status and enable id detect irqs */ |
498 | ret = property_enable(base: rphy->grf, |
499 | reg: &rport->port_cfg->idfall_det_clr, |
500 | en: true); |
501 | if (ret) |
502 | goto out; |
503 | |
504 | ret = property_enable(base: rphy->grf, |
505 | reg: &rport->port_cfg->idrise_det_clr, |
506 | en: true); |
507 | if (ret) |
508 | goto out; |
509 | |
510 | ret = property_enable(base: rphy->grf, |
511 | reg: &rport->port_cfg->idfall_det_en, |
512 | en: true); |
513 | if (ret) |
514 | goto out; |
515 | |
516 | ret = property_enable(base: rphy->grf, |
517 | reg: &rport->port_cfg->idrise_det_en, |
518 | en: true); |
519 | if (ret) |
520 | goto out; |
521 | |
522 | schedule_delayed_work(dwork: &rport->otg_sm_work, |
523 | OTG_SCHEDULE_DELAY * 3); |
524 | } else { |
525 | /* If OTG works in host only mode, do nothing. */ |
526 | dev_dbg(&rport->phy->dev, "mode %d\n" , rport->mode); |
527 | } |
528 | } else if (rport->port_id == USB2PHY_PORT_HOST) { |
529 | if (rport->port_cfg->disfall_en.offset) { |
530 | rport->host_disconnect = true; |
531 | ret = rockchip_usb2phy_enable_host_disc_irq(rphy, rport, en: true); |
532 | if (ret) { |
533 | dev_err(rphy->dev, "failed to enable disconnect irq\n" ); |
534 | goto out; |
535 | } |
536 | } |
537 | |
538 | /* clear linestate and enable linestate detect irq */ |
539 | ret = property_enable(base: rphy->grf, |
540 | reg: &rport->port_cfg->ls_det_clr, en: true); |
541 | if (ret) |
542 | goto out; |
543 | |
544 | ret = property_enable(base: rphy->grf, |
545 | reg: &rport->port_cfg->ls_det_en, en: true); |
546 | if (ret) |
547 | goto out; |
548 | |
549 | schedule_delayed_work(dwork: &rport->sm_work, SCHEDULE_DELAY); |
550 | } |
551 | |
552 | out: |
553 | mutex_unlock(lock: &rport->mutex); |
554 | return ret; |
555 | } |
556 | |
557 | static int rockchip_usb2phy_power_on(struct phy *phy) |
558 | { |
559 | struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); |
560 | struct rockchip_usb2phy *rphy = dev_get_drvdata(dev: phy->dev.parent); |
561 | struct regmap *base = get_reg_base(rphy); |
562 | int ret; |
563 | |
564 | dev_dbg(&rport->phy->dev, "port power on\n" ); |
565 | |
566 | if (!rport->suspended) |
567 | return 0; |
568 | |
569 | ret = clk_prepare_enable(clk: rphy->clk480m); |
570 | if (ret) |
571 | return ret; |
572 | |
573 | ret = property_enable(base, reg: &rport->port_cfg->phy_sus, en: false); |
574 | if (ret) { |
575 | clk_disable_unprepare(clk: rphy->clk480m); |
576 | return ret; |
577 | } |
578 | |
579 | /* |
580 | * For rk3588, it needs to reset phy when exit from |
581 | * suspend mode with common_on_n 1'b1(aka REFCLK_LOGIC, |
582 | * Bias, and PLL blocks are powered down) for lower |
583 | * power consumption. If you don't want to reset phy, |
584 | * please keep the common_on_n 1'b0 to set these blocks |
585 | * remain powered. |
586 | */ |
587 | ret = rockchip_usb2phy_reset(rphy); |
588 | if (ret) |
589 | return ret; |
590 | |
591 | /* waiting for the utmi_clk to become stable */ |
592 | usleep_range(min: 1500, max: 2000); |
593 | |
594 | rport->suspended = false; |
595 | return 0; |
596 | } |
597 | |
598 | static int rockchip_usb2phy_power_off(struct phy *phy) |
599 | { |
600 | struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); |
601 | struct rockchip_usb2phy *rphy = dev_get_drvdata(dev: phy->dev.parent); |
602 | struct regmap *base = get_reg_base(rphy); |
603 | int ret; |
604 | |
605 | dev_dbg(&rport->phy->dev, "port power off\n" ); |
606 | |
607 | if (rport->suspended) |
608 | return 0; |
609 | |
610 | ret = property_enable(base, reg: &rport->port_cfg->phy_sus, en: true); |
611 | if (ret) |
612 | return ret; |
613 | |
614 | rport->suspended = true; |
615 | clk_disable_unprepare(clk: rphy->clk480m); |
616 | |
617 | return 0; |
618 | } |
619 | |
620 | static int rockchip_usb2phy_exit(struct phy *phy) |
621 | { |
622 | struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); |
623 | |
624 | if (rport->port_id == USB2PHY_PORT_OTG && |
625 | rport->mode != USB_DR_MODE_HOST && |
626 | rport->mode != USB_DR_MODE_UNKNOWN) { |
627 | cancel_delayed_work_sync(dwork: &rport->otg_sm_work); |
628 | cancel_delayed_work_sync(dwork: &rport->chg_work); |
629 | } else if (rport->port_id == USB2PHY_PORT_HOST) |
630 | cancel_delayed_work_sync(dwork: &rport->sm_work); |
631 | |
632 | return 0; |
633 | } |
634 | |
635 | static const struct phy_ops rockchip_usb2phy_ops = { |
636 | .init = rockchip_usb2phy_init, |
637 | .exit = rockchip_usb2phy_exit, |
638 | .power_on = rockchip_usb2phy_power_on, |
639 | .power_off = rockchip_usb2phy_power_off, |
640 | .owner = THIS_MODULE, |
641 | }; |
642 | |
643 | static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) |
644 | { |
645 | struct rockchip_usb2phy_port *rport = |
646 | container_of(work, struct rockchip_usb2phy_port, |
647 | otg_sm_work.work); |
648 | struct rockchip_usb2phy *rphy = dev_get_drvdata(dev: rport->phy->dev.parent); |
649 | static unsigned int cable; |
650 | unsigned long delay; |
651 | bool vbus_attach, sch_work, notify_charger; |
652 | |
653 | vbus_attach = property_enabled(base: rphy->grf, |
654 | reg: &rport->port_cfg->utmi_bvalid); |
655 | |
656 | sch_work = false; |
657 | notify_charger = false; |
658 | delay = OTG_SCHEDULE_DELAY; |
659 | dev_dbg(&rport->phy->dev, "%s otg sm work\n" , |
660 | usb_otg_state_string(rport->state)); |
661 | |
662 | switch (rport->state) { |
663 | case OTG_STATE_UNDEFINED: |
664 | rport->state = OTG_STATE_B_IDLE; |
665 | if (!vbus_attach) |
666 | rockchip_usb2phy_power_off(phy: rport->phy); |
667 | fallthrough; |
668 | case OTG_STATE_B_IDLE: |
669 | if (extcon_get_state(edev: rphy->edev, EXTCON_USB_HOST) > 0) { |
670 | dev_dbg(&rport->phy->dev, "usb otg host connect\n" ); |
671 | rport->state = OTG_STATE_A_HOST; |
672 | rockchip_usb2phy_power_on(phy: rport->phy); |
673 | return; |
674 | } else if (vbus_attach) { |
675 | dev_dbg(&rport->phy->dev, "vbus_attach\n" ); |
676 | switch (rphy->chg_state) { |
677 | case USB_CHG_STATE_UNDEFINED: |
678 | schedule_delayed_work(dwork: &rport->chg_work, delay: 0); |
679 | return; |
680 | case USB_CHG_STATE_DETECTED: |
681 | switch (rphy->chg_type) { |
682 | case POWER_SUPPLY_TYPE_USB: |
683 | dev_dbg(&rport->phy->dev, "sdp cable is connected\n" ); |
684 | rockchip_usb2phy_power_on(phy: rport->phy); |
685 | rport->state = OTG_STATE_B_PERIPHERAL; |
686 | notify_charger = true; |
687 | sch_work = true; |
688 | cable = EXTCON_CHG_USB_SDP; |
689 | break; |
690 | case POWER_SUPPLY_TYPE_USB_DCP: |
691 | dev_dbg(&rport->phy->dev, "dcp cable is connected\n" ); |
692 | rockchip_usb2phy_power_off(phy: rport->phy); |
693 | notify_charger = true; |
694 | sch_work = true; |
695 | cable = EXTCON_CHG_USB_DCP; |
696 | break; |
697 | case POWER_SUPPLY_TYPE_USB_CDP: |
698 | dev_dbg(&rport->phy->dev, "cdp cable is connected\n" ); |
699 | rockchip_usb2phy_power_on(phy: rport->phy); |
700 | rport->state = OTG_STATE_B_PERIPHERAL; |
701 | notify_charger = true; |
702 | sch_work = true; |
703 | cable = EXTCON_CHG_USB_CDP; |
704 | break; |
705 | default: |
706 | break; |
707 | } |
708 | break; |
709 | default: |
710 | break; |
711 | } |
712 | } else { |
713 | notify_charger = true; |
714 | rphy->chg_state = USB_CHG_STATE_UNDEFINED; |
715 | rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; |
716 | } |
717 | |
718 | if (rport->vbus_attached != vbus_attach) { |
719 | rport->vbus_attached = vbus_attach; |
720 | |
721 | if (notify_charger && rphy->edev) { |
722 | extcon_set_state_sync(edev: rphy->edev, |
723 | id: cable, state: vbus_attach); |
724 | if (cable == EXTCON_CHG_USB_SDP) |
725 | extcon_set_state_sync(edev: rphy->edev, |
726 | EXTCON_USB, |
727 | state: vbus_attach); |
728 | } |
729 | } |
730 | break; |
731 | case OTG_STATE_B_PERIPHERAL: |
732 | if (!vbus_attach) { |
733 | dev_dbg(&rport->phy->dev, "usb disconnect\n" ); |
734 | rphy->chg_state = USB_CHG_STATE_UNDEFINED; |
735 | rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; |
736 | rport->state = OTG_STATE_B_IDLE; |
737 | delay = 0; |
738 | rockchip_usb2phy_power_off(phy: rport->phy); |
739 | } |
740 | sch_work = true; |
741 | break; |
742 | case OTG_STATE_A_HOST: |
743 | if (extcon_get_state(edev: rphy->edev, EXTCON_USB_HOST) == 0) { |
744 | dev_dbg(&rport->phy->dev, "usb otg host disconnect\n" ); |
745 | rport->state = OTG_STATE_B_IDLE; |
746 | rockchip_usb2phy_power_off(phy: rport->phy); |
747 | } |
748 | break; |
749 | default: |
750 | break; |
751 | } |
752 | |
753 | if (sch_work) |
754 | schedule_delayed_work(dwork: &rport->otg_sm_work, delay); |
755 | } |
756 | |
757 | static const char *chg_to_string(enum power_supply_type chg_type) |
758 | { |
759 | switch (chg_type) { |
760 | case POWER_SUPPLY_TYPE_USB: |
761 | return "USB_SDP_CHARGER" ; |
762 | case POWER_SUPPLY_TYPE_USB_DCP: |
763 | return "USB_DCP_CHARGER" ; |
764 | case POWER_SUPPLY_TYPE_USB_CDP: |
765 | return "USB_CDP_CHARGER" ; |
766 | default: |
767 | return "INVALID_CHARGER" ; |
768 | } |
769 | } |
770 | |
771 | static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy, |
772 | bool en) |
773 | { |
774 | struct regmap *base = get_reg_base(rphy); |
775 | |
776 | property_enable(base, reg: &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); |
777 | property_enable(base, reg: &rphy->phy_cfg->chg_det.idp_src_en, en); |
778 | } |
779 | |
780 | static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy, |
781 | bool en) |
782 | { |
783 | struct regmap *base = get_reg_base(rphy); |
784 | |
785 | property_enable(base, reg: &rphy->phy_cfg->chg_det.vdp_src_en, en); |
786 | property_enable(base, reg: &rphy->phy_cfg->chg_det.idm_sink_en, en); |
787 | } |
788 | |
789 | static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, |
790 | bool en) |
791 | { |
792 | struct regmap *base = get_reg_base(rphy); |
793 | |
794 | property_enable(base, reg: &rphy->phy_cfg->chg_det.vdm_src_en, en); |
795 | property_enable(base, reg: &rphy->phy_cfg->chg_det.idp_sink_en, en); |
796 | } |
797 | |
798 | #define CHG_DCD_POLL_TIME (100 * HZ / 1000) |
799 | #define CHG_DCD_MAX_RETRIES 6 |
800 | #define CHG_PRIMARY_DET_TIME (40 * HZ / 1000) |
801 | #define CHG_SECONDARY_DET_TIME (40 * HZ / 1000) |
802 | static void rockchip_chg_detect_work(struct work_struct *work) |
803 | { |
804 | struct rockchip_usb2phy_port *rport = |
805 | container_of(work, struct rockchip_usb2phy_port, chg_work.work); |
806 | struct rockchip_usb2phy *rphy = dev_get_drvdata(dev: rport->phy->dev.parent); |
807 | struct regmap *base = get_reg_base(rphy); |
808 | bool is_dcd, tmout, vout; |
809 | unsigned long delay; |
810 | |
811 | dev_dbg(&rport->phy->dev, "chg detection work state = %d\n" , |
812 | rphy->chg_state); |
813 | switch (rphy->chg_state) { |
814 | case USB_CHG_STATE_UNDEFINED: |
815 | if (!rport->suspended) |
816 | rockchip_usb2phy_power_off(phy: rport->phy); |
817 | /* put the controller in non-driving mode */ |
818 | property_enable(base, reg: &rphy->phy_cfg->chg_det.opmode, en: false); |
819 | /* Start DCD processing stage 1 */ |
820 | rockchip_chg_enable_dcd(rphy, en: true); |
821 | rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; |
822 | rphy->dcd_retries = 0; |
823 | delay = CHG_DCD_POLL_TIME; |
824 | break; |
825 | case USB_CHG_STATE_WAIT_FOR_DCD: |
826 | /* get data contact detection status */ |
827 | is_dcd = property_enabled(base: rphy->grf, |
828 | reg: &rphy->phy_cfg->chg_det.dp_det); |
829 | tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES; |
830 | /* stage 2 */ |
831 | if (is_dcd || tmout) { |
832 | /* stage 4 */ |
833 | /* Turn off DCD circuitry */ |
834 | rockchip_chg_enable_dcd(rphy, en: false); |
835 | /* Voltage Source on DP, Probe on DM */ |
836 | rockchip_chg_enable_primary_det(rphy, en: true); |
837 | delay = CHG_PRIMARY_DET_TIME; |
838 | rphy->chg_state = USB_CHG_STATE_DCD_DONE; |
839 | } else { |
840 | /* stage 3 */ |
841 | delay = CHG_DCD_POLL_TIME; |
842 | } |
843 | break; |
844 | case USB_CHG_STATE_DCD_DONE: |
845 | vout = property_enabled(base: rphy->grf, |
846 | reg: &rphy->phy_cfg->chg_det.cp_det); |
847 | rockchip_chg_enable_primary_det(rphy, en: false); |
848 | if (vout) { |
849 | /* Voltage Source on DM, Probe on DP */ |
850 | rockchip_chg_enable_secondary_det(rphy, en: true); |
851 | delay = CHG_SECONDARY_DET_TIME; |
852 | rphy->chg_state = USB_CHG_STATE_PRIMARY_DONE; |
853 | } else { |
854 | if (rphy->dcd_retries == CHG_DCD_MAX_RETRIES) { |
855 | /* floating charger found */ |
856 | rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; |
857 | rphy->chg_state = USB_CHG_STATE_DETECTED; |
858 | delay = 0; |
859 | } else { |
860 | rphy->chg_type = POWER_SUPPLY_TYPE_USB; |
861 | rphy->chg_state = USB_CHG_STATE_DETECTED; |
862 | delay = 0; |
863 | } |
864 | } |
865 | break; |
866 | case USB_CHG_STATE_PRIMARY_DONE: |
867 | vout = property_enabled(base: rphy->grf, |
868 | reg: &rphy->phy_cfg->chg_det.dcp_det); |
869 | /* Turn off voltage source */ |
870 | rockchip_chg_enable_secondary_det(rphy, en: false); |
871 | if (vout) |
872 | rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; |
873 | else |
874 | rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP; |
875 | fallthrough; |
876 | case USB_CHG_STATE_SECONDARY_DONE: |
877 | rphy->chg_state = USB_CHG_STATE_DETECTED; |
878 | fallthrough; |
879 | case USB_CHG_STATE_DETECTED: |
880 | /* put the controller in normal mode */ |
881 | property_enable(base, reg: &rphy->phy_cfg->chg_det.opmode, en: true); |
882 | rockchip_usb2phy_otg_sm_work(work: &rport->otg_sm_work.work); |
883 | dev_dbg(&rport->phy->dev, "charger = %s\n" , |
884 | chg_to_string(rphy->chg_type)); |
885 | return; |
886 | default: |
887 | return; |
888 | } |
889 | |
890 | schedule_delayed_work(dwork: &rport->chg_work, delay); |
891 | } |
892 | |
893 | /* |
894 | * The function manage host-phy port state and suspend/resume phy port |
895 | * to save power. |
896 | * |
897 | * we rely on utmi_linestate and utmi_hostdisconnect to identify whether |
898 | * devices is disconnect or not. Besides, we do not need care it is FS/LS |
899 | * disconnected or HS disconnected, actually, we just only need get the |
900 | * device is disconnected at last through rearm the delayed work, |
901 | * to suspend the phy port in _PHY_STATE_DISCONNECT_ case. |
902 | * |
903 | * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke |
904 | * some clk related APIs, so do not invoke it from interrupt context directly. |
905 | */ |
906 | static void rockchip_usb2phy_sm_work(struct work_struct *work) |
907 | { |
908 | struct rockchip_usb2phy_port *rport = |
909 | container_of(work, struct rockchip_usb2phy_port, sm_work.work); |
910 | struct rockchip_usb2phy *rphy = dev_get_drvdata(dev: rport->phy->dev.parent); |
911 | unsigned int sh, ul, uhd, state; |
912 | unsigned int ul_mask, uhd_mask; |
913 | int ret; |
914 | |
915 | mutex_lock(&rport->mutex); |
916 | |
917 | ret = regmap_read(map: rphy->grf, reg: rport->port_cfg->utmi_ls.offset, val: &ul); |
918 | if (ret < 0) |
919 | goto next_schedule; |
920 | |
921 | ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend, |
922 | rport->port_cfg->utmi_ls.bitstart); |
923 | |
924 | if (rport->port_cfg->utmi_hstdet.offset) { |
925 | ret = regmap_read(map: rphy->grf, reg: rport->port_cfg->utmi_hstdet.offset, val: &uhd); |
926 | if (ret < 0) |
927 | goto next_schedule; |
928 | |
929 | uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend, |
930 | rport->port_cfg->utmi_hstdet.bitstart); |
931 | |
932 | sh = rport->port_cfg->utmi_hstdet.bitend - |
933 | rport->port_cfg->utmi_hstdet.bitstart + 1; |
934 | /* stitch on utmi_ls and utmi_hstdet as phy state */ |
935 | state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) | |
936 | (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh); |
937 | } else { |
938 | state = ((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << 1 | |
939 | rport->host_disconnect; |
940 | } |
941 | |
942 | switch (state) { |
943 | case PHY_STATE_HS_ONLINE: |
944 | dev_dbg(&rport->phy->dev, "HS online\n" ); |
945 | break; |
946 | case PHY_STATE_FS_LS_ONLINE: |
947 | /* |
948 | * For FS/LS device, the online state share with connect state |
949 | * from utmi_ls and utmi_hstdet register, so we distinguish |
950 | * them via suspended flag. |
951 | * |
952 | * Plus, there are two cases, one is D- Line pull-up, and D+ |
953 | * line pull-down, the state is 4; another is D+ line pull-up, |
954 | * and D- line pull-down, the state is 2. |
955 | */ |
956 | if (!rport->suspended) { |
957 | /* D- line pull-up, D+ line pull-down */ |
958 | dev_dbg(&rport->phy->dev, "FS/LS online\n" ); |
959 | break; |
960 | } |
961 | fallthrough; |
962 | case PHY_STATE_CONNECT: |
963 | if (rport->suspended) { |
964 | dev_dbg(&rport->phy->dev, "Connected\n" ); |
965 | rockchip_usb2phy_power_on(phy: rport->phy); |
966 | rport->suspended = false; |
967 | } else { |
968 | /* D+ line pull-up, D- line pull-down */ |
969 | dev_dbg(&rport->phy->dev, "FS/LS online\n" ); |
970 | } |
971 | break; |
972 | case PHY_STATE_DISCONNECT: |
973 | if (!rport->suspended) { |
974 | dev_dbg(&rport->phy->dev, "Disconnected\n" ); |
975 | rockchip_usb2phy_power_off(phy: rport->phy); |
976 | rport->suspended = true; |
977 | } |
978 | |
979 | /* |
980 | * activate the linestate detection to get the next device |
981 | * plug-in irq. |
982 | */ |
983 | property_enable(base: rphy->grf, reg: &rport->port_cfg->ls_det_clr, en: true); |
984 | property_enable(base: rphy->grf, reg: &rport->port_cfg->ls_det_en, en: true); |
985 | |
986 | /* |
987 | * we don't need to rearm the delayed work when the phy port |
988 | * is suspended. |
989 | */ |
990 | mutex_unlock(lock: &rport->mutex); |
991 | return; |
992 | default: |
993 | dev_dbg(&rport->phy->dev, "unknown phy state\n" ); |
994 | break; |
995 | } |
996 | |
997 | next_schedule: |
998 | mutex_unlock(lock: &rport->mutex); |
999 | schedule_delayed_work(dwork: &rport->sm_work, SCHEDULE_DELAY); |
1000 | } |
1001 | |
1002 | static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data) |
1003 | { |
1004 | struct rockchip_usb2phy_port *rport = data; |
1005 | struct rockchip_usb2phy *rphy = dev_get_drvdata(dev: rport->phy->dev.parent); |
1006 | |
1007 | if (!property_enabled(base: rphy->grf, reg: &rport->port_cfg->ls_det_st)) |
1008 | return IRQ_NONE; |
1009 | |
1010 | mutex_lock(&rport->mutex); |
1011 | |
1012 | /* disable linestate detect irq and clear its status */ |
1013 | property_enable(base: rphy->grf, reg: &rport->port_cfg->ls_det_en, en: false); |
1014 | property_enable(base: rphy->grf, reg: &rport->port_cfg->ls_det_clr, en: true); |
1015 | |
1016 | mutex_unlock(lock: &rport->mutex); |
1017 | |
1018 | /* |
1019 | * In this case for host phy port, a new device is plugged in, |
1020 | * meanwhile, if the phy port is suspended, we need rearm the work to |
1021 | * resume it and mange its states; otherwise, we do nothing about that. |
1022 | */ |
1023 | if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST) |
1024 | rockchip_usb2phy_sm_work(work: &rport->sm_work.work); |
1025 | |
1026 | return IRQ_HANDLED; |
1027 | } |
1028 | |
1029 | static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) |
1030 | { |
1031 | struct rockchip_usb2phy_port *rport = data; |
1032 | struct rockchip_usb2phy *rphy = dev_get_drvdata(dev: rport->phy->dev.parent); |
1033 | |
1034 | if (!property_enabled(base: rphy->grf, reg: &rport->port_cfg->bvalid_det_st)) |
1035 | return IRQ_NONE; |
1036 | |
1037 | /* clear bvalid detect irq pending status */ |
1038 | property_enable(base: rphy->grf, reg: &rport->port_cfg->bvalid_det_clr, en: true); |
1039 | |
1040 | rockchip_usb2phy_otg_sm_work(work: &rport->otg_sm_work.work); |
1041 | |
1042 | return IRQ_HANDLED; |
1043 | } |
1044 | |
1045 | static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data) |
1046 | { |
1047 | struct rockchip_usb2phy_port *rport = data; |
1048 | struct rockchip_usb2phy *rphy = dev_get_drvdata(dev: rport->phy->dev.parent); |
1049 | bool id; |
1050 | |
1051 | if (!property_enabled(base: rphy->grf, reg: &rport->port_cfg->idfall_det_st) && |
1052 | !property_enabled(base: rphy->grf, reg: &rport->port_cfg->idrise_det_st)) |
1053 | return IRQ_NONE; |
1054 | |
1055 | /* clear id detect irq pending status */ |
1056 | if (property_enabled(base: rphy->grf, reg: &rport->port_cfg->idfall_det_st)) |
1057 | property_enable(base: rphy->grf, reg: &rport->port_cfg->idfall_det_clr, en: true); |
1058 | |
1059 | if (property_enabled(base: rphy->grf, reg: &rport->port_cfg->idrise_det_st)) |
1060 | property_enable(base: rphy->grf, reg: &rport->port_cfg->idrise_det_clr, en: true); |
1061 | |
1062 | id = property_enabled(base: rphy->grf, reg: &rport->port_cfg->utmi_id); |
1063 | extcon_set_state_sync(edev: rphy->edev, EXTCON_USB_HOST, state: !id); |
1064 | |
1065 | return IRQ_HANDLED; |
1066 | } |
1067 | |
1068 | static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data) |
1069 | { |
1070 | irqreturn_t ret = IRQ_NONE; |
1071 | |
1072 | ret |= rockchip_usb2phy_bvalid_irq(irq, data); |
1073 | ret |= rockchip_usb2phy_id_irq(irq, data); |
1074 | |
1075 | return ret; |
1076 | } |
1077 | |
1078 | static irqreturn_t rockchip_usb2phy_host_disc_irq(int irq, void *data) |
1079 | { |
1080 | struct rockchip_usb2phy_port *rport = data; |
1081 | struct rockchip_usb2phy *rphy = dev_get_drvdata(dev: rport->phy->dev.parent); |
1082 | |
1083 | if (!property_enabled(base: rphy->grf, reg: &rport->port_cfg->disfall_st) && |
1084 | !property_enabled(base: rphy->grf, reg: &rport->port_cfg->disrise_st)) |
1085 | return IRQ_NONE; |
1086 | |
1087 | mutex_lock(&rport->mutex); |
1088 | |
1089 | /* clear disconnect fall or rise detect irq pending status */ |
1090 | if (property_enabled(base: rphy->grf, reg: &rport->port_cfg->disfall_st)) { |
1091 | property_enable(base: rphy->grf, reg: &rport->port_cfg->disfall_clr, en: true); |
1092 | rport->host_disconnect = false; |
1093 | } else if (property_enabled(base: rphy->grf, reg: &rport->port_cfg->disrise_st)) { |
1094 | property_enable(base: rphy->grf, reg: &rport->port_cfg->disrise_clr, en: true); |
1095 | rport->host_disconnect = true; |
1096 | } |
1097 | |
1098 | mutex_unlock(lock: &rport->mutex); |
1099 | |
1100 | return IRQ_HANDLED; |
1101 | } |
1102 | |
1103 | static irqreturn_t rockchip_usb2phy_irq(int irq, void *data) |
1104 | { |
1105 | struct rockchip_usb2phy *rphy = data; |
1106 | struct rockchip_usb2phy_port *rport; |
1107 | irqreturn_t ret = IRQ_NONE; |
1108 | unsigned int index; |
1109 | |
1110 | for (index = 0; index < rphy->phy_cfg->num_ports; index++) { |
1111 | rport = &rphy->ports[index]; |
1112 | if (!rport->phy) |
1113 | continue; |
1114 | |
1115 | if (rport->port_id == USB2PHY_PORT_HOST && |
1116 | rport->port_cfg->disfall_en.offset) |
1117 | ret |= rockchip_usb2phy_host_disc_irq(irq, data: rport); |
1118 | |
1119 | switch (rport->port_id) { |
1120 | case USB2PHY_PORT_OTG: |
1121 | if (rport->mode != USB_DR_MODE_HOST && |
1122 | rport->mode != USB_DR_MODE_UNKNOWN) |
1123 | ret |= rockchip_usb2phy_otg_mux_irq(irq, data: rport); |
1124 | break; |
1125 | case USB2PHY_PORT_HOST: |
1126 | ret |= rockchip_usb2phy_linestate_irq(irq, data: rport); |
1127 | break; |
1128 | } |
1129 | } |
1130 | |
1131 | return ret; |
1132 | } |
1133 | |
1134 | static int rockchip_usb2phy_port_irq_init(struct rockchip_usb2phy *rphy, |
1135 | struct rockchip_usb2phy_port *rport, |
1136 | struct device_node *child_np) |
1137 | { |
1138 | int ret; |
1139 | |
1140 | /* |
1141 | * If the usb2 phy used combined irq for otg and host port, |
1142 | * don't need to init otg and host port irq separately. |
1143 | */ |
1144 | if (rphy->irq > 0) |
1145 | return 0; |
1146 | |
1147 | switch (rport->port_id) { |
1148 | case USB2PHY_PORT_HOST: |
1149 | rport->ls_irq = of_irq_get_byname(dev: child_np, name: "linestate" ); |
1150 | if (rport->ls_irq < 0) { |
1151 | dev_err(rphy->dev, "no linestate irq provided\n" ); |
1152 | return rport->ls_irq; |
1153 | } |
1154 | |
1155 | ret = devm_request_threaded_irq(dev: rphy->dev, irq: rport->ls_irq, NULL, |
1156 | thread_fn: rockchip_usb2phy_linestate_irq, |
1157 | IRQF_ONESHOT, |
1158 | devname: "rockchip_usb2phy" , dev_id: rport); |
1159 | if (ret) { |
1160 | dev_err(rphy->dev, "failed to request linestate irq handle\n" ); |
1161 | return ret; |
1162 | } |
1163 | break; |
1164 | case USB2PHY_PORT_OTG: |
1165 | /* |
1166 | * Some SoCs use one interrupt with otg-id/otg-bvalid/linestate |
1167 | * interrupts muxed together, so probe the otg-mux interrupt first, |
1168 | * if not found, then look for the regular interrupts one by one. |
1169 | */ |
1170 | rport->otg_mux_irq = of_irq_get_byname(dev: child_np, name: "otg-mux" ); |
1171 | if (rport->otg_mux_irq > 0) { |
1172 | ret = devm_request_threaded_irq(dev: rphy->dev, irq: rport->otg_mux_irq, |
1173 | NULL, |
1174 | thread_fn: rockchip_usb2phy_otg_mux_irq, |
1175 | IRQF_ONESHOT, |
1176 | devname: "rockchip_usb2phy_otg" , |
1177 | dev_id: rport); |
1178 | if (ret) { |
1179 | dev_err(rphy->dev, |
1180 | "failed to request otg-mux irq handle\n" ); |
1181 | return ret; |
1182 | } |
1183 | } else { |
1184 | rport->bvalid_irq = of_irq_get_byname(dev: child_np, name: "otg-bvalid" ); |
1185 | if (rport->bvalid_irq < 0) { |
1186 | dev_err(rphy->dev, "no vbus valid irq provided\n" ); |
1187 | ret = rport->bvalid_irq; |
1188 | return ret; |
1189 | } |
1190 | |
1191 | ret = devm_request_threaded_irq(dev: rphy->dev, irq: rport->bvalid_irq, |
1192 | NULL, |
1193 | thread_fn: rockchip_usb2phy_bvalid_irq, |
1194 | IRQF_ONESHOT, |
1195 | devname: "rockchip_usb2phy_bvalid" , |
1196 | dev_id: rport); |
1197 | if (ret) { |
1198 | dev_err(rphy->dev, |
1199 | "failed to request otg-bvalid irq handle\n" ); |
1200 | return ret; |
1201 | } |
1202 | |
1203 | rport->id_irq = of_irq_get_byname(dev: child_np, name: "otg-id" ); |
1204 | if (rport->id_irq < 0) { |
1205 | dev_err(rphy->dev, "no otg-id irq provided\n" ); |
1206 | ret = rport->id_irq; |
1207 | return ret; |
1208 | } |
1209 | |
1210 | ret = devm_request_threaded_irq(dev: rphy->dev, irq: rport->id_irq, |
1211 | NULL, |
1212 | thread_fn: rockchip_usb2phy_id_irq, |
1213 | IRQF_ONESHOT, |
1214 | devname: "rockchip_usb2phy_id" , |
1215 | dev_id: rport); |
1216 | if (ret) { |
1217 | dev_err(rphy->dev, |
1218 | "failed to request otg-id irq handle\n" ); |
1219 | return ret; |
1220 | } |
1221 | } |
1222 | break; |
1223 | default: |
1224 | return -EINVAL; |
1225 | } |
1226 | |
1227 | return 0; |
1228 | } |
1229 | |
1230 | static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, |
1231 | struct rockchip_usb2phy_port *rport, |
1232 | struct device_node *child_np) |
1233 | { |
1234 | int ret; |
1235 | |
1236 | rport->port_id = USB2PHY_PORT_HOST; |
1237 | rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST]; |
1238 | rport->suspended = true; |
1239 | |
1240 | mutex_init(&rport->mutex); |
1241 | INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work); |
1242 | |
1243 | ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np); |
1244 | if (ret) { |
1245 | dev_err(rphy->dev, "failed to setup host irq\n" ); |
1246 | return ret; |
1247 | } |
1248 | |
1249 | return 0; |
1250 | } |
1251 | |
1252 | static int rockchip_otg_event(struct notifier_block *nb, |
1253 | unsigned long event, void *ptr) |
1254 | { |
1255 | struct rockchip_usb2phy_port *rport = |
1256 | container_of(nb, struct rockchip_usb2phy_port, event_nb); |
1257 | |
1258 | schedule_delayed_work(dwork: &rport->otg_sm_work, OTG_SCHEDULE_DELAY); |
1259 | |
1260 | return NOTIFY_DONE; |
1261 | } |
1262 | |
1263 | static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, |
1264 | struct rockchip_usb2phy_port *rport, |
1265 | struct device_node *child_np) |
1266 | { |
1267 | int ret, id; |
1268 | |
1269 | rport->port_id = USB2PHY_PORT_OTG; |
1270 | rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG]; |
1271 | rport->state = OTG_STATE_UNDEFINED; |
1272 | |
1273 | /* |
1274 | * set suspended flag to true, but actually don't |
1275 | * put phy in suspend mode, it aims to enable usb |
1276 | * phy and clock in power_on() called by usb controller |
1277 | * driver during probe. |
1278 | */ |
1279 | rport->suspended = true; |
1280 | rport->vbus_attached = false; |
1281 | |
1282 | mutex_init(&rport->mutex); |
1283 | |
1284 | rport->mode = of_usb_get_dr_mode_by_phy(np: child_np, arg0: -1); |
1285 | if (rport->mode == USB_DR_MODE_HOST || |
1286 | rport->mode == USB_DR_MODE_UNKNOWN) { |
1287 | ret = 0; |
1288 | goto out; |
1289 | } |
1290 | |
1291 | INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); |
1292 | INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); |
1293 | |
1294 | ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np); |
1295 | if (ret) { |
1296 | dev_err(rphy->dev, "failed to init irq for host port\n" ); |
1297 | goto out; |
1298 | } |
1299 | |
1300 | if (!IS_ERR(ptr: rphy->edev)) { |
1301 | rport->event_nb.notifier_call = rockchip_otg_event; |
1302 | |
1303 | ret = devm_extcon_register_notifier(dev: rphy->dev, edev: rphy->edev, |
1304 | EXTCON_USB_HOST, nb: &rport->event_nb); |
1305 | if (ret) { |
1306 | dev_err(rphy->dev, "register USB HOST notifier failed\n" ); |
1307 | goto out; |
1308 | } |
1309 | |
1310 | if (!of_property_read_bool(np: rphy->dev->of_node, propname: "extcon" )) { |
1311 | /* do initial sync of usb state */ |
1312 | id = property_enabled(base: rphy->grf, reg: &rport->port_cfg->utmi_id); |
1313 | extcon_set_state_sync(edev: rphy->edev, EXTCON_USB_HOST, state: !id); |
1314 | } |
1315 | } |
1316 | |
1317 | out: |
1318 | return ret; |
1319 | } |
1320 | |
1321 | static int rockchip_usb2phy_probe(struct platform_device *pdev) |
1322 | { |
1323 | struct device *dev = &pdev->dev; |
1324 | struct device_node *np = dev->of_node; |
1325 | struct device_node *child_np; |
1326 | struct phy_provider *provider; |
1327 | struct rockchip_usb2phy *rphy; |
1328 | const struct rockchip_usb2phy_cfg *phy_cfgs; |
1329 | unsigned int reg; |
1330 | int index, ret; |
1331 | |
1332 | rphy = devm_kzalloc(dev, size: sizeof(*rphy), GFP_KERNEL); |
1333 | if (!rphy) |
1334 | return -ENOMEM; |
1335 | |
1336 | if (!dev->parent || !dev->parent->of_node) { |
1337 | rphy->grf = syscon_regmap_lookup_by_phandle(np, property: "rockchip,usbgrf" ); |
1338 | if (IS_ERR(ptr: rphy->grf)) { |
1339 | dev_err(dev, "failed to locate usbgrf\n" ); |
1340 | return PTR_ERR(ptr: rphy->grf); |
1341 | } |
1342 | } |
1343 | |
1344 | else { |
1345 | rphy->grf = syscon_node_to_regmap(np: dev->parent->of_node); |
1346 | if (IS_ERR(ptr: rphy->grf)) |
1347 | return PTR_ERR(ptr: rphy->grf); |
1348 | } |
1349 | |
1350 | if (of_device_is_compatible(device: np, "rockchip,rv1108-usb2phy" )) { |
1351 | rphy->usbgrf = |
1352 | syscon_regmap_lookup_by_phandle(np: dev->of_node, |
1353 | property: "rockchip,usbgrf" ); |
1354 | if (IS_ERR(ptr: rphy->usbgrf)) |
1355 | return PTR_ERR(ptr: rphy->usbgrf); |
1356 | } else { |
1357 | rphy->usbgrf = NULL; |
1358 | } |
1359 | |
1360 | if (of_property_read_u32_index(np, propname: "reg" , index: 0, out_value: ®)) { |
1361 | dev_err(dev, "the reg property is not assigned in %pOFn node\n" , |
1362 | np); |
1363 | return -EINVAL; |
1364 | } |
1365 | |
1366 | /* support address_cells=2 */ |
1367 | if (of_property_count_u32_elems(np, propname: "reg" ) > 2 && reg == 0) { |
1368 | if (of_property_read_u32_index(np, propname: "reg" , index: 1, out_value: ®)) { |
1369 | dev_err(dev, "the reg property is not assigned in %pOFn node\n" , |
1370 | np); |
1371 | return -EINVAL; |
1372 | } |
1373 | } |
1374 | |
1375 | rphy->dev = dev; |
1376 | phy_cfgs = device_get_match_data(dev); |
1377 | rphy->chg_state = USB_CHG_STATE_UNDEFINED; |
1378 | rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; |
1379 | rphy->irq = platform_get_irq_optional(pdev, 0); |
1380 | platform_set_drvdata(pdev, data: rphy); |
1381 | |
1382 | if (!phy_cfgs) |
1383 | return dev_err_probe(dev, err: -EINVAL, fmt: "phy configs are not assigned!\n" ); |
1384 | |
1385 | ret = rockchip_usb2phy_extcon_register(rphy); |
1386 | if (ret) |
1387 | return ret; |
1388 | |
1389 | /* find out a proper config which can be matched with dt. */ |
1390 | index = 0; |
1391 | do { |
1392 | if (phy_cfgs[index].reg == reg) { |
1393 | rphy->phy_cfg = &phy_cfgs[index]; |
1394 | break; |
1395 | } |
1396 | |
1397 | ++index; |
1398 | } while (phy_cfgs[index].reg); |
1399 | |
1400 | if (!rphy->phy_cfg) { |
1401 | dev_err(dev, "could not find phy config for reg=0x%08x\n" , reg); |
1402 | return -EINVAL; |
1403 | } |
1404 | |
1405 | rphy->phy_reset = devm_reset_control_get_optional(dev, id: "phy" ); |
1406 | if (IS_ERR(ptr: rphy->phy_reset)) |
1407 | return PTR_ERR(ptr: rphy->phy_reset); |
1408 | |
1409 | rphy->clk = devm_clk_get_optional_enabled(dev, id: "phyclk" ); |
1410 | if (IS_ERR(ptr: rphy->clk)) { |
1411 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: rphy->clk), |
1412 | fmt: "failed to get phyclk\n" ); |
1413 | } |
1414 | |
1415 | ret = rockchip_usb2phy_clk480m_register(rphy); |
1416 | if (ret) { |
1417 | dev_err(dev, "failed to register 480m output clock\n" ); |
1418 | return ret; |
1419 | } |
1420 | |
1421 | if (rphy->phy_cfg->phy_tuning) { |
1422 | ret = rphy->phy_cfg->phy_tuning(rphy); |
1423 | if (ret) |
1424 | return ret; |
1425 | } |
1426 | |
1427 | index = 0; |
1428 | for_each_available_child_of_node(np, child_np) { |
1429 | struct rockchip_usb2phy_port *rport = &rphy->ports[index]; |
1430 | struct phy *phy; |
1431 | |
1432 | /* This driver aims to support both otg-port and host-port */ |
1433 | if (!of_node_name_eq(np: child_np, name: "host-port" ) && |
1434 | !of_node_name_eq(np: child_np, name: "otg-port" )) |
1435 | goto next_child; |
1436 | |
1437 | phy = devm_phy_create(dev, node: child_np, ops: &rockchip_usb2phy_ops); |
1438 | if (IS_ERR(ptr: phy)) { |
1439 | dev_err_probe(dev, err: PTR_ERR(ptr: phy), fmt: "failed to create phy\n" ); |
1440 | ret = PTR_ERR(ptr: phy); |
1441 | goto put_child; |
1442 | } |
1443 | |
1444 | rport->phy = phy; |
1445 | phy_set_drvdata(phy: rport->phy, data: rport); |
1446 | |
1447 | /* initialize otg/host port separately */ |
1448 | if (of_node_name_eq(np: child_np, name: "host-port" )) { |
1449 | ret = rockchip_usb2phy_host_port_init(rphy, rport, |
1450 | child_np); |
1451 | if (ret) |
1452 | goto put_child; |
1453 | } else { |
1454 | ret = rockchip_usb2phy_otg_port_init(rphy, rport, |
1455 | child_np); |
1456 | if (ret) |
1457 | goto put_child; |
1458 | } |
1459 | |
1460 | next_child: |
1461 | /* to prevent out of boundary */ |
1462 | if (++index >= rphy->phy_cfg->num_ports) { |
1463 | of_node_put(node: child_np); |
1464 | break; |
1465 | } |
1466 | } |
1467 | |
1468 | provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
1469 | |
1470 | if (rphy->irq > 0) { |
1471 | ret = devm_request_threaded_irq(dev: rphy->dev, irq: rphy->irq, NULL, |
1472 | thread_fn: rockchip_usb2phy_irq, |
1473 | IRQF_ONESHOT, |
1474 | devname: "rockchip_usb2phy" , |
1475 | dev_id: rphy); |
1476 | if (ret) { |
1477 | dev_err(rphy->dev, |
1478 | "failed to request usb2phy irq handle\n" ); |
1479 | goto put_child; |
1480 | } |
1481 | } |
1482 | |
1483 | return PTR_ERR_OR_ZERO(ptr: provider); |
1484 | |
1485 | put_child: |
1486 | of_node_put(node: child_np); |
1487 | return ret; |
1488 | } |
1489 | |
1490 | static int rk3128_usb2phy_tuning(struct rockchip_usb2phy *rphy) |
1491 | { |
1492 | /* Turn off differential receiver in suspend mode */ |
1493 | return regmap_write_bits(map: rphy->grf, reg: 0x298, |
1494 | BIT(2) << BIT_WRITEABLE_SHIFT | BIT(2), |
1495 | BIT(2) << BIT_WRITEABLE_SHIFT | 0); |
1496 | } |
1497 | |
1498 | static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy) |
1499 | { |
1500 | int ret; |
1501 | bool usb3otg = false; |
1502 | /* |
1503 | * utmi_termselect = 1'b1 (en FS terminations) |
1504 | * utmi_xcvrselect = 2'b01 (FS transceiver) |
1505 | */ |
1506 | int suspend_cfg = 0x14; |
1507 | |
1508 | if (rphy->phy_cfg->reg == 0x0000 || rphy->phy_cfg->reg == 0x4000) { |
1509 | /* USB2 config for USB3_0 and USB3_1 */ |
1510 | suspend_cfg |= 0x01; /* utmi_opmode = 2'b01 (no-driving) */ |
1511 | usb3otg = true; |
1512 | } else if (rphy->phy_cfg->reg == 0x8000 || rphy->phy_cfg->reg == 0xc000) { |
1513 | /* USB2 config for USB2_0 and USB2_1 */ |
1514 | suspend_cfg |= 0x00; /* utmi_opmode = 2'b00 (normal) */ |
1515 | } else { |
1516 | return -EINVAL; |
1517 | } |
1518 | |
1519 | /* Deassert SIDDQ to power on analog block */ |
1520 | ret = regmap_write(map: rphy->grf, reg: 0x0008, GENMASK(29, 29) | 0x0000); |
1521 | if (ret) |
1522 | return ret; |
1523 | |
1524 | /* Do reset after exit IDDQ mode */ |
1525 | ret = rockchip_usb2phy_reset(rphy); |
1526 | if (ret) |
1527 | return ret; |
1528 | |
1529 | /* suspend configuration */ |
1530 | ret |= regmap_write(map: rphy->grf, reg: 0x000c, GENMASK(20, 16) | suspend_cfg); |
1531 | |
1532 | /* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */ |
1533 | ret |= regmap_write(map: rphy->grf, reg: 0x0004, GENMASK(27, 24) | 0x0900); |
1534 | |
1535 | /* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */ |
1536 | ret |= regmap_write(map: rphy->grf, reg: 0x0008, GENMASK(20, 19) | 0x0010); |
1537 | |
1538 | if (!usb3otg) |
1539 | return ret; |
1540 | |
1541 | /* Pullup iddig pin for USB3_0 OTG mode */ |
1542 | ret |= regmap_write(map: rphy->grf, reg: 0x0010, GENMASK(17, 16) | 0x0003); |
1543 | |
1544 | return ret; |
1545 | } |
1546 | |
1547 | static const struct rockchip_usb2phy_cfg rk3128_phy_cfgs[] = { |
1548 | { |
1549 | .reg = 0x17c, |
1550 | .num_ports = 2, |
1551 | .phy_tuning = rk3128_usb2phy_tuning, |
1552 | .clkout_ctl = { .offset: 0x0190, .bitend: 15, .bitstart: 15, .disable: 1, .enable: 0 }, |
1553 | .port_cfgs = { |
1554 | [USB2PHY_PORT_OTG] = { |
1555 | .phy_sus = { 0x017c, 8, 0, 0, 0x1d1 }, |
1556 | .bvalid_det_en = { 0x017c, 14, 14, 0, 1 }, |
1557 | .bvalid_det_st = { 0x017c, 15, 15, 0, 1 }, |
1558 | .bvalid_det_clr = { 0x017c, 15, 15, 0, 1 }, |
1559 | .idfall_det_en = { 0x01a0, 2, 2, 0, 1 }, |
1560 | .idfall_det_st = { 0x01a0, 3, 3, 0, 1 }, |
1561 | .idfall_det_clr = { 0x01a0, 3, 3, 0, 1 }, |
1562 | .idrise_det_en = { 0x01a0, 0, 0, 0, 1 }, |
1563 | .idrise_det_st = { 0x01a0, 1, 1, 0, 1 }, |
1564 | .idrise_det_clr = { 0x01a0, 1, 1, 0, 1 }, |
1565 | .ls_det_en = { 0x017c, 12, 12, 0, 1 }, |
1566 | .ls_det_st = { 0x017c, 13, 13, 0, 1 }, |
1567 | .ls_det_clr = { 0x017c, 13, 13, 0, 1 }, |
1568 | .utmi_bvalid = { 0x014c, 5, 5, 0, 1 }, |
1569 | .utmi_id = { 0x014c, 8, 8, 0, 1 }, |
1570 | .utmi_ls = { 0x014c, 7, 6, 0, 1 }, |
1571 | }, |
1572 | [USB2PHY_PORT_HOST] = { |
1573 | .phy_sus = { 0x0194, 8, 0, 0, 0x1d1 }, |
1574 | .ls_det_en = { 0x0194, 14, 14, 0, 1 }, |
1575 | .ls_det_st = { 0x0194, 15, 15, 0, 1 }, |
1576 | .ls_det_clr = { 0x0194, 15, 15, 0, 1 } |
1577 | } |
1578 | }, |
1579 | .chg_det = { |
1580 | .opmode = { .offset: 0x017c, .bitend: 3, .bitstart: 0, .disable: 5, .enable: 1 }, |
1581 | .cp_det = { 0x02c0, 6, 6, 0, 1 }, |
1582 | .dcp_det = { 0x02c0, 5, 5, 0, 1 }, |
1583 | .dp_det = { 0x02c0, 7, 7, 0, 1 }, |
1584 | .idm_sink_en = { 0x0184, 8, 8, 0, 1 }, |
1585 | .idp_sink_en = { 0x0184, 7, 7, 0, 1 }, |
1586 | .idp_src_en = { 0x0184, 9, 9, 0, 1 }, |
1587 | .rdm_pdwn_en = { 0x0184, 10, 10, 0, 1 }, |
1588 | .vdm_src_en = { 0x0184, 12, 12, 0, 1 }, |
1589 | .vdp_src_en = { 0x0184, 11, 11, 0, 1 }, |
1590 | }, |
1591 | }, |
1592 | { /* sentinel */ } |
1593 | }; |
1594 | |
1595 | static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { |
1596 | { |
1597 | .reg = 0x760, |
1598 | .num_ports = 2, |
1599 | .clkout_ctl = { 0x0768, 4, 4, 1, 0 }, |
1600 | .port_cfgs = { |
1601 | [USB2PHY_PORT_OTG] = { |
1602 | .phy_sus = { 0x0760, 15, 0, 0, 0x1d1 }, |
1603 | .bvalid_det_en = { 0x0680, 3, 3, 0, 1 }, |
1604 | .bvalid_det_st = { 0x0690, 3, 3, 0, 1 }, |
1605 | .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 }, |
1606 | .idfall_det_en = { 0x0680, 6, 6, 0, 1 }, |
1607 | .idfall_det_st = { 0x0690, 6, 6, 0, 1 }, |
1608 | .idfall_det_clr = { 0x06a0, 6, 6, 0, 1 }, |
1609 | .idrise_det_en = { 0x0680, 5, 5, 0, 1 }, |
1610 | .idrise_det_st = { 0x0690, 5, 5, 0, 1 }, |
1611 | .idrise_det_clr = { 0x06a0, 5, 5, 0, 1 }, |
1612 | .ls_det_en = { 0x0680, 2, 2, 0, 1 }, |
1613 | .ls_det_st = { 0x0690, 2, 2, 0, 1 }, |
1614 | .ls_det_clr = { 0x06a0, 2, 2, 0, 1 }, |
1615 | .utmi_bvalid = { 0x0480, 4, 4, 0, 1 }, |
1616 | .utmi_id = { 0x0480, 1, 1, 0, 1 }, |
1617 | .utmi_ls = { 0x0480, 3, 2, 0, 1 }, |
1618 | }, |
1619 | [USB2PHY_PORT_HOST] = { |
1620 | .phy_sus = { 0x0764, 15, 0, 0, 0x1d1 }, |
1621 | .ls_det_en = { 0x0680, 4, 4, 0, 1 }, |
1622 | .ls_det_st = { 0x0690, 4, 4, 0, 1 }, |
1623 | .ls_det_clr = { 0x06a0, 4, 4, 0, 1 } |
1624 | } |
1625 | }, |
1626 | .chg_det = { |
1627 | .opmode = { .offset: 0x0760, .bitend: 3, .bitstart: 0, .disable: 5, .enable: 1 }, |
1628 | .cp_det = { 0x0884, 4, 4, 0, 1 }, |
1629 | .dcp_det = { 0x0884, 3, 3, 0, 1 }, |
1630 | .dp_det = { 0x0884, 5, 5, 0, 1 }, |
1631 | .idm_sink_en = { 0x0768, 8, 8, 0, 1 }, |
1632 | .idp_sink_en = { 0x0768, 7, 7, 0, 1 }, |
1633 | .idp_src_en = { 0x0768, 9, 9, 0, 1 }, |
1634 | .rdm_pdwn_en = { 0x0768, 10, 10, 0, 1 }, |
1635 | .vdm_src_en = { 0x0768, 12, 12, 0, 1 }, |
1636 | .vdp_src_en = { 0x0768, 11, 11, 0, 1 }, |
1637 | }, |
1638 | }, |
1639 | { |
1640 | .reg = 0x800, |
1641 | .num_ports = 2, |
1642 | .clkout_ctl = { 0x0808, 4, 4, 1, 0 }, |
1643 | .port_cfgs = { |
1644 | [USB2PHY_PORT_OTG] = { |
1645 | .phy_sus = { 0x800, 15, 0, 0, 0x1d1 }, |
1646 | .ls_det_en = { 0x0684, 0, 0, 0, 1 }, |
1647 | .ls_det_st = { 0x0694, 0, 0, 0, 1 }, |
1648 | .ls_det_clr = { 0x06a4, 0, 0, 0, 1 } |
1649 | }, |
1650 | [USB2PHY_PORT_HOST] = { |
1651 | .phy_sus = { 0x804, 15, 0, 0, 0x1d1 }, |
1652 | .ls_det_en = { 0x0684, 1, 1, 0, 1 }, |
1653 | .ls_det_st = { 0x0694, 1, 1, 0, 1 }, |
1654 | .ls_det_clr = { 0x06a4, 1, 1, 0, 1 } |
1655 | } |
1656 | }, |
1657 | }, |
1658 | { /* sentinel */ } |
1659 | }; |
1660 | |
1661 | static const struct rockchip_usb2phy_cfg rk3308_phy_cfgs[] = { |
1662 | { |
1663 | .reg = 0x100, |
1664 | .num_ports = 2, |
1665 | .clkout_ctl = { 0x108, 4, 4, 1, 0 }, |
1666 | .port_cfgs = { |
1667 | [USB2PHY_PORT_OTG] = { |
1668 | .phy_sus = { 0x0100, 8, 0, 0, 0x1d1 }, |
1669 | .bvalid_det_en = { 0x3020, 3, 2, 0, 3 }, |
1670 | .bvalid_det_st = { 0x3024, 3, 2, 0, 3 }, |
1671 | .bvalid_det_clr = { 0x3028, 3, 2, 0, 3 }, |
1672 | .idfall_det_en = { 0x3020, 5, 5, 0, 1 }, |
1673 | .idfall_det_st = { 0x3024, 5, 5, 0, 1 }, |
1674 | .idfall_det_clr = { 0x3028, 5, 5, 0, 1 }, |
1675 | .idrise_det_en = { 0x3020, 4, 4, 0, 1 }, |
1676 | .idrise_det_st = { 0x3024, 4, 4, 0, 1 }, |
1677 | .idrise_det_clr = { 0x3028, 4, 4, 0, 1 }, |
1678 | .ls_det_en = { 0x3020, 0, 0, 0, 1 }, |
1679 | .ls_det_st = { 0x3024, 0, 0, 0, 1 }, |
1680 | .ls_det_clr = { 0x3028, 0, 0, 0, 1 }, |
1681 | .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, |
1682 | .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, |
1683 | .utmi_id = { 0x0120, 6, 6, 0, 1 }, |
1684 | .utmi_ls = { 0x0120, 5, 4, 0, 1 }, |
1685 | }, |
1686 | [USB2PHY_PORT_HOST] = { |
1687 | .phy_sus = { 0x0104, 8, 0, 0, 0x1d1 }, |
1688 | .ls_det_en = { 0x3020, 1, 1, 0, 1 }, |
1689 | .ls_det_st = { 0x3024, 1, 1, 0, 1 }, |
1690 | .ls_det_clr = { 0x3028, 1, 1, 0, 1 }, |
1691 | .utmi_ls = { 0x0120, 17, 16, 0, 1 }, |
1692 | .utmi_hstdet = { 0x0120, 19, 19, 0, 1 } |
1693 | } |
1694 | }, |
1695 | .chg_det = { |
1696 | .opmode = { .offset: 0x0100, .bitend: 3, .bitstart: 0, .disable: 5, .enable: 1 }, |
1697 | .cp_det = { 0x0120, 24, 24, 0, 1 }, |
1698 | .dcp_det = { 0x0120, 23, 23, 0, 1 }, |
1699 | .dp_det = { 0x0120, 25, 25, 0, 1 }, |
1700 | .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, |
1701 | .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, |
1702 | .idp_src_en = { 0x0108, 9, 9, 0, 1 }, |
1703 | .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, |
1704 | .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, |
1705 | .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, |
1706 | }, |
1707 | }, |
1708 | { /* sentinel */ } |
1709 | }; |
1710 | |
1711 | static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { |
1712 | { |
1713 | .reg = 0x100, |
1714 | .num_ports = 2, |
1715 | .clkout_ctl = { 0x108, 4, 4, 1, 0 }, |
1716 | .port_cfgs = { |
1717 | [USB2PHY_PORT_OTG] = { |
1718 | .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, |
1719 | .bvalid_det_en = { 0x0110, 3, 2, 0, 3 }, |
1720 | .bvalid_det_st = { 0x0114, 3, 2, 0, 3 }, |
1721 | .bvalid_det_clr = { 0x0118, 3, 2, 0, 3 }, |
1722 | .idfall_det_en = { 0x0110, 5, 5, 0, 1 }, |
1723 | .idfall_det_st = { 0x0114, 5, 5, 0, 1 }, |
1724 | .idfall_det_clr = { 0x0118, 5, 5, 0, 1 }, |
1725 | .idrise_det_en = { 0x0110, 4, 4, 0, 1 }, |
1726 | .idrise_det_st = { 0x0114, 4, 4, 0, 1 }, |
1727 | .idrise_det_clr = { 0x0118, 4, 4, 0, 1 }, |
1728 | .ls_det_en = { 0x0110, 0, 0, 0, 1 }, |
1729 | .ls_det_st = { 0x0114, 0, 0, 0, 1 }, |
1730 | .ls_det_clr = { 0x0118, 0, 0, 0, 1 }, |
1731 | .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, |
1732 | .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, |
1733 | .utmi_id = { 0x0120, 6, 6, 0, 1 }, |
1734 | .utmi_ls = { 0x0120, 5, 4, 0, 1 }, |
1735 | }, |
1736 | [USB2PHY_PORT_HOST] = { |
1737 | .phy_sus = { 0x104, 15, 0, 0, 0x1d1 }, |
1738 | .ls_det_en = { 0x110, 1, 1, 0, 1 }, |
1739 | .ls_det_st = { 0x114, 1, 1, 0, 1 }, |
1740 | .ls_det_clr = { 0x118, 1, 1, 0, 1 }, |
1741 | .utmi_ls = { 0x120, 17, 16, 0, 1 }, |
1742 | .utmi_hstdet = { 0x120, 19, 19, 0, 1 } |
1743 | } |
1744 | }, |
1745 | .chg_det = { |
1746 | .opmode = { .offset: 0x0100, .bitend: 3, .bitstart: 0, .disable: 5, .enable: 1 }, |
1747 | .cp_det = { 0x0120, 24, 24, 0, 1 }, |
1748 | .dcp_det = { 0x0120, 23, 23, 0, 1 }, |
1749 | .dp_det = { 0x0120, 25, 25, 0, 1 }, |
1750 | .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, |
1751 | .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, |
1752 | .idp_src_en = { 0x0108, 9, 9, 0, 1 }, |
1753 | .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, |
1754 | .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, |
1755 | .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, |
1756 | }, |
1757 | }, |
1758 | { /* sentinel */ } |
1759 | }; |
1760 | |
1761 | static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = { |
1762 | { |
1763 | .reg = 0x700, |
1764 | .num_ports = 2, |
1765 | .clkout_ctl = { 0x0724, 15, 15, 1, 0 }, |
1766 | .port_cfgs = { |
1767 | [USB2PHY_PORT_HOST] = { |
1768 | .phy_sus = { 0x0728, 15, 0, 0, 0x1d1 }, |
1769 | .ls_det_en = { 0x0680, 4, 4, 0, 1 }, |
1770 | .ls_det_st = { 0x0690, 4, 4, 0, 1 }, |
1771 | .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }, |
1772 | .utmi_ls = { 0x049c, 14, 13, 0, 1 }, |
1773 | .utmi_hstdet = { 0x049c, 12, 12, 0, 1 } |
1774 | } |
1775 | }, |
1776 | }, |
1777 | { /* sentinel */ } |
1778 | }; |
1779 | |
1780 | static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { |
1781 | { |
1782 | .reg = 0xe450, |
1783 | .num_ports = 2, |
1784 | .clkout_ctl = { 0xe450, 4, 4, 1, 0 }, |
1785 | .port_cfgs = { |
1786 | [USB2PHY_PORT_OTG] = { |
1787 | .phy_sus = { 0xe454, 1, 0, 2, 1 }, |
1788 | .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, |
1789 | .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, |
1790 | .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, |
1791 | .idfall_det_en = { 0xe3c0, 5, 5, 0, 1 }, |
1792 | .idfall_det_st = { 0xe3e0, 5, 5, 0, 1 }, |
1793 | .idfall_det_clr = { 0xe3d0, 5, 5, 0, 1 }, |
1794 | .idrise_det_en = { 0xe3c0, 4, 4, 0, 1 }, |
1795 | .idrise_det_st = { 0xe3e0, 4, 4, 0, 1 }, |
1796 | .idrise_det_clr = { 0xe3d0, 4, 4, 0, 1 }, |
1797 | .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 }, |
1798 | .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 }, |
1799 | .utmi_id = { 0xe2ac, 8, 8, 0, 1 }, |
1800 | }, |
1801 | [USB2PHY_PORT_HOST] = { |
1802 | .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 }, |
1803 | .ls_det_en = { 0xe3c0, 6, 6, 0, 1 }, |
1804 | .ls_det_st = { 0xe3e0, 6, 6, 0, 1 }, |
1805 | .ls_det_clr = { 0xe3d0, 6, 6, 0, 1 }, |
1806 | .utmi_ls = { 0xe2ac, 22, 21, 0, 1 }, |
1807 | .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 } |
1808 | } |
1809 | }, |
1810 | .chg_det = { |
1811 | .opmode = { .offset: 0xe454, .bitend: 3, .bitstart: 0, .disable: 5, .enable: 1 }, |
1812 | .cp_det = { 0xe2ac, 2, 2, 0, 1 }, |
1813 | .dcp_det = { 0xe2ac, 1, 1, 0, 1 }, |
1814 | .dp_det = { 0xe2ac, 0, 0, 0, 1 }, |
1815 | .idm_sink_en = { 0xe450, 8, 8, 0, 1 }, |
1816 | .idp_sink_en = { 0xe450, 7, 7, 0, 1 }, |
1817 | .idp_src_en = { 0xe450, 9, 9, 0, 1 }, |
1818 | .rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 }, |
1819 | .vdm_src_en = { 0xe450, 12, 12, 0, 1 }, |
1820 | .vdp_src_en = { 0xe450, 11, 11, 0, 1 }, |
1821 | }, |
1822 | }, |
1823 | { |
1824 | .reg = 0xe460, |
1825 | .num_ports = 2, |
1826 | .clkout_ctl = { 0xe460, 4, 4, 1, 0 }, |
1827 | .port_cfgs = { |
1828 | [USB2PHY_PORT_OTG] = { |
1829 | .phy_sus = { 0xe464, 1, 0, 2, 1 }, |
1830 | .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, |
1831 | .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, |
1832 | .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 }, |
1833 | .idfall_det_en = { 0xe3c0, 10, 10, 0, 1 }, |
1834 | .idfall_det_st = { 0xe3e0, 10, 10, 0, 1 }, |
1835 | .idfall_det_clr = { 0xe3d0, 10, 10, 0, 1 }, |
1836 | .idrise_det_en = { 0xe3c0, 9, 9, 0, 1 }, |
1837 | .idrise_det_st = { 0xe3e0, 9, 9, 0, 1 }, |
1838 | .idrise_det_clr = { 0xe3d0, 9, 9, 0, 1 }, |
1839 | .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 }, |
1840 | .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 }, |
1841 | .utmi_id = { 0xe2ac, 11, 11, 0, 1 }, |
1842 | }, |
1843 | [USB2PHY_PORT_HOST] = { |
1844 | .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 }, |
1845 | .ls_det_en = { 0xe3c0, 11, 11, 0, 1 }, |
1846 | .ls_det_st = { 0xe3e0, 11, 11, 0, 1 }, |
1847 | .ls_det_clr = { 0xe3d0, 11, 11, 0, 1 }, |
1848 | .utmi_ls = { 0xe2ac, 26, 25, 0, 1 }, |
1849 | .utmi_hstdet = { 0xe2ac, 27, 27, 0, 1 } |
1850 | } |
1851 | }, |
1852 | }, |
1853 | { /* sentinel */ } |
1854 | }; |
1855 | |
1856 | static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { |
1857 | { |
1858 | .reg = 0xfe8a0000, |
1859 | .num_ports = 2, |
1860 | .clkout_ctl = { 0x0008, 4, 4, 1, 0 }, |
1861 | .port_cfgs = { |
1862 | [USB2PHY_PORT_OTG] = { |
1863 | .phy_sus = { 0x0000, 8, 0, 0, 0x1d1 }, |
1864 | .bvalid_det_en = { 0x0080, 3, 2, 0, 3 }, |
1865 | .bvalid_det_st = { 0x0084, 3, 2, 0, 3 }, |
1866 | .bvalid_det_clr = { 0x0088, 3, 2, 0, 3 }, |
1867 | .idfall_det_en = { 0x0080, 5, 5, 0, 1 }, |
1868 | .idfall_det_st = { 0x0084, 5, 5, 0, 1 }, |
1869 | .idfall_det_clr = { 0x0088, 5, 5, 0, 1 }, |
1870 | .idrise_det_en = { 0x0080, 4, 4, 0, 1 }, |
1871 | .idrise_det_st = { 0x0084, 4, 4, 0, 1 }, |
1872 | .idrise_det_clr = { 0x0088, 4, 4, 0, 1 }, |
1873 | .utmi_avalid = { 0x00c0, 10, 10, 0, 1 }, |
1874 | .utmi_bvalid = { 0x00c0, 9, 9, 0, 1 }, |
1875 | .utmi_id = { 0x00c0, 6, 6, 0, 1 }, |
1876 | }, |
1877 | [USB2PHY_PORT_HOST] = { |
1878 | /* Select suspend control from controller */ |
1879 | .phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d2 }, |
1880 | .ls_det_en = { 0x0080, 1, 1, 0, 1 }, |
1881 | .ls_det_st = { 0x0084, 1, 1, 0, 1 }, |
1882 | .ls_det_clr = { 0x0088, 1, 1, 0, 1 }, |
1883 | .utmi_ls = { 0x00c0, 17, 16, 0, 1 }, |
1884 | .utmi_hstdet = { 0x00c0, 19, 19, 0, 1 } |
1885 | } |
1886 | }, |
1887 | .chg_det = { |
1888 | .opmode = { .offset: 0x0000, .bitend: 3, .bitstart: 0, .disable: 5, .enable: 1 }, |
1889 | .cp_det = { 0x00c0, 24, 24, 0, 1 }, |
1890 | .dcp_det = { 0x00c0, 23, 23, 0, 1 }, |
1891 | .dp_det = { 0x00c0, 25, 25, 0, 1 }, |
1892 | .idm_sink_en = { 0x0008, 8, 8, 0, 1 }, |
1893 | .idp_sink_en = { 0x0008, 7, 7, 0, 1 }, |
1894 | .idp_src_en = { 0x0008, 9, 9, 0, 1 }, |
1895 | .rdm_pdwn_en = { 0x0008, 10, 10, 0, 1 }, |
1896 | .vdm_src_en = { 0x0008, 12, 12, 0, 1 }, |
1897 | .vdp_src_en = { 0x0008, 11, 11, 0, 1 }, |
1898 | }, |
1899 | }, |
1900 | { |
1901 | .reg = 0xfe8b0000, |
1902 | .num_ports = 2, |
1903 | .clkout_ctl = { 0x0008, 4, 4, 1, 0 }, |
1904 | .port_cfgs = { |
1905 | [USB2PHY_PORT_OTG] = { |
1906 | .phy_sus = { 0x0000, 8, 0, 0x1d2, 0x1d1 }, |
1907 | .ls_det_en = { 0x0080, 0, 0, 0, 1 }, |
1908 | .ls_det_st = { 0x0084, 0, 0, 0, 1 }, |
1909 | .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, |
1910 | .utmi_ls = { 0x00c0, 5, 4, 0, 1 }, |
1911 | .utmi_hstdet = { 0x00c0, 7, 7, 0, 1 } |
1912 | }, |
1913 | [USB2PHY_PORT_HOST] = { |
1914 | .phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d1 }, |
1915 | .ls_det_en = { 0x0080, 1, 1, 0, 1 }, |
1916 | .ls_det_st = { 0x0084, 1, 1, 0, 1 }, |
1917 | .ls_det_clr = { 0x0088, 1, 1, 0, 1 }, |
1918 | .utmi_ls = { 0x00c0, 17, 16, 0, 1 }, |
1919 | .utmi_hstdet = { 0x00c0, 19, 19, 0, 1 } |
1920 | } |
1921 | }, |
1922 | }, |
1923 | { /* sentinel */ } |
1924 | }; |
1925 | |
1926 | static const struct rockchip_usb2phy_cfg rk3588_phy_cfgs[] = { |
1927 | { |
1928 | .reg = 0x0000, |
1929 | .num_ports = 1, |
1930 | .phy_tuning = rk3588_usb2phy_tuning, |
1931 | .clkout_ctl = { .offset: 0x0000, .bitend: 0, .bitstart: 0, .disable: 1, .enable: 0 }, |
1932 | .port_cfgs = { |
1933 | [USB2PHY_PORT_OTG] = { |
1934 | .phy_sus = { 0x000c, 11, 11, 0, 1 }, |
1935 | .bvalid_det_en = { 0x0080, 1, 1, 0, 1 }, |
1936 | .bvalid_det_st = { 0x0084, 1, 1, 0, 1 }, |
1937 | .bvalid_det_clr = { 0x0088, 1, 1, 0, 1 }, |
1938 | .ls_det_en = { 0x0080, 0, 0, 0, 1 }, |
1939 | .ls_det_st = { 0x0084, 0, 0, 0, 1 }, |
1940 | .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, |
1941 | .disfall_en = { 0x0080, 6, 6, 0, 1 }, |
1942 | .disfall_st = { 0x0084, 6, 6, 0, 1 }, |
1943 | .disfall_clr = { 0x0088, 6, 6, 0, 1 }, |
1944 | .disrise_en = { 0x0080, 5, 5, 0, 1 }, |
1945 | .disrise_st = { 0x0084, 5, 5, 0, 1 }, |
1946 | .disrise_clr = { 0x0088, 5, 5, 0, 1 }, |
1947 | .utmi_avalid = { 0x00c0, 7, 7, 0, 1 }, |
1948 | .utmi_bvalid = { 0x00c0, 6, 6, 0, 1 }, |
1949 | .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, |
1950 | } |
1951 | }, |
1952 | .chg_det = { |
1953 | .cp_det = { .offset: 0x00c0, .bitend: 0, .bitstart: 0, .disable: 0, .enable: 1 }, |
1954 | .dcp_det = { 0x00c0, 0, 0, 0, 1 }, |
1955 | .dp_det = { 0x00c0, 1, 1, 1, 0 }, |
1956 | .idm_sink_en = { 0x0008, 5, 5, 1, 0 }, |
1957 | .idp_sink_en = { 0x0008, 5, 5, 0, 1 }, |
1958 | .idp_src_en = { 0x0008, 14, 14, 0, 1 }, |
1959 | .rdm_pdwn_en = { 0x0008, 14, 14, 0, 1 }, |
1960 | .vdm_src_en = { 0x0008, 7, 6, 0, 3 }, |
1961 | .vdp_src_en = { 0x0008, 7, 6, 0, 3 }, |
1962 | }, |
1963 | }, |
1964 | { |
1965 | .reg = 0x4000, |
1966 | .num_ports = 1, |
1967 | .phy_tuning = rk3588_usb2phy_tuning, |
1968 | .clkout_ctl = { .offset: 0x0000, .bitend: 0, .bitstart: 0, .disable: 1, .enable: 0 }, |
1969 | .port_cfgs = { |
1970 | [USB2PHY_PORT_OTG] = { |
1971 | .phy_sus = { 0x000c, 11, 11, 0, 1 }, |
1972 | .bvalid_det_en = { 0x0080, 1, 1, 0, 1 }, |
1973 | .bvalid_det_st = { 0x0084, 1, 1, 0, 1 }, |
1974 | .bvalid_det_clr = { 0x0088, 1, 1, 0, 1 }, |
1975 | .ls_det_en = { 0x0080, 0, 0, 0, 1 }, |
1976 | .ls_det_st = { 0x0084, 0, 0, 0, 1 }, |
1977 | .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, |
1978 | .disfall_en = { 0x0080, 6, 6, 0, 1 }, |
1979 | .disfall_st = { 0x0084, 6, 6, 0, 1 }, |
1980 | .disfall_clr = { 0x0088, 6, 6, 0, 1 }, |
1981 | .disrise_en = { 0x0080, 5, 5, 0, 1 }, |
1982 | .disrise_st = { 0x0084, 5, 5, 0, 1 }, |
1983 | .disrise_clr = { 0x0088, 5, 5, 0, 1 }, |
1984 | .utmi_avalid = { 0x00c0, 7, 7, 0, 1 }, |
1985 | .utmi_bvalid = { 0x00c0, 6, 6, 0, 1 }, |
1986 | .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, |
1987 | } |
1988 | }, |
1989 | .chg_det = { |
1990 | .cp_det = { .offset: 0x00c0, .bitend: 0, .bitstart: 0, .disable: 0, .enable: 1 }, |
1991 | .dcp_det = { 0x00c0, 0, 0, 0, 1 }, |
1992 | .dp_det = { 0x00c0, 1, 1, 1, 0 }, |
1993 | .idm_sink_en = { 0x0008, 5, 5, 1, 0 }, |
1994 | .idp_sink_en = { 0x0008, 5, 5, 0, 1 }, |
1995 | .idp_src_en = { 0x0008, 14, 14, 0, 1 }, |
1996 | .rdm_pdwn_en = { 0x0008, 14, 14, 0, 1 }, |
1997 | .vdm_src_en = { 0x0008, 7, 6, 0, 3 }, |
1998 | .vdp_src_en = { 0x0008, 7, 6, 0, 3 }, |
1999 | }, |
2000 | }, |
2001 | { |
2002 | .reg = 0x8000, |
2003 | .num_ports = 1, |
2004 | .phy_tuning = rk3588_usb2phy_tuning, |
2005 | .clkout_ctl = { .offset: 0x0000, .bitend: 0, .bitstart: 0, .disable: 1, .enable: 0 }, |
2006 | .port_cfgs = { |
2007 | [USB2PHY_PORT_HOST] = { |
2008 | .phy_sus = { 0x0008, 2, 2, 0, 1 }, |
2009 | .ls_det_en = { 0x0080, 0, 0, 0, 1 }, |
2010 | .ls_det_st = { 0x0084, 0, 0, 0, 1 }, |
2011 | .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, |
2012 | .disfall_en = { 0x0080, 6, 6, 0, 1 }, |
2013 | .disfall_st = { 0x0084, 6, 6, 0, 1 }, |
2014 | .disfall_clr = { 0x0088, 6, 6, 0, 1 }, |
2015 | .disrise_en = { 0x0080, 5, 5, 0, 1 }, |
2016 | .disrise_st = { 0x0084, 5, 5, 0, 1 }, |
2017 | .disrise_clr = { 0x0088, 5, 5, 0, 1 }, |
2018 | .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, |
2019 | } |
2020 | }, |
2021 | }, |
2022 | { |
2023 | .reg = 0xc000, |
2024 | .num_ports = 1, |
2025 | .phy_tuning = rk3588_usb2phy_tuning, |
2026 | .clkout_ctl = { .offset: 0x0000, .bitend: 0, .bitstart: 0, .disable: 1, .enable: 0 }, |
2027 | .port_cfgs = { |
2028 | [USB2PHY_PORT_HOST] = { |
2029 | .phy_sus = { 0x0008, 2, 2, 0, 1 }, |
2030 | .ls_det_en = { 0x0080, 0, 0, 0, 1 }, |
2031 | .ls_det_st = { 0x0084, 0, 0, 0, 1 }, |
2032 | .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, |
2033 | .disfall_en = { 0x0080, 6, 6, 0, 1 }, |
2034 | .disfall_st = { 0x0084, 6, 6, 0, 1 }, |
2035 | .disfall_clr = { 0x0088, 6, 6, 0, 1 }, |
2036 | .disrise_en = { 0x0080, 5, 5, 0, 1 }, |
2037 | .disrise_st = { 0x0084, 5, 5, 0, 1 }, |
2038 | .disrise_clr = { 0x0088, 5, 5, 0, 1 }, |
2039 | .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, |
2040 | } |
2041 | }, |
2042 | }, |
2043 | { /* sentinel */ } |
2044 | }; |
2045 | |
2046 | static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { |
2047 | { |
2048 | .reg = 0x100, |
2049 | .num_ports = 2, |
2050 | .clkout_ctl = { 0x108, 4, 4, 1, 0 }, |
2051 | .port_cfgs = { |
2052 | [USB2PHY_PORT_OTG] = { |
2053 | .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, |
2054 | .bvalid_det_en = { 0x0680, 3, 3, 0, 1 }, |
2055 | .bvalid_det_st = { 0x0690, 3, 3, 0, 1 }, |
2056 | .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 }, |
2057 | .ls_det_en = { 0x0680, 2, 2, 0, 1 }, |
2058 | .ls_det_st = { 0x0690, 2, 2, 0, 1 }, |
2059 | .ls_det_clr = { 0x06a0, 2, 2, 0, 1 }, |
2060 | .utmi_bvalid = { 0x0804, 10, 10, 0, 1 }, |
2061 | .utmi_ls = { 0x0804, 13, 12, 0, 1 }, |
2062 | }, |
2063 | [USB2PHY_PORT_HOST] = { |
2064 | .phy_sus = { 0x0104, 15, 0, 0, 0x1d1 }, |
2065 | .ls_det_en = { 0x0680, 4, 4, 0, 1 }, |
2066 | .ls_det_st = { 0x0690, 4, 4, 0, 1 }, |
2067 | .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }, |
2068 | .utmi_ls = { 0x0804, 9, 8, 0, 1 }, |
2069 | .utmi_hstdet = { 0x0804, 7, 7, 0, 1 } |
2070 | } |
2071 | }, |
2072 | .chg_det = { |
2073 | .opmode = { .offset: 0x0100, .bitend: 3, .bitstart: 0, .disable: 5, .enable: 1 }, |
2074 | .cp_det = { 0x0804, 1, 1, 0, 1 }, |
2075 | .dcp_det = { 0x0804, 0, 0, 0, 1 }, |
2076 | .dp_det = { 0x0804, 2, 2, 0, 1 }, |
2077 | .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, |
2078 | .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, |
2079 | .idp_src_en = { 0x0108, 9, 9, 0, 1 }, |
2080 | .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, |
2081 | .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, |
2082 | .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, |
2083 | }, |
2084 | }, |
2085 | { /* sentinel */ } |
2086 | }; |
2087 | |
2088 | static const struct of_device_id rockchip_usb2phy_dt_match[] = { |
2089 | { .compatible = "rockchip,px30-usb2phy" , .data = &rk3328_phy_cfgs }, |
2090 | { .compatible = "rockchip,rk3128-usb2phy" , .data = &rk3128_phy_cfgs }, |
2091 | { .compatible = "rockchip,rk3228-usb2phy" , .data = &rk3228_phy_cfgs }, |
2092 | { .compatible = "rockchip,rk3308-usb2phy" , .data = &rk3308_phy_cfgs }, |
2093 | { .compatible = "rockchip,rk3328-usb2phy" , .data = &rk3328_phy_cfgs }, |
2094 | { .compatible = "rockchip,rk3366-usb2phy" , .data = &rk3366_phy_cfgs }, |
2095 | { .compatible = "rockchip,rk3399-usb2phy" , .data = &rk3399_phy_cfgs }, |
2096 | { .compatible = "rockchip,rk3568-usb2phy" , .data = &rk3568_phy_cfgs }, |
2097 | { .compatible = "rockchip,rk3588-usb2phy" , .data = &rk3588_phy_cfgs }, |
2098 | { .compatible = "rockchip,rv1108-usb2phy" , .data = &rv1108_phy_cfgs }, |
2099 | {} |
2100 | }; |
2101 | MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match); |
2102 | |
2103 | static struct platform_driver rockchip_usb2phy_driver = { |
2104 | .probe = rockchip_usb2phy_probe, |
2105 | .driver = { |
2106 | .name = "rockchip-usb2phy" , |
2107 | .of_match_table = rockchip_usb2phy_dt_match, |
2108 | }, |
2109 | }; |
2110 | module_platform_driver(rockchip_usb2phy_driver); |
2111 | |
2112 | MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>" ); |
2113 | MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver" ); |
2114 | MODULE_LICENSE("GPL v2" ); |
2115 | |