[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[MiNT] [PATCH] EHCI driver. Translate to bus addresses where needed.



Commit message:

EHCI host controllers in PCI bus needs to deal with physical addresses
instead of the logical (CPU) ones. In some machines like the Milan
they don't match, so we need to translate those addresses before they
are given to the host controller.
Index: sys/usb/src.km/ucd/ehci/ehci-hcd.c
===================================================================
RCS file: /mint/freemint/sys/usb/src.km/ucd/ehci/ehci-hcd.c,v
retrieving revision 1.7
diff -u -8 -r1.7 ehci-hcd.c
--- sys/usb/src.km/ucd/ehci/ehci-hcd.c	20 Oct 2014 17:29:00 -0000	1.7
+++ sys/usb/src.km/ucd/ehci/ehci-hcd.c	29 Oct 2014 09:11:23 -0000
@@ -327,59 +327,67 @@
 		tmp |= USBMODE_BE;
 #endif
 		ehci_writel(reg_ptr, tmp);
 	}
 out:
 	return ret;
 }
 
-static void *ehci_alloc(struct ehci *gehci, size_t sz, size_t align)
+static void *ehci_alloc(struct ehci *gehci, size_t sz, size_t align, unsigned long *td_offset)
 {
 	static long ntds;
 	void *p;
 	switch(sz)
 	{
 		case sizeof(struct QH):
 			p = gehci->qh;
 			ntds = 0;
 			break;
 		case sizeof(struct qTD):
 			if(ntds == 3)
 			{
 				DEBUG(("out of TDs"));
 				return NULL;
 			}
 			p = gehci->td[ntds];
+			*td_offset = gehci->td_offset[ntds];
 			ntds++;
 			break;
 		default:
 			DEBUG(("unknown allocation size"));
 			return NULL;
 	}
 	memset(p, 0, sz);
 	return p;
 }
 
 static long ehci_td_buffer(struct ehci *gehci, struct qTD *td, void *buf, size_t sz)
 {
 	unsigned long delta, next;
-	unsigned long addr = (ulong)buf;
+	unsigned long addr;
 	size_t rsz = ROUNDUP(sz, 32);
-	long idx;
+	long idx, r;
+
+	r = ehci_bus_getaddr(gehci, (unsigned long)buf, &addr);
+	if (r < 0)
+	{
+		DEBUG(("EHCI_HCD: Unable to get bus address"));
+		return -1;
+	}
 
 	if (sz != rsz)
 		DEBUG(("EHCI-HCD: Misaligned buffer size (%d)\n", sz));
 	if (addr & 31)
 		DEBUG(("EHCI-HCD: Misaligned buffer address (0x%08lx)\n", buf));
 
 	idx = 0;
 	while(idx < 5)
 	{
-		td->qt_buffer[idx] = cpu_to_hc32(addr - gehci->dma_offset);
+		td->qt_buffer[idx] = cpu_to_hc32(addr);
 		next = (addr + 4096) & ~4095;
 		delta = next - addr;
 		if (delta >= sz)
 			break;
 		sz -= delta;
 		addr = next;
 		idx++;
 	}
@@ -410,51 +418,52 @@
 	volatile struct qTD *vtd;
 	unsigned long ts;
 	unsigned long *tdp;
 	unsigned long endpt, token, usbsts;
 	unsigned long c, toggle;
 	unsigned long cmd;
 	int timeout;
 	long ret = 0;
+	unsigned long td_offset;
 
 	struct ehci *gehci = (struct ehci *)dev->controller->ucd_priv;
 
 	DEBUG(("dev=0x%lx, pipe=0x%lx, buffer=0x%lx, length=%ld, req=0x%lx", dev, pipe, buffer, length, req));
 	if(req != NULL)
 		DEBUG(("ehci_submit_async req=%u (0x%x), type=%u (0x%x), value=%u (0x%x), index=%u",
 		req->request, req->request, req->requesttype, req->requesttype,
 		le2cpu16(req->value), le2cpu16(req->value), le2cpu16(req->index)));
-	qh = ehci_alloc(gehci, sizeof(struct QH), 32);
+	qh = ehci_alloc(gehci, sizeof(struct QH), 32, &td_offset);
 
 	if(qh == NULL)
 	{
 		DEBUG(("unable to allocate QH"));
 		return -1;
 	}
 
 	toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
 
-	qh->qh_link = cpu_to_hc32(((unsigned long)gehci->qh_list - gehci->dma_offset) | QH_LINK_TYPE_QH);
+	qh->qh_link = cpu_to_hc32(((unsigned long)gehci->qh_list_busaddr) | QH_LINK_TYPE_QH);
 	c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && usb_pipeendpoint(pipe) == 0) ? 1 : 0;
 
 	endpt = (8UL << 28) | (c << 27) | (usb_maxpacket(dev, pipe) << 16) |
 		(0 << 15) | (1 << 14) | (ehci_encode_speed(usb_pipespeed(pipe)) << 12) |
 		(usb_pipeendpoint(pipe) << 8) | (0 << 7) | (usb_pipedevice(pipe) << 0);
 	qh->qh_endpt1 = cpu_to_hc32(endpt);
 	endpt = (1UL << 30) | (dev->portnr << 23) | (dev->parent->devnum << 16) | (0 << 8) | (0 << 0);
 	qh->qh_endpt2 = cpu_to_hc32(endpt);
 	qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
 
 	td = NULL;
 	tdp = &qh->qh_overlay.qt_next;
 
 	if(req != NULL)
 	{
-		td = ehci_alloc(gehci, sizeof(struct qTD), 32);
+		td = ehci_alloc(gehci, sizeof(struct qTD), 32, &td_offset);
 		if(td == NULL)
 		{
 			DEBUG(("unable to allocate SETUP td"));
 			goto fail;
 		}
 		td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
 		td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
 		token = (0UL << 31) | (sizeof(*req) << 16) | (0 << 15) |
@@ -462,62 +471,62 @@
 		td->qt_token = cpu_to_hc32(token);
 
 		if(ehci_td_buffer(gehci, td, req, sizeof(*req)) != 0)
 		{
 			DEBUG(("unable construct SETUP td"));
 			ehci_free(td, sizeof(*td));
 			goto fail;
 		}
-		*tdp = cpu_to_hc32((unsigned long)td - gehci->dma_offset);
+		*tdp = cpu_to_hc32((unsigned long)td - td_offset);
 		tdp = &td->qt_next;
 		toggle = 1;
 	}
 
 	if(length > 0 || req == NULL)
 	{
-		td = ehci_alloc(gehci, sizeof(struct qTD), 32);
+		td = ehci_alloc(gehci, sizeof(struct qTD), 32, &td_offset);
 		if(td == NULL)
 		{
 			DEBUG(("unable to allocate DATA td"));
 			goto fail;
 		}
 		td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
 		td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
 		token = (toggle << 31) | (length << 16) | ((req == NULL ? 1U : 0) << 15) |
 			(0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0);
 		td->qt_token = cpu_to_hc32(token);
 		if(ehci_td_buffer(gehci, td, buffer, length) != 0)
 		{
 			DEBUG(("unable construct DATA td"));
 			ehci_free(td, sizeof(*td));
 			goto fail;
 		}
-		*tdp = cpu_to_hc32((unsigned long)td - gehci->dma_offset);
+		*tdp = cpu_to_hc32((unsigned long)td - td_offset);
 		tdp = &td->qt_next;
 	}
 
 	if(req != NULL)
 	{
-		td = ehci_alloc(gehci, sizeof(struct qTD), 32);
+		td = ehci_alloc(gehci, sizeof(struct qTD), 32, &td_offset);
 		if(td == NULL)
 		{
 			DEBUG(("unable to allocate ACK td"));
 			goto fail;
 		}
 		td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
 		td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
 		token = (toggle << 31) | (0UL << 16) | (1U << 15) | (0 << 12) |
 			(3 << 10) | ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0);
 		td->qt_token = cpu_to_hc32(token);
