Merge branch 'samsung/ohci' into next/drivers

* samsung/ohci:
  ARM: EXYNOS: Add USB OHCI support to ORIGEN board
  USB: Add Samsung Exynos OHCI diver
  ARM: EXYNOS: Add USB OHCI support to SMDKV310 board
  ARM: EXYNOS: Add USB OHCI device
This commit is contained in:
Arnd Bergmann 2011-12-27 23:41:37 +00:00
commit e814fb635c
13 changed files with 410 additions and 0 deletions

View File

@ -82,6 +82,11 @@ config EXYNOS4_DEV_DWMCI
help help
Compile in platform device definitions for DWMCI Compile in platform device definitions for DWMCI
config EXYNOS4_DEV_USB_OHCI
bool
help
Compile in platform device definition for USB OHCI
config EXYNOS4_SETUP_I2C1 config EXYNOS4_SETUP_I2C1
bool bool
help help
@ -179,6 +184,7 @@ config MACH_SMDKV310
select SAMSUNG_DEV_KEYPAD select SAMSUNG_DEV_KEYPAD
select EXYNOS4_DEV_PD select EXYNOS4_DEV_PD
select SAMSUNG_DEV_PWM select SAMSUNG_DEV_PWM
select EXYNOS4_DEV_USB_OHCI
select EXYNOS4_DEV_SYSMMU select EXYNOS4_DEV_SYSMMU
select EXYNOS4_SETUP_FIMD0 select EXYNOS4_SETUP_FIMD0
select EXYNOS4_SETUP_I2C1 select EXYNOS4_SETUP_I2C1
@ -288,6 +294,7 @@ config MACH_ORIGEN
select SAMSUNG_DEV_BACKLIGHT select SAMSUNG_DEV_BACKLIGHT
select SAMSUNG_DEV_PWM select SAMSUNG_DEV_PWM
select EXYNOS4_DEV_PD select EXYNOS4_DEV_PD
select EXYNOS4_DEV_USB_OHCI
select EXYNOS4_SETUP_FIMD0 select EXYNOS4_SETUP_FIMD0
select EXYNOS4_SETUP_SDHCI select EXYNOS4_SETUP_SDHCI
select EXYNOS4_SETUP_USB_PHY select EXYNOS4_SETUP_USB_PHY

View File

@ -44,6 +44,7 @@ obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o
obj-$(CONFIG_EXYNOS4_DEV_PD) += dev-pd.o obj-$(CONFIG_EXYNOS4_DEV_PD) += dev-pd.o
obj-$(CONFIG_EXYNOS4_DEV_SYSMMU) += dev-sysmmu.o obj-$(CONFIG_EXYNOS4_DEV_SYSMMU) += dev-sysmmu.o
obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o
obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI) += dev-ohci.o
obj-$(CONFIG_EXYNOS4_SETUP_FIMC) += setup-fimc.o obj-$(CONFIG_EXYNOS4_SETUP_FIMC) += setup-fimc.o
obj-$(CONFIG_EXYNOS4_SETUP_FIMD0) += setup-fimd0.o obj-$(CONFIG_EXYNOS4_SETUP_FIMD0) += setup-fimd0.o

View File

