diff options
author | James <> | 2015-11-04 11:49:21 +0000 |
---|---|---|
committer | James <> | 2015-11-04 11:49:21 +0000 |
commit | 716ca530e1c4515d8683c9d5be3d56b301758b66 (patch) | |
tree | 700eb5bcc1a462a5f21dcec15ce7c97ecfefa772 /target/linux/gemini/files/drivers | |
download | trunk-47381-master.tar.gz trunk-47381-master.tar.bz2 trunk-47381-master.zip |
Diffstat (limited to 'target/linux/gemini/files/drivers')
-rw-r--r-- | target/linux/gemini/files/drivers/ata/pata_gemini.c | 234 | ||||
-rw-r--r-- | target/linux/gemini/files/drivers/rtc/rtc-gemini.c | 220 | ||||
-rw-r--r-- | target/linux/gemini/files/drivers/usb/host/ehci-fotg2.c | 258 |
3 files changed, 712 insertions, 0 deletions
diff --git a/target/linux/gemini/files/drivers/ata/pata_gemini.c b/target/linux/gemini/files/drivers/ata/pata_gemini.c new file mode 100644 index 0000000..707e870 --- /dev/null +++ b/target/linux/gemini/files/drivers/ata/pata_gemini.c @@ -0,0 +1,234 @@ +/* + * Support for Gemini PATA + * + * Copyright (C) 2009 Janos Laube <janos.dev@gmail.com> + * Copyright (C) 2010 Frederic Pecourt <opengemini@free.fr> + * Copyright (C) 2011 Tobias Waldvogel <tobias.waldvogel@gmail.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Values of IOMUX + * 26:24 bits is "IDE IO Select" + * 111:100 - Reserved + * 011 - ata0 <-> sata0, sata1; bring out ata1 + * 010 - ata1 <-> sata1, sata0; bring out ata0 + * 001 - ata0 <-> sata0, ata1 <-> sata1; bring out ata1 + * 000 - ata0 <-> sata0, ata1 <-> sata1; bring out ata0 + * + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/libata.h> +#include <linux/leds.h> + +#include <mach/hardware.h> +#include <mach/global_reg.h> + +#define DRV_NAME "pata-gemini" + +#define PATA_GEMINI_PORTS 1 + +#define PIO_TIMING_REG 0x10 +#define MDMA_TIMING_REG 0x11 +#define UDMA_TIMING0_REG 0x12 +#define UDMA_TIMING1_REG 0x13 +#define CLK_MOD_REG 0x14 + +#define CLK_MOD_66M_DEV0_BIT 0 +#define CLK_MOD_66M_DEV1_BIT 1 +#define CLK_MOD_UDMA_DEV0_BIT 4 +#define CLK_MOD_UDMA_DEV1_BIT 5 + +#define CLK_MOD_66M_DEV0 (1 << CLK_MOD_66M_DEV0_BIT) +#define CLK_MOD_66M_DEV1 (1 << CLK_MOD_66M_DEV1_BIT) +#define CLK_MOD_UDMA_DEV0 (1 << CLK_MOD_UDMA_DEV0_BIT) +#define CLK_MOD_UDMA_DEV1 (1 << CLK_MOD_UDMA_DEV1_BIT) + +#define SATA_ENABLE_PDEV_MASK 0x01 +#define SATA_ENABLE_PDEV_PM 0x02 +#define SATA_ENABLE_PDEV_ADDED 0x04 +#define SATA_ENABLE_PDEV_REMOVED 0x08 +#define SATA_ENABLE_SDEV_MASK 0x10 +#define SATA_ENABLE_SDEV_PM 0x20 +#define SATA_ENABLE_SDEV_ADDED 0x40 +#define SATA_ENABLE_SDEV_REMOVED 0x80 + +MODULE_AUTHOR("Janos Laube <janos.dev@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); + +static unsigned char PIO_TIMING[5] = { + 0xaa, 0xa3, 0xa1, 0x33, 0x31 +}; + +static unsigned char TIMING_MW_DMA[4][2] = { + { 0x44, 1 }, // 480 4.2 + { 0x42, 1 }, // 150 13.3 + { 0x31, 1 }, // 120 16.7 + { 0x21, 1 }, // 100 20 +}; + +static unsigned char TIMING_UDMA[7][2] = { + { 0x33, 0 }, //240 16.7 + { 0x31, 0 }, //160 25 + { 0x21, 0 }, //120 33.3 + { 0x21, 1 }, //90 44.4 + { 0x11, 1 }, //60 66.7 + { 0x11 | 0x80, 0 }, //40 100 + { 0x11 | 0x80, 1 }, //30 133 +}; + +static struct scsi_host_template pata_gemini_sht = { + ATA_NCQ_SHT(DRV_NAME), + .can_queue = 1, + .sg_tablesize = 128, + .dma_boundary = 0xffffU, +}; + +static void gemini_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + void __iomem *clk_reg = ap->ioaddr.bmdma_addr + CLK_MOD_REG; + void __iomem *tim_reg = ap->ioaddr.bmdma_addr + UDMA_TIMING0_REG; + unsigned short udma = adev->dma_mode; + unsigned short speed = udma; + unsigned short devno = adev->devno & 1; + unsigned short i; + u8 mod_udma_mask = 1 << (CLK_MOD_UDMA_DEV0_BIT + devno); + u8 mod_66m_mask = 1 << (CLK_MOD_66M_DEV0_BIT + devno); + u8 clk_mod; + u8 timing; + + clk_mod = ioread8(clk_reg); + clk_mod &= ~mod_udma_mask; + + if (speed & XFER_UDMA_0) { + i = speed & ~XFER_UDMA_0; + timing = TIMING_UDMA[i][0]; + clk_mod |= mod_udma_mask; + if (TIMING_UDMA[i][1]) + clk_mod |= mod_66m_mask; + } else { + i = speed & ~XFER_MW_DMA_0; + timing = TIMING_MW_DMA[i][0]; + clk_mod |= mod_udma_mask; + if (TIMING_MW_DMA[i][1]) + clk_mod |= mod_66m_mask; + } + + iowrite8(clk_mod, clk_reg); + iowrite8(timing, tim_reg + devno); + return; +} + +static void gemini_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + void __iomem *pio_reg = ap->ioaddr.bmdma_addr + PIO_TIMING_REG; + unsigned int pio = adev->pio_mode - XFER_PIO_0; + + iowrite8(PIO_TIMING[pio], pio_reg); +} + +unsigned int gemini_qc_issue(struct ata_queued_cmd *qc) +{ + ledtrig_ide_activity(); + return ata_bmdma_qc_issue(qc); +} + +static struct ata_port_operations pata_gemini_port_ops = { + .inherits = &ata_bmdma_port_ops, + .set_dmamode = gemini_set_dmamode, + .set_piomode = gemini_set_piomode, + .qc_issue = gemini_qc_issue, +}; + +static struct ata_port_info pata_gemini_portinfo = { + .flags = 0, + .udma_mask = ATA_UDMA6, + .pio_mask = ATA_PIO4, + .port_ops = &pata_gemini_port_ops, +}; + +static const struct ata_port_info *pata_gemini_ports = &pata_gemini_portinfo; + +static int pata_gemini_probe(struct platform_device *pdev) +{ + struct ata_host *host; + struct resource *res; + unsigned int irq, i; + void __iomem *mmio_base; + + /* standard bdma init */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + pr_info(DRV_NAME ": irq %d, io base 0x%08x\n", irq, res->start); + + mmio_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + + host = ata_host_alloc_pinfo(&pdev->dev, &pata_gemini_ports, 1); + if (!host) + return -ENOMEM; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + struct ata_ioports *ioaddr = &ap->ioaddr; + + ioaddr->bmdma_addr = mmio_base; + ioaddr->cmd_addr = mmio_base + 0x20; + ioaddr->ctl_addr = mmio_base + 0x36; + ioaddr->altstatus_addr = ioaddr->ctl_addr; + ata_sff_std_ports(ioaddr); + host->ports[i]->cbl = ATA_CBL_SATA; + } + + return ata_host_activate(host, irq, ata_bmdma_interrupt, + IRQF_SHARED, &pata_gemini_sht); +} + +static int pata_gemini_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ata_host *host = dev_get_drvdata(dev); + ata_host_detach(host); + return 0; +} + +static struct platform_driver pata_gemini_driver = { + .probe = pata_gemini_probe, + .remove = pata_gemini_remove, + .driver.owner = THIS_MODULE, + .driver.name = DRV_NAME, +}; + +static int __init pata_gemini_module_init(void) +{ + return platform_driver_probe(&pata_gemini_driver, pata_gemini_probe); +} + +static void __exit pata_gemini_module_exit(void) +{ + platform_driver_unregister(&pata_gemini_driver); +} + +module_init(pata_gemini_module_init); +module_exit(pata_gemini_module_exit); diff --git a/target/linux/gemini/files/drivers/rtc/rtc-gemini.c b/target/linux/gemini/files/drivers/rtc/rtc-gemini.c new file mode 100644 index 0000000..587d812 --- /dev/null +++ b/target/linux/gemini/files/drivers/rtc/rtc-gemini.c @@ -0,0 +1,220 @@ +/* + * Gemini OnChip RTC + * + * Copyright (C) 2009 Janos Laube <janos.dev@gmail.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Original code for older kernel 2.6.15 are form Stormlinksemi + * first update from Janos Laube for > 2.6.29 kernels + * + * checkpatch fixes and usage off rtc-lib code + * Hans Ulli Kroll <ulli.kroll@googlemail.com> + */ + +#include <linux/rtc.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#include <mach/hardware.h> + +#define DRV_NAME "rtc-gemini" + +MODULE_DESCRIPTION("RTC driver for Gemini SoC"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>"); +MODULE_LICENSE("GPL"); + +struct gemini_rtc { + struct rtc_device *dev; + void __iomem *base; + int irq; +}; + +enum gemini_rtc_offsets { + GEMINI_RTC_SECOND = 0x00, + GEMINI_RTC_MINUTE = 0x04, + GEMINI_RTC_HOUR = 0x08, + GEMINI_RTC_DAYS = 0x0C, + GEMINI_RTC_ALARM_SECOND = 0x10, + GEMINI_RTC_ALARM_MINUTE = 0x14, + GEMINI_RTC_ALARM_HOUR = 0x18, + GEMINI_RTC_RECORD = 0x1C, + GEMINI_RTC_CR = 0x20 +}; + +static irqreturn_t gemini_rtc_interrupt(int irq, void *dev) +{ + return IRQ_HANDLED; +} + +/* + * Looks like the RTC in the Gemini SoC is (totaly) broken + * We can't read/write directly the time from RTC registers. + * We must do some "offset" calculation to get the real time + * + * The register "day" seams to be fixed, and the register "hour" + * has his own mind. + * + * Maybe we can write directly the hour and days since EPOCH + * but in this case the RTC will recalucate to some (other) strange values. + * If you write time to the registers you will not read the same values. + * + * This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does + * the same thing, without the rtc-lib.c calls. + */ + +static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct gemini_rtc *rtc = dev_get_drvdata(dev); + + unsigned int days, hour, min, sec; + unsigned long offset, time; + + sec = readl(rtc->base + GEMINI_RTC_SECOND); + min = readl(rtc->base + GEMINI_RTC_MINUTE); + hour = readl(rtc->base + GEMINI_RTC_HOUR); + days = readl(rtc->base + GEMINI_RTC_DAYS); + offset = readl(rtc->base + GEMINI_RTC_RECORD); + + time = offset + days * 86400 + hour * 3600 + min * 60 + sec; + + rtc_time_to_tm(time, tm); + return 0; +} + +/* + * Maybe there is some hidden register to care ? + * looks like register GEMINI_RTC_DAY can count + * 365 days * 179 years >= 65535 (uint16) + */ + +static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct gemini_rtc *rtc = dev_get_drvdata(dev); + unsigned int sec, min, hour, day; + unsigned long offset, time; + + if (tm->tm_year >= 2148) /* EPOCH Year + 179 */ + return -EINVAL; + + rtc_tm_to_time(tm , &time); + + sec = readl(rtc->base + GEMINI_RTC_SECOND); + min = readl(rtc->base + GEMINI_RTC_MINUTE); + hour = readl(rtc->base + GEMINI_RTC_HOUR); + day = readl(rtc->base + GEMINI_RTC_DAYS); + + offset = time - (day*86400 + hour*3600 + min*60 + sec); + + writel(offset, rtc->base + GEMINI_RTC_RECORD); + writel(0x01, rtc->base + GEMINI_RTC_CR); + return 0; +} + +static struct rtc_class_ops gemini_rtc_ops = { + .read_time = gemini_rtc_read_time, + .set_time = gemini_rtc_set_time, +}; + +static int gemini_rtc_probe(struct platform_device *pdev) +{ + struct gemini_rtc *rtc; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + if (unlikely(!rtc)) + return -ENOMEM; + platform_set_drvdata(pdev, rtc); + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) { + ret = -rtc->irq; + goto err_mem; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto err_mem; + } + rtc->base = devm_ioremap(&pdev->dev, res->start, + res->end - res->start + 1); + + ret = request_irq(rtc->irq, gemini_rtc_interrupt, + IRQF_SHARED, pdev->name, dev); + if (unlikely(ret)) + goto err_mem; + + rtc->dev = rtc_device_register(pdev->name, dev, + &gemini_rtc_ops, THIS_MODULE); + if (unlikely(IS_ERR(rtc->dev))) { + ret = PTR_ERR(rtc->dev); + goto err_irq; + } + return 0; + +err_irq: + free_irq(rtc->irq, dev); + +err_mem: + kfree(rtc); + return ret; +} + +static int gemini_rtc_remove(struct platform_device *pdev) +{ + struct gemini_rtc *rtc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + free_irq(rtc->irq, dev); + rtc_device_unregister(rtc->dev); + platform_set_drvdata(pdev, NULL); + kfree(rtc); + + return 0; +} + +static struct platform_driver gemini_rtc_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = gemini_rtc_probe, + .remove = gemini_rtc_remove, +}; + +static int __init gemini_rtc_init(void) +{ + int retval; + + retval = platform_driver_register(&gemini_rtc_driver); + if (retval == 0) + pr_info(DRV_NAME ": registered successfully"); + return retval; +} + +static void __exit gemini_rtc_exit(void) +{ + platform_driver_unregister(&gemini_rtc_driver); +} + +module_init(gemini_rtc_init); +module_exit(gemini_rtc_exit); diff --git a/target/linux/gemini/files/drivers/usb/host/ehci-fotg2.c b/target/linux/gemini/files/drivers/usb/host/ehci-fotg2.c new file mode 100644 index 0000000..0717abc --- /dev/null +++ b/target/linux/gemini/files/drivers/usb/host/ehci-fotg2.c @@ -0,0 +1,258 @@ +/* + * Gemini EHCI Host Controller driver + * + * Copyright (C) 2014 Roman Yeryomin <roman@advem.lv> + * Copyright (C) 2012 Tobias Waldvogel + * based on GPLd code from Sony Computer Entertainment Inc. + * + * 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; version 2 of the License. + */ +#include <linux/kernel.h> +#include <linux/hrtimer.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <linux/usb/ehci_pdriver.h> + +#include <mach/hardware.h> +#include <mach/global_reg.h> + +#include "ehci.h" + +#define DRV_NAME "ehci-fotg2" + +#define HCD_MISC 0x40 + +#define OTGC_SCR 0x80 +#define OTGC_INT_STS 0x84 +#define OTGC_INT_EN 0x88 + +#define GLOBAL_ISR 0xC0 +#define GLOBAL_ICR 0xC4 + +#define GLOBAL_INT_POLARITY (1 << 3) +#define GLOBAL_INT_MASK_HC (1 << 2) +#define GLOBAL_INT_MASK_OTG (1 << 1) +#define GLOBAL_INT_MASK_DEV (1 << 0) + +#define OTGC_SCR_ID (1 << 21) +#define OTGC_SCR_CROLE (1 << 20) +#define OTGC_SCR_VBUS_VLD (1 << 19) +#define OTGC_SCR_A_SRP_RESP_TYPE (1 << 8) +#define OTGC_SCR_A_SRP_DET_EN (1 << 7) +#define OTGC_SCR_A_SET_B_HNP_EN (1 << 6) +#define OTGC_SCR_A_BUS_DROP (1 << 5) +#define OTGC_SCR_A_BUS_REQ (1 << 4) + +#define OTGC_INT_APLGRMV (1 << 12) +#define OTGC_INT_BPLGRMV (1 << 11) +#define OTGC_INT_OVC (1 << 10) +#define OTGC_INT_IDCHG (1 << 9) +#define OTGC_INT_RLCHG (1 << 8) +#define OTGC_INT_AVBUSERR (1 << 5) +#define OTGC_INT_ASRPDET (1 << 4) +#define OTGC_INT_BSRPDN (1 << 0) + +#define OTGC_INT_A_TYPE ( \ + OTGC_INT_ASRPDET | \ + OTGC_INT_AVBUSERR | \ + OTGC_INT_OVC | \ + OTGC_INT_RLCHG | \ + OTGC_INT_IDCHG | \ + OTGC_INT_APLGRMV \ + ) +#define OTGC_INT_B_TYPE ( \ + OTGC_INT_AVBUSERR | \ + OTGC_INT_OVC | \ + OTGC_INT_RLCHG | \ + OTGC_INT_IDCHG \ + ) + + +static void fotg2_otg_init(struct usb_hcd *hcd) +{ + u32 val; + + writel(GLOBAL_INT_POLARITY | GLOBAL_INT_MASK_HC | + GLOBAL_INT_MASK_OTG | GLOBAL_INT_MASK_DEV, + hcd->regs + GLOBAL_ICR); + + val = readl(hcd->regs + OTGC_SCR); + val &= ~(OTGC_SCR_A_SRP_RESP_TYPE | OTGC_SCR_A_SRP_DET_EN | + OTGC_SCR_A_BUS_DROP | OTGC_SCR_A_SET_B_HNP_EN); + val |= OTGC_SCR_A_BUS_REQ; + writel(val, hcd->regs + OTGC_SCR); + + writel(OTGC_INT_A_TYPE, hcd->regs + OTGC_INT_EN); + + /* setup MISC register, fixes timing problems */ + val = readl(hcd->regs + HCD_MISC); + val |= 0xD; + writel(val, hcd->regs + HCD_MISC); + + writel(~0, hcd->regs + GLOBAL_ISR); + writel(~0, hcd->regs + OTGC_INT_STS); +} + +static int fotg2_ehci_reset(struct usb_hcd *hcd) +{ + int retval; + + retval = ehci_setup(hcd); + if (retval) + return retval; + + writel(GLOBAL_INT_POLARITY, hcd->regs + GLOBAL_ICR); + return 0; +} + +static const struct hc_driver fotg2_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "FOTG2 EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + .reset = fotg2_ehci_reset, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + .get_frame_number = ehci_get_frame, + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#if defined(CONFIG_PM) + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static irqreturn_t fotg2_ehci_irq(int irq, void *data) +{ + struct usb_hcd *hcd = data; + u32 icr, sts; + irqreturn_t retval; + + icr = readl(hcd->regs + GLOBAL_ICR); + writel(GLOBAL_INT_POLARITY | GLOBAL_INT_MASK_HC | + GLOBAL_INT_MASK_OTG | GLOBAL_INT_MASK_DEV, + hcd->regs + GLOBAL_ICR); + + retval = IRQ_NONE; + + sts = ~icr; + sts &= GLOBAL_INT_MASK_HC | GLOBAL_INT_MASK_OTG | GLOBAL_INT_MASK_DEV; + sts &= readl(hcd->regs + GLOBAL_ISR); + writel(sts, hcd->regs + GLOBAL_ISR); + + if (unlikely(sts & GLOBAL_INT_MASK_DEV)) { + ehci_warn(hcd_to_ehci(hcd), + "Received unexpected irq for device role\n"); + retval = IRQ_HANDLED; + } + + if (unlikely(sts & GLOBAL_INT_MASK_OTG)) { + u32 otg_sts; + + otg_sts = readl(hcd->regs + OTGC_INT_STS); + writel(otg_sts, hcd->regs + OTGC_INT_STS); + + ehci_warn(hcd_to_ehci(hcd), + "Received unexpected irq for OTG management\n"); + retval = IRQ_HANDLED; + } + + if (sts & GLOBAL_INT_MASK_HC) { + retval = IRQ_NONE; + } + + writel(icr, hcd->regs + GLOBAL_ICR); + return retval; +} + +static int fotg2_ehci_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct resource *res; + int irq , err; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + pr_err("no irq provided"); + return irq; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("no memory resource provided"); + return -ENXIO; + } + + hcd = usb_create_hcd(&fotg2_ehci_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + err = -ENOMEM; + goto err_put_hcd; + } + + hcd->has_tt = 1; + hcd_to_ehci(hcd)->caps = hcd->regs; + + fotg2_otg_init(hcd); + + err = request_irq(irq, &fotg2_ehci_irq, IRQF_SHARED, "fotg2", hcd); + if (err) + goto err_put_hcd; + + err = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (err) + goto err_put_hcd; + + platform_set_drvdata(pdev, hcd); + return 0; + +err_put_hcd: + usb_put_hcd(hcd); + return err; +} + +static int fotg2_ehci_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + writel(GLOBAL_INT_POLARITY | GLOBAL_INT_MASK_HC | + GLOBAL_INT_MASK_OTG | GLOBAL_INT_MASK_DEV, + hcd->regs + GLOBAL_ICR); + + free_irq(hcd->irq, hcd); + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +MODULE_ALIAS("platform:" DRV_NAME); + +static struct platform_driver ehci_fotg2_driver = { + .probe = fotg2_ehci_probe, + .remove = fotg2_ehci_remove, + .driver.name = DRV_NAME, +}; |