ARM: mach-shmobile: armadillo800eva: add USB function support
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Tue, 24 Apr 2012 09:09:08 +0000 (02:09 -0700)
committerRafael J. Wysocki <rjw@sisk.pl>
Sat, 12 May 2012 20:33:05 +0000 (22:33 +0200)
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>
arch/arm/mach-shmobile/board-armadillo800eva.c

index 4d066f9..1d25d5f 100644 (file)
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/input.h>
+#include <linux/irq.h>
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
 #include <linux/gpio_keys.h>
 #include <linux/sh_eth.h>
 #include <linux/videodev2.h>
+#include <linux/usb/renesas_usbhs.h>
 #include <mach/common.h>
 #include <mach/irqs.h>
 #include <asm/page.h>
@@ -90,6 +93,9 @@
  *      0    | SDHI1         | COM8 enable,  COM14 disable
  *      1    | SDHI1         | COM8 enable,  COM14 disable
  * -12345678-+---------------+----------------------------
+ *       0   | USB0          | COM20 enable,  COM24 disable
+ *       1   | USB0          | COM20 disable, COM24 enable
+ * -12345678-+---------------+----------------------------
  *        00 | JTAG          | SH-X2
  *        10 | JTAG          | ARM
  *        01 | JTAG          | -
  *-----------+---------------+----------------------------
  */
 
