Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 1 | /* |
| 2 | * (C) Copyright 2007 Michal Simek |
| 3 | * |
| 4 | * Michal SIMEK <monstr@monstr.eu> |
| 5 | * |
| 6 | * See file CREDITS for list of people who contributed to this |
| 7 | * project. |
| 8 | * |
| 9 | * This program is free software; you can redistribute it and/or |
| 10 | * modify it under the terms of the GNU General Public License as |
| 11 | * published by the Free Software Foundation; either version 2 of |
| 12 | * the License, or (at your option) any later version. |
| 13 | * |
| 14 | * This program is distributed in the hope that it will be useful, |
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | * GNU General Public License for more details. |
| 18 | * |
| 19 | * You should have received a copy of the GNU General Public License |
| 20 | * along with this program; if not, write to the Free Software |
| 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| 22 | * MA 02111-1307 USA |
| 23 | * |
| 24 | * Based on Xilinx drivers |
| 25 | * |
| 26 | */ |
| 27 | |
| 28 | #include <config.h> |
| 29 | #include <common.h> |
| 30 | #include <net.h> |
| 31 | #include <asm/io.h> |
| 32 | #include <asm/asm.h> |
| 33 | #include "xilinx_emac.h" |
| 34 | |
| 35 | #ifdef XILINX_EMAC |
| 36 | |
| 37 | #undef DEBUG |
| 38 | |
| 39 | #define ENET_MAX_MTU PKTSIZE |
| 40 | #define ENET_ADDR_LENGTH 6 |
| 41 | |
| 42 | static unsigned int etherrxbuff[PKTSIZE_ALIGN/4]; /* Receive buffer */ |
| 43 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 44 | static u8 emacaddr[ENET_ADDR_LENGTH] = { 0x00, 0x0a, 0x35, 0x00, 0x22, 0x01 }; |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 45 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 46 | static xemac emac; |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 47 | |
| 48 | void eth_halt(void) |
| 49 | { |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 50 | #ifdef DEBUG |
| 51 | puts ("eth_halt\n"); |
| 52 | #endif |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | int eth_init(bd_t * bis) |
| 56 | { |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 57 | u32 helpreg; |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 58 | #ifdef DEBUG |
| 59 | printf("EMAC Initialization Started\n\r"); |
| 60 | #endif |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 61 | if (emac.isstarted) { |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 62 | puts("Emac is started\n"); |
| 63 | return 0; |
| 64 | } |
| 65 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 66 | memset (&emac, 0, sizeof (xemac)); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 67 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 68 | emac.baseaddress = XILINX_EMAC_BASEADDR; |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 69 | |
| 70 | /* Setting up FIFOs */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 71 | emac.recvfifo.regbaseaddress = emac.baseaddress + |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 72 | XEM_PFIFO_RXREG_OFFSET; |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 73 | emac.recvfifo.databaseaddress = emac.baseaddress + |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 74 | XEM_PFIFO_RXDATA_OFFSET; |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 75 | out_be32 (emac.recvfifo.regbaseaddress, XPF_RESET_FIFO_MASK); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 76 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 77 | emac.sendfifo.regbaseaddress = emac.baseaddress + |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 78 | XEM_PFIFO_TXREG_OFFSET; |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 79 | emac.sendfifo.databaseaddress = emac.baseaddress + |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 80 | XEM_PFIFO_TXDATA_OFFSET; |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 81 | out_be32 (emac.sendfifo.regbaseaddress, XPF_RESET_FIFO_MASK); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 82 | |
| 83 | /* Reset the entire IPIF */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 84 | out_be32 (emac.baseaddress + XIIF_V123B_RESETR_OFFSET, |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 85 | XIIF_V123B_RESET_MASK); |
| 86 | |
| 87 | /* Stopping EMAC for setting up MAC */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 88 | helpreg = in_be32 (emac.baseaddress + XEM_ECR_OFFSET); |
| 89 | helpreg &= ~(XEM_ECR_XMIT_ENABLE_MASK | XEM_ECR_RECV_ENABLE_MASK); |
| 90 | out_be32 (emac.baseaddress + XEM_ECR_OFFSET, helpreg); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 91 | |
| 92 | if (!getenv("ethaddr")) { |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 93 | memcpy(bis->bi_enetaddr, emacaddr, ENET_ADDR_LENGTH); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | /* Set the device station address high and low registers */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 97 | helpreg = (bis->bi_enetaddr[0] << 8) | bis->bi_enetaddr[1]; |
| 98 | out_be32 (emac.baseaddress + XEM_SAH_OFFSET, helpreg); |
| 99 | helpreg = (bis->bi_enetaddr[2] << 24) | (bis->bi_enetaddr[3] << 16) | |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 100 | (bis->bi_enetaddr[4] << 8) | bis->bi_enetaddr[5]; |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 101 | out_be32 (emac.baseaddress + XEM_SAL_OFFSET, helpreg); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 102 | |
| 103 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 104 | helpreg = XEM_ECR_UNICAST_ENABLE_MASK | XEM_ECR_BROAD_ENABLE_MASK | |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 105 | XEM_ECR_FULL_DUPLEX_MASK | XEM_ECR_XMIT_FCS_ENABLE_MASK | |
| 106 | XEM_ECR_XMIT_PAD_ENABLE_MASK | XEM_ECR_PHY_ENABLE_MASK; |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 107 | out_be32 (emac.baseaddress + XEM_ECR_OFFSET, helpreg); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 108 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 109 | emac.isstarted = 1; |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 110 | |
| 111 | /* Enable the transmitter, and receiver */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 112 | helpreg = in_be32 (emac.baseaddress + XEM_ECR_OFFSET); |
| 113 | helpreg &= ~(XEM_ECR_XMIT_RESET_MASK | XEM_ECR_RECV_RESET_MASK); |
| 114 | helpreg |= (XEM_ECR_XMIT_ENABLE_MASK | XEM_ECR_RECV_ENABLE_MASK); |
| 115 | out_be32 (emac.baseaddress + XEM_ECR_OFFSET, helpreg); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 116 | |
| 117 | printf("EMAC Initialization complete\n\r"); |
| 118 | return 0; |
| 119 | } |
| 120 | |
| 121 | int eth_send(volatile void *ptr, int len) |
| 122 | { |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 123 | u32 intrstatus; |
| 124 | u32 xmitstatus; |
| 125 | u32 fifocount; |
| 126 | u32 wordcount; |
| 127 | u32 extrabytecount; |
| 128 | u32 *wordbuffer = (u32 *) ptr; |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 129 | |
| 130 | if (len > ENET_MAX_MTU) |
| 131 | len = ENET_MAX_MTU; |
| 132 | |
| 133 | /* |
| 134 | * Check for overruns and underruns for the transmit status and length |
| 135 | * FIFOs and make sure the send packet FIFO is not deadlocked. |
| 136 | * Any of these conditions is bad enough that we do not want to |
| 137 | * continue. The upper layer software should reset the device to resolve |
| 138 | * the error. |
| 139 | */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 140 | intrstatus = in_be32 ((emac.baseaddress) + XIIF_V123B_IISR_OFFSET); |
| 141 | if (intrstatus & (XEM_EIR_XMIT_SFIFO_OVER_MASK | |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 142 | XEM_EIR_XMIT_LFIFO_OVER_MASK)) { |
| 143 | #ifdef DEBUG |
| 144 | puts ("Transmitting overrun error\n"); |
| 145 | #endif |
| 146 | return 0; |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 147 | } else if (intrstatus & (XEM_EIR_XMIT_SFIFO_UNDER_MASK | |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 148 | XEM_EIR_XMIT_LFIFO_UNDER_MASK)) { |
| 149 | #ifdef DEBUG |
| 150 | puts ("Transmitting underrun error\n"); |
| 151 | #endif |
| 152 | return 0; |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 153 | } else if (in_be32 (emac.sendfifo.regbaseaddress + |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 154 | XPF_COUNT_STATUS_REG_OFFSET) & XPF_DEADLOCK_MASK) { |
| 155 | #ifdef DEBUG |
| 156 | puts("Transmitting fifo error\n"); |
| 157 | #endif |
| 158 | return 0; |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | * Before writing to the data FIFO, make sure the length FIFO is not |
| 163 | * full. The data FIFO might not be full yet even though the length FIFO |
| 164 | * is. This avoids an overrun condition on the length FIFO and keeps the |
| 165 | * FIFOs in sync. |
| 166 | * |
| 167 | * Clear the latched LFIFO_FULL bit so next time around the most |
| 168 | * current status is represented |
| 169 | */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 170 | if (intrstatus & XEM_EIR_XMIT_LFIFO_FULL_MASK) { |
| 171 | out_be32 ((emac.baseaddress) + XIIF_V123B_IISR_OFFSET, |
| 172 | intrstatus & XEM_EIR_XMIT_LFIFO_FULL_MASK); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 173 | #ifdef DEBUG |
| 174 | puts ("Fifo is full\n"); |
| 175 | #endif |
| 176 | return 0; |
| 177 | } |
| 178 | |
| 179 | /* get the count of how many words may be inserted into the FIFO */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 180 | fifocount = in_be32 (emac.sendfifo.regbaseaddress + |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 181 | XPF_COUNT_STATUS_REG_OFFSET) & XPF_COUNT_MASK; |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 182 | wordcount = len >> 2; |
| 183 | extrabytecount = len & 0x3; |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 184 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 185 | if (fifocount < wordcount) { |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 186 | #ifdef DEBUG |
| 187 | puts ("Sending packet is larger then size of FIFO\n"); |
| 188 | #endif |
| 189 | return 0; |
| 190 | } |
| 191 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 192 | for (fifocount = 0; fifocount < wordcount; fifocount++) { |
| 193 | out_be32 (emac.sendfifo.databaseaddress, wordbuffer[fifocount]); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 194 | } |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 195 | if (extrabytecount > 0) { |
| 196 | u32 lastword = 0; |
| 197 | u8 *extrabytesbuffer = (u8 *) (wordbuffer + wordcount); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 198 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 199 | if (extrabytecount == 1) { |
| 200 | lastword = extrabytesbuffer[0] << 24; |
| 201 | } else if (extrabytecount == 2) { |
| 202 | lastword = extrabytesbuffer[0] << 24 | |
| 203 | extrabytesbuffer[1] << 16; |
| 204 | } else if (extrabytecount == 3) { |
| 205 | lastword = extrabytesbuffer[0] << 24 | |
| 206 | extrabytesbuffer[1] << 16 | |
| 207 | extrabytesbuffer[2] << 8; |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 208 | } |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 209 | out_be32 (emac.sendfifo.databaseaddress, lastword); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 210 | } |
| 211 | |
| 212 | /* Loop on the MAC's status to wait for any pause to complete */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 213 | intrstatus = in_be32 ((emac.baseaddress) + XIIF_V123B_IISR_OFFSET); |
| 214 | while ((intrstatus & XEM_EIR_XMIT_PAUSE_MASK) != 0) { |
| 215 | intrstatus = in_be32 ((emac.baseaddress) + |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 216 | XIIF_V123B_IISR_OFFSET); |
| 217 | /* Clear the pause status from the transmit status register */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 218 | out_be32 ((emac.baseaddress) + XIIF_V123B_IISR_OFFSET, |
| 219 | intrstatus & XEM_EIR_XMIT_PAUSE_MASK); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 220 | } |
| 221 | |
| 222 | /* |
| 223 | * Set the MAC's transmit packet length register to tell it to transmit |
| 224 | */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 225 | out_be32 (emac.baseaddress + XEM_TPLR_OFFSET, len); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 226 | |
| 227 | /* |
| 228 | * Loop on the MAC's status to wait for the transmit to complete. |
| 229 | * The transmit status is in the FIFO when the XMIT_DONE bit is set. |
| 230 | */ |
| 231 | do { |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 232 | intrstatus = in_be32 ((emac.baseaddress) + |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 233 | XIIF_V123B_IISR_OFFSET); |
| 234 | } |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 235 | while ((intrstatus & XEM_EIR_XMIT_DONE_MASK) == 0); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 236 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 237 | xmitstatus = in_be32 (emac.baseaddress + XEM_TSR_OFFSET); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 238 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 239 | if (intrstatus & (XEM_EIR_XMIT_SFIFO_OVER_MASK | |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 240 | XEM_EIR_XMIT_LFIFO_OVER_MASK)) { |
| 241 | #ifdef DEBUG |
| 242 | puts ("Transmitting overrun error\n"); |
| 243 | #endif |
| 244 | return 0; |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 245 | } else if (intrstatus & (XEM_EIR_XMIT_SFIFO_UNDER_MASK | |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 246 | XEM_EIR_XMIT_LFIFO_UNDER_MASK)) { |
| 247 | #ifdef DEBUG |
| 248 | puts ("Transmitting underrun error\n"); |
| 249 | #endif |
| 250 | return 0; |
| 251 | } |
| 252 | |
| 253 | /* Clear the interrupt status register of transmit statuses */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 254 | out_be32 ((emac.baseaddress) + XIIF_V123B_IISR_OFFSET, |
| 255 | intrstatus & XEM_EIR_XMIT_ALL_MASK); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 256 | |
| 257 | /* |
| 258 | * Collision errors are stored in the transmit status register |
| 259 | * instead of the interrupt status register |
| 260 | */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 261 | if ((xmitstatus & XEM_TSR_EXCESS_DEFERRAL_MASK) || |
| 262 | (xmitstatus & XEM_TSR_LATE_COLLISION_MASK)) { |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 263 | #ifdef DEBUG |
| 264 | puts ("Transmitting collision error\n"); |
| 265 | #endif |
| 266 | return 0; |
| 267 | } |
| 268 | return 1; |
| 269 | } |
| 270 | |
| 271 | int eth_rx(void) |
| 272 | { |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 273 | u32 pktlength; |
| 274 | u32 intrstatus; |
| 275 | u32 fifocount; |
| 276 | u32 wordcount; |
| 277 | u32 extrabytecount; |
| 278 | u32 lastword; |
| 279 | u8 *extrabytesbuffer; |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 280 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 281 | if (in_be32 (emac.recvfifo.regbaseaddress + XPF_COUNT_STATUS_REG_OFFSET) |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 282 | & XPF_DEADLOCK_MASK) { |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 283 | out_be32 (emac.recvfifo.regbaseaddress, XPF_RESET_FIFO_MASK); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 284 | #ifdef DEBUG |
| 285 | puts ("Receiving FIFO deadlock\n"); |
| 286 | #endif |
| 287 | return 0; |
| 288 | } |
| 289 | |
| 290 | /* |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 291 | * Get the interrupt status to know what happened (whether an error |
| 292 | * occurred and/or whether frames have been received successfully). |
| 293 | * When clearing the intr status register, clear only statuses that |
| 294 | * pertain to receive. |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 295 | */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 296 | intrstatus = in_be32 ((emac.baseaddress) + XIIF_V123B_IISR_OFFSET); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 297 | /* |
| 298 | * Before reading from the length FIFO, make sure the length FIFO is not |
| 299 | * empty. We could cause an underrun error if we try to read from an |
| 300 | * empty FIFO. |
| 301 | */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 302 | if (!(intrstatus & XEM_EIR_RECV_DONE_MASK)) { |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 303 | #ifdef DEBUG |
| 304 | /* puts("Receiving FIFO is empty\n"); */ |
| 305 | #endif |
| 306 | return 0; |
| 307 | } |
| 308 | |
| 309 | /* |
| 310 | * Determine, from the MAC, the length of the next packet available |
| 311 | * in the data FIFO (there should be a non-zero length here) |
| 312 | */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 313 | pktlength = in_be32 (emac.baseaddress + XEM_RPLR_OFFSET); |
| 314 | if (!pktlength) { |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 315 | return 0; |
| 316 | } |
| 317 | |
| 318 | /* |
| 319 | * Write the RECV_DONE bit in the status register to clear it. This bit |
| 320 | * indicates the RPLR is non-empty, and we know it's set at this point. |
| 321 | * We clear it so that subsequent entry into this routine will reflect |
| 322 | * the current status. This is done because the non-empty bit is latched |
| 323 | * in the IPIF, which means it may indicate a non-empty condition even |
| 324 | * though there is something in the FIFO. |
| 325 | */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 326 | out_be32 ((emac.baseaddress) + XIIF_V123B_IISR_OFFSET, |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 327 | XEM_EIR_RECV_DONE_MASK); |
| 328 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 329 | fifocount = in_be32 (emac.recvfifo.regbaseaddress + |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 330 | XPF_COUNT_STATUS_REG_OFFSET) & XPF_COUNT_MASK; |
| 331 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 332 | if ((fifocount * 4) < pktlength) { |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 333 | #ifdef DEBUG |
| 334 | puts ("Receiving FIFO is smaller than packet size.\n"); |
| 335 | #endif |
| 336 | return 0; |
| 337 | } |
| 338 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 339 | wordcount = pktlength >> 2; |
| 340 | extrabytecount = pktlength & 0x3; |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 341 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 342 | for (fifocount = 0; fifocount < wordcount; fifocount++) { |
| 343 | etherrxbuff[fifocount] = |
| 344 | in_be32 (emac.recvfifo.databaseaddress); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 345 | } |
| 346 | |
| 347 | /* |
| 348 | * if there are extra bytes to handle, read the last word from the FIFO |
| 349 | * and insert the extra bytes into the buffer |
| 350 | */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 351 | if (extrabytecount > 0) { |
| 352 | extrabytesbuffer = (u8 *) (etherrxbuff + wordcount); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 353 | |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 354 | lastword = in_be32 (emac.recvfifo.databaseaddress); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 355 | |
| 356 | /* |
| 357 | * one extra byte in the last word, put the byte into the next |
| 358 | * location of the buffer, bytes in a word of the FIFO are |
| 359 | * ordered from most significant byte to least |
| 360 | */ |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 361 | if (extrabytecount == 1) { |
| 362 | extrabytesbuffer[0] = (u8) (lastword >> 24); |
| 363 | } else if (extrabytecount == 2) { |
| 364 | extrabytesbuffer[0] = (u8) (lastword >> 24); |
| 365 | extrabytesbuffer[1] = (u8) (lastword >> 16); |
| 366 | } else if (extrabytecount == 3) { |
| 367 | extrabytesbuffer[0] = (u8) (lastword >> 24); |
| 368 | extrabytesbuffer[1] = (u8) (lastword >> 16); |
| 369 | extrabytesbuffer[2] = (u8) (lastword >> 8); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 370 | } |
| 371 | } |
Michal Simek | 706714d | 2007-08-06 23:41:53 +0200 | [diff] [blame^] | 372 | NetReceive((uchar *)etherrxbuff, pktlength); |
Michal Simek | 537091b | 2007-08-05 15:53:50 +0200 | [diff] [blame] | 373 | return 1; |
| 374 | } |
| 375 | #endif |