summaryrefslogtreecommitdiff
path: root/cleopatre/linux-2.6.25.10-spc300/drivers/usb/host/ehci-mstar.c
diff options
context:
space:
mode:
Diffstat (limited to 'cleopatre/linux-2.6.25.10-spc300/drivers/usb/host/ehci-mstar.c')
-rw-r--r--cleopatre/linux-2.6.25.10-spc300/drivers/usb/host/ehci-mstar.c518
1 files changed, 518 insertions, 0 deletions
diff --git a/cleopatre/linux-2.6.25.10-spc300/drivers/usb/host/ehci-mstar.c b/cleopatre/linux-2.6.25.10-spc300/drivers/usb/host/ehci-mstar.c
new file mode 100644
index 0000000000..df57d7da02
--- /dev/null
+++ b/cleopatre/linux-2.6.25.10-spc300/drivers/usb/host/ehci-mstar.c
@@ -0,0 +1,518 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2006-2008 MStar Semiconductor, Inc.
+// All rights reserved.
+//
+// Unless otherwise stipulated in writing, any and all information contained
+// herein regardless in any format shall remain the sole proprietary of
+// MStar Semiconductor Inc. and be kept in strict confidence
+// (ˇ§MStar Confidential Informationˇ¨) by the recipient.
+// Any unauthorized act including without limitation unauthorized disclosure,
+// copying, use, reproduction, sale, distribution, modification, disassembling,
+// reverse engineering and compiling of the contents of MStar Confidential
+// Information is unlawful and strictly prohibited. MStar hereby reserves the
+// rights to any and all damages, losses, costs and expenses resulting therefrom.
+//
+////////////////////////////////////////////////////////////////////////////////
+#include <linux/platform_device.h>
+#include "ehci.h"
+#include "ehci-mstar.h"
+
+//#define MDrv_Timer_Delayms(x) mdelay(x)
+
+#define SA_INTERRUPT IRQF_DISABLED
+
+//#define EHCI_POLLING
+#if defined(EHCI_POLLING)
+ #include <linux/kthread.h>
+ struct semaphore thread_sem;
+#endif
+
+static const struct hc_driver ehci_mstar_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Mstar EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ *
+ * FIXME -- ehci_init() doesn't do enough here.
+ * See ehci-ppc-soc for a complete implementation.
+ */
+
+ .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,
+
+ /*
+ * 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,
+};
+
+spinlock_t l2prefetch_lock=SPIN_LOCK_UNLOCKED;
+#define REG_REC_CLR_FLAG (*((volatile u32 *)(IO_ADDRESS(ARM_BRG_BASE))))
+#define REG_MIU_SW_RST (*((volatile u32 *)(IO_ADDRESS(MIU_BASE) + 0x3C)))
+#define REG_MIU_PROTECT2_STR (*((volatile u32 *)(IO_ADDRESS(MIU_BASE) + 0x60)))
+
+void chip_flush_memory(void)
+{
+ unsigned long dw_lock_flag = 0;
+ spin_lock_irqsave(&l2prefetch_lock, dw_lock_flag);
+ REG_REC_CLR_FLAG |= 0x00000004;
+ do {
+ if ((REG_REC_CLR_FLAG&0x00000004)==0)
+ break;
+ } while(1);
+ spin_unlock_irqrestore(&l2prefetch_lock, dw_lock_flag);
+}
+
+unsigned int gehc_base, gutmi_base;
+
+void usb_pll_initial(unsigned int utmi_base, unsigned int usbc_base,
+ unsigned int uhc_base)
+{
+ // Disable MAC initial suspend, Reset UHC
+ writeb(0x0a, (void*) (usbc_base));
+
+ // Release UHC reset, enable UHC and OTG XIU function
+ writeb(0x28, (void*) (usbc_base));
+
+ writew(readb((void*)(utmi_base+0x02)) | 0x04, (void*) (utmi_base+0x02));
+
+ // PLL_TEST[30:28]: 3'b101 for IBIAS current select
+ // PLL_TEST[23] CLK480 to digital output source selection
+ writew(0x00e0, (void*) (utmi_base+0x22*2));
+
+ // PLL_TEST[15]: Bypass 480MHz clock divider
+ // PLL_TEST[7:4]: 5'b0101_0 for 1.0x
+ // PLL_TEST[0]: 1: synthesizer clock, 2'b11, 0: XIN_C
+ writew(0x8003, (void*) (utmi_base+0x20*2));
+
+ writew(0x0001, (void*) (utmi_base+0x24*2));
+
+ // Enable CLK12_SEL bit <2> for select low voltage crystal clock
+ writew(0x2084, (void*) (utmi_base+0x02*2));
+
+ // #7=0 Enable band-gap current #3=0 Disable force_pll_on
+ writew(0x0007, (void*) (utmi_base+0x08*2));
+
+ // reg_pdn: bit<15>, bit <2> ref_pdn # Turn on reference voltage and regulator
+ writew(0x6bc3, (void*) (utmi_base));
+ mdelay(1);
+
+ // Turn on UPLL, reg_pdn: bit<9>
+ writew(0x69c3, (void*) (utmi_base));
+ mdelay(2);
+
+ // Turn all (including hs_current) use override mode
+ writew(0x0001, (void*) (utmi_base));
+}
+
+//-----------------------------------------
+void titania3_series_start_ehc(unsigned int utmi_base, unsigned int usbc_base,
+ unsigned int uhc_base, unsigned int flag)
+{
+ printk("titania3_series_start_ehc start\n");
+
+ gehc_base = uhc_base;
+ gutmi_base = utmi_base;
+ usb_pll_initial(utmi_base, usbc_base, uhc_base);
+
+ if (flag & EHCFLAG_TESTPKG) {
+ writew(0x2084, (void*) (utmi_base+0x2*2));
+ writew(0x8051, (void*) (utmi_base+0x20*2));
+ }
+
+ // Disable MAC initial suspend, Reset UHC
+ writeb(0x0a, (void*) (usbc_base));
+
+ // Release UHC reset, enable UHC and OTG XIU function
+ writeb(0x28, (void*) (usbc_base));
+
+ // set reg_double_data_rate, To get better jitter performance
+ writeb(readb((void*)(utmi_base+0x0D*2-1)) | 0x01,
+ (void*) (utmi_base+0x0D*2-1));
+
+ if (flag & EHCFLAG_DOUBLE_DATARATE) {
+ if ((flag & EHCFLAG_DDR_MASK) == EHCFLAG_DDR_x15) {
+ // Set usb bus = 480MHz x 1.5
+ writeb(readb((void*)(utmi_base+0x20*2)) | 0x76,
+ (void*) (utmi_base+0x20*2));
+ } else if ((flag & EHCFLAG_DDR_MASK) == EHCFLAG_DDR_x18) {
+ // Set usb bus = 480MHz x 1.8
+ writeb(readb((void*)(utmi_base+0x20*2)) | 0x8e,
+ (void*) (utmi_base+0x20*2));
+ }
+
+ //Set slew rate control for overspeed (or 960MHz)
+ writeb(readb((void*)(utmi_base+0x2c*2)) |0x1,
+ (void*) (utmi_base+0x2c*2));
+ }
+
+ // Disable force_pll_on
+ writeb(readb((void*)(utmi_base+0x09*2-1)) & ~0x08,
+ (void*) (utmi_base+0x09*2-1));
+
+ // Enable band-gap current
+ writeb(readb((void*)(utmi_base+0x08*2)) & ~0x80,
+ (void*) (utmi_base+0x08*2));
+
+ // reg_pdn: bit<15>, bit <2> ref_pdn
+ writeb(0xC3, (void*)(utmi_base));
+
+ // delay 1ms
+ mdelay(1);
+
+ // Turn on UPLL, reg_pdn: bit<9>
+ writeb(0x69, (void*) (utmi_base+0x01*2-1));
+
+ // delay 2ms
+ mdelay(2);
+
+ // Turn all (including hs_current) use override mode
+ writeb(0x01, (void*) (utmi_base));
+
+ // Turn on UPLL, reg_pdn: bit<9>
+ writeb(0, (void*) (utmi_base+0x01*2-1));
+
+ // Disable MAC initial suspend, Reset UHC
+ writeb(0x0A, (void*) (usbc_base));
+
+ // Release UHC reset, enable UHC XIU function
+ writeb(0x28, (void*) (usbc_base));
+
+ // set CA_START as 1
+ writeb(readb((void*)(utmi_base+0x3C*2)) | 0x01,
+ (void*) (utmi_base+0x3C*2));
+ mdelay(10);
+
+ // release CA_START
+ writeb(readb((void*)(utmi_base+0x3C*2)) & ~0x01,
+ (void*) (utmi_base+0x3C*2));
+
+ // polling bit <1> (CA_END)
+ while ((readb((void*)(utmi_base+0x3C*2)) & 0x02) == 0);
+
+ if (flag & EHCFLAG_DPDM_SWAP) {
+ // dp dm swap
+ writeb(readb((void*)(utmi_base+0x0b*2-1)) |0x20,
+ (void*) (utmi_base+0x0b*2-1));
+ }
+
+ //UHC select enable
+ writeb(readb((void*)(usbc_base+0x02*2)) & ~0x03,
+ (void*) (usbc_base+0x02*2));
+
+ //UHC select enable
+ writeb(readb((void*)(usbc_base+0x02*2)) | 0x01,
+ (void*) (usbc_base+0x02*2));
+
+ //0: VBUS On.
+ writeb(readb((void*)(uhc_base+0x40*2)) & ~0x10,
+ (void*) (uhc_base+0x40*2));
+ mdelay(1); // delay 1ms
+
+ // Active HIGH
+ writeb(readb((void*)(uhc_base+0x40*2)) | 0x08,
+ (void*) (uhc_base+0x40*2));
+ mdelay(1); // delay 1ms
+
+ //improve the efficiency of USB access MIU when system is busy
+ writeb(readb((void*)(uhc_base+0x81*2-1)) | 0x8F,
+ (void*) (uhc_base+0x81*2-1));
+
+ //reg_tx_force_hs_current_enable
+ writeb((readb((void*)(utmi_base+0x06*2)) & 0x9F) | 0x40,
+ (void*) (utmi_base+0x06*2));
+
+ //Disconnect window select
+ writeb(readb((void*)(utmi_base+0x03*2-1)) | 0x28,
+ (void*) (utmi_base+0x03*2-1));
+
+ //Disconnect window select
+ writeb(readb((void*)(utmi_base+0x03*2-1)) & 0xef,
+ (void*) (utmi_base+0x03*2-1));
+
+ //Disable improved CDR
+ writeb(readb((void*)(utmi_base+0x07*2-1)) & 0xfd,
+ (void*) (utmi_base+0x07*2-1));
+
+ // UTMI RX anti-dead-loc, ISI effect improvement
+ writeb(readb((void*)(utmi_base+0x09*2-1)) |0x81,
+ (void*) (utmi_base+0x09*2-1));
+
+ if ((flag & EHCFLAG_DOUBLE_DATARATE)==0) {
+ // TX timing select latch path
+ writeb(readb((void*)(utmi_base+0x0b*2-1)) |0x80,
+ (void*) (utmi_base+0x0b*2-1));
+ }
+
+ // Chirp signal source select
+ writeb(readb((void*)(utmi_base+0x15*2-1)) |0x20,
+ (void*) (utmi_base+0x15*2-1));
+ // change to 55 interface
+ writeb(readb((void*)(utmi_base+0x15*2-1)) |0x40,
+ (void*) (utmi_base+0x15*2-1));
+ writeb(readb((void*)(utmi_base+0x2c*2)) |0x98,
+ (void*) (utmi_base+0x2c*2));
+ writeb(readb((void*)(utmi_base+0x2e*2)) |0x10,
+ (void*) (utmi_base+0x2e*2));
+ writeb(readb((void*)(utmi_base+0x2f*2-1)) |0x01,
+ (void*) (utmi_base+0x2f*2-1));
+ writeb(readb((void*)(utmi_base+0x2d*2-1)) |0x02,
+ (void*) (utmi_base+0x2d*2-1));
+
+
+#if defined(ENABLE_PWR_NOISE_ECO)
+ //enable use eof2 to reset state machineˇ¨ (power noise)
+ writeb(readb((void*)(usbc_base+0x02*2)) | 0x40,
+ (void*) (usbc_base+0x02*2));
+#endif
+ //enable Preamble function
+ writeb(readb((void*)(usbc_base+0x0e*2)) | 0x08,
+ (void*) (usbc_base+0x0e*2));
+
+ if (flag & EHCFLAG_TESTPKG) {
+ writew(0x0600, (void*) (utmi_base+0x14*2));
+ writew(0x0038, (void*) (utmi_base+0x10*2));
+ writew(0x0BFE, (void*) (utmi_base+0x32*2));
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_ehci_au1xxx_probe - initialize Au1xxx-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+
+
+#if defined(EHCI_POLLING)
+static int ehci_polling_thread(void * __hcd)
+{
+ struct usb_hcd *hcd = __hcd;
+ spinlock_t *poll_lock;
+ u8 regv;
+
+ printk(KERN_WARNING "ehci_polling_thread start\n");
+ down(&thread_sem);
+ do {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ regv = readb((void*)(hcd->ehc_base+0x14*2));
+
+ if (regv == 0) {
+ msleep(1);
+ continue;
+ }
+
+ set_current_state(TASK_RUNNING);
+ printk("ehci_irq\n");
+ ehci_irq(__hcd);
+ } while(1);
+ up(&thread_sem);
+
+ return 0;
+}
+#endif
+
+int usb_ehci_mstar_probe(const struct hc_driver *driver,
+ struct usb_hcd **hcd_out, struct platform_device *dev)
+{
+ int retval=0;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+
+ struct resource *regs;
+ u32 utmi_base;
+ u32 uhc_base;
+ u32 usbc_base;
+
+#if defined(EHCI_POLLING)
+ struct task_struct *th;
+#endif
+
+ //Benson modify for kafe
+ //Restore Registers base address
+ regs = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!regs)
+ return -ENXIO;
+
+ utmi_base = (u32)ioremap(regs->start, (regs->end - regs->start) + 1);
+
+ printk("utmi_base:%x\n", utmi_base);
+ regs = platform_get_resource(dev, IORESOURCE_MEM, 1);
+ if (!regs)
+ return -ENXIO;
+
+ uhc_base = (u32)ioremap(regs->start, (regs->end - regs->start) + 1);
+ printk("uhc_base:%x\n", uhc_base);
+ regs = platform_get_resource(dev, IORESOURCE_MEM, 2);
+ if (!regs)
+ return -ENXIO;
+
+ usbc_base = (u32)ioremap(regs->start, (regs->end - regs->start) + 1);
+ printk("usbc_base:%x\n", usbc_base);
+ if (0==strcmp(dev->name, "Mstar-ehci-1")) {
+ printk("Mstar-ehci-1 H.W init\n");
+ titania3_series_start_ehc(utmi_base, usbc_base, uhc_base, 0);
+ }
+
+ //-------------------------------------------
+ if (dev->resource[3].flags != IORESOURCE_IRQ) {
+ pr_debug("resource[3] is not IORESOURCE_IRQ");
+ retval = -ENOMEM;
+ }
+ hcd = usb_create_hcd(driver, &dev->dev, "mstar");
+ if (!hcd)
+ return -ENOMEM;
+ hcd->rsrc_start = dev->resource[1].start;
+ hcd->rsrc_len = dev->resource[1].end - dev->resource[1].start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ pr_debug("request_mem_region failed");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_debug("ioremap failed");
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs;
+
+ ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(0, &ehci->caps->hc_capbase));
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(0, &ehci->caps->hcs_params);
+
+ /* ehci_hcd_init(hcd_to_ehci(hcd)); */
+ retval = usb_add_hcd(hcd, dev->resource[3].start, SA_INTERRUPT);
+
+#if defined(MSTAR_PATCH_USB_HCD)
+ if (0==strcmp(dev->name, "Mstar-ehci-1")) {
+ hcd->port_index = 1;
+ hcd->utmi_base = utmi_base;
+ hcd->ehc_base = uhc_base;
+ hcd->usbc_base = usbc_base;
+ }
+ hcd->root_port_devnum = 0;
+ hcd->enum_port_flag = 0;
+ hcd->rootflag = 0;
+ hcd->lock_usbreset = SPIN_LOCK_UNLOCKED;
+#endif
+
+#if defined(EHCI_POLLING)
+ init_MUTEX(&thread_sem);
+ th = kthread_run(ehci_polling_thread, hcd, "ehci-polling");
+ if (IS_ERR(th)) {
+ printk(KERN_WARNING "Unable to start the ehci-polling thread\n");
+ retval = PTR_ERR(th);
+ goto err2;
+ }
+#endif
+
+ if (retval == 0)
+ return retval;
+
+err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err1:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_ehci_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_ehci_hcd_au1xxx_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_ehci_mstar_remove(struct usb_hcd *hcd, struct platform_device *dev)
+{
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_hcd_mstar_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = NULL;
+ int ret;
+
+ pr_debug("In ehci_hcd_mstar_drv_probe\n");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /* FIXME we only want one one probe() not two */
+ ret = usb_ehci_mstar_probe(&ehci_mstar_hc_driver, &hcd, pdev);
+ return ret;
+}
+
+static int ehci_hcd_mstar_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ /* FIXME we only want one one remove() not two */
+ usb_ehci_mstar_remove(hcd, pdev);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct platform_driver ehci_hcd_mstar_driver = {
+ .probe = ehci_hcd_mstar_drv_probe,
+ .remove = ehci_hcd_mstar_drv_remove,
+ .driver = {
+ .name = "Mstar-ehci-1",
+ }
+};
+