-		*tdp = cpu_to_hc32((unsigned long)td - gehci->dma_offset);
+		*tdp = cpu_to_hc32((unsigned long)td - td_offset);
 		tdp = &td->qt_next;
 	}
 
-	gehci->qh_list->qh_link = cpu_to_hc32(((unsigned long)qh - gehci->dma_offset) | QH_LINK_TYPE_QH);
+	gehci->qh_list->qh_link = cpu_to_hc32(((unsigned long)gehci->qh_busaddr) | QH_LINK_TYPE_QH);
 	/* Flush dcache */
 	cpush(&gehci->qh_list, (long)&gehci->qh_list + sizeof(struct QH));
 	cpush(&qh, (long)&qh + sizeof(struct QH));
 	cpush(td, (long)td + sizeof(struct qTD));
 	usbsts = ehci_readl(&gehci->hcor->or_usbsts);
 	ehci_writel(&gehci->hcor->or_usbsts, (usbsts & 0x3f));
 
 	/* Enable async. schedule. */
@@ -563,17 +572,17 @@
 	ehci_writel(&gehci->hcor->or_usbcmd, cmd);
 	ret = handshake((unsigned long *)&gehci->hcor->or_usbsts, STD_ASS, 0, 100L * 1000L);
 	if(ret < 0)
 	{
 		ALERT(("EHCI fail timeout STD_ASS reset (usbsts=%x)", ehci_readl(&gehci->hcor->or_usbsts)));
 		goto fail;
 	}
 
