From: Hans de Goede Date: Tue, 5 May 2015 21:56:13 +0000 (+0200) Subject: usb: ohci: Add proper cache flushing / invalidating for non cache coherent cpus X-Git-Tag: v2015.07-rc2~285^2~6 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=8d005ef81a7a6608401410a0f55fbcd5dd72f29c;p=oweals%2Fu-boot.git usb: ohci: Add proper cache flushing / invalidating for non cache coherent cpus 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 Reviewed-by: Marek Vasut --- diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 02aa7f3927..3744658e40 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 24f5e4e3e8..f52b4c1bb5 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;*/