Hi, On Sat, Oct 9, 2010 at 3:17 AM, David Daney <ddaney@xxxxxxxxxxxxxxxxxx> wrote: > The OCTEON II SOC has USB EHCI and OHCI controllers connected directly > to the internal I/O bus. This patch adds the necessary 'glue' logic > to allow ehci-hcd and ohci-hcd drivers to work on OCTEON II. > > The OCTEON normally runs big-endian, and the ehci/ohci internal > registers have host endianness, so we need to select > USB_EHCI_BIG_ENDIAN_MMIO. > > The ehci and ohci blocks share a common clocking and PHY > infrastructure. Initialization of the host controller and PHY clocks > is common between the two and is factored out into the > octeon2-common.c file. > > Setting of USB_ARCH_HAS_OHCI and USB_ARCH_HAS_EHCI is done in > arch/mips/Kconfig in a following patch. > > Signed-off-by: David Daney <ddaney@xxxxxxxxxxxxxxxxxx> > --- > drivers/usb/host/Kconfig | 27 +++++- > drivers/usb/host/Makefile | 1 + > drivers/usb/host/ehci-hcd.c | 5 + > drivers/usb/host/ehci-octeon.c | 207 +++++++++++++++++++++++++++++++++++ > drivers/usb/host/octeon2-common.c | 185 ++++++++++++++++++++++++++++++++ > drivers/usb/host/ohci-hcd.c | 5 + > drivers/usb/host/ohci-octeon.c | 214 +++++++++++++++++++++++++++++++++++++ > 7 files changed, 643 insertions(+), 1 deletions(-) > create mode 100644 drivers/usb/host/ehci-octeon.c > create mode 100644 drivers/usb/host/octeon2-common.c > create mode 100644 drivers/usb/host/ohci-octeon.c > It would be good to have EHCI and OHCI support in separate patches. > diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig > index 2d926ce..a03d688 100644 > --- a/drivers/usb/host/Kconfig > +++ b/drivers/usb/host/Kconfig > @@ -93,7 +93,7 @@ config USB_EHCI_TT_NEWSCHED > > config USB_EHCI_BIG_ENDIAN_MMIO > bool > - depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX) > + depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || CPU_CAVIUM_OCTEON) > default y > > config USB_EHCI_BIG_ENDIAN_DESC > @@ -428,3 +428,28 @@ config USB_IMX21_HCD > To compile this driver as a module, choose M here: the > module will be called "imx21-hcd". > > +config USB_OCTEON_EHCI > + bool "Octeon on-chip EHCI support" > + depends on USB && USB_EHCI_HCD && CPU_CAVIUM_OCTEON > + default n > + select USB_EHCI_BIG_ENDIAN_MMIO > + help > + Enable support for the Octeon II SOC's on-chip EHCI > + controller. It is needed for high-speed (480Mbit/sec) > + USB 2.0 device support. All CN6XXX based chips with USB are > + supported. > + > +config USB_OCTEON_OHCI > + bool "Octeon on-chip OHCI support" > + depends on USB && USB_OHCI_HCD && CPU_CAVIUM_OCTEON > + default USB_OCTEON_EHCI > + select USB_OHCI_BIG_ENDIAN_MMIO > + select USB_OHCI_LITTLE_ENDIAN > + help > + Enable support for the Octeon II SOC's on-chip OHCI > + controller. It is needed for low-speed USB 1.0 device > + support. All CN6XXX based chips with USB are supported. > + > +config USB_OCTEON2_COMMON > + bool > + default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI > diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile > index b6315aa..36099ce 100644 > --- a/drivers/usb/host/Makefile > +++ b/drivers/usb/host/Makefile > @@ -33,4 +33,5 @@ obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o > obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o > obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o > obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o > +obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o > > diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c > index 34a928d..158a520 100644 > --- a/drivers/usb/host/ehci-hcd.c > +++ b/drivers/usb/host/ehci-hcd.c > @@ -1197,6 +1197,11 @@ MODULE_LICENSE ("GPL"); > #define PLATFORM_DRIVER ehci_atmel_driver > #endif > > +#ifdef CONFIG_USB_OCTEON_EHCI > +#include "ehci-octeon.c" > +#define PLATFORM_DRIVER ehci_octeon_driver > +#endif > + > #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ > !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ > !defined(XILINX_OF_PLATFORM_DRIVER) > diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c > new file mode 100644 > index 0000000..a31a031 > --- /dev/null > +++ b/drivers/usb/host/ehci-octeon.c > @@ -0,0 +1,207 @@ > +/* > + * EHCI HCD glue for Cavium Octeon II SOCs. > + * > + * Loosely based on ehci-au1xxx.c > + * > + * This file is subject to the terms and conditions of the GNU General Public > + * License. See the file "COPYING" in the main directory of this archive > + * for more details. > + * > + * Copyright (C) 2010 Cavium Networks > + * > + */ > + > +#include <linux/platform_device.h> > + > +#include <asm/octeon/octeon.h> > +#include <asm/octeon/cvmx-uctlx-defs.h> > + > +#define OCTEON_EHCI_HCD_NAME "octeon-ehci" > + > +/* Common clock init code. */ > +void octeon2_usb_clocks_start(void); > +void octeon2_usb_clocks_stop(void); > + > +static void ehci_octeon_start(void) > +{ > + union cvmx_uctlx_ehci_ctl ehci_ctl; > + > + octeon2_usb_clocks_start(); > + > + ehci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_EHCI_CTL(0)); > + /* Use 64-bit addressing. */ > + ehci_ctl.s.ehci_64b_addr_en = 1; > + ehci_ctl.s.l2c_addr_msb = 0; > + ehci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */ > + ehci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */ > + cvmx_write_csr(CVMX_UCTLX_EHCI_CTL(0), ehci_ctl.u64); > +} > + > +static void ehci_octeon_stop(void) > +{ > + octeon2_usb_clocks_stop(); > +} > + > +static const struct hc_driver ehci_octeon_hc_driver = { > + .description = hcd_name, > + .product_desc = "Octeon EHCI", > + .hcd_priv_size = sizeof(struct ehci_hcd), > + > + /* > + * generic hardware linkage > + */ > + .irq = ehci_irq, > + .flags = HCD_MEMORY | HCD_USB2, > + > + /* > + * basic lifecycle operations > + */ > + .reset = ehci_init, > + .start = ehci_run, > + .stop = ehci_stop, > + .shutdown = ehci_shutdown, > + > + /* > + * managing i/o requests and associated device resources > + */ > + .urb_enqueue = ehci_urb_enqueue, > + .urb_dequeue = ehci_urb_dequeue, > + .endpoint_disable = ehci_endpoint_disable, > + .endpoint_reset = ehci_endpoint_reset, > + > + /* > + * scheduling support > + */ > + .get_frame_number = ehci_get_frame, > + > + /* > + * root hub support > + */ > + .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 u64 ehci_octeon_dma_mask = DMA_BIT_MASK(64); > + > +static int ehci_octeon_drv_probe(struct platform_device *pdev) > +{ > + struct usb_hcd *hcd; > + struct ehci_hcd *ehci; > + struct resource *res_mem; > + int irq; > + int ret; > + > + if (usb_disabled()) > + return -ENODEV; > + > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) { > + dev_err(&pdev->dev, "No irq assigned\n"); > + return -ENODEV; > + } > + > + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (res_mem == NULL) { > + dev_err(&pdev->dev, "No register space assigned\n"); > + return -ENODEV; > + } > + > + /* > + * We can DMA from anywhere. But the descriptors must be in > + * the lower 4GB. > + */ > + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); > + pdev->dev.dma_mask = &ehci_octeon_dma_mask; > + > + hcd = usb_create_hcd(&ehci_octeon_hc_driver, &pdev->dev, "octeon"); > + if (!hcd) > + return -ENOMEM; > + > + hcd->rsrc_start = res_mem->start; > + hcd->rsrc_len = res_mem->end - res_mem->start + 1; You can use resource_size() to compute the length of the resource. > + > + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, > + OCTEON_EHCI_HCD_NAME)) { > + dev_err(&pdev->dev, "request_mem_region failed\n"); > + ret = -EBUSY; > + goto err1; > + } > + > + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); > + if (!hcd->regs) { > + dev_err(&pdev->dev, "ioremap failed\n"); > + ret = -ENOMEM; > + goto err2; > + } > + > + ehci_octeon_start(); > + > + ehci = hcd_to_ehci(hcd); > + > + /* Octeon EHCI matches CPU endianness. */ > +#ifdef __BIG_ENDIAN > + ehci->big_endian_mmio = 1; > +#endif > + > + ehci->caps = hcd->regs; > + ehci->regs = hcd->regs + > + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); > + /* cache this readonly data; minimize chip reads */ > + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); > + > + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); > + if (ret) { > + dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); > + goto err3; > + } > + > + platform_set_drvdata(pdev, hcd); > + > + /* root ports should always stay powered */ > + ehci_port_power(ehci, 1); > + > + return 0; > +err3: > + ehci_octeon_stop(); > + > + iounmap(hcd->regs); > +err2: > + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); > +err1: > + usb_put_hcd(hcd); > + return ret; > +} [...] > +static int ohci_octeon_drv_probe(struct platform_device *pdev) > +{ > + struct usb_hcd *hcd; > + struct ohci_hcd *ohci; > + void *reg_base; > + struct resource *res_mem; > + int irq; > + int ret; > + > + if (usb_disabled()) > + return -ENODEV; > + > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) { > + dev_err(&pdev->dev, "No irq assigned\n"); > + return -ENODEV; > + } > + > + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (res_mem == NULL) { > + dev_err(&pdev->dev, "No register space assigned\n"); > + return -ENODEV; > + } > + > + /* Ohci is a 32-bit device. */ > + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); > + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; > + > + hcd = usb_create_hcd(&ohci_octeon_hc_driver, &pdev->dev, "octeon"); > + if (!hcd) > + return -ENOMEM; > + > + hcd->rsrc_start = res_mem->start; > + hcd->rsrc_len = res_mem->end - res_mem->start + 1; > + Same here. > + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, > + OCTEON_OHCI_HCD_NAME)) { > + dev_err(&pdev->dev, "request_mem_region failed\n"); > + ret = -EBUSY; > + goto err1; > + } > + > + reg_base = ioremap(hcd->rsrc_start, hcd->rsrc_len); > + if (!reg_base) { > + dev_err(&pdev->dev, "ioremap failed\n"); > + ret = -ENOMEM; > + goto err2; > + } > + > + ohci_octeon_hw_start(); > + > + hcd->regs = reg_base; > + > + ohci = hcd_to_ohci(hcd); > + > + /* Octeon OHCI matches CPU endianness. */ > +#ifdef __BIG_ENDIAN > + ohci->flags |= OHCI_QUIRK_BE_MMIO; > +#endif > + > + ohci_hcd_init(ohci); > + > + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); > + if (ret) { > + dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); > + goto err3; > + } > + > + platform_set_drvdata(pdev, hcd); > + > + return 0; > + > +err3: > + ohci_octeon_hw_stop(); > + > + iounmap(hcd->regs); > +err2: > + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); > +err1: > + usb_put_hcd(hcd); > + return ret; > +} > + Regards, Maulik