@ -0,0 +1,52 @@
/* linux/arch/arm/mach-exynos/dev-ohci.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* EXYNOS - OHCI support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <mach/irqs.h>
#include <mach/map.h>
#include <mach/ohci.h>
#include <plat/devs.h>
#include <plat/usb-phy.h>
static struct resource exynos4_ohci_resource[] = {
[0] = DEFINE_RES_MEM(EXYNOS4_PA_OHCI, SZ_256),
[1] = DEFINE_RES_IRQ(IRQ_USB_HOST),
};
static u64 exynos4_ohci_dma_mask = DMA_BIT_MASK(32);
struct platform_device exynos4_device_ohci = {
.name = "exynos-ohci",
.id = -1,
.num_resources = ARRAY_SIZE(exynos4_ohci_resource),
.resource = exynos4_ohci_resource,
.dev = {
.dma_mask = &exynos4_ohci_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
}
};
void __init exynos4_ohci_set_platdata(struct exynos4_ohci_platdata *pd)
{
struct exynos4_ohci_platdata *npd;
npd = s3c_set_platdata(pd, sizeof(struct exynos4_ohci_platdata),
&exynos4_device_ohci);
if (!npd->phy_init)
npd->phy_init = s5p_usb_phy_init;
if (!npd->phy_exit)
npd->phy_exit = s5p_usb_phy_exit;
}

View File

@ -107,6 +107,7 @@
#define EXYNOS4_PA_SROMC 0x12570000 #define EXYNOS4_PA_SROMC 0x12570000
#define EXYNOS4_PA_EHCI 0x12580000 #define EXYNOS4_PA_EHCI 0x12580000
#define EXYNOS4_PA_OHCI 0x12590000
#define EXYNOS4_PA_HSPHY 0x125B0000 #define EXYNOS4_PA_HSPHY 0x125B0000
#define EXYNOS4_PA_MFC 0x13400000 #define EXYNOS4_PA_MFC 0x13400000

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* http://www.samsung.com/
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef __MACH_EXYNOS_OHCI_H
#define __MACH_EXYNOS_OHCI_H
struct exynos4_ohci_platdata {
int (*phy_init)(struct platform_device *pdev, int type);
int (*phy_exit)(struct platform_device *pdev, int type);
};
extern void exynos4_ohci_set_platdata(struct exynos4_ohci_platdata *pd);
#endif /* __MACH_EXYNOS_OHCI_H */

View File

