X-Git-Url: https://git.rohieb.name/openwrt.git/blobdiff_plain/5e9852b169f255fa1094d08f470553035edf447a..87f5e12cd0a27c33cc970186de623b4eb667b564:/target/linux/adm5120-2.6/files/drivers/usb/host/adm5120-hcd.c?ds=sidebyside

diff --git a/target/linux/adm5120-2.6/files/drivers/usb/host/adm5120-hcd.c b/target/linux/adm5120-2.6/files/drivers/usb/host/adm5120-hcd.c
index 6f0df78c3..bd6f7a0aa 100644
--- a/target/linux/adm5120-2.6/files/drivers/usb/host/adm5120-hcd.c
+++ b/target/linux/adm5120-2.6/files/drivers/usb/host/adm5120-hcd.c
@@ -3,7 +3,7 @@
  *
  *	Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org)
  *
- *	Based on the ADMtek 2.4 driver 
+ *	Based on the ADMtek 2.4 driver
  *	(C) Copyright 2003 Junius Chen <juniusc@admtek.com.tw>
  *	Which again was based on the ohci and uhci drivers.
  */
@@ -18,6 +18,7 @@
 #include <linux/usb.h>
 #include <linux/platform_device.h>
 
+#include <asm/bootinfo.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/system.h>
@@ -30,6 +31,8 @@ MODULE_DESCRIPTION("ADM5120 USB Host Controller Driver");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jeroen Vreeken (pe1rxq@amsat.org)");
 
+#define PFX	"adm5120-hcd: "
+
 #define ADMHCD_REG_CONTROL		0x00
 #define ADMHCD_REG_INTSTATUS		0x04
 #define ADMHCD_REG_INTENABLE		0x08
@@ -60,8 +63,17 @@ MODULE_AUTHOR("Jeroen Vreeken (pe1rxq@amsat.org)");
 #define ADMHCD_STATE_SUS	0x00000003	/* bus state suspended */
 #define ADMHCD_DMA_EN		0x00000004	/* enable dma engine */
 
-#define ADMHCD_NPS		0x00000020	/* No Power Switch */
+#define ADMHCD_RST_ST		0x00		/* USB reset state */
+#define ADMHCD_RSM_ST		0x01		/* USB resume state */
+#define ADMHCD_OPR_ST		0x10		/* USB operational state */
+#define ADMHCD_SUS_ST		0x11		/* USB suspend state */
+
+#define ADMHCD_NPS		0x00000200	/* No Power Switch */
+#define ADMHCD_PSM		0x00000100	/* Power switch mode */
+#define ADMHCD_OPCM		0x00000400	/* Over current protect mode */
+#define ADMHCD_NOCP		0x00000800	/* No over current protect mode */
 #define ADMHCD_LPSC		0x04000000	/* Local power switch change */
+#define ADMHCD_LPS		0x01000000	/* Local power switch/global power switch */
 
 #define ADMHCD_CCS		0x00000001	/* current connect status */
 #define ADMHCD_PES		0x00000002	/* port enable status */
