Commit | Line | Data |
---|---|---|
eee0e5da | 1 | // SPDX-License-Identifier: GPL-2.0 |
f3b5a8d9 YS |
2 | /* |
3 | * Renesas R-Car Gen3 for USB2.0 PHY driver | |
4 | * | |
7e0540f4 | 5 | * Copyright (C) 2015-2017 Renesas Electronics Corporation |
f3b5a8d9 YS |
6 | * |
7 | * This is based on the phy-rcar-gen2 driver: | |
8 | * Copyright (C) 2014 Renesas Solutions Corp. | |
9 | * Copyright (C) 2014 Cogent Embedded, Inc. | |
f3b5a8d9 YS |
10 | */ |
11 | ||
176aa360 | 12 | #include <linux/extcon-provider.h> |
9f391c57 | 13 | #include <linux/interrupt.h> |
f3b5a8d9 YS |
14 | #include <linux/io.h> |
15 | #include <linux/module.h> | |
5c9dc637 | 16 | #include <linux/mutex.h> |
f3b5a8d9 YS |
17 | #include <linux/of.h> |
18 | #include <linux/of_address.h> | |
9adaaa9e | 19 | #include <linux/of_device.h> |
f3b5a8d9 YS |
20 | #include <linux/phy/phy.h> |
21 | #include <linux/platform_device.h> | |
441a681b | 22 | #include <linux/pm_runtime.h> |
6dcfd7c3 | 23 | #include <linux/regulator/consumer.h> |
4bd5ead8 | 24 | #include <linux/string.h> |
7e0540f4 | 25 | #include <linux/usb/of.h> |
c14f8a40 | 26 | #include <linux/workqueue.h> |
f3b5a8d9 YS |
27 | |
28 | /******* USB2.0 Host registers (original offset is +0x200) *******/ | |
29 | #define USB2_INT_ENABLE 0x000 | |
30 | #define USB2_USBCTR 0x00c | |
31 | #define USB2_SPD_RSM_TIMSET 0x10c | |
32 | #define USB2_OC_TIMSET 0x110 | |
1114e2d3 | 33 | #define USB2_COMMCTRL 0x600 |
9f391c57 YS |
34 | #define USB2_OBINTSTA 0x604 |
35 | #define USB2_OBINTEN 0x608 | |
1114e2d3 YS |
36 | #define USB2_VBCTRL 0x60c |
37 | #define USB2_LINECTRL1 0x610 | |
38 | #define USB2_ADPCTRL 0x630 | |
f3b5a8d9 YS |
39 | |
40 | /* INT_ENABLE */ | |
9f391c57 | 41 | #define USB2_INT_ENABLE_UCOM_INTEN BIT(3) |
549b6b55 YS |
42 | #define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) /* For EHCI */ |
43 | #define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) /* For OHCI */ | |
f3b5a8d9 YS |
44 | |
45 | /* USBCTR */ | |
46 | #define USB2_USBCTR_DIRPD BIT(2) | |
47 | #define USB2_USBCTR_PLL_RST BIT(1) | |
48 | ||
49 | /* SPD_RSM_TIMSET */ | |
50 | #define USB2_SPD_RSM_TIMSET_INIT 0x014e029b | |
51 | ||
52 | /* OC_TIMSET */ | |
53 | #define USB2_OC_TIMSET_INIT 0x000209ab | |
54 | ||
1114e2d3 YS |
55 | /* COMMCTRL */ |
56 | #define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */ | |
57 | ||
9f391c57 YS |
58 | /* OBINTSTA and OBINTEN */ |
59 | #define USB2_OBINT_SESSVLDCHG BIT(12) | |
60 | #define USB2_OBINT_IDDIGCHG BIT(11) | |
61 | #define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \ | |
62 | USB2_OBINT_IDDIGCHG) | |
63 | ||
1114e2d3 | 64 | /* VBCTRL */ |
e6839c31 | 65 | #define USB2_VBCTRL_OCCLREN BIT(16) |
1114e2d3 YS |
66 | #define USB2_VBCTRL_DRVVBUSSEL BIT(8) |
67 | ||
68 | /* LINECTRL1 */ | |
69 | #define USB2_LINECTRL1_DPRPD_EN BIT(19) | |
70 | #define USB2_LINECTRL1_DP_RPD BIT(18) | |
71 | #define USB2_LINECTRL1_DMRPD_EN BIT(17) | |
72 | #define USB2_LINECTRL1_DM_RPD BIT(16) | |
9bb86777 | 73 | #define USB2_LINECTRL1_OPMODE_NODRV BIT(6) |
1114e2d3 YS |
74 | |
75 | /* ADPCTRL */ | |
76 | #define USB2_ADPCTRL_OTGSESSVLD BIT(20) | |
77 | #define USB2_ADPCTRL_IDDIG BIT(19) | |
78 | #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ | |
79 | #define USB2_ADPCTRL_DRVVBUS BIT(4) | |
80 | ||
549b6b55 YS |
81 | #define NUM_OF_PHYS 4 |
82 | enum rcar_gen3_phy_index { | |
83 | PHY_INDEX_BOTH_HC, | |
84 | PHY_INDEX_OHCI, | |
85 | PHY_INDEX_EHCI, | |
86 | PHY_INDEX_HSUSB | |
87 | }; | |
88 | ||
89 | static const u32 rcar_gen3_int_enable[NUM_OF_PHYS] = { | |
90 | USB2_INT_ENABLE_USBH_INTB_EN | USB2_INT_ENABLE_USBH_INTA_EN, | |
91 | USB2_INT_ENABLE_USBH_INTA_EN, | |
92 | USB2_INT_ENABLE_USBH_INTB_EN, | |
93 | 0 | |
94 | }; | |
95 | ||
96 | struct rcar_gen3_phy { | |
97 | struct phy *phy; | |
98 | struct rcar_gen3_chan *ch; | |
99 | u32 int_enable_bits; | |
100 | bool initialized; | |
101 | bool otg_initialized; | |
102 | bool powered; | |
103 | }; | |
104 | ||
f3b5a8d9 | 105 | struct rcar_gen3_chan { |
801a69c7 | 106 | void __iomem *base; |
92fec1c2 | 107 | struct device *dev; /* platform_device's device */ |
2b38543c | 108 | struct extcon_dev *extcon; |
549b6b55 | 109 | struct rcar_gen3_phy rphys[NUM_OF_PHYS]; |
6dcfd7c3 | 110 | struct regulator *vbus; |
c14f8a40 | 111 | struct work_struct work; |
5c9dc637 | 112 | struct mutex lock; /* protects rphys[...].powered */ |
73801b90 | 113 | enum usb_dr_mode dr_mode; |
08b0ad37 | 114 | int irq; |
c14f8a40 | 115 | bool extcon_host; |
979b519c | 116 | bool is_otg_channel; |
cfdc6634 | 117 | bool uses_otg_pins; |
f3b5a8d9 YS |
118 | }; |
119 | ||
979b519c YS |
120 | /* |
121 | * Combination about is_otg_channel and uses_otg_pins: | |
122 | * | |
123 | * Parameters || Behaviors | |
124 | * is_otg_channel | uses_otg_pins || irqs | role sysfs | |
125 | * ---------------------+---------------++--------------+------------ | |
126 | * true | true || enabled | enabled | |
127 | * true | false || disabled | enabled | |
128 | * false | any || disabled | disabled | |
129 | */ | |
130 | ||
c14f8a40 YS |
131 | static void rcar_gen3_phy_usb2_work(struct work_struct *work) |
132 | { | |
133 | struct rcar_gen3_chan *ch = container_of(work, struct rcar_gen3_chan, | |
134 | work); | |
135 | ||
136 | if (ch->extcon_host) { | |
c6f30a5b CC |
137 | extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, true); |
138 | extcon_set_state_sync(ch->extcon, EXTCON_USB, false); | |
c14f8a40 | 139 | } else { |
c6f30a5b CC |
140 | extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, false); |
141 | extcon_set_state_sync(ch->extcon, EXTCON_USB, true); | |
c14f8a40 YS |
142 | } |
143 | } | |
144 | ||
1114e2d3 YS |
145 | static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) |
146 | { | |
801a69c7 | 147 | void __iomem *usb2_base = ch->base; |
1114e2d3 YS |
148 | u32 val = readl(usb2_base + USB2_COMMCTRL); |
149 | ||
92fec1c2 | 150 | dev_vdbg(ch->dev, "%s: %08x, %d\n", __func__, val, host); |
1114e2d3 YS |
151 | if (host) |
152 | val &= ~USB2_COMMCTRL_OTG_PERI; | |
153 | else | |
154 | val |= USB2_COMMCTRL_OTG_PERI; | |
155 | writel(val, usb2_base + USB2_COMMCTRL); | |
156 | } | |
157 | ||
158 | static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm) | |
159 | { | |
801a69c7 | 160 | void __iomem *usb2_base = ch->base; |
1114e2d3 YS |
161 | u32 val = readl(usb2_base + USB2_LINECTRL1); |
162 | ||
92fec1c2 | 163 | dev_vdbg(ch->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm); |
1114e2d3 YS |
164 | val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD); |
165 | if (dp) | |
166 | val |= USB2_LINECTRL1_DP_RPD; | |
167 | if (dm) | |
168 | val |= USB2_LINECTRL1_DM_RPD; | |
169 | writel(val, usb2_base + USB2_LINECTRL1); | |
170 | } | |
171 | ||
172 | static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) | |
173 | { | |
801a69c7 | 174 | void __iomem *usb2_base = ch->base; |
1114e2d3 YS |
175 | u32 val = readl(usb2_base + USB2_ADPCTRL); |
176 | ||
92fec1c2 | 177 | dev_vdbg(ch->dev, "%s: %08x, %d\n", __func__, val, vbus); |
1114e2d3 YS |
178 | if (vbus) |
179 | val |= USB2_ADPCTRL_DRVVBUS; | |
180 | else | |
181 | val &= ~USB2_ADPCTRL_DRVVBUS; | |
182 | writel(val, usb2_base + USB2_ADPCTRL); | |
183 | } | |
184 | ||
7ab0305d YS |
185 | static void rcar_gen3_control_otg_irq(struct rcar_gen3_chan *ch, int enable) |
186 | { | |
187 | void __iomem *usb2_base = ch->base; | |
188 | u32 val = readl(usb2_base + USB2_OBINTEN); | |
189 | ||
a602152c | 190 | if (ch->uses_otg_pins && enable) |
7ab0305d YS |
191 | val |= USB2_OBINT_BITS; |
192 | else | |
193 | val &= ~USB2_OBINT_BITS; | |
194 | writel(val, usb2_base + USB2_OBINTEN); | |
195 | } | |
196 | ||
1114e2d3 YS |
197 | static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch) |
198 | { | |
199 | rcar_gen3_set_linectrl(ch, 1, 1); | |
200 | rcar_gen3_set_host_mode(ch, 1); | |
201 | rcar_gen3_enable_vbus_ctrl(ch, 1); | |
2b38543c | 202 | |
c14f8a40 YS |
203 | ch->extcon_host = true; |
204 | schedule_work(&ch->work); | |
1114e2d3 YS |
205 | } |
206 | ||
207 | static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) | |
208 | { | |
209 | rcar_gen3_set_linectrl(ch, 0, 1); | |
210 | rcar_gen3_set_host_mode(ch, 0); | |
211 | rcar_gen3_enable_vbus_ctrl(ch, 0); | |
2b38543c | 212 | |
c14f8a40 YS |
213 | ch->extcon_host = false; |
214 | schedule_work(&ch->work); | |
1114e2d3 YS |
215 | } |
216 | ||
9bb86777 YS |
217 | static void rcar_gen3_init_for_b_host(struct rcar_gen3_chan *ch) |
218 | { | |
219 | void __iomem *usb2_base = ch->base; | |
220 | u32 val; | |
221 | ||
222 | val = readl(usb2_base + USB2_LINECTRL1); | |
223 | writel(val | USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); | |
224 | ||
225 | rcar_gen3_set_linectrl(ch, 1, 1); | |
226 | rcar_gen3_set_host_mode(ch, 1); | |
227 | rcar_gen3_enable_vbus_ctrl(ch, 0); | |
228 | ||
229 | val = readl(usb2_base + USB2_LINECTRL1); | |
230 | writel(val & ~USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); | |
231 | } | |
232 | ||
233 | static void rcar_gen3_init_for_a_peri(struct rcar_gen3_chan *ch) | |
234 | { | |
235 | rcar_gen3_set_linectrl(ch, 0, 1); | |
236 | rcar_gen3_set_host_mode(ch, 0); | |
237 | rcar_gen3_enable_vbus_ctrl(ch, 1); | |
238 | } | |
239 | ||
240 | static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch) | |
241 | { | |
7ab0305d | 242 | rcar_gen3_control_otg_irq(ch, 0); |
9bb86777 | 243 | |
09938ea9 | 244 | rcar_gen3_enable_vbus_ctrl(ch, 1); |
9bb86777 YS |
245 | rcar_gen3_init_for_host(ch); |
246 | ||
7ab0305d | 247 | rcar_gen3_control_otg_irq(ch, 1); |
9bb86777 YS |
248 | } |
249 | ||
1114e2d3 YS |
250 | static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) |
251 | { | |
a602152c YS |
252 | if (!ch->uses_otg_pins) |
253 | return (ch->dr_mode == USB_DR_MODE_HOST) ? false : true; | |
254 | ||
801a69c7 | 255 | return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); |
1114e2d3 YS |
256 | } |
257 | ||
258 | static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch) | |
259 | { | |
6762925d | 260 | if (!rcar_gen3_check_id(ch)) |
1114e2d3 YS |
261 | rcar_gen3_init_for_host(ch); |
262 | else | |
263 | rcar_gen3_init_for_peri(ch); | |
264 | } | |
265 | ||
9bb86777 YS |
266 | static bool rcar_gen3_is_host(struct rcar_gen3_chan *ch) |
267 | { | |
268 | return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI); | |
269 | } | |
270 | ||
b56acc82 YS |
271 | static enum phy_mode rcar_gen3_get_phy_mode(struct rcar_gen3_chan *ch) |
272 | { | |
273 | if (rcar_gen3_is_host(ch)) | |
274 | return PHY_MODE_USB_HOST; | |
275 | ||
276 | return PHY_MODE_USB_DEVICE; | |
277 | } | |
278 | ||
549b6b55 YS |
279 | static bool rcar_gen3_is_any_rphy_initialized(struct rcar_gen3_chan *ch) |
280 | { | |
281 | int i; | |
282 | ||
283 | for (i = 0; i < NUM_OF_PHYS; i++) { | |
284 | if (ch->rphys[i].initialized) | |
285 | return true; | |
286 | } | |
287 | ||
288 | return false; | |
289 | } | |
290 | ||
291 | static bool rcar_gen3_needs_init_otg(struct rcar_gen3_chan *ch) | |
292 | { | |
293 | int i; | |
294 | ||
295 | for (i = 0; i < NUM_OF_PHYS; i++) { | |
296 | if (ch->rphys[i].otg_initialized) | |
297 | return false; | |
298 | } | |
299 | ||
300 | return true; | |
301 | } | |
302 | ||
303 | static bool rcar_gen3_are_all_rphys_power_off(struct rcar_gen3_chan *ch) | |
304 | { | |
305 | int i; | |
306 | ||
307 | for (i = 0; i < NUM_OF_PHYS; i++) { | |
308 | if (ch->rphys[i].powered) | |
309 | return false; | |
310 | } | |
311 | ||
312 | return true; | |
313 | } | |
314 | ||
9bb86777 YS |
315 | static ssize_t role_store(struct device *dev, struct device_attribute *attr, |
316 | const char *buf, size_t count) | |
317 | { | |
318 | struct rcar_gen3_chan *ch = dev_get_drvdata(dev); | |
b56acc82 YS |
319 | bool is_b_device; |
320 | enum phy_mode cur_mode, new_mode; | |
9bb86777 | 321 | |
549b6b55 | 322 | if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch)) |
9bb86777 YS |
323 | return -EIO; |
324 | ||
4bd5ead8 | 325 | if (sysfs_streq(buf, "host")) |
b56acc82 | 326 | new_mode = PHY_MODE_USB_HOST; |
4bd5ead8 | 327 | else if (sysfs_streq(buf, "peripheral")) |
b56acc82 | 328 | new_mode = PHY_MODE_USB_DEVICE; |
9bb86777 YS |
329 | else |
330 | return -EINVAL; | |
331 | ||
b56acc82 YS |
332 | /* is_b_device: true is B-Device. false is A-Device. */ |
333 | is_b_device = rcar_gen3_check_id(ch); | |
334 | cur_mode = rcar_gen3_get_phy_mode(ch); | |
335 | ||
9bb86777 | 336 | /* If current and new mode is the same, this returns the error */ |
b56acc82 | 337 | if (cur_mode == new_mode) |
9bb86777 YS |
338 | return -EINVAL; |
339 | ||
b56acc82 | 340 | if (new_mode == PHY_MODE_USB_HOST) { /* And is_host must be false */ |
9bb86777 YS |
341 | if (!is_b_device) /* A-Peripheral */ |
342 | rcar_gen3_init_from_a_peri_to_a_host(ch); | |
343 | else /* B-Peripheral */ | |
344 | rcar_gen3_init_for_b_host(ch); | |
345 | } else { /* And is_host must be true */ | |
346 | if (!is_b_device) /* A-Host */ | |
347 | rcar_gen3_init_for_a_peri(ch); | |
348 | else /* B-Host */ | |
349 | rcar_gen3_init_for_peri(ch); | |
350 | } | |
351 | ||
352 | return count; | |
353 | } | |
354 | ||
355 | static ssize_t role_show(struct device *dev, struct device_attribute *attr, | |
356 | char *buf) | |
357 | { | |
358 | struct rcar_gen3_chan *ch = dev_get_drvdata(dev); | |
359 | ||
549b6b55 | 360 | if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch)) |
9bb86777 YS |
361 | return -EIO; |
362 | ||
363 | return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" : | |
364 | "peripheral"); | |
365 | } | |
366 | static DEVICE_ATTR_RW(role); | |
367 | ||
1114e2d3 YS |
368 | static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) |
369 | { | |
801a69c7 | 370 | void __iomem *usb2_base = ch->base; |
1114e2d3 YS |
371 | u32 val; |
372 | ||
72c0339c YS |
373 | /* Should not use functions of read-modify-write a register */ |
374 | val = readl(usb2_base + USB2_LINECTRL1); | |
375 | val = (val & ~USB2_LINECTRL1_DP_RPD) | USB2_LINECTRL1_DPRPD_EN | | |
376 | USB2_LINECTRL1_DMRPD_EN | USB2_LINECTRL1_DM_RPD; | |
377 | writel(val, usb2_base + USB2_LINECTRL1); | |
378 | ||
1114e2d3 | 379 | val = readl(usb2_base + USB2_VBCTRL); |
e6839c31 | 380 | val &= ~USB2_VBCTRL_OCCLREN; |
1114e2d3 YS |
381 | writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); |
382 | val = readl(usb2_base + USB2_ADPCTRL); | |
383 | writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); | |
72c0339c YS |
384 | |
385 | msleep(20); | |
386 | ||
387 | writel(0xffffffff, usb2_base + USB2_OBINTSTA); | |
388 | writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); | |
1114e2d3 YS |
389 | |
390 | rcar_gen3_device_recognition(ch); | |
391 | } | |
392 | ||
08b0ad37 YS |
393 | static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) |
394 | { | |
395 | struct rcar_gen3_chan *ch = _ch; | |
396 | void __iomem *usb2_base = ch->base; | |
397 | u32 status = readl(usb2_base + USB2_OBINTSTA); | |
398 | irqreturn_t ret = IRQ_NONE; | |
399 | ||
400 | if (status & USB2_OBINT_BITS) { | |
401 | dev_vdbg(ch->dev, "%s: %08x\n", __func__, status); | |
402 | writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); | |
403 | rcar_gen3_device_recognition(ch); | |
404 | ret = IRQ_HANDLED; | |
405 | } | |
406 | ||
407 | return ret; | |
408 | } | |
409 | ||
f3b5a8d9 YS |
410 | static int rcar_gen3_phy_usb2_init(struct phy *p) |
411 | { | |
549b6b55 YS |
412 | struct rcar_gen3_phy *rphy = phy_get_drvdata(p); |
413 | struct rcar_gen3_chan *channel = rphy->ch; | |
801a69c7 | 414 | void __iomem *usb2_base = channel->base; |
549b6b55 | 415 | u32 val; |
08b0ad37 YS |
416 | int ret; |
417 | ||
418 | if (!rcar_gen3_is_any_rphy_initialized(channel) && channel->irq >= 0) { | |
419 | INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); | |
420 | ret = request_irq(channel->irq, rcar_gen3_phy_usb2_irq, | |
421 | IRQF_SHARED, dev_name(channel->dev), channel); | |
b59aeb1a | 422 | if (ret < 0) { |
08b0ad37 | 423 | dev_err(channel->dev, "No irq handler (%d)\n", channel->irq); |
b59aeb1a YS |
424 | return ret; |
425 | } | |
08b0ad37 | 426 | } |
f3b5a8d9 YS |
427 | |
428 | /* Initialize USB2 part */ | |
549b6b55 YS |
429 | val = readl(usb2_base + USB2_INT_ENABLE); |
430 | val |= USB2_INT_ENABLE_UCOM_INTEN | rphy->int_enable_bits; | |
431 | writel(val, usb2_base + USB2_INT_ENABLE); | |
f3b5a8d9 YS |
432 | writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); |
433 | writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); | |
434 | ||
b9564016 | 435 | /* Initialize otg part */ |
549b6b55 YS |
436 | if (channel->is_otg_channel) { |
437 | if (rcar_gen3_needs_init_otg(channel)) | |
438 | rcar_gen3_init_otg(channel); | |
439 | rphy->otg_initialized = true; | |
440 | } | |
441 | ||
442 | rphy->initialized = true; | |
f3b5a8d9 YS |
443 | |
444 | return 0; | |
445 | } | |
446 | ||
447 | static int rcar_gen3_phy_usb2_exit(struct phy *p) | |
448 | { | |
549b6b55 YS |
449 | struct rcar_gen3_phy *rphy = phy_get_drvdata(p); |
450 | struct rcar_gen3_chan *channel = rphy->ch; | |
451 | void __iomem *usb2_base = channel->base; | |
452 | u32 val; | |
f3b5a8d9 | 453 | |
549b6b55 YS |
454 | rphy->initialized = false; |
455 | ||
456 | if (channel->is_otg_channel) | |
457 | rphy->otg_initialized = false; | |
458 | ||
459 | val = readl(usb2_base + USB2_INT_ENABLE); | |
460 | val &= ~rphy->int_enable_bits; | |
461 | if (!rcar_gen3_is_any_rphy_initialized(channel)) | |
462 | val &= ~USB2_INT_ENABLE_UCOM_INTEN; | |
463 | writel(val, usb2_base + USB2_INT_ENABLE); | |
f3b5a8d9 | 464 | |
08b0ad37 YS |
465 | if (channel->irq >= 0 && !rcar_gen3_is_any_rphy_initialized(channel)) |
466 | free_irq(channel->irq, channel); | |
467 | ||
f3b5a8d9 YS |
468 | return 0; |
469 | } | |
470 | ||
471 | static int rcar_gen3_phy_usb2_power_on(struct phy *p) | |
472 | { | |
549b6b55 YS |
473 | struct rcar_gen3_phy *rphy = phy_get_drvdata(p); |
474 | struct rcar_gen3_chan *channel = rphy->ch; | |
801a69c7 | 475 | void __iomem *usb2_base = channel->base; |
f3b5a8d9 | 476 | u32 val; |
5c9dc637 | 477 | int ret = 0; |
6dcfd7c3 | 478 | |
5c9dc637 | 479 | mutex_lock(&channel->lock); |
549b6b55 | 480 | if (!rcar_gen3_are_all_rphys_power_off(channel)) |
5c9dc637 | 481 | goto out; |
549b6b55 | 482 | |
6dcfd7c3 YS |
483 | if (channel->vbus) { |
484 | ret = regulator_enable(channel->vbus); | |
485 | if (ret) | |
5c9dc637 | 486 | goto out; |
6dcfd7c3 | 487 | } |
f3b5a8d9 YS |
488 | |
489 | val = readl(usb2_base + USB2_USBCTR); | |
490 | val |= USB2_USBCTR_PLL_RST; | |
491 | writel(val, usb2_base + USB2_USBCTR); | |
492 | val &= ~USB2_USBCTR_PLL_RST; | |
493 | writel(val, usb2_base + USB2_USBCTR); | |
494 | ||
5c9dc637 YS |
495 | out: |
496 | /* The powered flag should be set for any other phys anyway */ | |
549b6b55 | 497 | rphy->powered = true; |
5c9dc637 | 498 | mutex_unlock(&channel->lock); |
549b6b55 | 499 | |
f3b5a8d9 YS |
500 | return 0; |
501 | } | |
502 | ||
6dcfd7c3 YS |
503 | static int rcar_gen3_phy_usb2_power_off(struct phy *p) |
504 | { | |
549b6b55 YS |
505 | struct rcar_gen3_phy *rphy = phy_get_drvdata(p); |
506 | struct rcar_gen3_chan *channel = rphy->ch; | |
6dcfd7c3 YS |
507 | int ret = 0; |
508 | ||
5c9dc637 | 509 | mutex_lock(&channel->lock); |
549b6b55 YS |
510 | rphy->powered = false; |
511 | ||
512 | if (!rcar_gen3_are_all_rphys_power_off(channel)) | |
5c9dc637 | 513 | goto out; |
549b6b55 | 514 | |
6dcfd7c3 YS |
515 | if (channel->vbus) |
516 | ret = regulator_disable(channel->vbus); | |
517 | ||
5c9dc637 YS |
518 | out: |
519 | mutex_unlock(&channel->lock); | |
520 | ||
6dcfd7c3 YS |
521 | return ret; |
522 | } | |
523 | ||
a8df2768 | 524 | static const struct phy_ops rcar_gen3_phy_usb2_ops = { |
f3b5a8d9 YS |
525 | .init = rcar_gen3_phy_usb2_init, |
526 | .exit = rcar_gen3_phy_usb2_exit, | |
527 | .power_on = rcar_gen3_phy_usb2_power_on, | |
6dcfd7c3 | 528 | .power_off = rcar_gen3_phy_usb2_power_off, |
f3b5a8d9 YS |
529 | .owner = THIS_MODULE, |
530 | }; | |
531 | ||
5d8042e9 BD |
532 | static const struct phy_ops rz_g1c_phy_usb2_ops = { |
533 | .init = rcar_gen3_phy_usb2_init, | |
534 | .exit = rcar_gen3_phy_usb2_exit, | |
535 | .owner = THIS_MODULE, | |
536 | }; | |
537 | ||
f3b5a8d9 | 538 | static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { |
5d8042e9 BD |
539 | { |
540 | .compatible = "renesas,usb2-phy-r8a77470", | |
541 | .data = &rz_g1c_phy_usb2_ops, | |
542 | }, | |
543 | { | |
544 | .compatible = "renesas,usb2-phy-r8a7795", | |
545 | .data = &rcar_gen3_phy_usb2_ops, | |
546 | }, | |
547 | { | |
548 | .compatible = "renesas,usb2-phy-r8a7796", | |
549 | .data = &rcar_gen3_phy_usb2_ops, | |
550 | }, | |
551 | { | |
552 | .compatible = "renesas,usb2-phy-r8a77965", | |
553 | .data = &rcar_gen3_phy_usb2_ops, | |
554 | }, | |
555 | { | |
556 | .compatible = "renesas,rcar-gen3-usb2-phy", | |
557 | .data = &rcar_gen3_phy_usb2_ops, | |
558 | }, | |
559 | { /* sentinel */ }, | |
f3b5a8d9 YS |
560 | }; |
561 | MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table); | |
562 | ||
2b38543c YS |
563 | static const unsigned int rcar_gen3_phy_cable[] = { |
564 | EXTCON_USB, | |
565 | EXTCON_USB_HOST, | |
566 | EXTCON_NONE, | |
567 | }; | |
568 | ||
549b6b55 YS |
569 | static struct phy *rcar_gen3_phy_usb2_xlate(struct device *dev, |
570 | struct of_phandle_args *args) | |
571 | { | |
572 | struct rcar_gen3_chan *ch = dev_get_drvdata(dev); | |
573 | ||
574 | if (args->args_count == 0) /* For old version dts */ | |
575 | return ch->rphys[PHY_INDEX_BOTH_HC].phy; | |
576 | else if (args->args_count > 1) /* Prevent invalid args count */ | |
577 | return ERR_PTR(-ENODEV); | |
578 | ||
579 | if (args->args[0] >= NUM_OF_PHYS) | |
580 | return ERR_PTR(-ENODEV); | |
581 | ||
582 | return ch->rphys[args->args[0]].phy; | |
583 | } | |
584 | ||
585 | static enum usb_dr_mode rcar_gen3_get_dr_mode(struct device_node *np) | |
586 | { | |
587 | enum usb_dr_mode candidate = USB_DR_MODE_UNKNOWN; | |
588 | int i; | |
589 | ||
590 | /* | |
591 | * If one of device nodes has other dr_mode except UNKNOWN, | |
592 | * this function returns UNKNOWN. To achieve backward compatibility, | |
593 | * this loop starts the index as 0. | |
594 | */ | |
595 | for (i = 0; i < NUM_OF_PHYS; i++) { | |
596 | enum usb_dr_mode mode = of_usb_get_dr_mode_by_phy(np, i); | |
597 | ||
598 | if (mode != USB_DR_MODE_UNKNOWN) { | |
599 | if (candidate == USB_DR_MODE_UNKNOWN) | |
600 | candidate = mode; | |
601 | else if (candidate != mode) | |
602 | return USB_DR_MODE_UNKNOWN; | |
603 | } | |
604 | } | |
605 | ||
606 | return candidate; | |
607 | } | |
608 | ||
f3b5a8d9 YS |
609 | static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) |
610 | { | |
611 | struct device *dev = &pdev->dev; | |
612 | struct rcar_gen3_chan *channel; | |
613 | struct phy_provider *provider; | |
614 | struct resource *res; | |
5d8042e9 | 615 | const struct phy_ops *phy_usb2_ops; |
08b0ad37 | 616 | int ret = 0, i; |
f3b5a8d9 YS |
617 | |
618 | if (!dev->of_node) { | |
619 | dev_err(dev, "This driver needs device tree\n"); | |
620 | return -EINVAL; | |
621 | } | |
622 | ||
623 | channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); | |
624 | if (!channel) | |
625 | return -ENOMEM; | |
626 | ||
b9564016 | 627 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
801a69c7 YS |
628 | channel->base = devm_ioremap_resource(dev, res); |
629 | if (IS_ERR(channel->base)) | |
630 | return PTR_ERR(channel->base); | |
f3b5a8d9 | 631 | |
08b0ad37 YS |
632 | /* get irq number here and request_irq for OTG in phy_init */ |
633 | channel->irq = platform_get_irq_optional(pdev, 0); | |
549b6b55 | 634 | channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node); |
73801b90 | 635 | if (channel->dr_mode != USB_DR_MODE_UNKNOWN) { |
7e0540f4 YS |
636 | int ret; |
637 | ||
979b519c | 638 | channel->is_otg_channel = true; |
8dde0008 YS |
639 | channel->uses_otg_pins = !of_property_read_bool(dev->of_node, |
640 | "renesas,no-otg-pins"); | |
2b38543c YS |
641 | channel->extcon = devm_extcon_dev_allocate(dev, |
642 | rcar_gen3_phy_cable); | |
643 | if (IS_ERR(channel->extcon)) | |
644 | return PTR_ERR(channel->extcon); | |
645 | ||
646 | ret = devm_extcon_dev_register(dev, channel->extcon); | |
647 | if (ret < 0) { | |
648 | dev_err(dev, "Failed to register extcon\n"); | |
649 | return ret; | |
650 | } | |
f3b5a8d9 YS |
651 | } |
652 | ||
441a681b YS |
653 | /* |
654 | * devm_phy_create() will call pm_runtime_enable(&phy->dev); | |
655 | * And then, phy-core will manage runtime pm for this device. | |
656 | */ | |
657 | pm_runtime_enable(dev); | |
5d8042e9 BD |
658 | phy_usb2_ops = of_device_get_match_data(dev); |
659 | if (!phy_usb2_ops) | |
660 | return -EINVAL; | |
661 | ||
5c9dc637 | 662 | mutex_init(&channel->lock); |
549b6b55 YS |
663 | for (i = 0; i < NUM_OF_PHYS; i++) { |
664 | channel->rphys[i].phy = devm_phy_create(dev, NULL, | |
665 | phy_usb2_ops); | |
666 | if (IS_ERR(channel->rphys[i].phy)) { | |
667 | dev_err(dev, "Failed to create USB2 PHY\n"); | |
668 | ret = PTR_ERR(channel->rphys[i].phy); | |
669 | goto error; | |
670 | } | |
671 | channel->rphys[i].ch = channel; | |
672 | channel->rphys[i].int_enable_bits = rcar_gen3_int_enable[i]; | |
673 | phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]); | |
f3b5a8d9 YS |
674 | } |
675 | ||
6dcfd7c3 YS |
676 | channel->vbus = devm_regulator_get_optional(dev, "vbus"); |
677 | if (IS_ERR(channel->vbus)) { | |
441a681b YS |
678 | if (PTR_ERR(channel->vbus) == -EPROBE_DEFER) { |
679 | ret = PTR_ERR(channel->vbus); | |
680 | goto error; | |
681 | } | |
6dcfd7c3 YS |
682 | channel->vbus = NULL; |
683 | } | |
684 | ||
9bb86777 | 685 | platform_set_drvdata(pdev, channel); |
92fec1c2 | 686 | channel->dev = dev; |
f3b5a8d9 | 687 | |
549b6b55 | 688 | provider = devm_of_phy_provider_register(dev, rcar_gen3_phy_usb2_xlate); |
9bb86777 | 689 | if (IS_ERR(provider)) { |
f3b5a8d9 | 690 | dev_err(dev, "Failed to register PHY provider\n"); |
441a681b YS |
691 | ret = PTR_ERR(provider); |
692 | goto error; | |
979b519c | 693 | } else if (channel->is_otg_channel) { |
9bb86777 YS |
694 | int ret; |
695 | ||
696 | ret = device_create_file(dev, &dev_attr_role); | |
697 | if (ret < 0) | |
441a681b | 698 | goto error; |
9bb86777 | 699 | } |
f3b5a8d9 | 700 | |
441a681b YS |
701 | return 0; |
702 | ||
703 | error: | |
704 | pm_runtime_disable(dev); | |
705 | ||
706 | return ret; | |
f3b5a8d9 YS |
707 | } |
708 | ||
9bb86777 YS |
709 | static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev) |
710 | { | |
711 | struct rcar_gen3_chan *channel = platform_get_drvdata(pdev); | |
712 | ||
979b519c | 713 | if (channel->is_otg_channel) |
9bb86777 YS |
714 | device_remove_file(&pdev->dev, &dev_attr_role); |
715 | ||
441a681b YS |
716 | pm_runtime_disable(&pdev->dev); |
717 | ||
9bb86777 YS |
718 | return 0; |
719 | }; | |
720 | ||
f3b5a8d9 YS |
721 | static struct platform_driver rcar_gen3_phy_usb2_driver = { |
722 | .driver = { | |
723 | .name = "phy_rcar_gen3_usb2", | |
724 | .of_match_table = rcar_gen3_phy_usb2_match_table, | |
725 | }, | |
726 | .probe = rcar_gen3_phy_usb2_probe, | |
9bb86777 | 727 | .remove = rcar_gen3_phy_usb2_remove, |
f3b5a8d9 YS |
728 | }; |
729 | module_platform_driver(rcar_gen3_phy_usb2_driver); | |
730 | ||
731 | MODULE_LICENSE("GPL v2"); | |
732 | MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY"); | |
733 | MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>"); |