This patch changes usb-ohci.[ch] to use pci_alloc_consistent to allocate
the HCCA, ED's and TD's and to call pci_dma_map_single in td_fill to ensure
that the data in DRAM is viewed consistently by the CPU and the OHCI
controller on processors such as StrongARM that do not have cache-coherent
DMA. All coherence issues are handled in this driver so none of the USB
device drivers need to be modified.
I posted this yesterday with a note that it had not been tested yet. I
tested it today under pre6 with no errors.
-Jamey Hicks
PATCH FOLLOWS
Index: drivers/usb/usb-ohci.c
===================================================================
RCS file: /cvs/linux/kernel/drivers/usb/usb-ohci.c,v
retrieving revision 1.1.1.3
retrieving revision 1.4
diff -u -w -r1.1.1.3 -r1.4
--- drivers/usb/usb-ohci.c 2000/05/03 22:37:02 1.1.1.3
+++ drivers/usb/usb-ohci.c 2000/05/04 15:51:38 1.4
@@ -70,8 +70,126 @@
static DECLARE_WAIT_QUEUE_HEAD (op_wakeup);
static LIST_HEAD (ohci_hcd_list);
static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t usb_td_lock = SPIN_LOCK_UNLOCKED;
/*-------------------------------------------------------------------------*
+ * Code to check addresses are in range
+ *-------------------------------------------------------------------------*/
+
+static int __verify_virt(volatile void *virt, int line)
+{
+ unsigned long addr = (unsigned long)virt;
+ int ok = ((addr & 0xF0000000) == 0xC0000000);
+ if (!ok)
+ printk(KERN_CRIT __FILE__ ":%d: Expected virtual address, got %p\n", line, virt);
+ return ok;
+}
+static int __verify_dma(dma_addr_t dma, int line)
+{
+ unsigned long addr = (unsigned long)dma;
+ int ok = ((addr & 0xF0000000) == 0xE0000000);
+ if (!ok)
+ printk(KERN_CRIT __FILE__ ":%d: Expected DMA address, got %#08x\n", line, dma);
+ return ok;
+}
+#define verify_virt(virt) __verify_virt((virt), __LINE__)
+#define verify_dma(dma) __verify_dma((dma), __LINE__)
+
+/*-------------------------------------------------------------------------*
+ * Consistent allocation functions
+ *-------------------------------------------------------------------------*/
+
+static int ohci_consistent_storage_create(struct pci_dev *pdev, struct
+ohci_consistent_storage *ocsp)
+{
+ int num_eds = NUM_EDS * 256;
+ int num_tds = 2048;
+ int i;
+ char *storage = NULL;
+ if (ocsp == NULL)
+ return -EINVAL;
+ storage = ocsp->virt = pci_alloc_consistent(pdev,
+ sizeof(ed_t) * num_eds + sizeof(td_t)
+* num_tds,
+ &ocsp->dma_addr);
+ if (storage == NULL)
+ return -ENOMEM;
+ ocsp->edlist = NULL;
+ for (i = 0; i < num_eds; i++) {
+ ed_t *ed = (ed_t *)(storage + i * sizeof(ed_t));
+ *(ed_t **)ed = ocsp->edlist;
+ ocsp->edlist = ed;
+ }
+ ocsp->tdlist = NULL;
+ for (i = 0; i < num_tds; i++) {
+ td_t *td = (td_t *)(storage + i * sizeof(td_t));
+ *(td_t **)td = ocsp->tdlist;
+ ocsp->tdlist = td;
+ }
+
+ ocsp->virt_to_dma = (long)ocsp->dma_addr - (long)ocsp->virt;
+ return 0;
+}
+
+static ed_t *ohci_alloc_consistent_ed(struct ohci *ohci)
+{
+ int flags = 0;
+ ed_t *ed;
+ if (!in_interrupt()) spin_lock_irqsave (&usb_ed_lock, flags);
+ ed = ohci->consistent_storage.edlist;
+ ohci->consistent_storage.edlist = *(ed_t **)ed;
+ if (!in_interrupt()) spin_unlock_irqrestore (&usb_ed_lock, flags);
+ if (0) printk(KERN_CRIT "alloc_consistent_ed: ed=%p\n", ed);
+ return ed;
+}
+
+static void ohci_free_consistent_ed(struct ohci *ohci, ed_t *ed)
+{
+ int flags = 0;
+ if (!in_interrupt()) spin_lock_irqsave (&usb_ed_lock, flags);
+ *(ed_t **)ed = ohci->consistent_storage.edlist;
+ ohci->consistent_storage.edlist = ed;
+ if (!in_interrupt()) spin_unlock_irqrestore (&usb_ed_lock, flags);
+}
+
+static td_t *ohci_alloc_consistent_td(struct ohci *ohci)
+{
+ int flags = 0;
+ td_t *td;
+ if (!in_interrupt()) spin_lock_irqsave (&usb_td_lock, flags);
+ td = ohci->consistent_storage.tdlist;
+ ohci->consistent_storage.tdlist = *(td_t **)td;
+ if (!in_interrupt()) spin_unlock_irqrestore (&usb_td_lock, flags);
+ verify_virt(td);
+ if (0) printk(KERN_CRIT "alloc_consistent_td: td=%p\n", td);
+ return td;
+}
+
+static void ohci_free_consistent_td(struct ohci *ohci, td_t *td)
+{
+ int flags = 0;
+ verify_virt(td);
+ if (!in_interrupt()) spin_lock_irqsave (&usb_td_lock, flags);
+ *(td_t **)td = ohci->consistent_storage.tdlist;
+ ohci->consistent_storage.tdlist = td;
+ if (!in_interrupt()) spin_unlock_irqrestore (&usb_td_lock, flags);
+}
+
+static dma_addr_t ohci_virt_to_bus(struct ohci *ohci, volatile void *obp)
+{
+ dma_addr_t dma = (long)(obp + ohci->consistent_storage.virt_to_dma);
+ verify_virt(obp);
+ verify_dma(dma);
+ return dma;
+}
+
+static void *ohci_bus_to_virt(struct ohci *ohci, dma_addr_t dma_addr)
+{
+ void *virt = (void *)(dma_addr - ohci->consistent_storage.virt_to_dma);
+ verify_virt(virt);
+ verify_dma(dma_addr);
+ return virt;
+}
+
+/*-------------------------------------------------------------------------*
* URB support functions
*-------------------------------------------------------------------------*/
@@ -79,6 +197,7 @@
static void urb_rm_priv (urb_t * urb)
{
+ struct ohci *ohci = (ohci_t *) urb->dev->bus->hcpriv;
urb_priv_t * urb_priv = urb->hcpriv;
int i;
void * wait;
@@ -89,7 +208,7 @@
for (i = 0; i < urb_priv->length; i++) {
if (urb_priv->td [i]) {
- OHCI_FREE (urb_priv->td [i]);
+ OHCI_FREE_TD (ohci, urb_priv->td [i]);
}
}
kfree (urb->hcpriv);
@@ -157,12 +276,12 @@
__u32 * ed_p;
for (i= 0; i < 32; i++) {
j = 5;
- ed_p = &(ohci->hcca.int_table [i]);
+ ed_p = &(ohci->hcca->int_table [i]);
if (*ed_p == 0)
continue;
printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):", str, i, i);
while (*ed_p != 0 && j--) {
- ed_t *ed = (ed_t *) bus_to_virt(le32_to_cpup(ed_p));
+ ed_t *ed = (ed_t *) ohci_bus_to_virt(ohci, le32_to_cpup(ed_p));
printk (" ed: %4x;", ed->hwINFO);
ed_p = &ed->hwNextED;
}
@@ -320,7 +439,7 @@
ohci_dump_status (controller);
if (verbose)
ep_print_int_eds (controller, "hcca");
- dbg ("hcca frame #%04x", controller->hcca.frame_no);
+ dbg ("hcca frame #%04x", controller->hcca->frame_no);
ohci_dump_roothub (controller, 1);
}
@@ -480,7 +599,7 @@
/* allocate the TDs */
for (i = 0; i < size; i++) {
- OHCI_ALLOC (urb_priv->td[i], sizeof (td_t));
+ OHCI_ALLOC_TD (ohci, urb_priv->td[i]);
if (!urb_priv->td[i]) {
usb_dec_dev_use (urb->dev);
urb_rm_priv (urb);
@@ -498,7 +617,7 @@
/* for ISOC transfers calculate start frame index */
if (urb->transfer_flags & USB_ISO_ASAP) {
urb->start_frame = ((ed->state == ED_OPER)? (ed->last_iso + 1):
- (le16_to_cpu
(ohci->hcca.frame_no) + 10)) & 0xffff;
+ (le16_to_cpu
+(ohci->hcca->frame_no) + 10)) & 0xffff;
}
if (ed->state != ED_OPER) /* link the ed into a chain if is not already */
@@ -567,6 +686,8 @@
static int sohci_alloc_dev (struct usb_device *usb_dev)
{
+ int i;
+ struct ohci *ohci = usb_dev->bus->hcpriv;
struct ohci_device * dev;
dev = kmalloc (sizeof (*dev), GFP_KERNEL);
@@ -575,6 +696,12 @@
memset (dev, 0, sizeof (*dev));
+ for (i = 0; i < NUM_EDS; i++) {
+ dev->ed[i] = ohci_alloc_consistent_ed(ohci);
+ if (dev->ed[i] == NULL)
+ return -ENOMEM;
+ }
+
usb_dev->hcpriv = dev;
return 0;
@@ -600,13 +727,14 @@
/* delete all TDs of all EDs */
spin_lock_irqsave (&usb_ed_lock, flags);
for(i = 0; i < NUM_EDS; i++) {
- ed = &(dev->ed[i]);
+ ed = dev->ed[i];
if (ed->state != ED_NEW) {
if (ed->state == ED_OPER) ep_unlink (ohci, ed);
ep_rm_ed (usb_dev, ed);
ed->state = ED_DEL;
cnt++;
}
+ ohci_free_consistent_ed(ohci, ed);
}
spin_unlock_irqrestore (&usb_ed_lock, flags);
@@ -630,7 +758,7 @@
{
ohci_t * ohci = usb_dev->bus->hcpriv;
- return le16_to_cpu (ohci->hcca.frame_no);
+ return le16_to_cpu (ohci->hcca->frame_no);
}
/*-------------------------------------------------------------------------*/
@@ -711,9 +839,9 @@
case CTRL:
ed->hwNextED = 0;
if (ohci->ed_controltail == NULL) {
- writel (virt_to_bus (ed), &ohci->regs->ed_controlhead);
+ writel (ohci_virt_to_bus (ohci, ed),
+&ohci->regs->ed_controlhead);
} else {
- ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus
(ed));
+ ohci->ed_controltail->hwNextED = cpu_to_le32 (ohci_virt_to_bus
+(ohci, ed));
}
ed->ed_prev = ohci->ed_controltail;
ohci->ed_controltail = edi;
@@ -722,9 +850,9 @@
case BULK:
ed->hwNextED = 0;
if (ohci->ed_bulktail == NULL) {
- writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead);
+ writel (ohci_virt_to_bus (ohci, ed), &ohci->regs->ed_bulkhead);
} else {
- ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+ ohci->ed_bulktail->hwNextED = cpu_to_le32 (ohci_virt_to_bus
+(ohci, ed));
}
ed->ed_prev = ohci->ed_bulktail;
ohci->ed_bulktail = edi;
@@ -739,12 +867,12 @@
for (i = 0; i < ep_rev (6, interval); i += inter) {
inter = 1;
- for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) +
int_branch]);
- (*ed_p != 0) && (((ed_t *) bus_to_virt (le32_to_cpup
(ed_p)))->int_interval >= interval);
- ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup
(ed_p)))->hwNextED))
- inter = ep_rev (6, ((ed_t *) bus_to_virt
(le32_to_cpup (ed_p)))->int_interval);
+ for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) +
+int_branch]);
+ (*ed_p != 0) && (((ed_t *) ohci_bus_to_virt (ohci,
+le32_to_cpup (ed_p)))->int_interval >= interval);
+ ed_p = &(((ed_t *) ohci_bus_to_virt (ohci,
+le32_to_cpup (ed_p)))->hwNextED))
+ inter = ep_rev (6, ((ed_t *) ohci_bus_to_virt
+(ohci, le32_to_cpup (ed_p)))->int_interval);
ed->hwNextED = *ed_p;
- *ed_p = cpu_to_le32 (virt_to_bus (ed));
+ *ed_p = cpu_to_le32 (ohci_virt_to_bus (ohci, ed));
}
#ifdef DEBUG
ep_print_int_eds (ohci, "LINK_INT");
@@ -755,16 +883,16 @@
ed->hwNextED = 0;
ed->int_interval = 1;
if (ohci->ed_isotail != NULL) {
- ohci->ed_isotail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+ ohci->ed_isotail->hwNextED = cpu_to_le32 (ohci_virt_to_bus
+(ohci, ed));
ed->ed_prev = ohci->ed_isotail;
} else {
for ( i = 0; i < 32; i += inter) {
inter = 1;
- for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]);
+ for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]);
*ed_p != 0;
- ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup
(ed_p)))->hwNextED))
- inter = ep_rev (6, ((ed_t *)
bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
- *ed_p = cpu_to_le32 (virt_to_bus (ed));
+ ed_p = &(((ed_t *) ohci_bus_to_virt (ohci,
+le32_to_cpup (ed_p)))->hwNextED))
+ inter = ep_rev (6, ((ed_t *)
+ohci_bus_to_virt (ohci, le32_to_cpup (ed_p)))->int_interval);
+ *ed_p = cpu_to_le32 (ohci_virt_to_bus (ohci, ed));
+
}
ed->ed_prev = NULL;
}
@@ -803,7 +931,7 @@
if(ohci->ed_controltail == ed) {
ohci->ed_controltail = ed->ed_prev;
} else {
- ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev
= ed->ed_prev;
+ ((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup
+(&ed->hwNextED)))->ed_prev = ed->ed_prev;
}
break;
@@ -816,7 +944,7 @@
if (ohci->ed_bulktail == ed) {
ohci->ed_bulktail = ed->ed_prev;
} else {
- ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev
= ed->ed_prev;
+ ((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup
+(&ed->hwNextED)))->ed_prev = ed->ed_prev;
}
break;
@@ -825,11 +953,11 @@
interval = ed->int_interval;
for (i = 0; i < ep_rev (6, interval); i += inter) {
- for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) +
int_branch]), inter = 1;
+ for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) +
+int_branch]), inter = 1;
(*ed_p != 0) && (*ed_p != ed->hwNextED);
- ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup
(ed_p)))->hwNextED),
- inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup
(ed_p)))->int_interval)) {
- if(((ed_t *) bus_to_virt (le32_to_cpup
(ed_p))) == ed) {
+ ed_p = &(((ed_t *) ohci_bus_to_virt (ohci,
+le32_to_cpup (ed_p)))->hwNextED),
+ inter = ep_rev (6, ((ed_t *) ohci_bus_to_virt (ohci,
+le32_to_cpup (ed_p)))->int_interval)) {
+ if(((ed_t *) ohci_bus_to_virt (ohci,
+le32_to_cpup (ed_p))) == ed) {
*ed_p = ed->hwNextED;
break;
}
@@ -846,18 +974,18 @@
if (ohci->ed_isotail == ed)
ohci->ed_isotail = ed->ed_prev;
if (ed->hwNextED != 0)
- ((ed_t *) bus_to_virt (le32_to_cpup
(&ed->hwNextED)))->ed_prev = ed->ed_prev;
+ ((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup
+(&ed->hwNextED)))->ed_prev = ed->ed_prev;
if (ed->ed_prev != NULL) {
ed->ed_prev->hwNextED = ed->hwNextED;
} else {
for (i = 0; i < 32; i += inter) {
inter = 1;
- for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]);
+ for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]);
*ed_p != 0;
- ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup
(ed_p)))->hwNextED)) {
- inter = ep_rev (6, ((ed_t *)
bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
- if(((ed_t *) bus_to_virt (le32_to_cpup
(ed_p))) == ed) {
+ ed_p = &(((ed_t *) ohci_bus_to_virt (ohci,
+le32_to_cpup (ed_p)))->hwNextED)) {
+ inter = ep_rev (6, ((ed_t *)
+ohci_bus_to_virt (ohci, le32_to_cpup (ed_p)))->int_interval);
+ if(((ed_t *) ohci_bus_to_virt (ohci,
+le32_to_cpup (ed_p))) == ed) {
*ed_p = ed->hwNextED;
break;
}
@@ -892,17 +1020,16 @@
spin_lock (&usb_ed_lock);
- ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) |
- (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
+ ed = ed_ret = (usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) |
+(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
if((ed->state & ED_DEL) || (ed->state & ED_URB_DEL))
return NULL; /* pending delete request */
if (ed->state == ED_NEW) {
ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
- OHCI_ALLOC (td, sizeof (*td)); /* dummy td; end of td list for ed */
+ OHCI_ALLOC_TD (ohci, td); /* dummy td; end of td list for ed */
if(!td) return NULL; /* out of memory */
- ed->hwTailP = cpu_to_le32 (virt_to_bus (td));
+ ed->hwTailP = cpu_to_le32 (ohci_virt_to_bus (ohci, td));
ed->hwHeadP = ed->hwTailP;
ed->state = ED_UNLINK;
ed->type = usb_pipetype (pipe);
@@ -945,7 +1072,7 @@
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
writel (OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */
- frame = le16_to_cpu (ohci->hcca.frame_no) & 0x1;
+ frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1;
ed->ed_rm_list = ohci->ed_rm_list[frame];
ohci->ed_rm_list[frame] = ed;
@@ -965,34 +1092,45 @@
/* prepare a TD */
-static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int type,
int index)
+static void td_fill (struct ohci *ohci, unsigned int info, dma_addr_t data, int len,
+urb_t * urb, int type, int index)
{
volatile td_t * td, * td_pt;
urb_priv_t * urb_priv = urb->hcpriv;
+ if (urb_priv == NULL) return;
+
if (index >= urb_priv->length) {
err("internal OHCI error: TD index > length");
return;
}
td_pt = urb_priv->td [index];
+ if (td_pt == NULL) return;
+
+
/* fill the old dummy TD */
- td = urb_priv->td [index] = (td_t *) bus_to_virt (le32_to_cpup
(&urb_priv->ed->hwTailP) & 0xfffffff0);
+ td = urb_priv->td [index] = (td_t *) ohci_bus_to_virt (ohci, le32_to_cpup
+(&urb_priv->ed->hwTailP) & 0xfffffff0);
+ if (td == NULL) return;
+
td->ed = urb_priv->ed;
+
+ if (td->ed == NULL) return;
+
td->index = index;
td->urb = urb;
td->hwINFO = cpu_to_le32 (info);
td->type = type;
if ((td->ed->type & 3) == PIPE_ISOCHRONOUS) {
td->hwCBP = cpu_to_le32 (((!data || !len)?
- 0 : virt_to_bus
(data)) & 0xFFFFF000);
+ 0 : data) & 0xFFFFF000);
td->ed->last_iso = info & 0xffff;
} else {
- td->hwCBP = cpu_to_le32 (((!data || !len)? 0 : virt_to_bus (data)));
+ td->hwCBP = cpu_to_le32 (((!data || !len)? 0 : data));
}
- td->hwBE = cpu_to_le32 ((!data || !len )? 0: virt_to_bus (data + len - 1));
- td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt));
- td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000);
+ td->hwBE = cpu_to_le32 ((!data || !len )? 0: (data + len - 1));
+ td->hwNextTD = cpu_to_le32 (ohci_virt_to_bus (ohci, td_pt));
+ td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
+
td_pt->hwNextTD = 0;
td->ed->hwTailP = td->hwNextTD;
@@ -1010,10 +1148,15 @@
void * ctrl = urb->setup_packet;
void * data = urb->transfer_buffer;
int data_len = urb->transfer_buffer_length;
+ dma_addr_t dma_data =
+ pci_map_single(ohci->consistent_storage.pci_dev,
+ data, data_len, usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE
+: PCI_DMA_FROMDEVICE);
int cnt = 0;
__u32 info = 0;
unsigned int toggle = 0;
+ urb_priv->transfer_buffer_dma_addr = dma_data;
+
/* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for
reseting */
if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe))) {
toggle = TD_T_TOGGLE;
@@ -1029,12 +1172,12 @@
info = usb_pipeout (urb->pipe)?
TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
while(data_len > 4096) {
- td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096,
urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
- data += 4096; data_len -= 4096; cnt++;
+ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle),
+dma_data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
+ dma_data += 4096; data_len -= 4096; cnt++;
}
info = usb_pipeout (urb->pipe)?
TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
- td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len,
urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
+ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), dma_data,
+data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
cnt++;
writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list
*/
break;
@@ -1042,27 +1185,29 @@
case PIPE_INTERRUPT:
info = usb_pipeout (urb->pipe)?
TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN |
toggle;
- td_fill (info, data, data_len, urb, ST_ADDR | ADD_LEN, cnt++);
+ td_fill (ohci, info, dma_data, data_len, urb, ST_ADDR |
+ADD_LEN, cnt++);
break;
case PIPE_CONTROL:
info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
- td_fill (info, ctrl, 8, urb, ST_ADDR, cnt++);
+ td_fill (ohci, info,
+pci_map_single(ohci->consistent_storage.pci_dev, ctrl, 8, PCI_DMA_TODEVICE),
+ 8, urb, ST_ADDR, cnt++);
if (data_len > 0) {
info = usb_pipeout (urb->pipe)?
TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC
| TD_R | TD_DP_IN | TD_T_DATA1;
- td_fill (info, data, data_len, urb, ADD_LEN, cnt++);
+ td_fill (ohci, info, dma_data, data_len, urb, ADD_LEN,
+cnt++);
}
info = usb_pipeout (urb->pipe)?
TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT |
TD_T_DATA1;
- td_fill (info, NULL, 0, urb, 0, cnt++);
+ td_fill (ohci, info, (dma_addr_t)NULL, 0, urb, 0, cnt++);
writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control
list */
break;
case PIPE_ISOCHRONOUS:
for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
- td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) &
0xffff),
- (__u8 *) data +
urb->iso_frame_desc[cnt].offset,
+ td_fill (ohci,
+ TD_CC|TD_ISO | ((urb->start_frame + cnt) &
+0xffff),
+ dma_data + urb->iso_frame_desc[cnt].offset,
urb->iso_frame_desc[cnt].length, urb, (cnt? 0:
ST_ADDR) | ADD_LEN, cnt);
}
break;
@@ -1088,11 +1233,11 @@
spin_lock_irqsave (&usb_ed_lock, flags);
- td_list_hc = le32_to_cpup (&ohci->hcca.done_head) & 0xfffffff0;
- ohci->hcca.done_head = 0;
+ td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0;
+ ohci->hcca->done_head = 0;
while (td_list_hc) {
- td_list = (td_t *) bus_to_virt (td_list_hc);
+ td_list = (td_t *) ohci_bus_to_virt (ohci, td_list_hc);
if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
@@ -1135,8 +1280,8 @@
spin_lock_irqsave (&usb_ed_lock, flags);
for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
- tdTailP = bus_to_virt (le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
- tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+ tdTailP = ohci_bus_to_virt (ohci, le32_to_cpup (&ed->hwTailP) &
+0xfffffff0);
+ tdHeadP = ohci_bus_to_virt (ohci, le32_to_cpup (&ed->hwHeadP) &
+0xfffffff0);
edINFO = le32_to_cpup (&ed->hwINFO);
td_p = &ed->hwHeadP;
@@ -1144,7 +1289,7 @@
urb_t * urb = td->urb;
urb_priv_t * urb_priv = td->urb->hcpriv;
- td_next = bus_to_virt (le32_to_cpup (&td->hwNextTD) &
0xfffffff0);
+ td_next = ohci_bus_to_virt (ohci, le32_to_cpup (&td->hwNextTD)
+& 0xfffffff0);
if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {
*td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3));
if(++ (urb_priv->td_cnt) == urb_priv->length)
@@ -1156,7 +1301,7 @@
}
if (ed->state & ED_DEL) { /* set by sohci_free_dev */
struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO &
0x7F]);
- OHCI_FREE (tdTailP); /* free dummy td */
+ OHCI_FREE_TD (ohci, tdTailP); /* free dummy td */
ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP);
ed->state = ED_NEW;
/* if all eds are removed wake up sohci_free_dev */
@@ -1235,9 +1380,9 @@
} else {
if (tdBE != 0) {
if (td_list->hwCBP == 0)
- urb->actual_length = bus_to_virt
(tdBE) - urb->transfer_buffer + 1;
+ urb->actual_length = tdBE -
+urb_priv->transfer_buffer_dma_addr + 1;
else
- urb->actual_length = bus_to_virt
(tdCBP) - urb->transfer_buffer;
+ urb->actual_length = tdCBP -
+urb_priv->transfer_buffer_dma_addr;
}
}
}
@@ -1699,7 +1844,7 @@
writel (0, &ohci->regs->ed_controlhead);
writel (0, &ohci->regs->ed_bulkhead);
- writel (virt_to_bus (&ohci->hcca), &ohci->regs->hcca); /* a reset clears this
*/
+ writel (ohci->hcca_dma_addr, &ohci->regs->hcca); /* a reset clears this */
fminterval = 0x2edf;
writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
@@ -1751,14 +1896,14 @@
struct ohci_regs * regs = ohci->regs;
int ints;
- if ((ohci->hcca.done_head != 0) && !(le32_to_cpup (&ohci->hcca.done_head) &
0x01)) {
+ if ((ohci->hcca->done_head != 0) && !(le32_to_cpup (&ohci->hcca->done_head) &
+0x01)) {
ints = OHCI_INTR_WDH;
} else {
if ((ints = (readl (®s->intrstatus) & readl (®s->intrenable)))
== 0)
return;
}
- // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no));
+ // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no));
if (ints & OHCI_INTR_UE) {
ohci->disabled++;
@@ -1778,7 +1923,7 @@
}
if (ints & OHCI_INTR_SF) {
- unsigned int frame = le16_to_cpu (ohci->hcca.frame_no) & 1;
+ unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1;
writel (OHCI_INTR_SF, ®s->intrdisable);
if (ohci->ed_rm_list[!frame] != NULL) {
dl_del_list (ohci, !frame);
@@ -1793,30 +1938,39 @@
/* allocate OHCI */
-static ohci_t * hc_alloc_ohci (void * mem_base)
+static ohci_t * hc_alloc_ohci (struct pci_dev *pdev, void * mem_base)
{
int i;
ohci_t * ohci;
struct usb_bus * bus;
+ int err;
ohci = (ohci_t *) __get_free_pages (GFP_KERNEL, 1);
if (!ohci)
return NULL;
memset (ohci, 0, sizeof (ohci_t));
+ ohci->hcca = (struct ohci_hcca *)pci_alloc_consistent(pdev, sizeof(struct
+ohci_hcca), &ohci->hcca_dma_addr);
+ if (!ohci->hcca)
+ return NULL;
ohci->irq = -1;
ohci->regs = mem_base;
/* for load ballancing of the interrupt branches */
for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
- for (i = 0; i < NUM_INTS; i++) ohci->hcca.int_table[i] = 0;
+ for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0;
/* end of control and bulk lists */
ohci->ed_isotail = NULL;
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
+ if ((err = ohci_consistent_storage_create(pdev, &ohci->consistent_storage))) {
+ printk(KERN_ERR "hc_alloc_ohci: failed to allocate consistent storage:
+err=%d\n", err);
+ return NULL;
+ }
+
bus = usb_alloc_bus (&sohci_device_operations);
if (!bus) {
free_pages ((unsigned long) ohci, 1);
@@ -1878,7 +2032,7 @@
(unsigned long) mem_base, bufp);
printk(KERN_INFO __FILE__ ": %s\n", dev->name);
- ohci = hc_alloc_ohci (mem_base);
+ ohci = hc_alloc_ohci (dev, mem_base);
if (!ohci) {
return -ENOMEM;
}
Index: drivers/usb/usb-ohci.h
===================================================================
RCS file: /cvs/linux/kernel/drivers/usb/usb-ohci.h,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -w -r1.1.1.1 -r1.3
--- drivers/usb/usb-ohci.h 2000/05/03 22:12:55 1.1.1.1
+++ drivers/usb/usb-ohci.h 2000/05/04 15:51:38 1.3
@@ -374,21 +374,39 @@
__u16 td_cnt; // number of tds already serviced
int state;
void * wait;
+ dma_addr_t transfer_buffer_dma_addr;
td_t * td[0]; // list pointer to all corresponding TDs associated with this
request
} urb_priv_t;
#define URB_DEL 1
+
/*
+ * In order to use the pci_alloc_consistent interface to allocate ED's and
+ * TD's, we need to keep a mapping between the virtual addresses and the
+ * dma addresses. We do this using a kmem_cache, allowing it a single slab
+ * only.
+ */
+
+struct ohci_consistent_storage {
+ char *virt;
+ dma_addr_t dma_addr;
+ long virt_to_dma;
+ ed_t *edlist; /* freelist of eds */
+ td_t *tdlist; /* freelist of tds */
+ struct pci_dev *pci_dev;
+};
+
+/*
* This is the full ohci controller description
*
* Note how the "proper" USB information is just
* a subset of what the full implementation needs. (Linus)
*/
-
typedef struct ohci {
- struct ohci_hcca hcca; /* hcca */
+ struct ohci_hcca *hcca; /* hcca */
+ dma_addr_t hcca_dma_addr;
int irq;
int disabled; /* e.g. got a UE, we're hung */
@@ -410,6 +428,7 @@
struct usb_bus * bus;
struct usb_device * dev[128];
struct virt_root_hub rh;
+ struct ohci_consistent_storage consistent_storage;
} ohci_t;
@@ -417,7 +436,7 @@
#define NUM_EDS 32 /* num of preallocated endpoint descriptors */
struct ohci_device {
- ed_t ed[NUM_EDS];
+ ed_t *ed[NUM_EDS];
int ed_cnt;
void * wait;
};
@@ -432,7 +451,7 @@
static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval,
int load);
static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
/* td */
-static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int type,
int index);
+static void td_fill(struct ohci *, unsigned int info, dma_addr_t data, int len, urb_t
+* urb, int type, int index);
static void td_submit_urb(urb_t * urb);
/* root hub */
static int rh_submit_urb(urb_t * urb);
@@ -440,11 +459,11 @@
static int rh_init_int_timer(urb_t * urb);
#ifdef DEBUG
-#define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d: %4x\n", -- __ohci_free_cnt,
(unsigned int) x)
-#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC :
GFP_KERNEL); printk("OHCI ALLO: %d: %4x\n", ++ __ohci_free_cnt,(unsigned int) x)
+#define OHCI_FREE_TD(ohci,x) ohci_free_consistent_td(ohci, x); printk("OHCI FREE: %d:
+%4x\n", -- __ohci_free_cnt, (unsigned int) x)
+#define OHCI_ALLOC_TD(ohci,x) (x) = ohci_alloc_consistent_td(ohci); printk("OHCI
+ALLO: %d: %4x\n", ++ __ohci_free_cnt,(unsigned int) x)
static int __ohci_free_cnt = 0;
#else
-#define OHCI_FREE(x) kfree(x)
-#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC :
GFP_KERNEL)
+#define OHCI_FREE_TD(ohci, x) ohci_free_consistent_td(ohci, x)
+#define OHCI_ALLOC_TD(ohci, x) (x) = ohci_alloc_consistent_td(ohci)
#endif
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]