diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2012-04-24 05:09:08 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-05-12 16:33:05 -0400 |
commit | 1c96293e9f8b8ec9620201bcf7f776f0e0f89edb (patch) | |
tree | 4c08046761891b95acd0930d30de2640720207a4 /arch/arm/mach-shmobile/board-armadillo800eva.c | |
parent | 46cf668748070e54879d528fa58107abc835dff3 (diff) |
ARM: mach-shmobile: armadillo800eva: add USB function support
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Tested-by: Simon Horman <horms@verge.net.au>
Acked-by: Magnus Damm <damm@opensource.se>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'arch/arm/mach-shmobile/board-armadillo800eva.c')
-rw-r--r-- | arch/arm/mach-shmobile/board-armadillo800eva.c | 247 |
1 files changed, 237 insertions, 10 deletions
diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index 4d066f9230dd..1d25d5fee605 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c | |||
@@ -20,14 +20,17 @@ | |||
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/clk.h> | 22 | #include <linux/clk.h> |
23 | #include <linux/delay.h> | ||
23 | #include <linux/err.h> | 24 | #include <linux/err.h> |
24 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
25 | #include <linux/input.h> | 26 | #include <linux/input.h> |
27 | #include <linux/irq.h> | ||
26 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
27 | #include <linux/gpio.h> | 29 | #include <linux/gpio.h> |
28 | #include <linux/gpio_keys.h> | 30 | #include <linux/gpio_keys.h> |
29 | #include <linux/sh_eth.h> | 31 | #include <linux/sh_eth.h> |
30 | #include <linux/videodev2.h> | 32 | #include <linux/videodev2.h> |
33 | #include <linux/usb/renesas_usbhs.h> | ||
31 | #include <mach/common.h> | 34 | #include <mach/common.h> |
32 | #include <mach/irqs.h> | 35 | #include <mach/irqs.h> |
33 | #include <asm/page.h> | 36 | #include <asm/page.h> |
@@ -90,6 +93,9 @@ | |||
90 | * 0 | SDHI1 | COM8 enable, COM14 disable | 93 | * 0 | SDHI1 | COM8 enable, COM14 disable |
91 | * 1 | SDHI1 | COM8 enable, COM14 disable | 94 | * 1 | SDHI1 | COM8 enable, COM14 disable |
92 | * -12345678-+---------------+---------------------------- | 95 | * -12345678-+---------------+---------------------------- |
96 | * 0 | USB0 | COM20 enable, COM24 disable | ||
97 | * 1 | USB0 | COM20 disable, COM24 enable | ||
98 | * -12345678-+---------------+---------------------------- | ||
93 | * 00 | JTAG | SH-X2 | 99 | * 00 | JTAG | SH-X2 |
94 | * 10 | JTAG | ARM | 100 | * 10 | JTAG | ARM |
95 | * 01 | JTAG | - | 101 | * 01 | JTAG | - |
@@ -97,6 +103,195 @@ | |||
97 | *-----------+---------------+---------------------------- | 103 | *-----------+---------------+---------------------------- |
98 | */ | 104 | */ |
99 | 105 | ||
106 | /* | ||
107 | * USB function | ||
108 | * | ||
109 | * When you use USB Function, | ||
110 | * set SW1.6 ON, and connect cable to CN24. | ||
111 | * | ||
112 | * USBF needs workaround on R8A7740 chip. | ||
113 | * These are a little bit complex. | ||
114 | * see | ||
115 | * usbhsf_power_ctrl() | ||
116 | * | ||
117 | * CAUTION | ||
118 | * | ||
119 | * It uses autonomy mode for USB hotplug at this point | ||
120 | * (= usbhs_private.platform_callback.get_vbus is NULL), | ||
121 | * since we don't know what's happen on PM control | ||
122 | * on this workaround. | ||
123 | */ | ||
124 | #define USBCR1 0xe605810a | ||
125 | #define USBH 0xC6700000 | ||
126 | #define USBH_USBCTR 0x10834 | ||
127 | |||
128 | struct usbhsf_private { | ||
129 | struct clk *phy; | ||
130 | struct clk *usb24; | ||
131 | struct clk *pci; | ||
132 | struct clk *func; | ||
133 | struct clk *host; | ||
134 | void __iomem *usbh_base; | ||
135 | struct renesas_usbhs_platform_info info; | ||
136 | }; | ||
137 | |||
138 | #define usbhsf_get_priv(pdev) \ | ||
139 | container_of(renesas_usbhs_get_info(pdev), \ | ||
140 | struct usbhsf_private, info) | ||
141 | |||
142 | static int usbhsf_get_id(struct platform_device *pdev) | ||
143 | { | ||
144 | return USBHS_GADGET; | ||
145 | } | ||
146 | |||
147 | static void usbhsf_power_ctrl(struct platform_device *pdev, | ||
148 | void __iomem *base, int enable) | ||
149 | { | ||
150 | struct usbhsf_private *priv = usbhsf_get_priv(pdev); | ||
151 | |||
152 | /* | ||
153 | * Work around for USB Function. | ||
154 | * It needs USB host clock, and settings | ||
155 | */ | ||
156 | if (enable) { | ||
157 | /* | ||
158 | * enable all the related usb clocks | ||
159 | * for usb workaround | ||
160 | */ | ||
161 | clk_enable(priv->usb24); | ||
162 | clk_enable(priv->pci); | ||
163 | clk_enable(priv->host); | ||
164 | clk_enable(priv->func); | ||
165 | clk_enable(priv->phy); | ||
166 | |||
167 | /* | ||
168 | * set USBCR1 | ||
169 | * | ||
170 | * Port1 is driven by USB function, | ||
171 | * Port2 is driven by USB HOST | ||
172 | * One HOST (Port1 or Port2 is HOST) | ||
173 | * USB PLL input clock = 24MHz | ||
174 | */ | ||
175 | __raw_writew(0xd750, USBCR1); | ||
176 | mdelay(1); | ||
177 | |||
178 | /* | ||
179 | * start USB Host | ||
180 | */ | ||
181 | __raw_writel(0x0000000c, priv->usbh_base + USBH_USBCTR); | ||
182 | __raw_writel(0x00000008, priv->usbh_base + USBH_USBCTR); | ||
183 | mdelay(10); | ||
184 | |||
185 | /* | ||
186 | * USB PHY Power ON | ||
187 | */ | ||
188 | __raw_writew(0xd770, USBCR1); | ||
189 | __raw_writew(0x4000, base + 0x102); /* USBF :: SUSPMODE */ | ||
190 | |||
191 | } else { | ||
192 | __raw_writel(0x0000010f, priv->usbh_base + USBH_USBCTR); | ||
193 | __raw_writew(0xd7c0, USBCR1); /* GPIO */ | ||
194 | |||
195 | clk_disable(priv->phy); | ||
196 | clk_disable(priv->func); /* usb work around */ | ||
197 | clk_disable(priv->host); /* usb work around */ | ||
198 | clk_disable(priv->pci); /* usb work around */ | ||
199 | clk_disable(priv->usb24); /* usb work around */ | ||
200 | } | ||
201 | } | ||
202 | |||
203 | static void usbhsf_hardware_exit(struct platform_device *pdev) | ||
204 | { | ||
205 | struct usbhsf_private *priv = usbhsf_get_priv(pdev); | ||
206 | |||
207 | if (!IS_ERR(priv->phy)) | ||
208 | clk_put(priv->phy); | ||
209 | if (!IS_ERR(priv->usb24)) | ||
210 | clk_put(priv->usb24); | ||
211 | if (!IS_ERR(priv->pci)) | ||
212 | clk_put(priv->pci); | ||
213 | if (!IS_ERR(priv->host)) | ||
214 | clk_put(priv->host); | ||
215 | if (!IS_ERR(priv->func)) | ||
216 | clk_put(priv->func); | ||
217 | if (priv->usbh_base) | ||
218 | iounmap(priv->usbh_base); | ||
219 | |||
220 | priv->phy = NULL; | ||
221 | priv->usb24 = NULL; | ||
222 | priv->pci = NULL; | ||
223 | priv->host = NULL; | ||
224 | priv->func = NULL; | ||
225 | priv->usbh_base = NULL; | ||
226 | } | ||
227 | |||
228 | static int usbhsf_hardware_init(struct platform_device *pdev) | ||
229 | { | ||
230 | struct usbhsf_private *priv = usbhsf_get_priv(pdev); | ||
231 | |||
232 | priv->phy = clk_get(&pdev->dev, "phy"); | ||
233 | priv->usb24 = clk_get(&pdev->dev, "usb24"); | ||
234 | priv->pci = clk_get(&pdev->dev, "pci"); | ||
235 | priv->func = clk_get(&pdev->dev, "func"); | ||
236 | priv->host = clk_get(&pdev->dev, "host"); | ||
237 | priv->usbh_base = ioremap_nocache(USBH, 0x20000); | ||
238 | |||
239 | if (IS_ERR(priv->phy) || | ||
240 | IS_ERR(priv->usb24) || | ||
241 | IS_ERR(priv->pci) || | ||
242 | IS_ERR(priv->host) || | ||
243 | IS_ERR(priv->func) || | ||
244 | !priv->usbh_base) { | ||
245 | dev_err(&pdev->dev, "USB clock setting failed\n"); | ||
246 | usbhsf_hardware_exit(pdev); | ||
247 | return -EIO; | ||
248 | } | ||
249 | |||
250 | /* usb24 use 1/1 of parent clock (= usb24s = 24MHz) */ | ||
251 | clk_set_rate(priv->usb24, | ||
252 | clk_get_rate(clk_get_parent(priv->usb24))); | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static struct usbhsf_private usbhsf_private = { | ||
258 | .info = { | ||
259 | .platform_callback = { | ||
260 | .get_id = usbhsf_get_id, | ||
261 | .hardware_init = usbhsf_hardware_init, | ||
262 | .hardware_exit = usbhsf_hardware_exit, | ||
263 | .power_ctrl = usbhsf_power_ctrl, | ||
264 | }, | ||
265 | .driver_param = { | ||
266 | .buswait_bwait = 5, | ||
267 | .detection_delay = 5, | ||
268 | }, | ||
269 | } | ||
270 | }; | ||
271 | |||
272 | static struct resource usbhsf_resources[] = { | ||
273 | { | ||
274 | .name = "USBHS", | ||
275 | .start = 0xe6890000, | ||
276 | .end = 0xe6890104 - 1, | ||
277 | .flags = IORESOURCE_MEM, | ||
278 | }, | ||
279 | { | ||
280 | .start = evt2irq(0x0A20), | ||
281 | .flags = IORESOURCE_IRQ, | ||
282 | }, | ||
283 | }; | ||
284 | |||
285 | static struct platform_device usbhsf_device = { | ||
286 | .name = "renesas_usbhs", | ||
287 | .dev = { | ||
288 | .platform_data = &usbhsf_private.info, | ||
289 | }, | ||
290 | .id = -1, | ||
291 | .num_resources = ARRAY_SIZE(usbhsf_resources), | ||
292 | .resource = usbhsf_resources, | ||
293 | }; | ||
294 | |||
100 | /* Ether */ | 295 | /* Ether */ |
101 | static struct sh_eth_plat_data sh_eth_platdata = { | 296 | static struct sh_eth_plat_data sh_eth_platdata = { |
102 | .phy = 0x00, /* LAN8710A */ | 297 | .phy = 0x00, /* LAN8710A */ |
@@ -224,11 +419,41 @@ static struct platform_device *eva_devices[] __initdata = { | |||
224 | &sh_eth_device, | 419 | &sh_eth_device, |
225 | }; | 420 | }; |
226 | 421 | ||
422 | static void __init eva_clock_init(void) | ||
423 | { | ||
424 | struct clk *system = clk_get(NULL, "system_clk"); | ||
425 | struct clk *xtal1 = clk_get(NULL, "extal1"); | ||
426 | struct clk *usb24s = clk_get(NULL, "usb24s"); | ||
427 | |||
428 | if (IS_ERR(system) || | ||
429 | IS_ERR(xtal1) || | ||
430 | IS_ERR(usb24s)) { | ||
431 | pr_err("armadillo800eva board clock init failed\n"); | ||
432 | goto clock_error; | ||
433 | } | ||
434 | |||
435 | /* armadillo 800 eva extal1 is 24MHz */ | ||
436 | clk_set_rate(xtal1, 24000000); | ||
437 | |||
438 | /* usb24s use extal1 (= system) clock (= 24MHz) */ | ||
439 | clk_set_parent(usb24s, system); | ||
440 | |||
441 | clock_error: | ||
442 | if (!IS_ERR(system)) | ||
443 | clk_put(system); | ||
444 | if (!IS_ERR(xtal1)) | ||
445 | clk_put(xtal1); | ||
446 | if (!IS_ERR(usb24s)) | ||
447 | clk_put(usb24s); | ||
448 | } | ||
449 | |||
227 | /* | 450 | /* |
228 | * board init | 451 | * board init |
229 | */ | 452 | */ |
230 | static void __init eva_init(void) | 453 | static void __init eva_init(void) |
231 | { | 454 | { |
455 | eva_clock_init(); | ||
456 | |||
232 | r8a7740_pinmux_init(); | 457 | r8a7740_pinmux_init(); |
233 | 458 | ||
234 | /* SCIFA1 */ | 459 | /* SCIFA1 */ |
@@ -302,6 +527,18 @@ static void __init eva_init(void) | |||
302 | gpio_request(GPIO_PORT18, NULL); /* PHY_RST */ | 527 | gpio_request(GPIO_PORT18, NULL); /* PHY_RST */ |
303 | gpio_direction_output(GPIO_PORT18, 1); | 528 | gpio_direction_output(GPIO_PORT18, 1); |
304 | 529 | ||
530 | /* USB */ | ||
531 | gpio_request(GPIO_PORT159, NULL); /* USB_DEVICE_MODE */ | ||
532 | gpio_direction_input(GPIO_PORT159); | ||
533 | |||
534 | if (gpio_get_value(GPIO_PORT159)) { | ||
535 | /* USB Host */ | ||
536 | } else { | ||
537 | /* USB Func */ | ||
538 | gpio_request(GPIO_FN_VBUS, NULL); | ||
539 | platform_device_register(&usbhsf_device); | ||
540 | } | ||
541 | |||
305 | /* | 542 | /* |
306 | * CAUTION | 543 | * CAUTION |
307 | * | 544 | * |
@@ -326,17 +563,7 @@ static void __init eva_init(void) | |||
326 | 563 | ||
327 | static void __init eva_earlytimer_init(void) | 564 | static void __init eva_earlytimer_init(void) |
328 | { | 565 | { |
329 | struct clk *xtal1; | ||
330 | |||
331 | r8a7740_clock_init(MD_CK0 | MD_CK2); | 566 | r8a7740_clock_init(MD_CK0 | MD_CK2); |
332 | |||
333 | xtal1 = clk_get(NULL, "extal1"); | ||
334 | if (!IS_ERR(xtal1)) { | ||
335 | /* armadillo 800 eva extal1 is 24MHz */ | ||
336 | clk_set_rate(xtal1, 24000000); | ||
337 | clk_put(xtal1); | ||
338 | } | ||
339 | |||
340 | shmobile_earlytimer_init(); | 567 | shmobile_earlytimer_init(); |
341 | } | 568 | } |
342 | 569 | ||