-	gehci->qh_list->qh_link = cpu_to_hc32(((unsigned long)gehci->qh_list - gehci->dma_offset) | QH_LINK_TYPE_QH);
+	gehci->qh_list->qh_link = cpu_to_hc32(((unsigned long)gehci->qh_list_busaddr) | QH_LINK_TYPE_QH);
 	if(!(token & 0x80))
 	{
 		DEBUG(("TOKEN=%lx", token));
 		switch(token & 0xfc)
 		{
 			case 0:
 				toggle = token >> 31;
 				usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle);
@@ -603,24 +612,24 @@
 		dev->act_len = 0;
 		DEBUG(("dev=%ld, usbsts=0x%lx, p[1]=0x%lx, p[2]=0x%lx",
 		 dev->devnum, ehci_readl(&gehci->hcor->or_usbsts), ehci_readl(&gehci->hcor->or_portsc[0]), ehci_readl(&gehci->hcor->or_portsc[1])));
 	}
 	return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;
 fail:
 	td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
 	if(td != (void *)QT_NEXT_TERMINATE)
-		td = (struct qTD *)(gehci->dma_offset + (unsigned long)td);
+		td = (struct qTD *)(td_offset + (unsigned long)td);
 	while(td != (void *)QT_NEXT_TERMINATE)
 	{
 		qh->qh_overlay.qt_next = td->qt_next;
 		ehci_free(td, sizeof(*td));
 		td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
 		if(td != (void *)QT_NEXT_TERMINATE)
-			td = (struct qTD *)(gehci->dma_offset + (unsigned long)td);
+			td = (struct qTD *)(td_offset + (unsigned long)td);
 	}
 	ehci_free(qh, sizeof(*qh));
 	if(ehci_readl(&gehci->hcor->or_usbsts) & STS_HSE) /* Host System Error */
 	{
 		ALERT(("EHCI Host System Error"));
 		ehci_bus_error(gehci);
 	}
 	return -1;
