From 207b4c82021526c0a5dbc4d5cf28f62bf331589b Mon Sep 17 00:00:00 2001 From: haru Date: Sun, 3 Jul 2022 15:06:09 +0900 Subject: [PATCH] usb, clock controller --- .gitignore | 1 + arch/arm/boot/dts/celestial-cnc1800l.dts | 18 ++ arch/arm/mach-celestial/cnc1800l_registers.h | 7 + drivers/clk/Makefile | 1 + drivers/clk/clk-cnc1800l.c | 134 ++++++++++++ drivers/usb/host/Kconfig | 16 ++ drivers/usb/host/Makefile | 2 + drivers/usb/host/ehci-cnc1800l.c | 219 +++++++++++++++++++ drivers/usb/host/ohci-cnc1800l.c | 175 +++++++++++++++ 9 files changed, 573 insertions(+) create mode 100644 drivers/clk/clk-cnc1800l.c create mode 100644 drivers/usb/host/ehci-cnc1800l.c create mode 100644 drivers/usb/host/ohci-cnc1800l.c diff --git a/.gitignore b/.gitignore index 7afd412da..2c78992e7 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ *.zst Module.symvers modules.order +uImage # # Top-level generic files diff --git a/arch/arm/boot/dts/celestial-cnc1800l.dts b/arch/arm/boot/dts/celestial-cnc1800l.dts index 98318744e..903a7ddb5 100644 --- a/arch/arm/boot/dts/celestial-cnc1800l.dts +++ b/arch/arm/boot/dts/celestial-cnc1800l.dts @@ -80,4 +80,22 @@ interrupts = <12>; reg-shift = <2>; }; + + clock-controller@b2100000 { + compatible = "cavium,cnc1800l-clockctrl"; + reg = <0xb2100000 0x1000>; + clocks = <&pclk>; + }; + + usb0: ohci@80210000 { + compatible = "cavium,cnc1800l-ohci", "usb-ohci"; + reg = <0x80210000 0x1000>; + interrupts = <27>; + }; + + usb1: ehci@80210000 { + compatible = "cavium,cnc1800l-ehci", "usb-ehci"; + reg = <0x80200000 0x1000>; + interrupts = <22>; + }; }; \ No newline at end of file diff --git a/arch/arm/mach-celestial/cnc1800l_registers.h b/arch/arm/mach-celestial/cnc1800l_registers.h index 50d3e6127..b6acee516 100644 --- a/arch/arm/mach-celestial/cnc1800l_registers.h +++ b/arch/arm/mach-celestial/cnc1800l_registers.h @@ -25,4 +25,11 @@ #define PA_TIMER1_BASE (PA_IO_REGS_BASE + 0x180000) #define PA_APB_TIMER_BASE (PA_IO_REGS_BASE + 0xe2000) #define PA_VIC_BASE (PA_IO_REGS_BASE + 0x40000) +#define PA_I2C_BASE (PA_IO_REGS_BASE + 0x70000) +#define PA_I2C2_BASE (PA_IO_REGS_BASE + 0x74000) +#define PA_FPC_BASE (PA_IO_REGS_BASE + 0x72000) +#define PA_GPIO_BASE (PA_IO_REGS_BASE + 0x160000) +#define PA_SCI_BASE (PA_IO_REGS_BASE + 0xf0000) +#define PA_USB_EHCI_BASE (PA_IO_REGS_BASE + 0x100000) +#define PA_USB_OHCI_BASE (PA_IO_REGS_BASE + 0x110000) #endif \ No newline at end of file diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index e42312121..2a1baa477 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -18,6 +18,7 @@ endif # hardware specific clock types # please keep this section sorted lexicographically by file path name obj-$(CONFIG_MACH_ASM9260) += clk-asm9260.o +obj-$(CONFIG_MACH_CELESTIAL) += clk-cnc1800l.o obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o obj-$(CONFIG_COMMON_CLK_BD718XX) += clk-bd718x7.o diff --git a/drivers/clk/clk-cnc1800l.c b/drivers/clk/clk-cnc1800l.c new file mode 100644 index 000000000..d2436bed4 --- /dev/null +++ b/drivers/clk/clk-cnc1800l.c @@ -0,0 +1,134 @@ +/**************************************************************************** + * Copyright (C) 2008-2010 Celestial Semiconductor Inc. + * All rights reserved + * + * [RELEASE HISTORY] + * VERSION DATE AUTHOR DESCRIPTION + * 0.1 10-03-10 Jia Ma Original + **************************************************************************** +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +// #define CLOCK_ID_BASE (CLOCK_REG_BASE+(0<<2)) +// #define CLOCK_PLL_BASE (CLOCK_REG_BASE+(0x40<<2)) +// #define CLOCK_MUX_BASE (CLOCK_REG_BASE+(0x50<<2)) +// #define CLOCK_RESET_BASE (CLOCK_REG_BASE+(0x80<<2)) + +// #define CLOCK_ID_LO (CLOCK_ID_BASE+(0<<2)) +// #define CLOCK_ID_HI (CLOCK_ID_BASE+(1<<2)) + +// #define CLOCK_PLL_DDR_DIV (CLOCK_PLL_BASE+(0<<2)) +// #define CLOCK_PLL_AVS_DIV (CLOCK_PLL_BASE+(1<<2)) +// #define CLOCK_PLL_TVE_DIV (CLOCK_PLL_BASE+(2<<2)) +// #define CLOCK_AUD_HPD (CLOCK_PLL_BASE+(3<<2)) +// #define CLOCK_AUD_FREQ (CLOCK_PLL_BASE+(6<<2)) +// #define CLOCK_AUD_JITTER (CLOCK_PLL_BASE+(7<<2)) +// #define CLOCK_CLK_GEN_DIV (CLOCK_PLL_BASE+(8<<2)) + +// #define CLOCK_PLL_CLK_EN (CLOCK_MUX_BASE+(0<<2)) +// #define CLOCK_TVE_SEL (CLOCK_MUX_BASE+(1<<2)) +// #define CLOCK_TVE_DIV_N (CLOCK_MUX_BASE+(2<<2)) + +// #define CLOCK_SOFT_RESET (CLOCK_RESET_BASE+(0<<2)) +// #define CLOCK_CLK_RESET (CLOCK_RESET_BASE+(1<<2)) + +// #define CLOCK_PLL_SLEEP_N (CLOCK_RESET_BASE+(3<<2)) +// #define CLOCK_DAC_POWER_DOWN (CLOCK_RESET_BASE+(4<<2)) +// #define CLOCK_XPORT_CLK_SEL (CLOCK_RESET_BASE+(5<<2)) +// #define CLOCK_USB_ULPI_BYPASS (CLOCK_RESET_BASE+(6<<2)) +// #define CLOCK_AUD_POWER_ON (CLOCK_RESET_BASE+(7<<2)) +// #define CLOCK_LOW_POWER_CTL (CLOCK_PLL_BASE+(0x10<<2)) + +#define CLOCK_ID_BASE (0<<2) +#define CLOCK_PLL_BASE (0x40<<2) +#define CLOCK_MUX_BASE (0x50<<2) +#define CLOCK_RESET_BASE (0x80<<2) + +#define CLOCK_ID_LO (CLOCK_ID_BASE+(0<<2)) +#define CLOCK_ID_HI (CLOCK_ID_BASE+(1<<2)) + +#define CLOCK_PLL_DDR_DIV (CLOCK_PLL_BASE+(0<<2)) +#define CLOCK_PLL_AVS_DIV (CLOCK_PLL_BASE+(1<<2)) +#define CLOCK_PLL_TVE_DIV (CLOCK_PLL_BASE+(2<<2)) +#define CLOCK_AUD_HPD (CLOCK_PLL_BASE+(3<<2)) +#define CLOCK_AUD_FREQ (CLOCK_PLL_BASE+(6<<2)) +#define CLOCK_AUD_JITTER (CLOCK_PLL_BASE+(7<<2)) +#define CLOCK_CLK_GEN_DIV (CLOCK_PLL_BASE+(8<<2)) + +#define CLOCK_PLL_CLK_EN (CLOCK_MUX_BASE+(0<<2)) +#define CLOCK_TVE_SEL (CLOCK_MUX_BASE+(1<<2)) +#define CLOCK_TVE_DIV_N (CLOCK_MUX_BASE+(2<<2)) + +#define CLOCK_SOFT_RESET (CLOCK_RESET_BASE+(0<<2)) +#define CLOCK_CLK_RESET (CLOCK_RESET_BASE+(1<<2)) + +#define CLOCK_PLL_SLEEP_N (CLOCK_RESET_BASE+(3<<2)) +#define CLOCK_DAC_POWER_DOWN (CLOCK_RESET_BASE+(4<<2)) +#define CLOCK_XPORT_CLK_SEL (CLOCK_RESET_BASE+(5<<2)) +#define CLOCK_USB_ULPI_BYPASS (CLOCK_RESET_BASE+(6<<2)) +#define CLOCK_AUD_POWER_ON (CLOCK_RESET_BASE+(7<<2)) +#define CLOCK_LOW_POWER_CTL (CLOCK_PLL_BASE+(0x10<<2)) + +static void __iomem *cnc1800l_clockctrl_base; + +#define cnc_clk_read(a) readl(cnc1800l_clockctrl_base+((a)&0xfff)) +#define cnc_clk_write(a,v) writel(v,cnc1800l_clockctrl_base+((a)&0xfff)) + + +//mode 0:reset +// 1:set +void cnc1800l_clock_usb_reset(int mode){ + unsigned int val; + if(mode == 0){ + val = cnc_clk_read(CLOCK_SOFT_RESET); + cnc_clk_write(CLOCK_SOFT_RESET, val&(~(0x1<<4))); + }else{ + val = cnc_clk_read(CLOCK_SOFT_RESET); + cnc_clk_write(CLOCK_SOFT_RESET, val|(0x1<<4)); + } +} + +void cnc1800l_clock_usb1phy_bypassenable(void) +{ + unsigned int val; + val = cnc_clk_read(CLOCK_USB_ULPI_BYPASS); + cnc_clk_write(CLOCK_USB_ULPI_BYPASS, val|0x3); +} + +void cnc1800l_clock_usb1phy_bypassdisable(void) +{ + unsigned int reg_val; + reg_val = cnc_clk_read(CLOCK_USB_ULPI_BYPASS); + cnc_clk_write(CLOCK_USB_ULPI_BYPASS, reg_val|0x1); +} + +static void __init cnc1800l_clock_init(struct device_node *np) +{ + cnc1800l_clockctrl_base = of_io_request_and_map(np, 0, np->name); + if (IS_ERR(cnc1800l_clockctrl_base)){ + panic("%pOFn: unable to map resource", np); + goto fail; + } + + printk("cnc1800l clockctrl init"); + + return; +fail: + iounmap(cnc1800l_clockctrl_base); +} + +EXPORT_SYMBOL(cnc1800l_clock_usb_reset); +EXPORT_SYMBOL(cnc1800l_clock_usb1phy_bypassenable); +EXPORT_SYMBOL(cnc1800l_clock_usb1phy_bypassdisable); + + +CLK_OF_DECLARE(cnc1800l_clock, "cavium,cnc1800l-clockctrl", + cnc1800l_clock_init); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index c4736d1d0..de7dcdadf 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -257,6 +257,14 @@ config USB_EHCI_HCD_AT91 Enables support for the on-chip EHCI controller on Atmel chips. +config USB_EHCI_HCD_CNC1800L + tristate "Support for Celestial on-chip EHCI USB controller" + depends on USB_EHCI_HCD && (MACH_CELESTIAL || COMPILE_TEST) + default y if MACH_CELESTIAL + help + Enables support for the on-chip EHCI controller on + Celestial chips. + config USB_EHCI_TEGRA tristate "NVIDIA Tegra HCD support" depends on ARCH_TEGRA @@ -477,6 +485,14 @@ config USB_OHCI_HCD_AT91 Enables support for the on-chip OHCI controller on Atmel chips. +config USB_OHCI_HCD_CNC1800L + tristate "Support for Celestial on-chip OHCI USB controller" + depends on USB_OHCI_HCD && (MACH_CELESTIAL || COMPILE_TEST) && OF + default y if MACH_CELESTIAL + help + Enables support for the on-chip OHCI controller on + Celestial chips. + config USB_OHCI_HCD_OMAP3 tristate "OHCI support for OMAP3 and later chips" depends on ARCH_OMAP3 || ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 171de4df5..d13d20e35 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o obj-$(CONFIG_USB_EHCI_HCD_STI) += ehci-st.o obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o +obj-$(CONFIG_USB_EHCI_HCD_CNC1800L) += ehci-cnc1800l.o obj-$(CONFIG_USB_EHCI_BRCMSTB) += ehci-brcm.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o @@ -59,6 +60,7 @@ obj-$(CONFIG_USB_OHCI_HCD_OMAP1) += ohci-omap.o obj-$(CONFIG_USB_OHCI_HCD_SPEAR) += ohci-spear.o obj-$(CONFIG_USB_OHCI_HCD_STI) += ohci-st.o obj-$(CONFIG_USB_OHCI_HCD_AT91) += ohci-at91.o +obj-$(CONFIG_USB_OHCI_HCD_CNC1800L) += ohci-cnc1800l.o obj-$(CONFIG_USB_OHCI_HCD_S3C2410) += ohci-s3c2410.o obj-$(CONFIG_USB_OHCI_HCD_LPC32XX) += ohci-nxp.o obj-$(CONFIG_USB_OHCI_HCD_PXA27X) += ohci-pxa27x.o diff --git a/drivers/usb/host/ehci-cnc1800l.c b/drivers/usb/host/ehci-cnc1800l.c new file mode 100644 index 000000000..fc4fbcae4 --- /dev/null +++ b/drivers/usb/host/ehci-cnc1800l.c @@ -0,0 +1,219 @@ +/* +* linux/driver/usb/host/ehci-csm18xx.c +* +* Copyright (c) 2010 celestialsemi corporation. +* +* Ran Hao +* +* 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 +* +*/ +/* +* ehci-omap.c - driver for USBHOST on OMAP3/4 processors +* +* Bus Glue for the EHCI controllers in OMAP3/4 +* Tested on several OMAP3 boards, and OMAP4 Pandaboard +* +* Copyright (C) 2007-2013 Texas Instruments, Inc. +* Author: Vikram Pandita +* Author: Anand Gadiyar +* Author: Keshava Munegowda +* Author: Roger Quadros +* +* Copyright (C) 2009 Nokia Corporation +* Contact: Felipe Balbi +* +* Based on "ehci-fsl.c" and "ehci-au1xxx.c" ehci glue layers +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" + +#define DRIVER_DESC "EHCI CNC1800L driver" + +extern void cnc1800l_clock_usb_reset(int mode); +extern void cnc1800l_clock_usb1phy_bypassenable(void); +extern void cnc1800l_clock_usb1phy_bypassdisable(void); + +static const char hcd_name[] = "ehci-cnc1800l"; + +static struct hc_driver __read_mostly ehci_cnc1800l_hc_driver; + +static int usb_hcd_cnc18xx_make_hc(struct usb_hcd *hcd) +{ + u32 *reg, val; + + reg = (u32 *)((char *)(hcd->regs) + 0x10); + val = readl(reg); + + if ((val & 3) != 3 && (val & 3) != 0) { + printk("usb_hcd_cnc18xx_make_hc(): unsane hardware state\n"); + return -EIO; + } + + val |= 3; + *reg = val; + + return 0; +} + +static void cnc18xx_start_ehc(struct platform_device *dev) +{ + /* Reset USB core */ + cnc1800l_clock_usb_reset(0); + udelay(100); + cnc1800l_clock_usb_reset(1); + cnc1800l_clock_usb1phy_bypassenable(); +} + +static void cnc18xx_stop_ehc(struct platform_device *dev) +{ + cnc1800l_clock_usb1phy_bypassdisable(); +} + + +static int ehci_hcd_cnc1800l_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct usb_hcd *hcd; + int ret; + int irq; + struct ehci_hcd *ehci; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + ret = -ENODEV; + hcd = usb_create_hcd(&ehci_cnc1800l_hc_driver, dev, + dev_name(dev)); + if (!hcd) { + dev_err(dev, "Failed to create HCD\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hcd->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(hcd->regs)) + return PTR_ERR(hcd->regs); + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + platform_set_drvdata(pdev, hcd); + + cnc18xx_start_ehc(pdev); + + ret = usb_hcd_cnc18xx_make_hc(hcd); + if(ret) + return ret; + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); + + ehci->hcs_params = readl(&ehci->caps->hcs_params); + + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (ret) { + dev_err(dev, "failed to add hcd with err %d\n", ret); + goto err; + } + device_wakeup_enable(hcd->self.controller); + + return 0; + +err: + cnc18xx_stop_ehc(pdev); + usb_put_hcd(hcd); + return ret; +} + +static int ehci_hcd_cnc1800l_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_hcd *hcd = dev_get_drvdata(dev); + + usb_remove_hcd(hcd); + cnc18xx_stop_ehc(pdev); + + usb_put_hcd(hcd); + return 0; +} + +void +usb_hcd_cnc18xx_shutdown(struct platform_device* dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + + +static const struct of_device_id cnc1800l_ehci_dt_ids[] = { + { .compatible = "cavium,cnc1800l-ehci" }, + { } +}; + +static struct platform_driver ehci_hcd_cnc1800l_driver = { + .probe = ehci_hcd_cnc1800l_probe, + .remove = ehci_hcd_cnc1800l_remove, + .shutdown = usb_hcd_cnc18xx_shutdown, + .driver = { + .name = hcd_name, + .of_match_table = cnc1800l_ehci_dt_ids, + } +}; + +MODULE_DEVICE_TABLE(of, cnc1800l_ehci_dt_ids); + +static int __init ehci_cnc1800l_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ehci_init_driver(&ehci_cnc1800l_hc_driver, NULL); + return platform_driver_register(&ehci_hcd_cnc1800l_driver); +} +module_init(ehci_cnc1800l_init); + +static void __exit ehci_cnc1800l_cleanup(void) +{ + platform_driver_unregister(&ehci_hcd_cnc1800l_driver); +} +module_exit(ehci_cnc1800l_cleanup); + + +MODULE_ALIAS("platform:cnc1800l-ehci"); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("post: clk-cnc1800l"); \ No newline at end of file diff --git a/drivers/usb/host/ohci-cnc1800l.c b/drivers/usb/host/ohci-cnc1800l.c new file mode 100644 index 000000000..7d67ed096 --- /dev/null +++ b/drivers/usb/host/ohci-cnc1800l.c @@ -0,0 +1,175 @@ +/* +* OHCI HCD (Host Controller Driver) for USB. +* +* (C) Copyright 2010 celestialsemi Company +* +* Bus Glue for CNC1800H +* +* Written by Ran Hao +* +* +* This file is licenced under the GPL. +*/ + +/* +* OHCI HCD (Host Controller Driver) for USB. +* +* Copyright (C) 2004 SAN People (Pty) Ltd. +* Copyright (C) 2005 Thibaut VARENE +* +* AT91 Bus Glue +* +* Based on fragments of 2.4 driver by Rick Bronson. +* Based on ohci-omap.c +* +* This file is licenced under the GPL. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ohci.h" + +#define DRIVER_DESC "OHCI CNC1800L driver" + +static const char hcd_name[] = "ohci-cnc1800l"; + +static struct hc_driver __read_mostly ohci_cnc1800l_hc_driver; + +#define hcd_to_ohci_cnc1800l_priv(h) \ + ((struct ohci_cnc1800l_priv *)hcd_to_ohci(h)->priv) + + +struct ohci_cnc1800l_priv { + struct clk *iclk; + bool clocked; + bool wakeup; /* Saved wake-up state for resume */ + struct regmap *sfr_regmap; +}; + +static void cnc1800l_start_hc(struct device *dev) +{ + /* + * Set register pin to enable and 48MHz + */ + + uint8_t* clkreg = ioremap(0xB2100218,1); + + // (*((volatile unsigned long *)(IO_ADDRESS(0xB2100218)))) =0x3; + if(clkreg != NULL) + *clkreg = 0x3; + else + dev_dbg(dev, "ohci: failed to enable 48m clock\n"); + + iounmap(clkreg); + udelay(100); +} + +static int ohci_hcd_cnc1800l_drv_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int ret, irq; + struct usb_hcd *hcd; + struct device *dev = &pdev->dev; + struct ohci_cnc1800l_priv *ohci_cnc; + struct resource *res; + + + /* Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + irq = irq_of_parse_and_map(np, 0); + if (irq < 0) { + dev_dbg(dev, "hcd probe: missing irq resource\n"); + return irq; + } + + hcd = usb_create_hcd(&ohci_cnc1800l_hc_driver, dev, dev_name(dev)); + if (!hcd) + return -ENOMEM; + ohci_cnc = hcd_to_ohci_cnc1800l_priv(hcd); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hcd->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); + goto err; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + cnc1800l_start_hc(dev); + + ret = usb_add_hcd(hcd,irq, IRQF_SHARED); + if(ret){ + dev_err(&pdev->dev, "Failed to add USB HCD\n"); + goto err; + } + + device_wakeup_enable(hcd->self.controller); + return 0; +err: + usb_put_hcd(hcd); + return ret; +} + +static int ohci_hcd_cnc1800l_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + return 0; +} + +static const struct of_device_id ohci_hcd_cnc1800l_dt_ids[] = { + { .compatible = "cavium,cnc1800l-ohci" }, + { } +}; + +MODULE_DEVICE_TABLE(of, ohci_hcd_cnc1800l_dt_ids); + +static struct platform_driver ohci_hcd_cnc1800l_driver = { + .probe = ohci_hcd_cnc1800l_drv_probe, + .remove = ohci_hcd_cnc1800l_drv_remove, + .driver = { + .name = "cnc1800l-ohci", + .of_match_table = ohci_hcd_cnc1800l_dt_ids, + }, +}; + +static int __init ohci_cnc1800l_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + ohci_init_driver(&ohci_cnc1800l_hc_driver, NULL); + + return platform_driver_register(&ohci_hcd_cnc1800l_driver); +} +module_init(ohci_cnc1800l_init); + +static void __exit ohci_cnc1800l_cleanup(void) +{ + platform_driver_unregister(&ohci_hcd_cnc1800l_driver); +} +module_exit(ohci_cnc1800l_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cnc1800l-ohci");