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 | |
| 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>
| -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 */ |
