arch\arm\plat-samsung\devs.c
/* 2015/11/17 fuchch */
/* USB HOST */
#ifdef CONFIG_USB_SUPPORT
#ifdef CONFIG_USB_EHCI_S5PV210
/* USB Host Controlle EHCI registrations */
static struct resource s5p_usb_ehci_resource[] = {
[0] = {
.start = S5PV210_PA_USB_EHCI,
.end = S5P_PA_USB_EHCI + S5P_SZ_USB_EHCI - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_UHOST,
.end = IRQ_UHOST,
.flags = IORESOURCE_IRQ,
}
};
static u64 s5p_device_usb_ehci_dmamask = 0xffffffffUL;
struct platform_device s5p_device_usb_ehci = {
.name = "s5p-ehci",
.id = -1,
.num_resources = ARRAY_SIZE(s5p_usb_ehci_resource),
.resource = s5p_usb_ehci_resource,
.dev = {
.dma_mask = &s5p_device_usb_ehci_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
#endif /* CONFIG_USB_EHCI_S5PV210 */
#ifdef CONFIG_USB_OHCI_S5PV210
/* USB Host Controlle OHCI registrations */
static struct resource s5p_usb__ohci_resource[] = {
[0] = {
.start = S5P_PA_USB_OHCI,
.end = S5P_PA_USB_OHCI + S5P_SZ_USB_OHCI - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_UHOST,
.end = IRQ_UHOST,
.flags = IORESOURCE_IRQ,
}
};
static u64 s5p_device_usb_ohci_dmamask = 0xffffffffUL;
struct platform_device s5p_device_usb_ohci = {
.name = "s5p-ohci",
.id = -1,
.num_resources = ARRAY_SIZE(s5p_usb__ohci_resource),
.resource = s5p_usb__ohci_resource,
.dev = {
.dma_mask = &s5p_device_usb_ohci_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
#endif /* CONFIG_USB_OHCI_S5PV210 */
/* USB Device (Gadget)*/
static struct resource s5p_usbgadget_resource[] = {
[0] = {
.start = S5PV210_PA_OTG,
.end = S5PV210_PA_OTG + S5PV210_SZ_OTG - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_OTG,
.end = IRQ_OTG,
.flags = IORESOURCE_IRQ,
}
};
struct platform_device s5p_device_usbgadget = {
.name = "s5p-usbgadget",
.id = -1,
.num_resources = ARRAY_SIZE(s5p_usbgadget_resource),
.resource = s5p_usbgadget_resource,
};
#endif /* CONFIG_USB_SUPPORT */
arch\arm\plat-samsung\include\plat\devs.h
/* 2015/11/7 fuchch */
extern struct platform_device s5p_device_usb_ehci;
extern struct platform_device s5p_device_usb_ohci;
arch\arm\mach-s5pv210\include\mach\map.h
/* 2015/11/7 fuchch */
/* usb */
#define S5PV210_PA_OTG (0xEC000000)
#define S5PV210_SZ_OTG SZ_1M
#define S5PV210_PA_OTGSFR (0xEC100000)
#define S5PV210_SZ_OTGSFR SZ_1M
#define S5PV210_PA_USB_EHCI (0xEC200000)
#define S5P_PA_USB_EHCI S5PV210_PA_USB_EHCI
#define S5P_SZ_USB_EHCI SZ_1M
#define S5PV210_PA_USB_OHCI (0xEC300000)
#define S5P_PA_USB_OHCI S5PV210_PA_USB_OHCI
#define S5P_SZ_USB_OHCI SZ_1M
/* end usb */
arch\arm\mach-s5pv210\mach-smdkv210.c
#include <plat/regs-usb-hsotg-phy.h>
static struct platform_device *smdkv210_devices[] __initdata = {
...
/* 2015/11/7 fuchch */
#ifdef CONFIG_USB_SUPPORT
#ifdef CONFIG_USB_EHCI_S5PV210
&s5p_device_usb_ehci,
#endif
#ifdef CONFIG_USB_OHCI_S5PV210
&s5p_device_usb_ohci,
#endif
#endif
}
#if CONFIG_USB_SUPPORT
extern struct clk *clk_get(struct device *dev, const char *id);
extern int clk_enable(struct clk *clk);
void usb_host_phy_init(void)
{
struct clk *otg_clk;
otg_clk = clk_get(NULL, "otg");
clk_enable(otg_clk);
if (readl(S5P_USB_PHY_CONTROL) & (0x1<<1))
return;
__raw_writel(__raw_readl(S5P_USB_PHY_CONTROL) | (0x1<<1),
S5P_USB_PHY_CONTROL);
__raw_writel((__raw_readl(S3C_PHYPWR)
& ~(0x1<<7) & ~(0x1<<6)) | (0x1<<8) | (0x1<<5),
S3C_PHYPWR);
__raw_writel((__raw_readl(S3C_PHYCLK) & ~(0x1<<7)) | (0x3<<0),
S3C_PHYCLK);
__raw_writel((__raw_readl(S3C_RSTCON)) | (0x1<<4) | (0x1<<3),
S3C_RSTCON);
__raw_writel(__raw_readl(S3C_RSTCON) & ~(0x1<<4) & ~(0x1<<3),
S3C_RSTCON);
}
EXPORT_SYMBOL(usb_host_phy_init);
void usb_host_phy_off(void)
{
__raw_writel(__raw_readl(S3C_PHYPWR) | (0x1<<7)|(0x1<<6),
S3C_PHYPWR);
__raw_writel(__raw_readl(S5P_USB_PHY_CONTROL) & ~(1<<1),
S5P_USB_PHY_CONTROL);
}
EXPORT_SYMBOL(usb_host_phy_off);
#endif
drivers\usb\host\
/* ehci-s5pv210.c - Driver for USB HOST on Samsung S5PV210 processor
*
* Bus Glue for SAMSUNG S5PV210 USB HOST EHCI Controller
*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* Author: Jingoo Han <[email protected]>
*
* Based on "ehci-au1xxx.c" by by K.Boge <[email protected]>
* Modified for SAMSUNG s5pv210 EHCI by Jingoo Han <[email protected]>
*
* 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/clk.h>
#include <linux/platform_device.h>
#include <linux/usb/hcd.h>
static struct clk *usb_clk;
extern int usb_disabled(void);
extern void usb_host_phy_init(void);
extern void usb_host_phy_off(void);
static void s5pv210_start_ehc(void);
static void s5pv210_stop_ehc(void);
static int ehci_hcd_s5pv210_drv_probe(struct platform_device *pdev);
static int ehci_hcd_s5pv210_drv_remove(struct platform_device *pdev);
#ifdef CONFIG_PM
static int ehci_hcd_s5pv210_drv_suspend(
struct platform_device *pdev,
pm_message_t message)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
unsigned long flags;
int rc = 0;
if (time_before(jiffies, ehci->next_statechange))
msleep(10);
/* 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(&ehci->lock, flags);
if (hcd->state != HC_STATE_SUSPENDED) {
rc = -EINVAL;
goto bail;
}
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
/* make sure snapshot being resumed re-enumerates everything */
if (message.event == PM_EVENT_PRETHAW) {
ehci_halt(ehci);
ehci_reset(ehci);
}
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
s5pv210_stop_ehc();
bail:
spin_unlock_irqrestore(&ehci->lock, flags);
return rc;
}
static int ehci_hcd_s5pv210_drv_resume(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
s5pv210_start_ehc();
if (time_before(jiffies, ehci->next_statechange))
msleep(10);
/* Mark hardware accessible again as we are out of D3 state by now */
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
int mask = INTR_MASK;
if (!hcd->self.root_hub->do_remote_wakeup)
mask &= ~STS_PCD;
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
ehci_readl(ehci, &ehci->regs->intr_enable);
return 0;
}
ehci_dbg(ehci, "lost power, restarting\n");
usb_root_hub_lost_power(hcd->self.root_hub);
(void) ehci_halt(ehci);
(void) ehci_reset(ehci);
/* emptying the schedule aborts any urbs */
spin_lock_irq(&ehci->lock);
if (ehci->reclaim)
end_unlink_async(ehci);
ehci_work(ehci);
spin_unlock_irq(&ehci->lock);
ehci_writel(ehci, ehci->command, &ehci->regs->command);
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
hcd->state = HC_STATE_SUSPENDED;
return 0;
}
#else
#define ehci_hcd_s5pv210_drv_suspend NULL
#define ehci_hcd_s5pv210_drv_resume NULL
#endif
static void s5pv210_start_ehc(void)
{
clk_enable(usb_clk);
usb_host_phy_init();
}
static void s5pv210_stop_ehc(void)
{
usb_host_phy_off();
clk_disable(usb_clk);
}
static const struct hc_driver ehci_s5pv210_hc_driver = {
.description = hcd_name,
.product_desc = "s5pv210 EHCI",
.hcd_priv_size = sizeof(struct ehci_hcd),
.irq = ehci_irq,
.flags = HCD_MEMORY|HCD_USB2,
.reset = ehci_init,
.start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,
.get_frame_number = ehci_get_frame,
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.endpoint_reset = ehci_endpoint_reset,
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
static int ehci_hcd_s5pv210_drv_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd = NULL;
struct ehci_hcd *ehci = NULL;
int retval = 0;
if (usb_disabled())
return -ENODEV;
usb_host_phy_off();
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
dev_err(&pdev->dev, "resource[1] is not IORESOURCE_IRQ.\n");
return -ENODEV;
}
hcd = usb_create_hcd(&ehci_s5pv210_hc_driver, &pdev->dev, "s5pv210");
if (!hcd) {
dev_err(&pdev->dev, "usb_create_hcd failed!\n");
return -ENODEV;
}
hcd->rsrc_start = pdev->resource[0].start;
hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dev_err(&pdev->dev, "request_mem_region failed!\n");
retval = -EBUSY;
goto err1;
}
usb_clk = clk_get(&pdev->dev, "usb-host");
if (IS_ERR(usb_clk)) {
dev_err(&pdev->dev, "cannot get usb-host clock\n");
retval = -ENODEV;
goto err2;
}
s5pv210_start_ehc();
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
dev_err(&pdev->dev, "ioremap failed!\n");
retval = -ENOMEM;
goto err2;
}
ehci = hcd_to_ehci(hcd);
ehci->caps = hcd->regs;
ehci->regs = hcd->regs + ((readl(&ehci->caps->hc_capbase)>>00)&0x00ff);
/* cache this readonly data; minimize chip reads */
ehci->hcs_params = readl(&ehci->caps->hcs_params);
#if defined(CONFIG_ARCH_S5PV210)
writel(0x000F0000, hcd->regs + 0x90);
#endif
retval = usb_add_hcd(hcd, pdev->resource[1].start,
IRQF_DISABLED | IRQF_SHARED);
if (retval == 0) {
platform_set_drvdata(pdev, hcd);
return retval;
}
s5pv210_stop_ehc();
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
clk_put(usb_clk);
usb_put_hcd(hcd);
return retval;
}
static int ehci_hcd_s5pv210_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
s5pv210_stop_ehc();
clk_put(usb_clk);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver ehci_hcd_s5pv210_driver = {
.probe = ehci_hcd_s5pv210_drv_probe,
.remove = ehci_hcd_s5pv210_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.suspend = ehci_hcd_s5pv210_drv_suspend,
.resume = ehci_hcd_s5pv210_drv_resume,
.driver = {
.name = "s5p-ehci",
.owner = THIS_MODULE,
}
};
MODULE_ALIAS("platform:s5p-ehci");
/* ohci-s5pv210.c - Driver for USB HOST on Samsung S5PV210 processor
*
* Bus Glue for SAMSUNG S5PV210 USB HOST OHCI Controller
*
* (C) Copyright 1999 Roman Weissgaerber <[email protected]>
* (C) Copyright 2000-2002 David Brownell <[email protected]>
* (C) Copyright 2002 Hewlett-Packard Company
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* Author: Jingoo Han <[email protected]>
*
* Based on "ohci-au1xxx.c" by Matt Porter <[email protected]>
* Modified for SAMSUNG s5pv210 OHCI by Jingoo Han <[email protected]>
*
* 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/clk.h>
#include <linux/platform_device.h>
static struct clk *usb_clk;
extern int usb_disabled(void);
extern void usb_host_phy_init(void);
extern void usb_host_phy_off(void);
static void s5pv210_start_ohc(void);
static void s5pv210_stop_ohc(void);
static int ohci_hcd_s5pv210_drv_probe(struct platform_device *pdev);
static int ohci_hcd_s5pv210_drv_remove(struct platform_device *pdev);
#ifdef CONFIG_PM
static int ohci_hcd_s5pv210_drv_suspend(
struct platform_device *pdev,
pm_message_t message
)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
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) {
rc = -EINVAL;
goto bail;
}
/* make sure snapshot being resumed re-enumerates everything */
if (message.event == PM_EVENT_PRETHAW)
ohci_usb_reset(ohci);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
s5pv210_stop_ohc();
bail:
spin_unlock_irqrestore(&ohci->lock, flags);
return rc;
}
static int ohci_hcd_s5pv210_drv_resume(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
int rc = 0;
s5pv210_start_ohc();
/* 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 rc;
}
#else
#define ohci_hcd_s5pv210_drv_suspend NULL
#define ohci_hcd_s5pv210_drv_resume NULL
#endif
static void s5pv210_start_ohc(void)
{
clk_enable(usb_clk);
usb_host_phy_init();
}
static void s5pv210_stop_ohc(void)
{
usb_host_phy_off();
clk_disable(usb_clk);
}
static int ohci_s5pv210_start(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
int ret;
ohci_dbg(ohci, "ohci_s5pv210_start, ohci:%p", ohci);
ret = ohci_init(ohci);
if (ret < 0)
return ret;
ret = ohci_run(ohci);
if (ret < 0) {
printk("can't start %s", hcd->self.bus_name);
ohci_stop(hcd);
return ret;
}
return 0;
}
static const struct hc_driver ohci_s5pv210_hc_driver = {
.description = hcd_name,
.product_desc = "s5pv210 OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
.irq = ohci_irq,
.flags = HCD_MEMORY|HCD_USB11,
.start = ohci_s5pv210_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 ohci_hcd_s5pv210_drv_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd = NULL;
int retval = 0;
if (usb_disabled())
return -ENODEV;
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
dev_err(&pdev->dev, "resource[1] is not IORESOURCE_IRQ.\n");
return -ENODEV;
}
hcd = usb_create_hcd(&ohci_s5pv210_hc_driver, &pdev->dev, "s5pv210");
if (!hcd) {
dev_err(&pdev->dev, "usb_create_hcd failed!\n");
return -ENODEV;
}
hcd->rsrc_start = pdev->resource[0].start;
hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dev_err(&pdev->dev, "request_mem_region failed!\n");
retval = -EBUSY;
goto err1;
}
usb_clk = clk_get(&pdev->dev, "usb-host");
if (IS_ERR(usb_clk)) {
dev_err(&pdev->dev, "cannot get usb-host clock\n");
retval = -ENODEV;
goto err2;
}
s5pv210_start_ohc();
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
dev_err(&pdev->dev, "ioremap failed!\n");
retval = -ENOMEM;
goto err2;
}
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, pdev->resource[1].start,
IRQF_DISABLED | IRQF_SHARED);
if (retval == 0) {
platform_set_drvdata(pdev, hcd);
return retval;
}
s5pv210_stop_ohc();
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
clk_put(usb_clk);
usb_put_hcd(hcd);
return retval;
}
static int ohci_hcd_s5pv210_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
s5pv210_stop_ohc();
clk_put(usb_clk);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver ohci_hcd_s5pv210_driver = {
.probe = ohci_hcd_s5pv210_drv_probe,
.remove = ohci_hcd_s5pv210_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.suspend = ohci_hcd_s5pv210_drv_suspend,
.resume = ohci_hcd_s5pv210_drv_resume,
.driver = {
.name = "s5p-ohci",
.owner = THIS_MODULE,
}
};
MODULE_ALIAS("platform:s5p-ohci");
drivers\usb\host\ehci-hcd.c
#ifdef CONFIG_USB_EHCI_S5PV210
#include "ehci-s5pv210.c"
#define PLATFORM_DRIVER ehci_hcd_s5pv210_driver
#endif
drivers\usb\host\ohci-hcd.c
#ifdef CONFIG_USB_OHCI_S5PV210
#include "ohci-s5pv210.c"
#define PLATFORM_DRIVER ohci_hcd_s5pv210_driver
#endif
drivers\usb\host\Kconfig
config USB_EHCI_S5PV210
tristate "S5PV210 support for Samsung S5P SoC Series"
depends on PLAT_S5P
help
Enable support for the Samsung S5PV210 SOC's on-chip EHCI controller.
config USB_OHCI_S5PV210
tristate "OHCI support for Samsung S5PV210 SoC Series"
depends on PLAT_S5P
help
Enable support for the Samsung S5PV210 SOC's on-chip OHCI controller.