diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2012-04-24 02:09:08 -0700 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-05-12 22:33:05 +0200 |
commit | 1c96293e9f8b8ec9620201bcf7f776f0e0f89edb (patch) | |
tree | 4c08046761891b95acd0930d30de2640720207a4 /arch/arm/mach-shmobile/board-armadillo800eva.c | |
parent | 46cf668748070e54879d528fa58107abc835dff3 (diff) | |
download | blackbird-op-linux-1c96293e9f8b8ec9620201bcf7f776f0e0f89edb.tar.gz blackbird-op-linux-1c96293e9f8b8ec9620201bcf7f776f0e0f89edb.zip |
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 @@ */ #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 | - @@ -97,6 +103,195 @@ *-----------+---------------+---------------------------- */ +/* + * 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(); } |