@@ -146,8 +158,7 @@ static int admhcd_td_err[16] = {
 
 struct admhcd {
 	spinlock_t	lock;
-	
-	void __iomem *addr_reg;
+
 	void __iomem *data_reg;
 	/* Root hub registers */
 	u32 rhdesca;
@@ -160,7 +171,7 @@ struct admhcd {
 	u32		base;
 	u32		dma_en;
 	unsigned long	flags;
-	
+
 };
 
 static inline struct admhcd *hcd_to_admhcd(struct usb_hcd *hcd)
@@ -213,7 +224,7 @@ static struct admhcd_td *admhcd_td_alloc(struct admhcd_ed *ed, struct urb *urb)
 	if (ed->cur == NULL) {
 		ed->cur = tdn;
 		ed->head = tdn;
-		ed->tail = tdn;	
+		ed->tail = tdn;
 		td = tdn;
 	} else {
 		/* Supply back the old tail and link in new td as tail */
@@ -328,9 +339,8 @@ static void admhcd_ed_start(struct admhcd *ahcd, struct admhcd_ed *ed)
 	ahcd->dma_en |= ADMHCD_DMA_EN;
 }
 
-static irqreturn_t adm5120hcd_irq(int irq, void *ptr, struct pt_regs *regs)
+static irqreturn_t adm5120hcd_irq(struct usb_hcd *hcd)
 {
-	struct usb_hcd *hcd = (struct usb_hcd *)ptr;
 	struct admhcd *ahcd = hcd_to_admhcd(hcd);
 	u32 intstatus;
 
@@ -345,7 +355,7 @@ static irqreturn_t adm5120hcd_irq(int irq, void *ptr, struct pt_regs *regs)
 	}
 	if (intstatus & ADMHCD_INT_TD) {
 		struct admhcd_ed *ed, *head;
-		
+
 		admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_TD);
 
 		head = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD);
@@ -355,7 +365,7 @@ static irqreturn_t adm5120hcd_irq(int irq, void *ptr, struct pt_regs *regs)
 			if (ed->urb && !(ed->cur->control & ADMHCD_TD_OWN)) {
 				struct admhcd_td *td;
 				int error;
-				
+
 				td = ed->cur;
 				error = (td->control & ADMHCD_TD_ERRMASK) >>
 				    ADMHCD_TD_ERRSHIFT;
@@ -376,7 +386,7 @@ static irqreturn_t adm5120hcd_irq(int irq, void *ptr, struct pt_regs *regs)
 }
 
 static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep,
-    struct urb *urb, int mem_flags)
+    struct urb *urb, gfp_t mem_flags)
 {
 	struct admhcd *ahcd = hcd_to_admhcd(hcd);
 	struct admhcd_ed *ed;
@@ -442,7 +452,7 @@ static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep,
 			td = admhcd_td_fill(ADMHCD_TD_SETUP | ADMHCD_TD_DATA0,
 			    td, (dma_addr_t)urb->setup_packet, 8);
 			while (data_len > 0) {
-				td = admhcd_td_fill(ADMHCD_TD_DATA1 
+				td = admhcd_td_fill(ADMHCD_TD_DATA1
 				    | ADMHCD_TD_R |
 				    (usb_pipeout(pipe) ?
 				    ADMHCD_TD_OUT : ADMHCD_TD_IN), td,
@@ -459,7 +469,7 @@ static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep,
 			i = 0;
 			while(data_len > 4096) {
 				td = admhcd_td_fill((usb_pipeout(pipe) ?
-				    ADMHCD_TD_OUT : 
+				    ADMHCD_TD_OUT :
 				    ADMHCD_TD_IN | ADMHCD_TD_R) |
 				    (i ? ADMHCD_TD_TOGGLE : toggle), td,
 				    data, 4096);
@@ -467,7 +477,7 @@ static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep,
 				data_len -= 4096;
 				i++;
 			}
-			td = admhcd_td_fill((usb_pipeout(pipe) ? 
+			td = admhcd_td_fill((usb_pipeout(pipe) ?
 			    ADMHCD_TD_OUT : ADMHCD_TD_IN) |
 			    (i ? ADMHCD_TD_TOGGLE : toggle), td, data, data_len);
 			i++;
@@ -616,7 +626,7 @@ static int admhcd_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 		case USB_PORT_FEAT_RESET:
 			if (admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4)
 			    & ADMHCD_CCS) {
-				admhcd_reg_set(ahcd, 
+				admhcd_reg_set(ahcd,
 				    ADMHCD_REG_PORTSTATUS0 + port*4,
 				    ADMHCD_PRS | ADMHCD_CSC);
 				mdelay(50);
@@ -685,98 +695,193 @@ err:
 	return retval;
 }
 
+static int admhcd_start(struct usb_hcd *hcd)
+{
+	struct admhcd *ahcd = hcd_to_admhcd(hcd);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ahcd->lock, flags);
+
+ 	/* Initialise the HCD registers */
+        admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0);
+        mdelay(10);
+
+        admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_SW_RESET);
+
+        while (admhcd_reg_get(ahcd, ADMHCD_REG_CONTROL) & ADMHCD_SW_RESET)
+                mdelay(1);
+
+	/* Enable USB host mode */
+        admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_HOST_EN);
+
+	/* Set host specific settings */
+        admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, 0x00000000);
+        admhcd_reg_set(ahcd, ADMHCD_REG_FMINTERVAL, 0x20002edf);
+        admhcd_reg_set(ahcd, ADMHCD_REG_LSTHRESH, 0x628);
+
+	/* Set interrupts */
+        admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE,
+            ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD);
+        admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS,
+            ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD);
+
+        admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, ADMHCD_NPS | ADMHCD_LPSC);
+        admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP);
+
+
+       	hcd->state = HC_STATE_RUNNING;
+
+	spin_unlock_irqrestore(&ahcd->lock, flags);
+       	return 0;
+}
+
+static int admhcd_sw_reset(struct admhcd *ahcd)
+{
+	int retries = 15;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ahcd->lock, flags);
+
+	admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0);
+        mdelay(10);
+
+        admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_SW_RESET);
+
+        while (--retries) {
+		mdelay(1);
+		if (!(admhcd_reg_get(ahcd, ADMHCD_REG_CONTROL) & ADMHCD_SW_RESET))
+			break;
+	}
+	if (!retries) {
+		printk(KERN_WARNING "%s Software reset timeout\n", hcd_name);
+		ret = -ETIME;
+	}
+	spin_unlock_irqrestore(&ahcd->lock, flags);
+	return ret;
+}
+
+static int admhcd_reset(struct usb_hcd *hcd)
+{
+	struct admhcd *ahcd = hcd_to_admhcd(hcd);
+        u32 val = 0;
+	int ret, timeout = 15; /* ms */
+	unsigned long t;
+
+	ret = admhcd_sw_reset(ahcd);
+	if (ret)
+		return ret;
+
+	t = jiffies + msecs_to_jiffies(timeout);
+        while (time_before_eq(jiffies, t)) {
+                msleep(4);
+                spin_lock_irq(&ahcd->lock);
+                val = admhcd_reg_get(ahcd, ADMHCD_REG_HOSTCONTROL) & ADMHCD_OPR_ST;
+                spin_unlock_irq(&ahcd->lock);
+                if (val)
+                        break;
+        }
+        if (!val) {
+                printk(KERN_WARNING "Device not ready after %dms\n", timeout);
+                ret = -ENODEV;
+        }
+        return ret;
+}
+
+static void admhcd_stop(struct usb_hcd *hcd)
+{
+	struct admhcd *ahcd = hcd_to_admhcd(hcd);
+        unsigned long flags;
+	u32 val;
+
+        spin_lock_irqsave(&ahcd->lock, flags);
+        admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0);
+	
+	/* Set global control of power for ports */
+	val = admhcd_reg_get(ahcd, ADMHCD_REG_RHDESCR);
+	val &= (~ADMHCD_PSM | ADMHCD_LPS);
+	admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, val);
+
+	spin_unlock_irqrestore(&ahcd->lock, flags);
+
+	/* Ask for software reset */
+	admhcd_sw_reset(ahcd);
+}
+
+
 static struct hc_driver adm5120_hc_driver = {
 	.description =		hcd_name,
 	.product_desc =		"ADM5120 HCD",
 	.hcd_priv_size =	sizeof(struct admhcd),
-	.flags =		HCD_USB11,
+	.irq =			adm5120hcd_irq,
+	.flags =		HCD_MEMORY|HCD_USB11,
 	.urb_enqueue =		admhcd_urb_enqueue,
 	.urb_dequeue =		admhcd_urb_dequeue,
 	.endpoint_disable =	admhcd_endpoint_disable,
 	.get_frame_number =	admhcd_get_frame_number,
 	.hub_status_data =	admhcd_hub_status_data,
 	.hub_control =		admhcd_hub_control,
+	.start	=		admhcd_start,
+	.stop	=		admhcd_stop,
+	.reset =		admhcd_reset,
 };
 
 #define resource_len(r) (((r)->end - (r)->start) + 1)
 
 static int __init adm5120hcd_probe(struct platform_device *pdev)
 {
-	struct usb_hcd *hcd;
-	struct admhcd *ahcd;
-	struct usb_device *udev;
-	struct resource *addr, *data;
-	void __iomem *addr_reg;
-	void __iomem *data_reg;	
-	int irq, err = 0;
+        struct usb_hcd *hcd;
+        struct admhcd *ahcd;
+	struct resource *data;
+	void __iomem *data_reg;
 
-	if (pdev->num_resources < 3) {
+        int err = 0, irq;
+
+	if (pdev->num_resources < 2) {
 		err = -ENODEV;
 		goto out;
-	}
+        }
+
+	if (pdev->dev.dma_mask) {
+                printk(KERN_DEBUG "no we won't dma\n");
+                return -EINVAL;
+        }
 
-	data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	addr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 	irq = platform_get_irq(pdev, 0);
+	data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
-	if (!addr || !data || irq < 0) {
-		err = -ENODEV;
-		goto out;
-	}
+	if (!data || irq < 0) {
+                err = -ENODEV;
+                goto out;
+        }
 
-	if (pdev->dev.dma_mask) {
-		printk(KERN_DEBUG "DMA not supported\n");
-		err = -EINVAL;
-		goto out;
-	}
+        if (!request_mem_region(data->start, 2, hcd_name)) {
+                err = -EBUSY;
+                goto out_unmap;
+        }
 
-	if (!request_mem_region(addr->start, 2, hcd_name)) {
-		err = -EBUSY;
-		goto out;
-	}
-	addr_reg = ioremap(addr->start, resource_len(addr));
-	if (addr_reg == NULL) {
-		err = -ENOMEM;
-		goto out_mem;
-	}
-	if (!request_mem_region(data->start, 2, hcd_name)) {
-		err = -EBUSY;
-		goto out_unmap;
-	}
-	data_reg = ioremap(data->start, resource_len(data));
-	if (data_reg == NULL) {
-		err = -ENOMEM;
-		goto out_mem;
-	}
-								
+        data_reg = ioremap(data->start, resource_len(data));
+        if (data_reg == NULL) {
+                err = -ENOMEM;
+                goto out_mem;
+        }
 
 	hcd = usb_create_hcd(&adm5120_hc_driver, &pdev->dev, pdev->dev.bus_id);
-	if (!hcd)
-		goto out_mem;
+        if (!hcd)
+                goto out_mem;
 
+	hcd->rsrc_start = data->start;
+	hcd->rsrc_len	= resource_len(data);
+	hcd->regs = (u32)data_reg;
 	ahcd = hcd_to_admhcd(hcd);
-	ahcd->data_reg = data_reg;
-	ahcd->addr_reg = addr_reg;
+
 	spin_lock_init(&ahcd->lock);
 	INIT_LIST_HEAD(&ahcd->async);
 
-	/* Initialise the HCD registers */
-	admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0);
-	mdelay(10);
-	admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_SW_RESET);
-	while (admhcd_reg_get(ahcd, ADMHCD_REG_CONTROL) & ADMHCD_SW_RESET)
-		mdelay(1);
+	ahcd->data_reg = data_reg;
+	ahcd->base = (u32)data_reg;
 
