| 1 | From 92b8d976e58fe5e6eb97aedcaa46e80c924fbc04 Mon Sep 17 00:00:00 2001 |
| 2 | From: Wang Huan <wanghuan@zch06.freescale.net> |
| 3 | Date: Mon, 5 Sep 2011 08:59:46 +0800 |
| 4 | Subject: [PATCH] Fix FEC driver bugs for MCF547x/MCF548x |
| 5 | |
| 6 | This patch fixed kernel panic during flood ping with huge packets. |
| 7 | It also fixed the data integrity errors when running iozone on an |
| 8 | NFSv3-mounted file system. |
| 9 | |
| 10 | Signed-off-by: Jason Jin <jason.jin@freescale.com> |
| 11 | --- |
| 12 | arch/m68k/include/asm/cf_548x_cacheflush.h | 45 ++++++++++++++++++++++++--- |
| 13 | drivers/net/fec_m547x.c | 18 +++++----- |
| 14 | 2 files changed, 49 insertions(+), 14 deletions(-) |
| 15 | |
| 16 | --- a/arch/m68k/include/asm/cf_548x_cacheflush.h |
| 17 | +++ b/arch/m68k/include/asm/cf_548x_cacheflush.h |
| 18 | @@ -27,8 +27,8 @@ |
| 19 | unsigned long end_set; \ |
| 20 | \ |
| 21 | start_set = 0; \ |
| 22 | - end_set = (unsigned long)LAST_DCACHE_ADDR; \ |
| 23 | - \ |
| 24 | + end_set = (unsigned long)LAST_ICACHE_ADDR; \ |
| 25 | + asm("nop"); \ |
| 26 | for (set = start_set; set <= end_set; set += (0x10 - 3)) {\ |
| 27 | asm volatile("cpushl %%ic,(%0)\n" \ |
| 28 | "\taddq%.l #1,%0\n" \ |
| 29 | @@ -48,7 +48,7 @@ |
| 30 | \ |
| 31 | start_set = 0; \ |
| 32 | end_set = (unsigned long)LAST_DCACHE_ADDR; \ |
| 33 | - \ |
| 34 | + asm("nop"); \ |
| 35 | for (set = start_set; set <= end_set; set += (0x10 - 3)) { \ |
| 36 | asm volatile("cpushl %%dc,(%0)\n" \ |
| 37 | "\taddq%.l #1,%0\n" \ |
| 38 | @@ -68,7 +68,7 @@ |
| 39 | \ |
| 40 | start_set = 0; \ |
| 41 | end_set = (unsigned long)LAST_DCACHE_ADDR; \ |
| 42 | - \ |
| 43 | + asm("nop"); \ |
| 44 | for (set = start_set; set <= end_set; set += (0x10 - 3)) { \ |
| 45 | asm volatile("cpushl %%bc,(%0)\n" \ |
| 46 | "\taddq%.l #1,%0\n" \ |
| 47 | @@ -240,12 +240,47 @@ extern inline void flush_icache_range(un |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | +static inline void flush_dcache_range(unsigned long address, |
| 52 | + unsigned long endaddr) |
| 53 | +{ |
| 54 | + unsigned long set; |
| 55 | + unsigned long start_set; |
| 56 | + unsigned long end_set; |
| 57 | + |
| 58 | + start_set = address & _DCACHE_SET_MASK; |
| 59 | + end_set = endaddr & _DCACHE_SET_MASK; |
| 60 | + |
| 61 | + if (start_set > end_set) { |
| 62 | + /* from the begining to the lowest address */ |
| 63 | + for (set = 0; set <= end_set; set += (0x10 - 3)) { |
| 64 | + asm volatile("cpushl %%dc,(%0)\n" |
| 65 | + "\taddq%.l #1,%0\n" |
| 66 | + "\tcpushl %%dc,(%0)\n" |
| 67 | + "\taddq%.l #1,%0\n" |
| 68 | + "\tcpushl %%dc,(%0)\n" |
| 69 | + "\taddq%.l #1,%0\n" |
| 70 | + "\tcpushl %%dc,(%0)" : "=a" (set) : "a" (set)); |
| 71 | + } |
| 72 | + /* next loop will finish the cache ie pass the hole */ |
| 73 | + end_set = LAST_ICACHE_ADDR; |
| 74 | + } |
| 75 | + for (set = start_set; set <= end_set; set += (0x10 - 3)) { |
| 76 | + asm volatile("cpushl %%dc,(%0)\n" |
| 77 | + "\taddq%.l #1,%0\n" |
| 78 | + "\tcpushl %%dc,(%0)\n" |
| 79 | + "\taddq%.l #1,%0\n" |
| 80 | + "\tcpushl %%dc,(%0)\n" |
| 81 | + "\taddq%.l #1,%0\n" |
| 82 | + "\tcpushl %%dc,(%0)" : "=a" (set) : "a" (set)); |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | static inline void copy_to_user_page(struct vm_area_struct *vma, |
| 87 | struct page *page, unsigned long vaddr, |
| 88 | void *dst, void *src, int len) |
| 89 | { |
| 90 | memcpy(dst, src, len); |
| 91 | - flush_icache_user_page(vma, page, vaddr, len); |
| 92 | + flush_dcache(); |
| 93 | } |
| 94 | static inline void copy_from_user_page(struct vm_area_struct *vma, |
| 95 | struct page *page, unsigned long vaddr, |
| 96 | --- a/drivers/net/fec_m547x.c |
| 97 | +++ b/drivers/net/fec_m547x.c |
| 98 | @@ -34,7 +34,7 @@ |
| 99 | #include <asm/m5485sram.h> |
| 100 | #include <asm/virtconvert.h> |
| 101 | #include <asm/irq.h> |
| 102 | - |
| 103 | +#include <asm/cf_cacheflush.h> |
| 104 | #include "fec_m547x.h" |
| 105 | |
| 106 | #ifdef CONFIG_FEC_548x_ENABLE_FEC2 |
| 107 | @@ -97,7 +97,7 @@ static void fec_interrupt_fec_rx_handler |
| 108 | static irqreturn_t fec_interrupt_handler(int irq, void *dev_id); |
| 109 | static void fec_interrupt_fec_tx_handler_fec0(void); |
| 110 | static void fec_interrupt_fec_rx_handler_fec0(void); |
| 111 | -static void fec_interrupt_fec_reinit(unsigned long data); |
| 112 | +static void fec_interrupt_fec_reinit(struct net_device *dev); |
| 113 | |
| 114 | /* default fec0 address */ |
| 115 | unsigned char fec_mac_addr_fec0[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x50 }; |
| 116 | @@ -145,6 +145,7 @@ static int coldfire_fec_mdio_read(struct |
| 117 | #ifdef CONFIG_FEC_548x_SHARED_PHY |
| 118 | unsigned long base_addr = (unsigned long)FEC_BASE_ADDR_FEC0; |
| 119 | #else |
| 120 | + struct net_device *dev = bus->priv; |
| 121 | unsigned long base_addr = (unsigned long) dev->base_addr; |
| 122 | #endif |
| 123 | int tries = 100; |
| 124 | @@ -179,6 +180,7 @@ static int coldfire_fec_mdio_write(struc |
| 125 | #ifdef CONFIG_FEC_548x_SHARED_PHY |
| 126 | unsigned long base_addr = (unsigned long)FEC_BASE_ADDR_FEC0; |
| 127 | #else |
| 128 | + struct net_device *dev = bus->priv; |
| 129 | unsigned long base_addr = (unsigned long) dev->base_addr; |
| 130 | #endif |
| 131 | int tries = 100; |
| 132 | @@ -394,9 +396,6 @@ static int mcf547x_fec_open(struct net_d |
| 133 | |
| 134 | dma_connect(channel, (int) fp->fecpriv_interrupt_fec_tx_handler); |
| 135 | |
| 136 | - /* init tasklet for controller reinitialization */ |
| 137 | - tasklet_init(&fp->fecpriv_tasklet_reinit, |
| 138 | - fec_interrupt_fec_reinit, (unsigned long) dev); |
| 139 | |
| 140 | /* Reset FIFOs */ |
| 141 | FEC_FECFRST(base_addr) |= FEC_SW_RST | FEC_RST_CTL; |
| 142 | @@ -790,6 +789,8 @@ static int mcf547x_fec_start_xmit(struct |
| 143 | |
| 144 | /* flush data cache before initializing |
| 145 | * the descriptor and starting DMA */ |
| 146 | + flush_dcache_range(virt_to_phys(data_aligned), |
| 147 | + virt_to_phys(data_aligned) + skb->len); |
| 148 | |
| 149 | spin_lock_irq(&fp->fecpriv_lock); |
| 150 | |
| 151 | @@ -1308,7 +1309,7 @@ irqreturn_t fec_interrupt_handler(int ir |
| 152 | netif_stop_queue(dev); |
| 153 | |
| 154 | /* execute reinitialization as tasklet */ |
| 155 | - tasklet_schedule(&fp->fecpriv_tasklet_reinit); |
| 156 | + fec_interrupt_fec_reinit(dev); |
| 157 | |
| 158 | fp->fecpriv_stat.rx_dropped++; |
| 159 | } |
| 160 | @@ -1343,10 +1344,9 @@ irqreturn_t fec_interrupt_handler(int ir |
| 161 | * when controller must be reinitialized. |
| 162 | * |
| 163 | *************************************************************************/ |
| 164 | -void fec_interrupt_fec_reinit(unsigned long data) |
| 165 | +void fec_interrupt_fec_reinit(struct net_device *dev) |
| 166 | { |
| 167 | int i; |
| 168 | - struct net_device *dev = (struct net_device *)data; |
| 169 | struct fec_priv *fp = netdev_priv(dev); |
| 170 | unsigned long base_addr = (unsigned long) dev->base_addr; |
| 171 | |
| 172 | @@ -1385,7 +1385,7 @@ void fec_interrupt_fec_reinit(unsigned l |
| 173 | fp->fecpriv_current_tx = fp->fecpriv_next_tx = 0; |
| 174 | |
| 175 | /* flush entire data cache before restarting the DMA */ |
| 176 | - |
| 177 | + flush_dcache(); |
| 178 | /* restart DMA from beginning */ |
| 179 | MCD_startDma(fp->fecpriv_fec_rx_channel, |
| 180 | (char *) fp->fecpriv_rxdesc, 0, |
| 181 | |