Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 1 | /* $Date: 2005/10/22 00:42:59 $ $RCSfile: mac.c,v $ $Revision: 1.32 $ */ |
| 2 | #include "gmac.h" |
| 3 | #include "regs.h" |
| 4 | #include "fpga_defs.h" |
| 5 | |
| 6 | #define MAC_CSR_INTERFACE_GMII 0x0 |
| 7 | #define MAC_CSR_INTERFACE_TBI 0x1 |
| 8 | #define MAC_CSR_INTERFACE_MII 0x2 |
| 9 | #define MAC_CSR_INTERFACE_RMII 0x3 |
| 10 | |
| 11 | /* Chelsio's MAC statistics. */ |
| 12 | struct mac_statistics { |
| 13 | |
| 14 | /* Transmit */ |
| 15 | u32 TxFramesTransmittedOK; |
| 16 | u32 TxReserved1; |
| 17 | u32 TxReserved2; |
| 18 | u32 TxOctetsTransmittedOK; |
| 19 | u32 TxFramesWithDeferredXmissions; |
| 20 | u32 TxLateCollisions; |
| 21 | u32 TxFramesAbortedDueToXSCollisions; |
| 22 | u32 TxFramesLostDueToIntMACXmitError; |
| 23 | u32 TxReserved3; |
| 24 | u32 TxMulticastFrameXmittedOK; |
| 25 | u32 TxBroadcastFramesXmittedOK; |
| 26 | u32 TxFramesWithExcessiveDeferral; |
| 27 | u32 TxPAUSEMACCtrlFramesTransmitted; |
| 28 | |
| 29 | /* Receive */ |
| 30 | u32 RxFramesReceivedOK; |
| 31 | u32 RxFrameCheckSequenceErrors; |
| 32 | u32 RxAlignmentErrors; |
| 33 | u32 RxOctetsReceivedOK; |
| 34 | u32 RxFramesLostDueToIntMACRcvError; |
| 35 | u32 RxMulticastFramesReceivedOK; |
| 36 | u32 RxBroadcastFramesReceivedOK; |
| 37 | u32 RxInRangeLengthErrors; |
| 38 | u32 RxTxOutOfRangeLengthField; |
| 39 | u32 RxFrameTooLongErrors; |
| 40 | u32 RxPAUSEMACCtrlFramesReceived; |
| 41 | }; |
| 42 | |
| 43 | static int static_aPorts[] = { |
| 44 | FPGA_GMAC_INTERRUPT_PORT0, |
| 45 | FPGA_GMAC_INTERRUPT_PORT1, |
| 46 | FPGA_GMAC_INTERRUPT_PORT2, |
| 47 | FPGA_GMAC_INTERRUPT_PORT3 |
| 48 | }; |
| 49 | |
| 50 | struct _cmac_instance { |
| 51 | u32 index; |
| 52 | }; |
| 53 | |
| 54 | static int mac_intr_enable(struct cmac *mac) |
| 55 | { |
| 56 | u32 mac_intr; |
| 57 | |
| 58 | if (t1_is_asic(mac->adapter)) { |
| 59 | /* ASIC */ |
| 60 | |
| 61 | /* We don't use the on chip MAC for ASIC products. */ |
| 62 | } else { |
| 63 | /* FPGA */ |
| 64 | |
| 65 | /* Set parent gmac interrupt. */ |
| 66 | mac_intr = readl(mac->adapter->regs + A_PL_ENABLE); |
| 67 | mac_intr |= FPGA_PCIX_INTERRUPT_GMAC; |
| 68 | writel(mac_intr, mac->adapter->regs + A_PL_ENABLE); |
| 69 | |
| 70 | mac_intr = readl(mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_ENABLE); |
| 71 | mac_intr |= static_aPorts[mac->instance->index]; |
| 72 | writel(mac_intr, |
| 73 | mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_ENABLE); |
| 74 | } |
| 75 | |
| 76 | return 0; |
| 77 | } |
| 78 | |
| 79 | static int mac_intr_disable(struct cmac *mac) |
| 80 | { |
| 81 | u32 mac_intr; |
| 82 | |
| 83 | if (t1_is_asic(mac->adapter)) { |
| 84 | /* ASIC */ |
| 85 | |
| 86 | /* We don't use the on chip MAC for ASIC products. */ |
| 87 | } else { |
| 88 | /* FPGA */ |
| 89 | |
| 90 | /* Set parent gmac interrupt. */ |
| 91 | mac_intr = readl(mac->adapter->regs + A_PL_ENABLE); |
| 92 | mac_intr &= ~FPGA_PCIX_INTERRUPT_GMAC; |
| 93 | writel(mac_intr, mac->adapter->regs + A_PL_ENABLE); |
| 94 | |
| 95 | mac_intr = readl(mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_ENABLE); |
| 96 | mac_intr &= ~(static_aPorts[mac->instance->index]); |
| 97 | writel(mac_intr, |
| 98 | mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_ENABLE); |
| 99 | } |
| 100 | |
| 101 | return 0; |
| 102 | } |
| 103 | |
| 104 | static int mac_intr_clear(struct cmac *mac) |
| 105 | { |
| 106 | u32 mac_intr; |
| 107 | |
| 108 | if (t1_is_asic(mac->adapter)) { |
| 109 | /* ASIC */ |
| 110 | |
| 111 | /* We don't use the on chip MAC for ASIC products. */ |
| 112 | } else { |
| 113 | /* FPGA */ |
| 114 | |
| 115 | /* Set parent gmac interrupt. */ |
| 116 | writel(FPGA_PCIX_INTERRUPT_GMAC, |
| 117 | mac->adapter->regs + A_PL_CAUSE); |
| 118 | mac_intr = readl(mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_CAUSE); |
| 119 | mac_intr |= (static_aPorts[mac->instance->index]); |
| 120 | writel(mac_intr, |
| 121 | mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_CAUSE); |
| 122 | } |
| 123 | |
| 124 | return 0; |
| 125 | } |
| 126 | |
| 127 | static int mac_get_address(struct cmac *mac, u8 addr[6]) |
| 128 | { |
| 129 | u32 data32_lo, data32_hi; |
| 130 | |
| 131 | data32_lo = readl(mac->adapter->regs |
| 132 | + MAC_REG_IDLO(mac->instance->index)); |
| 133 | data32_hi = readl(mac->adapter->regs |
| 134 | + MAC_REG_IDHI(mac->instance->index)); |
| 135 | |
| 136 | addr[0] = (u8) ((data32_hi >> 8) & 0xFF); |
| 137 | addr[1] = (u8) ((data32_hi) & 0xFF); |
| 138 | addr[2] = (u8) ((data32_lo >> 24) & 0xFF); |
| 139 | addr[3] = (u8) ((data32_lo >> 16) & 0xFF); |
| 140 | addr[4] = (u8) ((data32_lo >> 8) & 0xFF); |
| 141 | addr[5] = (u8) ((data32_lo) & 0xFF); |
| 142 | return 0; |
| 143 | } |
| 144 | |
| 145 | static int mac_reset(struct cmac *mac) |
| 146 | { |
| 147 | u32 data32; |
| 148 | int mac_in_reset, time_out = 100; |
| 149 | int idx = mac->instance->index; |
| 150 | |
| 151 | data32 = readl(mac->adapter->regs + MAC_REG_CSR(idx)); |
| 152 | writel(data32 | F_MAC_RESET, |
| 153 | mac->adapter->regs + MAC_REG_CSR(idx)); |
| 154 | |
| 155 | do { |
| 156 | data32 = readl(mac->adapter->regs + MAC_REG_CSR(idx)); |
| 157 | |
| 158 | mac_in_reset = data32 & F_MAC_RESET; |
| 159 | if (mac_in_reset) |
| 160 | udelay(1); |
| 161 | } while (mac_in_reset && --time_out); |
| 162 | |
| 163 | if (mac_in_reset) { |
| 164 | CH_ERR("%s: MAC %d reset timed out\n", |
| 165 | mac->adapter->name, idx); |
| 166 | return 2; |
| 167 | } |
| 168 | |
| 169 | return 0; |
| 170 | } |
| 171 | |
| 172 | static int mac_set_rx_mode(struct cmac *mac, struct t1_rx_mode *rm) |
| 173 | { |
| 174 | u32 val; |
| 175 | |
| 176 | val = readl(mac->adapter->regs |
| 177 | + MAC_REG_CSR(mac->instance->index)); |
| 178 | val &= ~(F_MAC_PROMISC | F_MAC_MC_ENABLE); |
| 179 | val |= V_MAC_PROMISC(t1_rx_mode_promisc(rm) != 0); |
| 180 | val |= V_MAC_MC_ENABLE(t1_rx_mode_allmulti(rm) != 0); |
| 181 | writel(val, |
| 182 | mac->adapter->regs + MAC_REG_CSR(mac->instance->index)); |
| 183 | |
| 184 | return 0; |
| 185 | } |
| 186 | |
| 187 | static int mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, |
| 188 | int fc) |
| 189 | { |
| 190 | u32 data32; |
| 191 | |
| 192 | data32 = readl(mac->adapter->regs |
| 193 | + MAC_REG_CSR(mac->instance->index)); |
| 194 | data32 &= ~(F_MAC_HALF_DUPLEX | V_MAC_SPEED(M_MAC_SPEED) | |
| 195 | V_INTERFACE(M_INTERFACE) | F_MAC_TX_PAUSE_ENABLE | |
| 196 | F_MAC_RX_PAUSE_ENABLE); |
| 197 | |
| 198 | switch (speed) { |
| 199 | case SPEED_10: |
| 200 | case SPEED_100: |
| 201 | data32 |= V_INTERFACE(MAC_CSR_INTERFACE_MII); |
| 202 | data32 |= V_MAC_SPEED(speed == SPEED_10 ? 0 : 1); |
| 203 | break; |
| 204 | case SPEED_1000: |
| 205 | data32 |= V_INTERFACE(MAC_CSR_INTERFACE_GMII); |
| 206 | data32 |= V_MAC_SPEED(2); |
| 207 | break; |
| 208 | } |
| 209 | |
| 210 | if (duplex >= 0) |
| 211 | data32 |= V_MAC_HALF_DUPLEX(duplex == DUPLEX_HALF); |
| 212 | |
| 213 | if (fc >= 0) { |
| 214 | data32 |= V_MAC_RX_PAUSE_ENABLE((fc & PAUSE_RX) != 0); |
| 215 | data32 |= V_MAC_TX_PAUSE_ENABLE((fc & PAUSE_TX) != 0); |
| 216 | } |
| 217 | |
| 218 | writel(data32, |
| 219 | mac->adapter->regs + MAC_REG_CSR(mac->instance->index)); |
| 220 | return 0; |
| 221 | } |
| 222 | |
| 223 | static int mac_enable(struct cmac *mac, int which) |
| 224 | { |
| 225 | u32 val; |
| 226 | |
| 227 | val = readl(mac->adapter->regs |
| 228 | + MAC_REG_CSR(mac->instance->index)); |
| 229 | if (which & MAC_DIRECTION_RX) |
| 230 | val |= F_MAC_RX_ENABLE; |
| 231 | if (which & MAC_DIRECTION_TX) |
| 232 | val |= F_MAC_TX_ENABLE; |
| 233 | writel(val, |
| 234 | mac->adapter->regs + MAC_REG_CSR(mac->instance->index)); |
| 235 | return 0; |
| 236 | } |
| 237 | |
| 238 | static int mac_disable(struct cmac *mac, int which) |
| 239 | { |
| 240 | u32 val; |
| 241 | |
| 242 | val = readl(mac->adapter->regs |
| 243 | + MAC_REG_CSR(mac->instance->index)); |
| 244 | if (which & MAC_DIRECTION_RX) |
| 245 | val &= ~F_MAC_RX_ENABLE; |
| 246 | if (which & MAC_DIRECTION_TX) |
| 247 | val &= ~F_MAC_TX_ENABLE; |
| 248 | writel(val, |
| 249 | mac->adapter->regs + MAC_REG_CSR(mac->instance->index)); |
| 250 | return 0; |
| 251 | } |
| 252 | |
| 253 | #if 0 |
| 254 | static int mac_set_ifs(struct cmac *mac, u32 mode) |
| 255 | { |
| 256 | t1_write_reg_4(mac->adapter, |
| 257 | MAC_REG_IFS(mac->instance->index), |
| 258 | mode); |
| 259 | return 0; |
| 260 | } |
| 261 | |
| 262 | static int mac_enable_isl(struct cmac *mac) |
| 263 | { |
| 264 | u32 data32 = readl(mac->adapter->regs |
| 265 | + MAC_REG_CSR(mac->instance->index)); |
| 266 | data32 |= F_MAC_RX_ENABLE | F_MAC_TX_ENABLE; |
| 267 | t1_write_reg_4(mac->adapter, |
| 268 | MAC_REG_CSR(mac->instance->index), |
| 269 | data32); |
| 270 | return 0; |
| 271 | } |
| 272 | #endif |
| 273 | |
| 274 | static int mac_set_mtu(struct cmac *mac, int mtu) |
| 275 | { |
| 276 | if (mtu > 9600) |
| 277 | return -EINVAL; |
| 278 | writel(mtu + ETH_HLEN + VLAN_HLEN, |
| 279 | mac->adapter->regs + MAC_REG_LARGEFRAMELENGTH(mac->instance->index)); |
| 280 | |
| 281 | return 0; |
| 282 | } |
| 283 | |
| 284 | static const struct cmac_statistics *mac_update_statistics(struct cmac *mac, |
| 285 | int flag) |
| 286 | { |
| 287 | struct mac_statistics st; |
| 288 | u32 *p = (u32 *) & st, i; |
| 289 | |
| 290 | writel(0, |
| 291 | mac->adapter->regs + MAC_REG_RMCNT(mac->instance->index)); |
| 292 | |
| 293 | for (i = 0; i < sizeof(st) / sizeof(u32); i++) |
| 294 | *p++ = readl(mac->adapter->regs |
| 295 | + MAC_REG_RMDATA(mac->instance->index)); |
| 296 | |
| 297 | /* XXX convert stats */ |
| 298 | return &mac->stats; |
| 299 | } |
| 300 | |
| 301 | static void mac_destroy(struct cmac *mac) |
| 302 | { |
| 303 | kfree(mac); |
| 304 | } |
| 305 | |
| 306 | static struct cmac_ops chelsio_mac_ops = { |
| 307 | .destroy = mac_destroy, |
| 308 | .reset = mac_reset, |
| 309 | .interrupt_enable = mac_intr_enable, |
| 310 | .interrupt_disable = mac_intr_disable, |
| 311 | .interrupt_clear = mac_intr_clear, |
| 312 | .enable = mac_enable, |
| 313 | .disable = mac_disable, |
| 314 | .set_mtu = mac_set_mtu, |
| 315 | .set_rx_mode = mac_set_rx_mode, |
| 316 | .set_speed_duplex_fc = mac_set_speed_duplex_fc, |
| 317 | .macaddress_get = mac_get_address, |
| 318 | .statistics_update = mac_update_statistics, |
| 319 | }; |
| 320 | |
| 321 | static struct cmac *mac_create(adapter_t *adapter, int index) |
| 322 | { |
| 323 | struct cmac *mac; |
| 324 | u32 data32; |
| 325 | |
| 326 | if (index >= 4) |
| 327 | return NULL; |
| 328 | |
| 329 | mac = kzalloc(sizeof(*mac) + sizeof(cmac_instance), GFP_KERNEL); |
| 330 | if (!mac) |
| 331 | return NULL; |
| 332 | |
| 333 | mac->ops = &chelsio_mac_ops; |
| 334 | mac->instance = (cmac_instance *) (mac + 1); |
| 335 | |
| 336 | mac->instance->index = index; |
| 337 | mac->adapter = adapter; |
| 338 | |
| 339 | data32 = readl(adapter->regs + MAC_REG_CSR(mac->instance->index)); |
| 340 | data32 &= ~(F_MAC_RESET | F_MAC_PROMISC | F_MAC_PROMISC | |
| 341 | F_MAC_LB_ENABLE | F_MAC_RX_ENABLE | F_MAC_TX_ENABLE); |
| 342 | data32 |= F_MAC_JUMBO_ENABLE; |
| 343 | writel(data32, adapter->regs + MAC_REG_CSR(mac->instance->index)); |
| 344 | |
| 345 | /* Initialize the random backoff seed. */ |
| 346 | data32 = 0x55aa + (3 * index); |
| 347 | writel(data32, |
| 348 | adapter->regs + MAC_REG_GMRANDBACKOFFSEED(mac->instance->index)); |
| 349 | |
| 350 | /* Check to see if the mac address needs to be set manually. */ |
| 351 | data32 = readl(adapter->regs + MAC_REG_IDLO(mac->instance->index)); |
| 352 | if (data32 == 0 || data32 == 0xffffffff) { |
| 353 | /* |
| 354 | * Add a default MAC address if we can't read one. |
| 355 | */ |
| 356 | writel(0x43FFFFFF - index, |
| 357 | adapter->regs + MAC_REG_IDLO(mac->instance->index)); |
| 358 | writel(0x0007, |
| 359 | adapter->regs + MAC_REG_IDHI(mac->instance->index)); |
| 360 | } |
| 361 | |
| 362 | (void) mac_set_mtu(mac, 1500); |
| 363 | return mac; |
| 364 | } |
| 365 | |
Stephen Hemminger | 459e536 | 2007-02-20 15:58:02 -0800 | [diff] [blame^] | 366 | const struct gmac t1_chelsio_mac_ops = { |
Stephen Hemminger | 352c417 | 2006-12-01 16:36:17 -0800 | [diff] [blame] | 367 | .create = mac_create |
| 368 | }; |