@@ -897,85 +906,116 @@
 }
 
 /*
  * IOCTL functions
  */
 
 long usb_lowlevel_init(void *ucd_priv)
 {
-	long i;
+	long i, r;
 	unsigned long reg;
 	unsigned long cmd;
 
 	struct ehci *gehci = (struct ehci*)ucd_priv;
 
-	if(ehci_bus_init(gehci)) 
-	{
-		hc_free_buffers(gehci);
-		return (-1);
-	};
-
 	gehci->qh_list_unaligned = (struct QH *)kmalloc(sizeof(struct QH) + 32);
 	if(gehci->qh_list_unaligned == NULL)
 	{
 		DEBUG(("QHs malloc failed"));
 		hc_free_buffers(gehci);
 		return(-1);
 	}
 	gehci->qh_list = (struct QH *)(((unsigned long)gehci->qh_list_unaligned + 31) & ~31);
 	memset(gehci->qh_list, 0, sizeof(struct QH));
+
 	gehci->qh_unaligned = (struct QH *)kmalloc(sizeof(struct QH) + 32);
 	if(gehci->qh_unaligned == NULL)
 	{
 		DEBUG(("QHs malloc failed"));
 		hc_free_buffers(gehci);
 		return(-1);
 	}
 	gehci->qh = (struct QH *)(((unsigned long)gehci->qh_unaligned + 31) & ~31);
 	memset(gehci->qh, 0, sizeof(struct QH));
+
 	for(i = 0; i < 3; i++)
 	{
 		gehci->td_unaligned[i] = (struct qTD *)kmalloc(sizeof(struct qTD) + 32);
 		if(gehci->td_unaligned[i] == NULL)
 		{
 			DEBUG(("TDs malloc failed"));
 			hc_free_buffers(gehci);
 			return(-1);
 		}
 		gehci->td[i] = (struct qTD *)(((unsigned long)gehci->td_unaligned[i] + 31) & ~31);
-		memset(gehci->td[i], 0, sizeof(struct qTD));	
+		memset(gehci->td[i], 0, sizeof(struct qTD));
 	}
+
 	gehci->descriptor = (struct descriptor *)kmalloc(sizeof(struct descriptor));
 	if(gehci->descriptor == NULL)
 	{
 		DEBUG(("decriptor malloc failed"));
 		hc_free_buffers(gehci);
 		return(-1);
 	}
 	memcpy(gehci->descriptor, &rom_descriptor, sizeof(struct descriptor));
 
+	if(ehci_bus_init(gehci)) 
+	{
+		hc_free_buffers(gehci);
+		return (-1);
+	};
 	gehci->hcor = (struct ehci_hcor *)((unsigned long)gehci->hccr + HC_LENGTH(ehci_readl(&gehci->hccr->cr_capbase)));
 
+	/* Get bus addresses */
+	r = ehci_bus_getaddr(gehci, (unsigned long)gehci->qh_list, (unsigned long *)&gehci->qh_list_busaddr);
+	if(r < 0)
+	{
+		DEBUG(("Getting qh_list bus address failed"));
+		hc_free_buffers(gehci);
+		return(-1);
+	}
+
+	r = ehci_bus_getaddr(gehci, (unsigned long)gehci->qh, (unsigned long *)&gehci->qh_busaddr);
+	if(r < 0)
+	{
+		DEBUG(("Getting qh bus address failed"));
+		hc_free_buffers(gehci);
+		return(-1);
+	}
+
+	for(i = 0; i < 3; i++)
+	{
+		r = ehci_bus_getaddr(gehci, (unsigned long)gehci->td[i], (unsigned long *)&gehci->td_busaddr[i]);
+		if(r < 0)
+		{
+			DEBUG(("Getting td bus address failed"));
+			hc_free_buffers(gehci);
+			return(-1);
+		}
+		gehci->td_offset[i] = (unsigned long)gehci->td[i] - (unsigned long)gehci->td_busaddr[i];
+	}
+
 	/* EHCI spec section 4.1 */
 	if(ehci_reset(gehci) != 0)
 	{
 		hc_free_buffers(gehci);
 		return(-1);
 	}
 	/* Set head of reclaim list */
-	gehci->qh_list->qh_link = cpu_to_hc32(((unsigned long)gehci->qh_list - gehci->dma_offset) | QH_LINK_TYPE_QH);
+	gehci->qh_list->qh_link = cpu_to_hc32(((unsigned long)gehci->qh_list_busaddr) | QH_LINK_TYPE_QH);
 	gehci->qh_list->qh_endpt1 = cpu_to_hc32((1UL << 15) | (USB_SPEED_HIGH << 12));
 	gehci->qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE);
 	gehci->qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
 	gehci->qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
 	gehci->qh_list->qh_overlay.qt_token = cpu_to_hc32(0x40);
 
 	/* Set async. queue head pointer. */
-	ehci_writel(&gehci->hcor->or_asynclistaddr, (unsigned long)gehci->qh_list - gehci->dma_offset);
+	ehci_writel(&gehci->hcor->or_asynclistaddr, (unsigned long)gehci->qh_list_busaddr);
 	reg = ehci_readl(&gehci->hccr->cr_hcsparams);
 	gehci->descriptor->hub.bNbrPorts = HCS_N_PORTS(reg);
 	DEBUG(("Register %lx NbrPorts %d", reg, gehci->descriptor->hub.bNbrPorts));
 
 	/* Port Indicators */
 	if(HCS_INDICATOR(reg))
 		gehci->descriptor->hub.wHubCharacteristics |= 0x80;
 	/* Port Power Control */
Index: sys/usb/src.km/ucd/ehci/ehci-pci.c
===================================================================
RCS file: /mint/freemint/sys/usb/src.km/ucd/ehci/ehci-pci.c,v
retrieving revision 1.5
diff -u -8 -r1.5 ehci-pci.c
--- sys/usb/src.km/ucd/ehci/ehci-pci.c	23 Oct 2014 10:05:47 -0000	1.5
+++ sys/usb/src.km/ucd/ehci/ehci-pci.c	29 Oct 2014 09:11:23 -0000
@@ -68,17 +68,17 @@
 /*
  * Function prototypes
  */
 long ehci_pci_init	(void *);
 void ehci_pci_stop	(struct ehci *);
 long ehci_pci_probe	(struct ucdif *);
 long ehci_pci_reset	(struct ehci *);
 void ehci_pci_error	(struct ehci *);
