aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/gemini/files/drivers
diff options
context:
space:
mode:
authorJames <>2015-11-04 11:49:21 +0000
committerJames <>2015-11-04 11:49:21 +0000
commit716ca530e1c4515d8683c9d5be3d56b301758b66 (patch)
tree700eb5bcc1a462a5f21dcec15ce7c97ecfefa772 /target/linux/gemini/files/drivers
downloadtrunk-47381-master.tar.gz
trunk-47381-master.tar.bz2
trunk-47381-master.zip
trunk-47381HEADmaster
Diffstat (limited to 'target/linux/gemini/files/drivers')
-rw-r--r--target/linux/gemini/files/drivers/ata/pata_gemini.c234
-rw-r--r--target/linux/gemini/files/drivers/rtc/rtc-gemini.c220
-rw-r--r--target/linux/gemini/files/drivers/usb/host/ehci-fotg2.c258
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,
+};