-	admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_HOST_EN);
-	admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, 0x00000000);
-	admhcd_reg_set(ahcd, ADMHCD_REG_FMINTERVAL, 0x20002edf);
-	admhcd_reg_set(ahcd, ADMHCD_REG_LSTHRESH, 0x628);
-	admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE,
-	    ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD);
-	admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS,
-	    ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD);
-	admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, ADMHCD_NPS | ADMHCD_LPSC);
-	admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP);
+	hcd->product_desc = "ADM5120 HCD";
 
 	err = usb_add_hcd(hcd, irq, IRQF_DISABLED);
 	if (err)
@@ -786,14 +891,10 @@ static int __init adm5120hcd_probe(struct platform_device *pdev)
 
 out_dev:
 	usb_put_hcd(hcd);
-out_irq:
-	free_irq(pdev->resource[1].start, hcd);
 out_unmap:
-	iounmap(addr_reg);
-out_hcd:
-	usb_put_hcd(hcd);
+	iounmap(data_reg);
 out_mem:
-	release_mem_region(addr->start, 2);
+	release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start +1);
 out:
 	return err;
 }
@@ -802,7 +903,6 @@ static int __init_or_module adm5120hcd_remove(struct platform_device *pdev)
 {
 	struct usb_hcd *hcd = platform_get_drvdata(pdev);
 	struct admhcd *ahcd;
-	struct resource *res;
 
 	if (!hcd)
 		return 0;
@@ -817,19 +917,30 @@ static struct platform_driver adm5120hcd_driver = {
 	.probe =	adm5120hcd_probe,
 	.remove =	adm5120hcd_remove,
 	.driver	=	{
-		.name 	= "adm5120-hcd",
+		.name 	= "adm5120-usbc",
 		.owner 	= THIS_MODULE,
 	},
 };
 
 static int __init adm5120hcd_init(void)
 {
-	if (usb_disabled())
+	int ret;
+
+	if (usb_disabled()) {
+		printk(KERN_DEBUG PFX "USB support is disabled\n");
 		return -ENODEV;
-	if (!adm5120_info.has_usb)
+	}
+
+	if (mips_machgroup != MACH_GROUP_ADM5120) {
+		printk(KERN_DEBUG PFX "unsupported machine group\n");
 		return -ENODEV;
+	}
 
-	return platform_driver_register(&adm5120hcd_driver);
+	ret = platform_driver_register(&adm5120hcd_driver);
+	if (ret == 0)
+		printk(KERN_INFO PFX "registered\n");
+
+	return ret;
 }
 
 static void __exit adm5120hcd_exit(void)