diff options
Diffstat (limited to 'drivers/net/pasemi_mac.c')
-rw-r--r-- | drivers/net/pasemi_mac.c | 259 |
1 files changed, 187 insertions, 72 deletions
diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c index bb88a41b7591..2e39e0285d8f 100644 --- a/drivers/net/pasemi_mac.c +++ b/drivers/net/pasemi_mac.c | |||
@@ -62,6 +62,10 @@ | |||
62 | 62 | ||
63 | #define LRO_MAX_AGGR 64 | 63 | #define LRO_MAX_AGGR 64 |
64 | 64 | ||
65 | #define PE_MIN_MTU 64 | ||
66 | #define PE_MAX_MTU 1500 | ||
67 | #define PE_DEF_MTU ETH_DATA_LEN | ||
68 | |||
65 | #define DEFAULT_MSG_ENABLE \ | 69 | #define DEFAULT_MSG_ENABLE \ |
66 | (NETIF_MSG_DRV | \ | 70 | (NETIF_MSG_DRV | \ |
67 | NETIF_MSG_PROBE | \ | 71 | NETIF_MSG_PROBE | \ |
@@ -82,8 +86,6 @@ | |||
82 | & ((ring)->size - 1)) | 86 | & ((ring)->size - 1)) |
83 | #define RING_AVAIL(ring) ((ring->size) - RING_USED(ring)) | 87 | #define RING_AVAIL(ring) ((ring->size) - RING_USED(ring)) |
84 | 88 | ||
85 | #define BUF_SIZE 1646 /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ | ||
86 | |||
87 | MODULE_LICENSE("GPL"); | 89 | MODULE_LICENSE("GPL"); |
88 | MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>"); | 90 | MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>"); |
89 | MODULE_DESCRIPTION("PA Semi PWRficient Ethernet driver"); | 91 | MODULE_DESCRIPTION("PA Semi PWRficient Ethernet driver"); |
@@ -175,6 +177,24 @@ static int mac_to_intf(struct pasemi_mac *mac) | |||
175 | return -1; | 177 | return -1; |
176 | } | 178 | } |
177 | 179 | ||
180 | static void pasemi_mac_intf_disable(struct pasemi_mac *mac) | ||
181 | { | ||
182 | unsigned int flags; | ||
183 | |||
184 | flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); | ||
185 | flags &= ~PAS_MAC_CFG_PCFG_PE; | ||
186 | write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); | ||
187 | } | ||
188 | |||
189 | static void pasemi_mac_intf_enable(struct pasemi_mac *mac) | ||
190 | { | ||
191 | unsigned int flags; | ||
192 | |||
193 | flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); | ||
194 | flags |= PAS_MAC_CFG_PCFG_PE; | ||
195 | write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); | ||
196 | } | ||
197 | |||
178 | static int pasemi_get_mac_addr(struct pasemi_mac *mac) | 198 | static int pasemi_get_mac_addr(struct pasemi_mac *mac) |
179 | { | 199 | { |
180 | struct pci_dev *pdev = mac->pdev; | 200 | struct pci_dev *pdev = mac->pdev; |
@@ -221,6 +241,33 @@ static int pasemi_get_mac_addr(struct pasemi_mac *mac) | |||
221 | return 0; | 241 | return 0; |
222 | } | 242 | } |
223 | 243 | ||
244 | static int pasemi_mac_set_mac_addr(struct net_device *dev, void *p) | ||
245 | { | ||
246 | struct pasemi_mac *mac = netdev_priv(dev); | ||
247 | struct sockaddr *addr = p; | ||
248 | unsigned int adr0, adr1; | ||
249 | |||
250 | if (!is_valid_ether_addr(addr->sa_data)) | ||
251 | return -EINVAL; | ||
252 | |||
253 | memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); | ||
254 | |||
255 | adr0 = dev->dev_addr[2] << 24 | | ||
256 | dev->dev_addr[3] << 16 | | ||
257 | dev->dev_addr[4] << 8 | | ||
258 | dev->dev_addr[5]; | ||
259 | adr1 = read_mac_reg(mac, PAS_MAC_CFG_ADR1); | ||
260 | adr1 &= ~0xffff; | ||
261 | adr1 |= dev->dev_addr[0] << 8 | dev->dev_addr[1]; | ||
262 | |||
263 | pasemi_mac_intf_disable(mac); | ||
264 | write_mac_reg(mac, PAS_MAC_CFG_ADR0, adr0); | ||
265 | write_mac_reg(mac, PAS_MAC_CFG_ADR1, adr1); | ||
266 | pasemi_mac_intf_enable(mac); | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
224 | static int get_skb_hdr(struct sk_buff *skb, void **iphdr, | 271 | static int get_skb_hdr(struct sk_buff *skb, void **iphdr, |
225 | void **tcph, u64 *hdr_flags, void *data) | 272 | void **tcph, u64 *hdr_flags, void *data) |
226 | { | 273 | { |
@@ -453,7 +500,7 @@ static void pasemi_mac_free_tx_resources(struct pasemi_mac *mac) | |||
453 | 500 | ||
454 | } | 501 | } |
455 | 502 | ||
456 | static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac) | 503 | static void pasemi_mac_free_rx_buffers(struct pasemi_mac *mac) |
457 | { | 504 | { |
458 | struct pasemi_mac_rxring *rx = rx_ring(mac); | 505 | struct pasemi_mac_rxring *rx = rx_ring(mac); |
459 | unsigned int i; | 506 | unsigned int i; |
@@ -473,7 +520,12 @@ static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac) | |||
473 | } | 520 | } |
474 | 521 | ||
475 | for (i = 0; i < RX_RING_SIZE; i++) | 522 | for (i = 0; i < RX_RING_SIZE; i++) |
476 | RX_DESC(rx, i) = 0; | 523 | RX_BUFF(rx, i) = 0; |
524 | } | ||
525 | |||
526 | static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac) | ||
527 | { | ||
528 | pasemi_mac_free_rx_buffers(mac); | ||
477 | 529 | ||
478 | dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64), | 530 | dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64), |
479 | rx_ring(mac)->buffers, rx_ring(mac)->buf_dma); | 531 | rx_ring(mac)->buffers, rx_ring(mac)->buf_dma); |
@@ -503,14 +555,14 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev, | |||
503 | /* Entry in use? */ | 555 | /* Entry in use? */ |
504 | WARN_ON(*buff); | 556 | WARN_ON(*buff); |
505 | 557 | ||
506 | skb = dev_alloc_skb(BUF_SIZE); | 558 | skb = dev_alloc_skb(mac->bufsz); |
507 | skb_reserve(skb, LOCAL_SKB_ALIGN); | 559 | skb_reserve(skb, LOCAL_SKB_ALIGN); |
508 | 560 | ||
509 | if (unlikely(!skb)) | 561 | if (unlikely(!skb)) |
510 | break; | 562 | break; |
511 | 563 | ||
512 | dma = pci_map_single(mac->dma_pdev, skb->data, | 564 | dma = pci_map_single(mac->dma_pdev, skb->data, |
513 | BUF_SIZE - LOCAL_SKB_ALIGN, | 565 | mac->bufsz - LOCAL_SKB_ALIGN, |
514 | PCI_DMA_FROMDEVICE); | 566 | PCI_DMA_FROMDEVICE); |
515 | 567 | ||
516 | if (unlikely(dma_mapping_error(dma))) { | 568 | if (unlikely(dma_mapping_error(dma))) { |
@@ -520,7 +572,7 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev, | |||
520 | 572 | ||
521 | info->skb = skb; | 573 | info->skb = skb; |
522 | info->dma = dma; | 574 | info->dma = dma; |
523 | *buff = XCT_RXB_LEN(BUF_SIZE) | XCT_RXB_ADDR(dma); | 575 | *buff = XCT_RXB_LEN(mac->bufsz) | XCT_RXB_ADDR(dma); |
524 | fill++; | 576 | fill++; |
525 | } | 577 | } |
526 | 578 | ||
@@ -650,7 +702,7 @@ static int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx, | |||
650 | 702 | ||
651 | len = (macrx & XCT_MACRX_LLEN_M) >> XCT_MACRX_LLEN_S; | 703 | len = (macrx & XCT_MACRX_LLEN_M) >> XCT_MACRX_LLEN_S; |
652 | 704 | ||
653 | pci_unmap_single(pdev, dma, BUF_SIZE-LOCAL_SKB_ALIGN, | 705 | pci_unmap_single(pdev, dma, mac->bufsz - LOCAL_SKB_ALIGN, |
654 | PCI_DMA_FROMDEVICE); | 706 | PCI_DMA_FROMDEVICE); |
655 | 707 | ||
656 | if (macrx & XCT_MACRX_CRC) { | 708 | if (macrx & XCT_MACRX_CRC) { |
@@ -874,24 +926,6 @@ static irqreturn_t pasemi_mac_tx_intr(int irq, void *data) | |||
874 | return IRQ_HANDLED; | 926 | return IRQ_HANDLED; |
875 | } | 927 | } |
876 | 928 | ||
877 | static void pasemi_mac_intf_disable(struct pasemi_mac *mac) | ||
878 | { | ||
879 | unsigned int flags; | ||
880 | |||
881 | flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); | ||
882 | flags &= ~PAS_MAC_CFG_PCFG_PE; | ||
883 | write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); | ||
884 | } | ||
885 | |||
886 | static void pasemi_mac_intf_enable(struct pasemi_mac *mac) | ||
887 | { | ||
888 | unsigned int flags; | ||
889 | |||
890 | flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); | ||
891 | flags |= PAS_MAC_CFG_PCFG_PE; | ||
892 | write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags); | ||
893 | } | ||
894 | |||
895 | static void pasemi_adjust_link(struct net_device *dev) | 929 | static void pasemi_adjust_link(struct net_device *dev) |
896 | { | 930 | { |
897 | struct pasemi_mac *mac = netdev_priv(dev); | 931 | struct pasemi_mac *mac = netdev_priv(dev); |
@@ -1148,11 +1182,71 @@ out_rx_resources: | |||
1148 | 1182 | ||
1149 | #define MAX_RETRIES 5000 | 1183 | #define MAX_RETRIES 5000 |
1150 | 1184 | ||
1185 | static void pasemi_mac_pause_txchan(struct pasemi_mac *mac) | ||
1186 | { | ||
1187 | unsigned int sta, retries; | ||
1188 | int txch = tx_ring(mac)->chan.chno; | ||
1189 | |||
1190 | write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), | ||
1191 | PAS_DMA_TXCHAN_TCMDSTA_ST); | ||
1192 | |||
1193 | for (retries = 0; retries < MAX_RETRIES; retries++) { | ||
1194 | sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch)); | ||
1195 | if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)) | ||
1196 | break; | ||
1197 | cond_resched(); | ||
1198 | } | ||
1199 | |||
1200 | if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT) | ||
1201 | dev_err(&mac->dma_pdev->dev, | ||
1202 | "Failed to stop tx channel, tcmdsta %08x\n", sta); | ||
1203 | |||
1204 | write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 0); | ||
1205 | } | ||
1206 | |||
1207 | static void pasemi_mac_pause_rxchan(struct pasemi_mac *mac) | ||
1208 | { | ||
1209 | unsigned int sta, retries; | ||
1210 | int rxch = rx_ring(mac)->chan.chno; | ||
1211 | |||
1212 | write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), | ||
1213 | PAS_DMA_RXCHAN_CCMDSTA_ST); | ||
1214 | for (retries = 0; retries < MAX_RETRIES; retries++) { | ||
1215 | sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch)); | ||
1216 | if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)) | ||
1217 | break; | ||
1218 | cond_resched(); | ||
1219 | } | ||
1220 | |||
1221 | if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT) | ||
1222 | dev_err(&mac->dma_pdev->dev, | ||
1223 | "Failed to stop rx channel, ccmdsta 08%x\n", sta); | ||
1224 | write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 0); | ||
1225 | } | ||
1226 | |||
1227 | static void pasemi_mac_pause_rxint(struct pasemi_mac *mac) | ||
1228 | { | ||
1229 | unsigned int sta, retries; | ||
1230 | |||
1231 | write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), | ||
1232 | PAS_DMA_RXINT_RCMDSTA_ST); | ||
1233 | for (retries = 0; retries < MAX_RETRIES; retries++) { | ||
1234 | sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); | ||
1235 | if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT)) | ||
1236 | break; | ||
1237 | cond_resched(); | ||
1238 | } | ||
1239 | |||
1240 | if (sta & PAS_DMA_RXINT_RCMDSTA_ACT) | ||
1241 | dev_err(&mac->dma_pdev->dev, | ||
1242 | "Failed to stop rx interface, rcmdsta %08x\n", sta); | ||
1243 | write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0); | ||
1244 | } | ||
1245 | |||
1151 | static int pasemi_mac_close(struct net_device *dev) | 1246 | static int pasemi_mac_close(struct net_device *dev) |
1152 | { | 1247 | { |
1153 | struct pasemi_mac *mac = netdev_priv(dev); | 1248 | struct pasemi_mac *mac = netdev_priv(dev); |
1154 | unsigned int sta; | 1249 | unsigned int sta; |
1155 | int retries; | ||
1156 | int rxch, txch; | 1250 | int rxch, txch; |
1157 | 1251 | ||
1158 | rxch = rx_ring(mac)->chan.chno; | 1252 | rxch = rx_ring(mac)->chan.chno; |
@@ -1190,51 +1284,10 @@ static int pasemi_mac_close(struct net_device *dev) | |||
1190 | pasemi_mac_clean_tx(tx_ring(mac)); | 1284 | pasemi_mac_clean_tx(tx_ring(mac)); |
1191 | pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE); | 1285 | pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE); |
1192 | 1286 | ||
1193 | /* Disable interface */ | 1287 | pasemi_mac_pause_txchan(mac); |
1194 | write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), | 1288 | pasemi_mac_pause_rxint(mac); |
1195 | PAS_DMA_TXCHAN_TCMDSTA_ST); | 1289 | pasemi_mac_pause_rxchan(mac); |
1196 | write_dma_reg( PAS_DMA_RXINT_RCMDSTA(mac->dma_if), | 1290 | pasemi_mac_intf_disable(mac); |
1197 | PAS_DMA_RXINT_RCMDSTA_ST); | ||
1198 | write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), | ||
1199 | PAS_DMA_RXCHAN_CCMDSTA_ST); | ||
1200 | |||
1201 | for (retries = 0; retries < MAX_RETRIES; retries++) { | ||
1202 | sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(rxch)); | ||
1203 | if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)) | ||
1204 | break; | ||
1205 | cond_resched(); | ||
1206 | } | ||
1207 | |||
1208 | if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT) | ||
1209 | dev_err(&mac->dma_pdev->dev, "Failed to stop tx channel\n"); | ||
1210 | |||
1211 | for (retries = 0; retries < MAX_RETRIES; retries++) { | ||
1212 | sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch)); | ||
1213 | if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)) | ||
1214 | break; | ||
1215 | cond_resched(); | ||
1216 | } | ||
1217 | |||
1218 | if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT) | ||
1219 | dev_err(&mac->dma_pdev->dev, "Failed to stop rx channel\n"); | ||
1220 | |||
1221 | for (retries = 0; retries < MAX_RETRIES; retries++) { | ||
1222 | sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); | ||
1223 | if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT)) | ||
1224 | break; | ||
1225 | cond_resched(); | ||
1226 | } | ||
1227 | |||
1228 | if (sta & PAS_DMA_RXINT_RCMDSTA_ACT) | ||
1229 | dev_err(&mac->dma_pdev->dev, "Failed to stop rx interface\n"); | ||
1230 | |||
1231 | /* Then, disable the channel. This must be done separately from | ||
1232 | * stopping, since you can't disable when active. | ||
1233 | */ | ||
1234 | |||
1235 | write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 0); | ||
1236 | write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 0); | ||
1237 | write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0); | ||
1238 | 1291 | ||
1239 | free_irq(mac->tx->chan.irq, mac->tx); | 1292 | free_irq(mac->tx->chan.irq, mac->tx); |
1240 | free_irq(mac->rx->chan.irq, mac->rx); | 1293 | free_irq(mac->rx->chan.irq, mac->rx); |
@@ -1388,6 +1441,62 @@ static int pasemi_mac_poll(struct napi_struct *napi, int budget) | |||
1388 | return pkts; | 1441 | return pkts; |
1389 | } | 1442 | } |
1390 | 1443 | ||
1444 | static int pasemi_mac_change_mtu(struct net_device *dev, int new_mtu) | ||
1445 | { | ||
1446 | struct pasemi_mac *mac = netdev_priv(dev); | ||
1447 | unsigned int reg; | ||
1448 | unsigned int rcmdsta; | ||
1449 | int running; | ||
1450 | |||
1451 | if (new_mtu < PE_MIN_MTU || new_mtu > PE_MAX_MTU) | ||
1452 | return -EINVAL; | ||
1453 | |||
1454 | running = netif_running(dev); | ||
1455 | |||
1456 | if (running) { | ||
1457 | /* Need to stop the interface, clean out all already | ||
1458 | * received buffers, free all unused buffers on the RX | ||
1459 | * interface ring, then finally re-fill the rx ring with | ||
1460 | * the new-size buffers and restart. | ||
1461 | */ | ||
1462 | |||
1463 | napi_disable(&mac->napi); | ||
1464 | netif_tx_disable(dev); | ||
1465 | pasemi_mac_intf_disable(mac); | ||
1466 | |||
1467 | rcmdsta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); | ||
1468 | pasemi_mac_pause_rxint(mac); | ||
1469 | pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE); | ||
1470 | pasemi_mac_free_rx_buffers(mac); | ||
1471 | } | ||
1472 | |||
1473 | /* Change maxf, i.e. what size frames are accepted. | ||
1474 | * Need room for ethernet header and CRC word | ||
1475 | */ | ||
1476 | reg = read_mac_reg(mac, PAS_MAC_CFG_MACCFG); | ||
1477 | reg &= ~PAS_MAC_CFG_MACCFG_MAXF_M; | ||
1478 | reg |= PAS_MAC_CFG_MACCFG_MAXF(new_mtu + ETH_HLEN + 4); | ||
1479 | write_mac_reg(mac, PAS_MAC_CFG_MACCFG, reg); | ||
1480 | |||
1481 | dev->mtu = new_mtu; | ||
1482 | /* MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ | ||
1483 | mac->bufsz = new_mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128; | ||
1484 | |||
1485 | if (running) { | ||
1486 | write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), | ||
1487 | rcmdsta | PAS_DMA_RXINT_RCMDSTA_EN); | ||
1488 | |||
1489 | rx_ring(mac)->next_to_fill = 0; | ||
1490 | pasemi_mac_replenish_rx_ring(dev, RX_RING_SIZE-1); | ||
1491 | |||
1492 | napi_enable(&mac->napi); | ||
1493 | netif_start_queue(dev); | ||
1494 | pasemi_mac_intf_enable(mac); | ||
1495 | } | ||
1496 | |||
1497 | return 0; | ||
1498 | } | ||
1499 | |||
1391 | static int __devinit | 1500 | static int __devinit |
1392 | pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | 1501 | pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
1393 | { | 1502 | { |
@@ -1475,6 +1584,12 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
1475 | dev->stop = pasemi_mac_close; | 1584 | dev->stop = pasemi_mac_close; |
1476 | dev->hard_start_xmit = pasemi_mac_start_tx; | 1585 | dev->hard_start_xmit = pasemi_mac_start_tx; |
1477 | dev->set_multicast_list = pasemi_mac_set_rx_mode; | 1586 | dev->set_multicast_list = pasemi_mac_set_rx_mode; |
1587 | dev->set_mac_address = pasemi_mac_set_mac_addr; | ||
1588 | dev->mtu = PE_DEF_MTU; | ||
1589 | /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ | ||
1590 | mac->bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128; | ||
1591 | |||
1592 | dev->change_mtu = pasemi_mac_change_mtu; | ||
1478 | 1593 | ||
1479 | if (err) | 1594 | if (err) |
1480 | goto out; | 1595 | goto out; |