Commit 100d4597 authored by Tony Prisk's avatar Tony Prisk Committed by Greg Kroah-Hartman

ARM: vt8500: Add support for UHCI companion controller

Add support for a generic non-pci UHCI companion controller.
Existing board files for arch-vt8500 updated to include UHCI
support.
Signed-off-by: default avatarTony Prisk <linux@prisktech.co.nz>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 8ad551d1
Generic Platform UHCI controllers.
Required properties:
- compatible: Should be "platform-uhci".
- reg: Address range of the uhci registers
- interrupts: Should contain the uhci interrupt.
usb: uhci@D8007301 {
compatible = "platform-uhci", "usb-uhci";
reg = <0xD8007301 0x200>;
interrupts = <0>;
};
...@@ -33,6 +33,7 @@ static struct platform_device *devices[] __initdata = { ...@@ -33,6 +33,7 @@ static struct platform_device *devices[] __initdata = {
&vt8500_device_uart0, &vt8500_device_uart0,
&vt8500_device_lcdc, &vt8500_device_lcdc,
&vt8500_device_ehci, &vt8500_device_ehci,
&vt8500_device_uhci,
&vt8500_device_ge_rops, &vt8500_device_ge_rops,
&vt8500_device_pwm, &vt8500_device_pwm,
&vt8500_device_pwmbl, &vt8500_device_pwmbl,
......
...@@ -48,6 +48,11 @@ void __init vt8500_set_resources(void) ...@@ -48,6 +48,11 @@ void __init vt8500_set_resources(void)
tmp[1] = wmt_irq_res(IRQ_EHCI); tmp[1] = wmt_irq_res(IRQ_EHCI);
wmt_res_add(&vt8500_device_ehci, tmp, 2); wmt_res_add(&vt8500_device_ehci, tmp, 2);
/* vt8500 uses a single IRQ for both EHCI and UHCI controllers */
tmp[0] = wmt_mmio_res(VT8500_UHCI_BASE, SZ_512);
tmp[1] = wmt_irq_res(IRQ_EHCI);
wmt_res_add(&vt8500_device_uhci, tmp, 2);
tmp[0] = wmt_mmio_res(VT8500_GEGEA_BASE, SZ_256); tmp[0] = wmt_mmio_res(VT8500_GEGEA_BASE, SZ_256);
wmt_res_add(&vt8500_device_ge_rops, tmp, 1); wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
......
...@@ -55,6 +55,10 @@ void __init wm8505_set_resources(void) ...@@ -55,6 +55,10 @@ void __init wm8505_set_resources(void)
tmp[1] = wmt_irq_res(IRQ_EHCI); tmp[1] = wmt_irq_res(IRQ_EHCI);
wmt_res_add(&vt8500_device_ehci, tmp, 2); wmt_res_add(&vt8500_device_ehci, tmp, 2);
tmp[0] = wmt_mmio_res(WM8505_UHCI_BASE, SZ_512);
tmp[1] = wmt_irq_res(IRQ_UHCI);
wmt_res_add(&vt8500_device_uhci, tmp, 2);
tmp[0] = wmt_mmio_res(WM8505_GEGEA_BASE, SZ_256); tmp[0] = wmt_mmio_res(WM8505_GEGEA_BASE, SZ_256);
wmt_res_add(&vt8500_device_ge_rops, tmp, 1); wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
......
...@@ -204,6 +204,17 @@ struct platform_device vt8500_device_ehci = { ...@@ -204,6 +204,17 @@ struct platform_device vt8500_device_ehci = {
}, },
}; };
static u64 uhci_dma_mask = DMA_BIT_MASK(32);
struct platform_device vt8500_device_uhci = {
.name = "platform-uhci",
.id = 0,
.dev = {
.dma_mask = &uhci_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
};
struct platform_device vt8500_device_ge_rops = { struct platform_device vt8500_device_ge_rops = {
.name = "wmt_ge_rops", .name = "wmt_ge_rops",
.id = -1, .id = -1,
......
...@@ -81,6 +81,7 @@ extern struct platform_device vt8500_device_uart5; ...@@ -81,6 +81,7 @@ extern struct platform_device vt8500_device_uart5;
extern struct platform_device vt8500_device_lcdc; extern struct platform_device vt8500_device_lcdc;
extern struct platform_device vt8500_device_wm8505_fb; extern struct platform_device vt8500_device_wm8505_fb;
extern struct platform_device vt8500_device_ehci; extern struct platform_device vt8500_device_ehci;
extern struct platform_device vt8500_device_uhci;
extern struct platform_device vt8500_device_ge_rops; extern struct platform_device vt8500_device_ge_rops;
extern struct platform_device vt8500_device_pwm; extern struct platform_device vt8500_device_pwm;
extern struct platform_device vt8500_device_pwmbl; extern struct platform_device vt8500_device_pwmbl;
......
...@@ -32,6 +32,7 @@ static void __iomem *pmc_hiber; ...@@ -32,6 +32,7 @@ static void __iomem *pmc_hiber;
static struct platform_device *devices[] __initdata = { static struct platform_device *devices[] __initdata = {
&vt8500_device_uart0, &vt8500_device_uart0,
&vt8500_device_ehci, &vt8500_device_ehci,
&vt8500_device_uhci,
&vt8500_device_wm8505_fb, &vt8500_device_wm8505_fb,
&vt8500_device_ge_rops, &vt8500_device_ge_rops,
&vt8500_device_pwm, &vt8500_device_pwm,
......
...@@ -450,7 +450,7 @@ config USB_OHCI_LITTLE_ENDIAN ...@@ -450,7 +450,7 @@ config USB_OHCI_LITTLE_ENDIAN
config USB_UHCI_HCD config USB_UHCI_HCD
tristate "UHCI HCD (most Intel and VIA) support" tristate "UHCI HCD (most Intel and VIA) support"
depends on USB && (PCI || SPARC_LEON) depends on USB && (PCI || SPARC_LEON || ARCH_VT8500)
---help--- ---help---
The Universal Host Controller Interface is a standard by Intel for The Universal Host Controller Interface is a standard by Intel for
accessing the USB hardware in the PC (which is also called the USB accessing the USB hardware in the PC (which is also called the USB
...@@ -468,7 +468,15 @@ config USB_UHCI_HCD ...@@ -468,7 +468,15 @@ config USB_UHCI_HCD
config USB_UHCI_SUPPORT_NON_PCI_HC config USB_UHCI_SUPPORT_NON_PCI_HC
bool bool
depends on USB_UHCI_HCD depends on USB_UHCI_HCD
default y if SPARC_LEON default y if (SPARC_LEON || ARCH_VT8500)
config USB_UHCI_PLATFORM
bool "Generic UHCI Platform Driver support"
depends on USB_UHCI_SUPPORT_NON_PCI_HC
default y if ARCH_VT8500
---help---
Enable support for generic UHCI platform devices that require no
additional configuration.
config USB_UHCI_BIG_ENDIAN_MMIO config USB_UHCI_BIG_ENDIAN_MMIO
bool bool
......
...@@ -846,6 +846,11 @@ static const char hcd_name[] = "uhci_hcd"; ...@@ -846,6 +846,11 @@ static const char hcd_name[] = "uhci_hcd";
#define PLATFORM_DRIVER uhci_grlib_driver #define PLATFORM_DRIVER uhci_grlib_driver
#endif #endif
#ifdef CONFIG_USB_UHCI_PLATFORM
#include "uhci-platform.c"
#define PLATFORM_DRIVER uhci_platform_driver
#endif
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER)
#error "missing bus glue for uhci-hcd" #error "missing bus glue for uhci-hcd"
#endif #endif
......
/*
* Generic UHCI HCD (Host Controller Driver) for Platform Devices
*
* Copyright (c) 2011 Tony Prisk <linux@prisktech.co.nz>
*
* This file is based on uhci-grlib.c
* (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
*/
#include <linux/of.h>
#include <linux/platform_device.h>
static int uhci_platform_init(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
uhci->rh_numports = uhci_count_ports(hcd);
/* Set up pointers to to generic functions */
uhci->reset_hc = uhci_generic_reset_hc;
uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc;
/* No special actions need to be taken for the functions below */
uhci->configure_hc = NULL;
uhci->resume_detect_interrupts_are_broken = NULL;
uhci->global_suspend_mode_is_broken = NULL;
/* Reset if the controller isn't already safely quiescent. */
check_and_reset_hc(uhci);
return 0;
}
static const struct hc_driver uhci_platform_hc_driver = {
.description = hcd_name,
.product_desc = "Generic UHCI Host Controller",
.hcd_priv_size = sizeof(struct uhci_hcd),
/* Generic hardware linkage */
.irq = uhci_irq,
.flags = HCD_MEMORY | HCD_USB11,
/* Basic lifecycle operations */
.reset = uhci_platform_init,
.start = uhci_start,
#ifdef CONFIG_PM
.pci_suspend = NULL,
.pci_resume = NULL,
.bus_suspend = uhci_rh_suspend,
.bus_resume = uhci_rh_resume,
#endif
.stop = uhci_stop,
.urb_enqueue = uhci_urb_enqueue,
.urb_dequeue = uhci_urb_dequeue,
.endpoint_disable = uhci_hcd_endpoint_disable,
.get_frame_number = uhci_hcd_get_frame_number,
.hub_status_data = uhci_hub_status_data,
.hub_control = uhci_hub_control,
};
static int __devinit uhci_hcd_platform_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
struct uhci_hcd *uhci;
struct resource *res;
int ret;
if (usb_disabled())
return -ENODEV;
hcd = usb_create_hcd(&uhci_platform_hc_driver, &pdev->dev,
pdev->name);
if (!hcd)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_err("%s: request_mem_region failed\n", __func__);
ret = -EBUSY;
goto err_rmr;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_err("%s: ioremap failed\n", __func__);
ret = -ENOMEM;
goto err_irq;
}
uhci = hcd_to_uhci(hcd);
uhci->regs = hcd->regs;
ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED |
IRQF_SHARED);
if (ret)
goto err_uhci;
return 0;
err_uhci:
iounmap(hcd->regs);
err_irq:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_rmr:
usb_put_hcd(hcd);
return ret;
}
static int uhci_hcd_platform_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);
platform_set_drvdata(pdev, NULL);
return 0;
}
/* Make sure the controller is quiescent and that we're not using it
* any more. This is mainly for the benefit of programs which, like kexec,
* expect the hardware to be idle: not doing DMA or generating IRQs.
*
* This routine may be called in a damaged or failing kernel. Hence we
* do not acquire the spinlock before shutting down the controller.
*/
static void uhci_hcd_platform_shutdown(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
uhci_hc_died(hcd_to_uhci(hcd));
}
static const struct of_device_id platform_uhci_ids[] = {
{ .compatible = "platform-uhci", },
{}
};
static struct platform_driver uhci_platform_driver = {
.probe = uhci_hcd_platform_probe,
.remove = uhci_hcd_platform_remove,
.shutdown = uhci_hcd_platform_shutdown,
.driver = {
.name = "platform-uhci",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(platform_uhci_ids),
},
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment