[U-Boot] [PATCH 10/16] usb: ohci: Add proper cache flushing / invalidating for non cache coherent cpus

Hans de Goede hdegoede at redhat.com
Tue May 5 23:56:13 CEST 2015


Add proper cache flushing / invalidating for non cache coherent cpus, for now
only enable this for new (driver-model) usb code to avoid regressions.

Signed-off-by: Hans de Goede <hdegoede at redhat.com>
---
 drivers/usb/host/ohci-hcd.c | 67 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/ohci.h     | 22 +++++++++++----
 2 files changed, 84 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 02aa7f3..3744658 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -103,6 +103,32 @@ static struct pci_device_id ehci_pci_ids[] = {
 # define m32_swap(x) cpu_to_le32(x)
 #endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */
 
+#ifdef CONFIG_DM_USB
+/*
+ * We really should do proper cache flushing everywhere, but for now we only
+ * do it for new (driver-model) usb code to avoid regressions.
+ */
+#define flush_dcache_buffer(addr, size) \
+	flush_dcache_range((unsigned long)(addr), \
+		ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN))
+#define invalidate_dcache_buffer(addr, size) \
+	invalidate_dcache_range((unsigned long)(addr), \
+		ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN))
+#else
+#define flush_dcache_buffer(addr, size)
+#define invalidate_dcache_buffer(addr, size)
+#endif
+
+/* Do not use sizeof(ed / td) as our ed / td structs contain extra members */
+#define flush_dcache_ed(addr) flush_dcache_buffer(addr, 16)
+#define flush_dcache_td(addr) flush_dcache_buffer(addr, 16)
+#define flush_dcache_iso_td(addr) flush_dcache_buffer(addr, 32)
+#define flush_dcache_hcca(addr) flush_dcache_buffer(addr, 256)
+#define invalidate_dcache_ed(addr) invalidate_dcache_buffer(addr, 16)
+#define invalidate_dcache_td(addr) invalidate_dcache_buffer(addr, 16)
+#define invalidate_dcache_iso_td(addr) invalidate_dcache_buffer(addr, 32)
+#define invalidate_dcache_hcca(addr) invalidate_dcache_buffer(addr, 256)
+
 /* global ohci_t */
 static ohci_t gohci;
 /* this must be aligned to a 256 byte boundary */
@@ -293,9 +319,11 @@ void ep_print_int_eds(ohci_t *ohci, char *str)
 		ed_p = &(ohci->hcca->int_table [i]);
 		if (*ed_p == 0)
 		    continue;
+		invalidate_dcache_ed(ed_p);
 		printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i);
 		while (*ed_p != 0 && j--) {
 			ed_t *ed = (ed_t *)m32_swap(ed_p);
+			invalidate_dcache_ed(ed);
 			printf(" ed: %4x;", ed->hwINFO);
 			ed_p = &ed->hwNextED;
 		}
@@ -326,6 +354,7 @@ static void maybe_print_eds(char *label, __u32 value)
 
 	if (value) {
 		dbg("%s %08x", label, value);
+		invalidate_dcache_ed(edp);
 		dbg("%08x", edp->hwINFO);
 		dbg("%08x", edp->hwTailP);
 		dbg("%08x", edp->hwHeadP);
@@ -460,6 +489,7 @@ static void ohci_dump(ohci_t *controller, int verbose)
 	ohci_dump_status(controller);
 	if (verbose)
 		ep_print_int_eds(controller, "hcca");
+	invalidate_dcache_hcca(controller->hcca);
 	dbg("hcca frame #%04x", controller->hcca->frame_no);
 	ohci_dump_roothub(controller, 1);
 }
@@ -597,6 +627,7 @@ static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb)
 /* tell us the current USB frame number */
 static int sohci_get_current_frame_number(ohci_t *ohci)
 {
+	invalidate_dcache_hcca(ohci->hcca);
 	return m16_swap(ohci->hcca->frame_no);
 }
 #endif
@@ -675,6 +706,7 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
 	switch (ed->type) {
 	case PIPE_CONTROL:
 		ed->hwNextED = 0;
+		flush_dcache_ed(ed);
 		if (ohci->ed_controltail == NULL)
 			ohci_writel(ed, &ohci->regs->ed_controlhead);
 		else
@@ -692,6 +724,7 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
 
 	case PIPE_BULK:
 		ed->hwNextED = 0;
+		flush_dcache_ed(ed);
 		if (ohci->ed_bulktail == NULL)
 			ohci_writel(ed, &ohci->regs->ed_bulkhead);
 		else
@@ -724,7 +757,9 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
 					inter = ep_rev(6,
 						 ((ed_t *)ed_p)->int_interval);
 			ed->hwNextED = *ed_p;
+			flush_dcache_ed(ed);
 			*ed_p = m32_swap((unsigned long)ed);
+			flush_dcache_hcca(ohci->hcca);
 		}
 		break;
 	}
@@ -737,6 +772,8 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
 static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed,
 			    unsigned index, unsigned period)
 {
+	__maybe_unused unsigned long aligned_ed_p;
+
 	for (; index < NUM_INTS; index += period) {
 		__u32	*ed_p = &ohci->hcca->int_table [index];
 
@@ -745,6 +782,12 @@ static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed,
 			if (((struct ed *)
 					m32_swap((unsigned long)ed_p)) == ed) {
 				*ed_p = ed->hwNextED;
+#ifdef CONFIG_DM_USB
+				aligned_ed_p = (unsigned long)ed_p;
+				aligned_ed_p &= ~(ARCH_DMA_MINALIGN - 1);
+				flush_dcache_range(aligned_ed_p,
+					aligned_ed_p + ARCH_DMA_MINALIGN);
+#endif
 				break;
 			}
 			ed_p = &(((struct ed *)
@@ -764,6 +807,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
 	int i;
 
 	ed->hwINFO |= m32_swap(OHCI_ED_SKIP);
+	flush_dcache_ed(ed);
 
 	switch (ed->type) {
 	case PIPE_CONTROL:
@@ -777,6 +821,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
 				&ohci->regs->ed_controlhead);
 		} else {
 			ed->ed_prev->hwNextED = ed->hwNextED;
+			flush_dcache_ed(ed->ed_prev);
 		}
 		if (ohci->ed_controltail == ed) {
 			ohci->ed_controltail = ed->ed_prev;
@@ -797,6 +842,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
 			       &ohci->regs->ed_bulkhead);
 		} else {
 			ed->ed_prev->hwNextED = ed->hwNextED;
+			flush_dcache_ed(ed->ed_prev);
 		}
 		if (ohci->ed_bulktail == ed) {
 			ohci->ed_bulktail = ed->ed_prev;
@@ -865,6 +911,8 @@ static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev,
 		ed->int_load = load;
 	}
 
+	flush_dcache_ed(ed);
+
 	return ed_ret;
 }
 
@@ -890,6 +938,7 @@ static void td_fill(ohci_t *ohci, unsigned int info,
 	/* use this td as the next dummy */
 	td_pt = urb_priv->td [index];
 	td_pt->hwNextTD = 0;
+	flush_dcache_td(td_pt);
 
 	/* fill the old dummy TD */
 	td = urb_priv->td [index] =
@@ -917,9 +966,11 @@ static void td_fill(ohci_t *ohci, unsigned int info,
 		td->hwBE = 0;
 
 	td->hwNextTD = m32_swap((unsigned long)td_pt);
+	flush_dcache_td(td);
 
 	/* append to queue */
 	td->ed->hwTailP = td->hwNextTD;
+	flush_dcache_ed(td->ed);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -937,6 +988,8 @@ static void td_submit_job(ohci_t *ohci, struct usb_device *dev,
 	__u32 info = 0;
 	unsigned int toggle = 0;
 
+	flush_dcache_buffer(buffer, data_len);
+
 	/* OHCI handles the DATA-toggles itself, we just use the USB-toggle
 	 * bits for reseting */
 	if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
@@ -976,6 +1029,7 @@ static void td_submit_job(ohci_t *ohci, struct usb_device *dev,
 	case PIPE_CONTROL:
 		/* Setup phase */
 		info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+		flush_dcache_buffer(setup, 8);
 		td_fill(ohci, info, setup, 8, dev, cnt++, urb);
 
 		/* Optional Data phase */
@@ -1047,6 +1101,7 @@ static void check_status(td_t *td_list)
 	if (cc) {
 		err(" USB-error: %s (%x)", cc_to_string[cc], cc);
 
+		invalidate_dcache_ed(td_list->ed);
 		if (*phwHeadP & m32_swap(0x1)) {
 			if (lurb_priv &&
 			    ((td_list->index + 1) < urb_len)) {
@@ -1059,9 +1114,11 @@ static void check_status(td_t *td_list)
 						     td_list->index - 1;
 			} else
 				*phwHeadP &= m32_swap(0xfffffff2);
+			flush_dcache_ed(td_list->ed);
 		}
 #ifdef CONFIG_MPC5200
 		td_list->hwNextTD = 0;
+		flush_dcache_td(td_list);
 #endif
 	}
 }
@@ -1074,11 +1131,14 @@ static td_t *dl_reverse_done_list(ohci_t *ohci)
 	td_t *td_rev = NULL;
 	td_t *td_list = NULL;
 
+	invalidate_dcache_hcca(ohci->hcca);
 	td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0;
 	ohci->hcca->done_head = 0;
+	flush_dcache_hcca(ohci->hcca);
 
 	while (td_list_hc) {
 		td_list = (td_t *)td_list_hc;
+		invalidate_dcache_td(td_list);
 		check_status(td_list);
 		td_list->next_dl_td = td_rev;
 		td_rev = td_list;
@@ -1113,6 +1173,7 @@ static int takeback_td(ohci_t *ohci, td_t *td_list)
 	urb_priv_t *lurb_priv;
 	__u32 tdINFO, edHeadP, edTailP;
 
+	invalidate_dcache_td(td_list);
 	tdINFO = m32_swap(td_list->hwINFO);
 
 	ed = td_list->ed;
@@ -1138,6 +1199,7 @@ static int takeback_td(ohci_t *ohci, td_t *td_list)
 		lurb_priv->td_cnt, lurb_priv->length);
 
 	if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) {
+		invalidate_dcache_ed(ed);
 		edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0;
 		edTailP = m32_swap(ed->hwTailP);
 
@@ -1521,6 +1583,9 @@ static int submit_common_msg(ohci_t *ohci, struct usb_device *dev,
 	dev->status = stat;
 	dev->act_len = urb->actual_length;
 
+	if (usb_pipein(pipe) && dev->status == 0 && dev->act_len)
+		invalidate_dcache_buffer(buffer, dev->act_len);
+
 #ifdef DEBUG
 	pkt_print(ohci, urb, dev, pipe, buffer, transfer_len,
 		  setup, "RET(ctlr)", usb_pipein(pipe));
@@ -1727,6 +1792,8 @@ static int hc_interrupt(ohci_t *ohci)
 	int ints;
 	int stat = -1;
 
+	invalidate_dcache_hcca(ohci->hcca);
+
 	if ((ohci->hcca->done_head != 0) &&
 				!(m32_swap(ohci->hcca->done_head) & 0x01)) {
 		ints =  OHCI_INTR_WDH;
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 24f5e4e..f52b4c1 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -18,6 +18,18 @@
 # define ohci_writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a))
 #endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */
 
+#if defined CONFIG_DM_USB && ARCH_DMA_MINALIGN > 16
+#define ED_ALIGNMENT ARCH_DMA_MINALIGN
+#else
+#define ED_ALIGNMENT 16
+#endif
+
+#if defined CONFIG_DM_USB && ARCH_DMA_MINALIGN > 32
+#define TD_ALIGNMENT ARCH_DMA_MINALIGN
+#else
+#define TD_ALIGNMENT 32
+#endif
+
 /* functions for doing board or CPU specific setup/cleanup */
 int usb_board_stop(void);
 
@@ -52,7 +64,7 @@ struct ed {
 	struct usb_device *usb_dev;
 	void *purb;
 	__u32 unused[2];
-} __attribute__((aligned(16)));
+} __attribute__((aligned(ED_ALIGNMENT)));
 typedef struct ed ed_t;
 
 
@@ -112,7 +124,7 @@ struct td {
 	__u32 data;
 
 	__u32 unused2[2];
-} __attribute__((aligned(32)));
+} __attribute__((aligned(TD_ALIGNMENT)));
 typedef struct td td_t;
 
 #define OHCI_ED_SKIP	(1 << 14)
@@ -356,8 +368,8 @@ typedef struct
 #define NUM_TD 64		/* we need more TDs than EDs */
 
 typedef struct ohci_device {
-	ed_t ed[NUM_EDS] __aligned(16);
-	td_t tds[NUM_TD] __aligned(32);
+	ed_t ed[NUM_EDS] __aligned(ED_ALIGNMENT);
+	td_t tds[NUM_TD] __aligned(TD_ALIGNMENT);
 	int ed_cnt;
 } ohci_dev_t;
 
@@ -371,7 +383,7 @@ typedef struct ohci_device {
 
 typedef struct ohci {
 	/* this allocates EDs for all possible endpoints */
-	struct ohci_device ohci_dev __aligned(32);
+	struct ohci_device ohci_dev __aligned(TD_ALIGNMENT);
 	struct ohci_hcca *hcca;		/* hcca */
 	/*dma_addr_t hcca_dma;*/
 
-- 
2.3.6



More information about the U-Boot mailing list