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>
This commit is contained in:
Kuninori Morimoto 2012-04-24 02:09:08 -07:00 committed by Rafael J. Wysocki
parent 46cf668748
commit 1c96293e9f
1 changed files with 237 additions and 10 deletions

View File

@ -20,14 +20,17 @@
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/irq.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/gpio_keys.h> #include <linux/gpio_keys.h>
#include <linux/sh_eth.h> #include <linux/sh_eth.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <linux/usb/renesas_usbhs.h>
#include <mach/common.h> #include <mach/common.h>
#include <mach/irqs.h> #include <mach/irqs.h>
#include <asm/page.h> #include <asm/page.h>
@ -90,6 +93,9 @@
* 0 | SDHI1 | COM8 enable, COM14 disable * 0 | SDHI1 | COM8 enable, COM14 disable
* 1 | SDHI1 | COM8 enable, COM14 disable * 1 | SDHI1 | COM8 enable, COM14 disable
* -12345678-+---------------+---------------------------- * -12345678-+---------------+----------------------------
* 0 | USB0 | COM20 enable, COM24 disable
* 1 | USB0 | COM20 disable, COM24 enable
* -12345678-+---------------+----------------------------
* 00 | JTAG | SH-X2 * 00 | JTAG | SH-X2
* 10 | JTAG | ARM * 10 | JTAG | ARM
* 01 | JTAG | - * 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 */ /* Ether */
static struct sh_eth_plat_data sh_eth_platdata = { static struct sh_eth_plat_data sh_eth_platdata = {
.phy = 0x00, /* LAN8710A */ .phy = 0x00, /* LAN8710A */
@ -224,11 +419,41 @@ static struct platform_device *eva_devices[] __initdata = {
&sh_eth_device, &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 * board init
*/ */
static void __init eva_init(void) static void __init eva_init(void)
{ {
eva_clock_init();
r8a7740_pinmux_init(); r8a7740_pinmux_init();
/* SCIFA1 */ /* SCIFA1 */
@ -302,6 +527,18 @@ static void __init eva_init(void)
gpio_request(GPIO_PORT18, NULL); /* PHY_RST */ gpio_request(GPIO_PORT18, NULL); /* PHY_RST */
gpio_direction_output(GPIO_PORT18, 1); 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 * CAUTION
* *
@ -326,17 +563,7 @@ static void __init eva_init(void)
static void __init eva_earlytimer_init(void) static void __init eva_earlytimer_init(void)
{ {
struct clk *xtal1;
r8a7740_clock_init(MD_CK0 | MD_CK2); 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(); shmobile_earlytimer_init();
} }