@ -41,6 +41,7 @@
#include <plat/fb.h> #include <plat/fb.h>
#include <plat/mfc.h> #include <plat/mfc.h>
#include <mach/ohci.h>
#include <mach/map.h> #include <mach/map.h>
/* Following are default values for UCON, ULCON and UFCON UART registers */ /* Following are default values for UCON, ULCON and UFCON UART registers */
@ -483,6 +484,16 @@ static void __init origen_ehci_init(void)
s5p_ehci_set_platdata(pdata); s5p_ehci_set_platdata(pdata);
} }
/* USB OHCI */
static struct exynos4_ohci_platdata origen_ohci_pdata;
static void __init origen_ohci_init(void)
{
struct exynos4_ohci_platdata *pdata = &origen_ohci_pdata;
exynos4_ohci_set_platdata(pdata);
}
static struct gpio_keys_button origen_gpio_keys_table[] = { static struct gpio_keys_button origen_gpio_keys_table[] = {
{ {
.code = KEY_MENU, .code = KEY_MENU,
@ -606,6 +617,7 @@ static struct platform_device *origen_devices[] __initdata = {
&s5p_device_mfc_l, &s5p_device_mfc_l,
&s5p_device_mfc_r, &s5p_device_mfc_r,
&s5p_device_mixer, &s5p_device_mixer,
&exynos4_device_ohci,
&exynos4_device_pd[PD_LCD0], &exynos4_device_pd[PD_LCD0],
&exynos4_device_pd[PD_TV], &exynos4_device_pd[PD_TV],
&exynos4_device_pd[PD_G3D], &exynos4_device_pd[PD_G3D],
@ -670,6 +682,7 @@ static void __init origen_machine_init(void)
s3c_sdhci0_set_platdata(&origen_hsmmc0_pdata); s3c_sdhci0_set_platdata(&origen_hsmmc0_pdata);
origen_ehci_init(); origen_ehci_init();
origen_ohci_init();
clk_xusbxti.rate = 24000000; clk_xusbxti.rate = 24000000;
s5p_tv_setup(); s5p_tv_setup();

View File

@ -42,6 +42,7 @@
#include <plat/clock.h> #include <plat/clock.h>
#include <mach/map.h> #include <mach/map.h>
#include <mach/ohci.h>
/* Following are default values for UCON, ULCON and UFCON UART registers */ /* Following are default values for UCON, ULCON and UFCON UART registers */
#define SMDKV310_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \ #define SMDKV310_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
@ -245,6 +246,16 @@ static void __init smdkv310_ehci_init(void)
s5p_ehci_set_platdata(pdata); s5p_ehci_set_platdata(pdata);
} }
/* USB OHCI */
static struct exynos4_ohci_platdata smdkv310_ohci_pdata;
static void __init smdkv310_ohci_init(void)
{
struct exynos4_ohci_platdata *pdata = &smdkv310_ohci_pdata;
exynos4_ohci_set_platdata(pdata);
}
static struct platform_device *smdkv310_devices[] __initdata = { static struct platform_device *smdkv310_devices[] __initdata = {
&s3c_device_hsmmc0, &s3c_device_hsmmc0,
&s3c_device_hsmmc1, &s3c_device_hsmmc1,
@ -261,6 +272,7 @@ static struct platform_device *smdkv310_devices[] __initdata = {
&s5p_device_fimc3, &s5p_device_fimc3,
&exynos4_device_ac97, &exynos4_device_ac97,
&exynos4_device_i2s0, &exynos4_device_i2s0,
&exynos4_device_ohci,
&samsung_device_keypad, &samsung_device_keypad,
&s5p_device_mfc, &s5p_device_mfc,
&s5p_device_mfc_l, &s5p_device_mfc_l,
@ -363,6 +375,7 @@ static void __init smdkv310_machine_init(void)
s5p_fimd0_set_platdata(&smdkv310_lcd0_pdata); s5p_fimd0_set_platdata(&smdkv310_lcd0_pdata);
smdkv310_ehci_init(); smdkv310_ehci_init();
smdkv310_ohci_init();
clk_xusbxti.rate = 24000000; clk_xusbxti.rate = 24000000;
platform_add_devices(smdkv310_devices, ARRAY_SIZE(smdkv310_devices)); platform_add_devices(smdkv310_devices, ARRAY_SIZE(smdkv310_devices));

View File

@ -19,6 +19,13 @@
#include <plat/cpu.h> #include <plat/cpu.h>
#include <plat/usb-phy.h> #include <plat/usb-phy.h>
static atomic_t host_usage;
static int exynos4_usb_host_phy_is_on(void)
{
return (readl(EXYNOS4_PHYPWR) & PHY1_STD_ANALOG_POWERDOWN) ? 0 : 1;
}
static int exynos4_usb_phy1_init(struct platform_device *pdev) static int exynos4_usb_phy1_init(struct platform_device *pdev)
{ {
struct clk *otg_clk; struct clk *otg_clk;
@ -27,6 +34,8 @@ static int exynos4_usb_phy1_init(struct platform_device *pdev)
u32 rstcon; u32 rstcon;
int err; int err;
atomic_inc(&host_usage);
otg_clk = clk_get(&pdev->dev, "otg"); otg_clk = clk_get(&pdev->dev, "otg");
if (IS_ERR(otg_clk)) { if (IS_ERR(otg_clk)) {
dev_err(&pdev->dev, "Failed to get otg clock\n"); dev_err(&pdev->dev, "Failed to get otg clock\n");
@ -39,6 +48,9 @@ static int exynos4_usb_phy1_init(struct platform_device *pdev)
return err; return err;
} }
if (exynos4_usb_host_phy_is_on())
return 0;
writel(readl(S5P_USBHOST_PHY_CONTROL) | S5P_USBHOST_PHY_ENABLE, writel(readl(S5P_USBHOST_PHY_CONTROL) | S5P_USBHOST_PHY_ENABLE,
S5P_USBHOST_PHY_CONTROL); S5P_USBHOST_PHY_CONTROL);
@ -95,6 +107,9 @@ static int exynos4_usb_phy1_exit(struct platform_device *pdev)
struct clk *otg_clk; struct clk *otg_clk;
int err; int err;
if (atomic_dec_return(&host_usage) > 0)
return 0;
otg_clk = clk_get(&pdev->dev, "otg"); otg_clk = clk_get(&pdev->dev, "otg");
if (IS_ERR(otg_clk)) { if (IS_ERR(otg_clk)) {
dev_err(&pdev->dev, "Failed to get otg clock\n"); dev_err(&pdev->dev, "Failed to get otg clock\n");

View File

@ -129,6 +129,7 @@ extern struct platform_device exynos4_device_dwmci;
extern struct platform_device exynos4_device_i2s0; extern struct platform_device exynos4_device_i2s0;
extern struct platform_device exynos4_device_i2s1; extern struct platform_device exynos4_device_i2s1;
extern struct platform_device exynos4_device_i2s2; extern struct platform_device exynos4_device_i2s2;
extern struct platform_device exynos4_device_ohci;
extern struct platform_device exynos4_device_pcm0; extern struct platform_device exynos4_device_pcm0;
extern struct platform_device exynos4_device_pcm1; extern struct platform_device exynos4_device_pcm1;
extern struct platform_device exynos4_device_pcm2; extern struct platform_device exynos4_device_pcm2;

View File

@ -48,6 +48,7 @@ config USB_ARCH_HAS_OHCI
default y if ARCH_DAVINCI_DA8XX default y if ARCH_DAVINCI_DA8XX
default y if ARCH_CNS3XXX default y if ARCH_CNS3XXX
default y if PLAT_SPEAR default y if PLAT_SPEAR
default y if ARCH_EXYNOS
# PPC: # PPC:
default y if STB03xxx default y if STB03xxx
default y if PPC_MPC52xx default y if PPC_MPC52xx

View File

@ -371,6 +371,12 @@ config USB_OHCI_SH
Enables support for the on-chip OHCI controller on the SuperH. Enables support for the on-chip OHCI controller on the SuperH.
If you use the PCI OHCI controller, this option is not necessary. If you use the PCI OHCI controller, this option is not necessary.
config USB_OHCI_EXYNOS
boolean "OHCI support for Samsung EXYNOS SoC Series"
depends on USB_OHCI_HCD && ARCH_EXYNOS
help
Enable support for the Samsung Exynos SOC's on-chip OHCI controller.
config USB_CNS3XXX_OHCI config USB_CNS3XXX_OHCI
bool "Cavium CNS3XXX OHCI Module" bool "Cavium CNS3XXX OHCI Module"
depends on USB_OHCI_HCD && ARCH_CNS3XXX depends on USB_OHCI_HCD && ARCH_CNS3XXX

View File

@ -0,0 +1,274 @@
/*
* SAMSUNG EXYNOS USB HOST OHCI Controller
*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Author: Jingoo Han <jg1.han@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <mach/ohci.h>
#include <plat/usb-phy.h>
struct exynos_ohci_hcd {
struct device *dev;
struct usb_hcd *hcd;
struct clk *clk;
};
static int ohci_exynos_start(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
int ret;
ohci_dbg(ohci, "ohci_exynos_start, ohci:%p", ohci);
ret = ohci_init(ohci);
if (ret < 0)
return ret;
ret = ohci_run(ohci);
if (ret < 0) {
err("can't start %s", hcd->self.bus_name);
ohci_stop(hcd);
return ret;
}
return 0;
}
static const struct hc_driver exynos_ohci_hc_driver = {
.description = hcd_name,
.product_desc = "EXYNOS OHCI Host Controller",
.hcd_priv_size = sizeof(struct ohci_hcd),
.irq = ohci_irq,
.flags = HCD_MEMORY|HCD_USB11,
.start = ohci_exynos_start,
.stop = ohci_stop,
.shutdown = ohci_shutdown,
.get_frame_number = ohci_get_frame,
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
static int __devinit exynos_ohci_probe(struct platform_device *pdev)
{
struct exynos4_ohci_platdata *pdata;
struct exynos_ohci_hcd *exynos_ohci;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
struct resource *res;
int irq;
int err;
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "No platform data defined\n");
return -EINVAL;
}
exynos_ohci = kzalloc(sizeof(struct exynos_ohci_hcd), GFP_KERNEL);
if (!exynos_ohci)
return -ENOMEM;
exynos_ohci->dev = &pdev->dev;
hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev,
dev_name(&pdev->dev));
if (!hcd) {
dev_err(&pdev->dev, "Unable to create HCD\n");
err = -ENOMEM;
goto fail_hcd;
}
exynos_ohci->hcd = hcd;
exynos_ohci->clk = clk_get(&pdev->dev, "usbhost");
if (IS_ERR(exynos_ohci->clk)) {
dev_err(&pdev->dev, "Failed to get usbhost clock\n");
err = PTR_ERR(exynos_ohci->clk);
goto fail_clk;
}
err = clk_enable(exynos_ohci->clk);
if (err)
goto fail_clken;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Failed to get I/O memory\n");
err = -ENXIO;
goto fail_io;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
hcd->regs = ioremap(res->start, resource_size(res));
if (!hcd->regs) {
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
err = -ENOMEM;
goto fail_io;
}
irq = platform_get_irq(pdev, 0);
if (!irq) {
dev_err(&pdev->dev, "Failed to get IRQ\n");
err = -ENODEV;
goto fail;
}
if (pdata->phy_init)
pdata->phy_init(pdev, S5P_USB_PHY_HOST);
ohci = hcd_to_ohci(hcd);
ohci_hcd_init(ohci);
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err) {
dev_err(&pdev->dev, "Failed to add USB HCD\n");
goto fail;
}
platform_set_drvdata(pdev, exynos_ohci);
return 0;
fail:
iounmap(hcd->regs);
fail_io:
clk_disable(exynos_ohci->clk);
fail_clken:
clk_put(exynos_ohci->clk);
fail_clk:
usb_put_hcd(hcd);
fail_hcd:
kfree(exynos_ohci);
return err;
}
static int __devexit exynos_ohci_remove(struct platform_device *pdev)
{
struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
struct usb_hcd *hcd = exynos_ohci->hcd;
usb_remove_hcd(hcd);
if (pdata && pdata->phy_exit)
pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
iounmap(hcd->regs);
clk_disable(exynos_ohci->clk);
clk_put(exynos_ohci->clk);
usb_put_hcd(hcd);
kfree(exynos_ohci);
return 0;
}
static void exynos_ohci_shutdown(struct platform_device *pdev)
{
struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
struct usb_hcd *hcd = exynos_ohci->hcd;
if (hcd->driver->shutdown)
hcd->driver->shutdown(hcd);
}
#ifdef CONFIG_PM
static int exynos_ohci_suspend(struct device *dev)
{
struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
struct usb_hcd *hcd = exynos_ohci->hcd;
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
struct platform_device *pdev = to_platform_device(dev);
struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
unsigned long flags;
int rc = 0;
/*
* Root hub was already suspended. Disable irq emission and
* mark HW unaccessible, bail out if RH has been resumed. Use
* the spinlock to properly synchronize with possible pending
* RH suspend or resume activity.
*
* This is still racy as hcd->state is manipulated outside of
* any locks =P But that will be a different fix.
*/
spin_lock_irqsave(&ohci->lock, flags);
if (hcd->state != HC_STATE_SUSPENDED && hcd->state != HC_STATE_HALT) {
rc = -EINVAL;
goto fail;
}
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
if (pdata && pdata->phy_exit)
pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
fail:
spin_unlock_irqrestore(&ohci->lock, flags);
return rc;
}
static int exynos_ohci_resume(struct device *dev)
{
struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
struct usb_hcd *hcd = exynos_ohci->hcd;
struct platform_device *pdev = to_platform_device(dev);
struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
if (pdata && pdata->phy_init)
pdata->phy_init(pdev, S5P_USB_PHY_HOST);
/* Mark hardware accessible again as we are out of D3 state by now */
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
ohci_finish_controller_resume(hcd);
return 0;
}
#else
#define exynos_ohci_suspend NULL
#define exynos_ohci_resume NULL
#endif
static const struct dev_pm_ops exynos_ohci_pm_ops = {
.suspend = exynos_ohci_suspend,
.resume = exynos_ohci_resume,
};
static struct platform_driver exynos_ohci_driver = {
.probe = exynos_ohci_probe,
.remove = __devexit_p(exynos_ohci_remove),
.shutdown = exynos_ohci_shutdown,
.driver = {
.name = "exynos-ohci",
.owner = THIS_MODULE,
.pm = &exynos_ohci_pm_ops,
}
};
MODULE_ALIAS("platform:exynos-ohci");
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");

View File

@ -1005,6 +1005,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ohci_hcd_s3c2410_driver #define PLATFORM_DRIVER ohci_hcd_s3c2410_driver
#endif #endif
#ifdef CONFIG_USB_OHCI_EXYNOS
#include "ohci-exynos.c"
#define PLATFORM_DRIVER exynos_ohci_driver
#endif
#ifdef CONFIG_USB_OHCI_HCD_OMAP1 #ifdef CONFIG_USB_OHCI_HCD_OMAP1
#include "ohci-omap.c" #include "ohci-omap.c"
#define OMAP1_PLATFORM_DRIVER ohci_hcd_omap_driver #define OMAP1_PLATFORM_DRIVER ohci_hcd_omap_driver