diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2011-05-25 03:56:25 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2011-05-25 04:07:36 -0400 |
commit | 66ee3bef3ce0bab155de082805388bd6ec2785f8 (patch) | |
tree | 6ab6da16cf6f0153b6aaf3c34e2f120fb1a8d591 /arch/arm/mach-shmobile | |
parent | 1acd3de54e242834a385810bc3f0fd44211e494e (diff) |
ARM: mach-shmobile: mackerel: add renesas_usbhs support for USB1
renesas_usbhs driver can use external interrupt mode
(which come from USB-PHY) or autonomy mode (it use own interrupt)
for detecting connection/disconnection when Function.
And it will be power OFF while it has been disconnecting
if external interrupt mode is selected.
mackerel board has 2 USB ports.
But we can not use external interrupt mode
on CN22 USB0 port which is only for USB Function.
IRQ7-PORT40 is already used by Touchscreen,
and USB-PHY needs IRQ7-PORT167.
It is impossible to use IRQ7 demux on mackerel.
We can use external interrupt mode USB-Function on "USB1".
USB1 can become Host by r8a66597, and become Function by renesas_usbhs.
But don't select both drivers in same time.
These 2 drivers are not supporting IRQ SHARD.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/arm/mach-shmobile')
-rw-r--r-- | arch/arm/mach-shmobile/board-mackerel.c | 191 |
1 files changed, 190 insertions, 1 deletions
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index 0d882754bff5..448ddbe43335 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include <linux/sh_intc.h> | 43 | #include <linux/sh_intc.h> |
44 | #include <linux/tca6416_keypad.h> | 44 | #include <linux/tca6416_keypad.h> |
45 | #include <linux/usb/r8a66597.h> | 45 | #include <linux/usb/r8a66597.h> |
46 | #include <linux/usb/renesas_usbhs.h> | ||
46 | 47 | ||
47 | #include <video/sh_mobile_hdmi.h> | 48 | #include <video/sh_mobile_hdmi.h> |
48 | #include <video/sh_mobile_lcdc.h> | 49 | #include <video/sh_mobile_lcdc.h> |
@@ -143,7 +144,30 @@ | |||
143 | * open | external VBUS | Function | 144 | * open | external VBUS | Function |
144 | * | 145 | * |
145 | * *1 | 146 | * *1 |
146 | * CN31 is used as Host in Linux. | 147 | * CN31 is used as |
148 | * CONFIG_USB_R8A66597_HCD Host | ||
149 | * CONFIG_USB_RENESAS_USBHS Function | ||
150 | * | ||
151 | * CAUTION | ||
152 | * | ||
153 | * renesas_usbhs driver can use external interrupt mode | ||
154 | * (which come from USB-PHY) or autonomy mode (it use own interrupt) | ||
155 | * for detecting connection/disconnection when Function. | ||
156 | * USB will be power OFF while it has been disconnecting | ||
157 | * if external interrupt mode, and it is always power ON if autonomy mode, | ||
158 | * | ||
159 | * mackerel can not use external interrupt (IRQ7-PORT167) mode on "USB0", | ||
160 | * because Touchscreen is using IRQ7-PORT40. | ||
161 | * It is impossible to use IRQ7 demux on this board. | ||
162 | * | ||
163 | * We can use external interrupt mode USB-Function on "USB1". | ||
164 | * USB1 can become Host by r8a66597, and become Function by renesas_usbhs. | ||
165 | * But don't select both drivers in same time. | ||
166 | * These uses same IRQ number for request_irq(), and aren't supporting | ||
167 | * IRQF_SHARD / IORESOURCE_IRQ_SHAREABLE. | ||
168 | * | ||
169 | * Actually these are old/new version of USB driver. | ||
170 | * This mean its register will be broken if it supports SHARD IRQ, | ||
147 | */ | 171 | */ |
148 | 172 | ||
149 | /* | 173 | /* |
@@ -185,6 +209,7 @@ | |||
185 | * FIXME !! | 209 | * FIXME !! |
186 | * | 210 | * |
187 | * gpio_no_direction | 211 | * gpio_no_direction |
212 | * gpio_pull_down | ||
188 | * are quick_hack. | 213 | * are quick_hack. |
189 | * | 214 | * |
190 | * current gpio frame work doesn't have | 215 | * current gpio frame work doesn't have |
@@ -196,6 +221,16 @@ static void __init gpio_no_direction(u32 addr) | |||
196 | __raw_writeb(0x00, addr); | 221 | __raw_writeb(0x00, addr); |
197 | } | 222 | } |
198 | 223 | ||
224 | static void __init gpio_pull_down(u32 addr) | ||
225 | { | ||
226 | u8 data = __raw_readb(addr); | ||
227 | |||
228 | data &= 0x0F; | ||
229 | data |= 0xA0; | ||
230 | |||
231 | __raw_writeb(data, addr); | ||
232 | } | ||
233 | |||
199 | /* MTD */ | 234 | /* MTD */ |
200 | static struct mtd_partition nor_flash_partitions[] = { | 235 | static struct mtd_partition nor_flash_partitions[] = { |
201 | { | 236 | { |
@@ -509,6 +544,157 @@ static struct platform_device usb1_host_device = { | |||
509 | .resource = usb1_host_resources, | 544 | .resource = usb1_host_resources, |
510 | }; | 545 | }; |
511 | 546 | ||
547 | /* USB1 (Function) */ | ||
548 | #define USB_PHY_MODE (1 << 4) | ||
549 | #define USB_PHY_INT_EN ((1 << 3) | (1 << 2)) | ||
550 | #define USB_PHY_ON (1 << 1) | ||
551 | #define USB_PHY_OFF (1 << 0) | ||
552 | #define USB_PHY_INT_CLR (USB_PHY_ON | USB_PHY_OFF) | ||
553 | |||
554 | struct usbhs_private { | ||
555 | unsigned int irq; | ||
556 | unsigned int usbphyaddr; | ||
557 | unsigned int usbcrcaddr; | ||
558 | struct renesas_usbhs_platform_info info; | ||
559 | }; | ||
560 | |||
561 | #define usbhs_get_priv(pdev) \ | ||
562 | container_of(renesas_usbhs_get_info(pdev), \ | ||
563 | struct usbhs_private, info) | ||
564 | |||
565 | #define usbhs_is_connected(priv) \ | ||
566 | (!((1 << 7) & __raw_readw(priv->usbcrcaddr))) | ||
567 | |||
568 | static int usbhs1_get_id(struct platform_device *pdev) | ||
569 | { | ||
570 | return USBHS_GADGET; | ||
571 | } | ||
572 | |||
573 | static int usbhs1_get_vbus(struct platform_device *pdev) | ||
574 | { | ||
575 | return usbhs_is_connected(usbhs_get_priv(pdev)); | ||
576 | } | ||
577 | |||
578 | static irqreturn_t usbhs1_interrupt(int irq, void *data) | ||
579 | { | ||
580 | struct platform_device *pdev = data; | ||
581 | struct usbhs_private *priv = usbhs_get_priv(pdev); | ||
582 | |||
583 | dev_dbg(&pdev->dev, "%s\n", __func__); | ||
584 | |||
585 | renesas_usbhs_call_notify_hotplug(pdev); | ||
586 | |||
587 | /* clear status */ | ||
588 | __raw_writew(__raw_readw(priv->usbphyaddr) | USB_PHY_INT_CLR, | ||
589 | priv->usbphyaddr); | ||
590 | |||
591 | return IRQ_HANDLED; | ||
592 | } | ||
593 | |||
594 | static int usbhs1_hardware_init(struct platform_device *pdev) | ||
595 | { | ||
596 | struct usbhs_private *priv = usbhs_get_priv(pdev); | ||
597 | int ret; | ||
598 | |||
599 | irq_set_irq_type(priv->irq, IRQ_TYPE_LEVEL_HIGH); | ||
600 | |||
601 | /* clear interrupt status */ | ||
602 | __raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->usbphyaddr); | ||
603 | |||
604 | ret = request_irq(priv->irq, usbhs1_interrupt, 0, | ||
605 | dev_name(&pdev->dev), pdev); | ||
606 | if (ret) { | ||
607 | dev_err(&pdev->dev, "request_irq err\n"); | ||
608 | return ret; | ||
609 | } | ||
610 | |||
611 | /* enable USB phy interrupt */ | ||
612 | __raw_writew(USB_PHY_MODE | USB_PHY_INT_EN, priv->usbphyaddr); | ||
613 | |||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | static void usbhs1_hardware_exit(struct platform_device *pdev) | ||
618 | { | ||
619 | struct usbhs_private *priv = usbhs_get_priv(pdev); | ||
620 | |||
621 | /* clear interrupt status */ | ||
622 | __raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->usbphyaddr); | ||
623 | |||
624 | free_irq(priv->irq, pdev); | ||
625 | } | ||
626 | |||
627 | static void usbhs1_phy_reset(struct platform_device *pdev) | ||
628 | { | ||
629 | struct usbhs_private *priv = usbhs_get_priv(pdev); | ||
630 | |||
631 | /* init phy */ | ||
632 | __raw_writew(0x8a0a, priv->usbcrcaddr); | ||
633 | } | ||
634 | |||
635 | static u32 usbhs1_pipe_cfg[] = { | ||
636 | USB_ENDPOINT_XFER_CONTROL, | ||
637 | USB_ENDPOINT_XFER_ISOC, | ||
638 | USB_ENDPOINT_XFER_ISOC, | ||
639 | USB_ENDPOINT_XFER_BULK, | ||
640 | USB_ENDPOINT_XFER_BULK, | ||
641 | USB_ENDPOINT_XFER_BULK, | ||
642 | USB_ENDPOINT_XFER_INT, | ||
643 | USB_ENDPOINT_XFER_INT, | ||
644 | USB_ENDPOINT_XFER_INT, | ||
645 | USB_ENDPOINT_XFER_BULK, | ||
646 | USB_ENDPOINT_XFER_BULK, | ||
647 | USB_ENDPOINT_XFER_BULK, | ||
648 | USB_ENDPOINT_XFER_BULK, | ||
649 | USB_ENDPOINT_XFER_BULK, | ||
650 | USB_ENDPOINT_XFER_BULK, | ||
651 | USB_ENDPOINT_XFER_BULK, | ||
652 | }; | ||
653 | |||
654 | static struct usbhs_private usbhs1_private = { | ||
655 | .irq = evt2irq(0x0300), /* IRQ8 */ | ||
656 | .usbphyaddr = 0xE60581E2, /* USBPHY1INTAP */ | ||
657 | .usbcrcaddr = 0xE6058130, /* USBCR4 */ | ||
658 | .info = { | ||
659 | .platform_callback = { | ||
660 | .hardware_init = usbhs1_hardware_init, | ||
661 | .hardware_exit = usbhs1_hardware_exit, | ||
662 | .phy_reset = usbhs1_phy_reset, | ||
663 | .get_id = usbhs1_get_id, | ||
664 | .get_vbus = usbhs1_get_vbus, | ||
665 | }, | ||
666 | .driver_param = { | ||
667 | .buswait_bwait = 4, | ||
668 | .pipe_type = usbhs1_pipe_cfg, | ||
669 | .pipe_size = ARRAY_SIZE(usbhs1_pipe_cfg), | ||
670 | }, | ||
671 | }, | ||
672 | }; | ||
673 | |||
674 | static struct resource usbhs1_resources[] = { | ||
675 | [0] = { | ||
676 | .name = "USBHS", | ||
677 | .start = 0xE68B0000, | ||
678 | .end = 0xE68B00E6 - 1, | ||
679 | .flags = IORESOURCE_MEM, | ||
680 | }, | ||
681 | [1] = { | ||
682 | .start = evt2irq(0x1ce0) /* USB1_USB1I0 */, | ||
683 | .flags = IORESOURCE_IRQ, | ||
684 | }, | ||
685 | }; | ||
686 | |||
687 | static struct platform_device usbhs1_device = { | ||
688 | .name = "renesas_usbhs", | ||
689 | .id = 1, | ||
690 | .dev = { | ||
691 | .platform_data = &usbhs1_private.info, | ||
692 | }, | ||
693 | .num_resources = ARRAY_SIZE(usbhs1_resources), | ||
694 | .resource = usbhs1_resources, | ||
695 | }; | ||
696 | |||
697 | |||
512 | /* LED */ | 698 | /* LED */ |
513 | static struct gpio_led mackerel_leds[] = { | 699 | static struct gpio_led mackerel_leds[] = { |
514 | { | 700 | { |
@@ -949,6 +1135,7 @@ static struct platform_device *mackerel_devices[] __initdata = { | |||
949 | &smc911x_device, | 1135 | &smc911x_device, |
950 | &lcdc_device, | 1136 | &lcdc_device, |
951 | &usb1_host_device, | 1137 | &usb1_host_device, |
1138 | &usbhs1_device, | ||
952 | &leds_device, | 1139 | &leds_device, |
953 | &fsi_device, | 1140 | &fsi_device, |
954 | &fsi_ak4643_device, | 1141 | &fsi_ak4643_device, |
@@ -1044,6 +1231,7 @@ static void __init mackerel_map_io(void) | |||
1044 | 1231 | ||
1045 | #define GPIO_PORT9CR 0xE6051009 | 1232 | #define GPIO_PORT9CR 0xE6051009 |
1046 | #define GPIO_PORT10CR 0xE605100A | 1233 | #define GPIO_PORT10CR 0xE605100A |
1234 | #define GPIO_PORT168CR 0xE60520A8 | ||
1047 | #define SRCR4 0xe61580bc | 1235 | #define SRCR4 0xe61580bc |
1048 | #define USCCR1 0xE6058144 | 1236 | #define USCCR1 0xE6058144 |
1049 | static void __init mackerel_init(void) | 1237 | static void __init mackerel_init(void) |
@@ -1102,6 +1290,7 @@ static void __init mackerel_init(void) | |||
1102 | gpio_request(GPIO_FN_OVCN_1_114, NULL); | 1290 | gpio_request(GPIO_FN_OVCN_1_114, NULL); |
1103 | gpio_request(GPIO_FN_EXTLP_1, NULL); | 1291 | gpio_request(GPIO_FN_EXTLP_1, NULL); |
1104 | gpio_request(GPIO_FN_OVCN2_1, NULL); | 1292 | gpio_request(GPIO_FN_OVCN2_1, NULL); |
1293 | gpio_pull_down(GPIO_PORT168CR); | ||
1105 | 1294 | ||
1106 | /* setup USB phy */ | 1295 | /* setup USB phy */ |
1107 | __raw_writew(0x8a0a, 0xE6058130); /* USBCR4 */ | 1296 | __raw_writew(0x8a0a, 0xE6058130); /* USBCR4 */ |