+/*
+ * USB function
+ *
+ * When you use USB Function,
+ * set SW1.6 ON, and connect cable to CN24.
+ *
+ * USBF needs workaround on R8A7740 chip.
+ * These are a little bit complex.
+ * see
+ *     usbhsf_power_ctrl()
+ *
+ * CAUTION
+ *
+ * It uses autonomy mode for USB hotplug at this point
+ * (= usbhs_private.platform_callback.get_vbus is NULL),
+ * since we don't know what's happen on PM control
+ * on this workaround.
+ */
+#define USBCR1         0xe605810a
+#define USBH           0xC6700000
+#define USBH_USBCTR    0x10834
+
+struct usbhsf_private {
+       struct clk *phy;
+       struct clk *usb24;
+       struct clk *pci;
+       struct clk *func;
+       struct clk *host;
+       void __iomem *usbh_base;
+       struct renesas_usbhs_platform_info info;
+};
+
+#define usbhsf_get_priv(pdev)                          \
+       container_of(renesas_usbhs_get_info(pdev),      \
+                    struct usbhsf_private, info)
+
+static int usbhsf_get_id(struct platform_device *pdev)
+{
+       return USBHS_GADGET;
+}
+
+static void usbhsf_power_ctrl(struct platform_device *pdev,
+                             void __iomem *base, int enable)
+{
+       struct usbhsf_private *priv = usbhsf_get_priv(pdev);
+
+       /*
+        * Work around for USB Function.
+        * It needs USB host clock, and settings
+        */
+       if (enable) {
+               /*
+                * enable all the related usb clocks
+                * for usb workaround
+                */
+               clk_enable(priv->usb24);
+               clk_enable(priv->pci);
+               clk_enable(priv->host);
+               clk_enable(priv->func);
+               clk_enable(priv->phy);
+
+               /*
+                * set USBCR1
+                *
+                * Port1 is driven by USB function,
+                * Port2 is driven by USB HOST
+                * One HOST (Port1 or Port2 is HOST)
+                * USB PLL input clock = 24MHz
+                */
+               __raw_writew(0xd750, USBCR1);
+               mdelay(1);
+
+               /*
+                * start USB Host
+                */
+               __raw_writel(0x0000000c, priv->usbh_base + USBH_USBCTR);
+               __raw_writel(0x00000008, priv->usbh_base + USBH_USBCTR);
+               mdelay(10);
+
+               /*
+                * USB PHY Power ON
+                */
+               __raw_writew(0xd770, USBCR1);
+               __raw_writew(0x4000, base + 0x102); /* USBF :: SUSPMODE */
+
+       } else {
+               __raw_writel(0x0000010f, priv->usbh_base + USBH_USBCTR);
+               __raw_writew(0xd7c0, USBCR1); /* GPIO */
+
+               clk_disable(priv->phy);
+               clk_disable(priv->func);        /* usb work around */
+               clk_disable(priv->host);        /* usb work around */
+               clk_disable(priv->pci);         /* usb work around */
+               clk_disable(priv->usb24);       /* usb work around */
+       }
+}
+
+static void usbhsf_hardware_exit(struct platform_device *pdev)
+{
+       struct usbhsf_private *priv = usbhsf_get_priv(pdev);
+
+       if (!IS_ERR(priv->phy))
+               clk_put(priv->phy);
+       if (!IS_ERR(priv->usb24))
+               clk_put(priv->usb24);
+       if (!IS_ERR(priv->pci))
+               clk_put(priv->pci);
+       if (!IS_ERR(priv->host))
+               clk_put(priv->host);
+       if (!IS_ERR(priv->func))
+               clk_put(priv->func);
+       if (priv->usbh_base)
+               iounmap(priv->usbh_base);
+
+       priv->phy       = NULL;
+       priv->usb24     = NULL;
+       priv->pci       = NULL;
+       priv->host      = NULL;
+       priv->func      = NULL;
+       priv->usbh_base = NULL;
+}
+
+static int usbhsf_hardware_init(struct platform_device *pdev)
+{
+       struct usbhsf_private *priv = usbhsf_get_priv(pdev);
+
+       priv->phy       = clk_get(&pdev->dev, "phy");
+       priv->usb24     = clk_get(&pdev->dev, "usb24");
+       priv->pci       = clk_get(&pdev->dev, "pci");
+       priv->func      = clk_get(&pdev->dev, "func");
+       priv->host      = clk_get(&pdev->dev, "host");
+       priv->usbh_base = ioremap_nocache(USBH, 0x20000);
+
+       if (IS_ERR(priv->phy)           ||
+           IS_ERR(priv->usb24)         ||
+           IS_ERR(priv->pci)           ||
+           IS_ERR(priv->host)          ||
+           IS_ERR(priv->func)          ||
+           !priv->usbh_base) {
+               dev_err(&pdev->dev, "USB clock setting failed\n");
+               usbhsf_hardware_exit(pdev);
+               return -EIO;
+       }
+
+       /* usb24 use 1/1 of parent clock (= usb24s = 24MHz) */
+       clk_set_rate(priv->usb24,
+                    clk_get_rate(clk_get_parent(priv->usb24)));
+
+       return 0;
+}
+
+static struct usbhsf_private usbhsf_private = {
+       .info = {
+               .platform_callback = {
+                       .get_id         = usbhsf_get_id,
+                       .hardware_init  = usbhsf_hardware_init,
+                       .hardware_exit  = usbhsf_hardware_exit,
+                       .power_ctrl     = usbhsf_power_ctrl,
+               },
+               .driver_param = {
+                       .buswait_bwait          = 5,
+                       .detection_delay        = 5,
+               },
+       }
+};
+
+static struct resource usbhsf_resources[] = {
+       {
+               .name   = "USBHS",
+               .start  = 0xe6890000,
+               .end    = 0xe6890104 - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .start  = evt2irq(0x0A20),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device usbhsf_device = {
+       .name   = "renesas_usbhs",
+       .dev = {
+               .platform_data = &usbhsf_private.info,
+       },
+       .id = -1,
+       .num_resources  = ARRAY_SIZE(usbhsf_resources),
+       .resource       = usbhsf_resources,
+};
+
 /* Ether */
 static struct sh_eth_plat_data sh_eth_platdata = {
        .phy                    = 0x00, /* LAN8710A */
@@ -224,11 +419,41 @@ static struct platform_device *eva_devices[] __initdata = {
        &sh_eth_device,
 };
 
+static void __init eva_clock_init(void)
+{
+       struct clk *system      = clk_get(NULL, "system_clk");
+       struct clk *xtal1       = clk_get(NULL, "extal1");
+       struct clk *usb24s      = clk_get(NULL, "usb24s");
+
+       if (IS_ERR(system)      ||
+           IS_ERR(xtal1)       ||
+           IS_ERR(usb24s)) {
+               pr_err("armadillo800eva board clock init failed\n");
+               goto clock_error;
+       }
+
+       /* armadillo 800 eva extal1 is 24MHz */
+       clk_set_rate(xtal1, 24000000);
+
+       /* usb24s use extal1 (= system) clock (= 24MHz) */
+       clk_set_parent(usb24s, system);
+
+clock_error:
+       if (!IS_ERR(system))
+               clk_put(system);
+       if (!IS_ERR(xtal1))
+               clk_put(xtal1);
+       if (!IS_ERR(usb24s))
+               clk_put(usb24s);
+}
+
 /*
  * board init
  */
 static void __init eva_init(void)
 {
+       eva_clock_init();
+
        r8a7740_pinmux_init();
 
        /* SCIFA1 */
@@ -302,6 +527,18 @@ static void __init eva_init(void)
        gpio_request(GPIO_PORT18, NULL); /* PHY_RST */
        gpio_direction_output(GPIO_PORT18, 1);
 
+       /* USB */
+       gpio_request(GPIO_PORT159, NULL); /* USB_DEVICE_MODE */
+       gpio_direction_input(GPIO_PORT159);
+
+       if (gpio_get_value(GPIO_PORT159)) {
+               /* USB Host */
+       } else {
+               /* USB Func */
+               gpio_request(GPIO_FN_VBUS, NULL);
+               platform_device_register(&usbhsf_device);
+       }
+
        /*
         * CAUTION
         *
@@ -326,17 +563,7 @@ static void __init eva_init(void)
 
 static void __init eva_earlytimer_init(void)
 {
-       struct clk *xtal1;
-
        r8a7740_clock_init(MD_CK0 | MD_CK2);
-
-       xtal1 = clk_get(NULL, "extal1");
-       if (!IS_ERR(xtal1)) {
-               /* armadillo 800 eva extal1 is 24MHz */
-               clk_set_rate(xtal1, 24000000);
-               clk_put(xtal1);
-       }
-
        shmobile_earlytimer_init();
 }