-
+unsigned long ehci_pci_getaddr	(struct ehci *, unsigned long, unsigned long *);
 
 struct ehci_pci {
 	long handle;				/* PCI BIOS */
 	const struct pci_device_id *ent;
 };
 
 struct pci_device_id ehci_usb_pci_table[] = {
 	{ PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB_2, 
@@ -90,16 +90,17 @@
 };
 
 struct ehci_bus ehci_bus = {
 	.init = ehci_pci_init,
 	.stop = ehci_pci_stop,
 	.probe = ehci_pci_probe,
 	.reset = ehci_pci_reset,
 	.error = ehci_pci_error,
+	.getaddr = ehci_pci_getaddr,
 };
 
 void
 ehci_pci_error(struct ehci *gehci)
 {
 	unsigned short status = Fast_read_config_word(((struct ehci_pci *)gehci->bus)->handle, PCISR);
 	ALERT(("EHCI Host System Error, controller usb-%s disabled\r\n(SR:0x%04x%s%s%s%s%s%s)", gehci->slot_name, status & 0xFFFF,
 	 status & 0x8000 ? ", Parity error" : "", status & 0x4000 ? ", Signaled system error" : "", status & 0x2000 ? ", Received master abort" : "",
@@ -298,8 +299,21 @@
 	}
 
 	/* Disable interrupt */
 	ehci_writel(&ehci->hcor->or_usbsts, status);
 
 	/* PCI_BIOS specification: if interrupt was for us set D0.0 */
 	return 1;
 }
+
+unsigned long ehci_pci_getaddr(struct ehci *gehci, unsigned long addr, unsigned long *pciaddr)
+{
+	long status;
+	PCI_CONV_ADR cr;
+
+	status = Virt_to_bus(((struct ehci_pci *)gehci->bus)->handle, addr, (struct pci_conv_adr *)&cr);
+	if (status != PCI_SUCCESSFUL)
+			return -1;
+
+	*pciaddr = cr.adr;
+	return 0;
+}
Index: sys/usb/src.km/ucd/ehci/ehci.h
===================================================================
RCS file: /mint/freemint/sys/usb/src.km/ucd/ehci/ehci.h,v
retrieving revision 1.5
diff -u -8 -r1.5 ehci.h
--- sys/usb/src.km/ucd/ehci/ehci.h	20 Oct 2014 15:00:47 -0000	1.5
+++ sys/usb/src.km/ucd/ehci/ehci.h	29 Oct 2014 09:11:23 -0000
@@ -179,20 +179,24 @@
 
 struct ehci {
 	void *bus;				/* Inteface with bus/platform. (ex: pci) */
 	long big_endian;
 	struct ehci_hccr *hccr;		/* R/O registers, not need for volatile */
 	volatile struct ehci_hcor *hcor;
 	struct QH *qh_list_unaligned;
 	struct QH *qh_list;
+	struct QH *qh_list_busaddr;
 	struct QH *qh_unaligned;
 	struct QH *qh;
+	struct QH *qh_busaddr;
 	struct qTD *td_unaligned[3];
 	struct qTD *td[3];
+	struct qTD *td_busaddr[3];
+	unsigned long td_offset[3];
 	struct descriptor *descriptor;
 	long irq;
 	unsigned long dma_offset;
 	const char *slot_name;
 	char ehci_inited;
 	long rootdev;
 	unsigned short portreset;
 	unsigned short companion;
@@ -208,19 +212,21 @@
 /* Interface with bus/platform */
 
 struct ehci_bus {
 	long (*init)(void *);
 	void (*stop)(struct ehci *);
 	long (*probe)(struct ucdif *);
 	long (*reset)(struct ehci *);
 	void (*error)(struct ehci *);
+	unsigned long (*getaddr)(struct ehci *, unsigned long, unsigned long *);
 };
 
-#define ehci_bus_init	ehci_bus.init
-#define ehci_bus_stop	ehci_bus.stop
-#define ehci_bus_probe	ehci_bus.probe
-#define ehci_bus_reset	ehci_bus.reset
-#define ehci_bus_error	ehci_bus.error
+#define ehci_bus_init		ehci_bus.init
+#define ehci_bus_stop		ehci_bus.stop
+#define ehci_bus_probe		ehci_bus.probe
+#define ehci_bus_reset		ehci_bus.reset
+#define ehci_bus_error		ehci_bus.error
+#define ehci_bus_getaddr	ehci_bus.getaddr
 
 extern struct ehci_bus ehci_bus;
 
 #endif /* USB_EHCI_H */