diff options
Diffstat (limited to 'drivers/net/typhoon.c')
-rw-r--r-- | drivers/net/typhoon.c | 131 |
1 files changed, 74 insertions, 57 deletions
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index a8e5651f3165..cd3283f766d9 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c | |||
@@ -129,16 +129,18 @@ static const int multicast_filter_limit = 32; | |||
129 | #include <asm/uaccess.h> | 129 | #include <asm/uaccess.h> |
130 | #include <linux/in6.h> | 130 | #include <linux/in6.h> |
131 | #include <linux/dma-mapping.h> | 131 | #include <linux/dma-mapping.h> |
132 | #include <linux/firmware.h> | ||
132 | 133 | ||
133 | #include "typhoon.h" | 134 | #include "typhoon.h" |
134 | #include "typhoon-firmware.h" | ||
135 | 135 | ||
136 | static char version[] __devinitdata = | 136 | static char version[] __devinitdata = |
137 | "typhoon.c: version " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; | 137 | "typhoon.c: version " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; |
138 | 138 | ||
139 | #define FIRMWARE_NAME "3com/typhoon.bin" | ||
139 | MODULE_AUTHOR("David Dillow <dave@thedillows.org>"); | 140 | MODULE_AUTHOR("David Dillow <dave@thedillows.org>"); |
140 | MODULE_VERSION(DRV_MODULE_VERSION); | 141 | MODULE_VERSION(DRV_MODULE_VERSION); |
141 | MODULE_LICENSE("GPL"); | 142 | MODULE_LICENSE("GPL"); |
143 | MODULE_FIRMWARE(FIRMWARE_NAME); | ||
142 | MODULE_DESCRIPTION("3Com Typhoon Family (3C990, 3CR990, and variants)"); | 144 | MODULE_DESCRIPTION("3Com Typhoon Family (3C990, 3CR990, and variants)"); |
143 | MODULE_PARM_DESC(rx_copybreak, "Packets smaller than this are copied and " | 145 | MODULE_PARM_DESC(rx_copybreak, "Packets smaller than this are copied and " |
144 | "the buffer given back to the NIC. Default " | 146 | "the buffer given back to the NIC. Default " |
@@ -1344,45 +1346,61 @@ typhoon_init_rings(struct typhoon *tp) | |||
1344 | tp->txHiRing.lastRead = 0; | 1346 | tp->txHiRing.lastRead = 0; |
1345 | } | 1347 | } |
1346 | 1348 | ||
1349 | static const struct firmware *typhoon_fw; | ||
1350 | |||
1351 | static int | ||
1352 | typhoon_request_firmware(struct typhoon *tp) | ||
1353 | { | ||
1354 | int err; | ||
1355 | |||
1356 | if (typhoon_fw) | ||
1357 | return 0; | ||
1358 | |||
1359 | err = request_firmware(&typhoon_fw, FIRMWARE_NAME, &tp->pdev->dev); | ||
1360 | if (err) { | ||
1361 | printk(KERN_ERR "%s: Failed to load firmware \"%s\"\n", | ||
1362 | tp->name, FIRMWARE_NAME); | ||
1363 | return err; | ||
1364 | } | ||
1365 | |||
1366 | if (typhoon_fw->size < sizeof(struct typhoon_file_header) || | ||
1367 | memcmp(typhoon_fw->data, "TYPHOON", 8)) { | ||
1368 | printk(KERN_ERR "%s: Invalid firmware image\n", | ||
1369 | tp->name); | ||
1370 | release_firmware(typhoon_fw); | ||
1371 | typhoon_fw = NULL; | ||
1372 | return -EINVAL; | ||
1373 | } | ||
1374 | |||
1375 | return 0; | ||
1376 | } | ||
1377 | |||
1347 | static int | 1378 | static int |
1348 | typhoon_download_firmware(struct typhoon *tp) | 1379 | typhoon_download_firmware(struct typhoon *tp) |
1349 | { | 1380 | { |
1350 | void __iomem *ioaddr = tp->ioaddr; | 1381 | void __iomem *ioaddr = tp->ioaddr; |
1351 | struct pci_dev *pdev = tp->pdev; | 1382 | struct pci_dev *pdev = tp->pdev; |
1352 | struct typhoon_file_header *fHdr; | 1383 | const struct typhoon_file_header *fHdr; |
1353 | struct typhoon_section_header *sHdr; | 1384 | const struct typhoon_section_header *sHdr; |
1354 | u8 *image_data; | 1385 | const u8 *image_data; |
1355 | void *dpage; | 1386 | dma_addr_t image_dma; |
1356 | dma_addr_t dpage_dma; | ||
1357 | __sum16 csum; | 1387 | __sum16 csum; |
1358 | u32 irqEnabled; | 1388 | u32 irqEnabled; |
1359 | u32 irqMasked; | 1389 | u32 irqMasked; |
1360 | u32 numSections; | 1390 | u32 numSections; |
1361 | u32 section_len; | 1391 | u32 section_len; |
1362 | u32 len; | ||
1363 | u32 load_addr; | 1392 | u32 load_addr; |
1364 | u32 hmac; | 1393 | u32 hmac; |
1365 | int i; | 1394 | int i; |
1366 | int err; | 1395 | int err; |
1367 | 1396 | ||
1368 | err = -EINVAL; | 1397 | image_data = typhoon_fw->data; |
1369 | fHdr = (struct typhoon_file_header *) typhoon_firmware_image; | 1398 | fHdr = (struct typhoon_file_header *) image_data; |
1370 | image_data = (u8 *) fHdr; | ||
1371 | |||
1372 | if(memcmp(fHdr->tag, "TYPHOON", 8)) { | ||
1373 | printk(KERN_ERR "%s: Invalid firmware image!\n", tp->name); | ||
1374 | goto err_out; | ||
1375 | } | ||
1376 | 1399 | ||
1377 | /* Cannot just map the firmware image using pci_map_single() as | ||
1378 | * the firmware is part of the kernel/module image, so we allocate | ||
1379 | * some consistent memory to copy the sections into, as it is simpler, | ||
1380 | * and short-lived. If we ever split out and require a userland | ||
1381 | * firmware loader, then we can revisit this. | ||
1382 | */ | ||
1383 | err = -ENOMEM; | 1400 | err = -ENOMEM; |
1384 | dpage = pci_alloc_consistent(pdev, PAGE_SIZE, &dpage_dma); | 1401 | image_dma = pci_map_single(pdev, (u8 *) typhoon_fw->data, |
1385 | if(!dpage) { | 1402 | typhoon_fw->size, PCI_DMA_TODEVICE); |
1403 | if (pci_dma_mapping_error(pdev, image_dma)) { | ||
1386 | printk(KERN_ERR "%s: no DMA mem for firmware\n", tp->name); | 1404 | printk(KERN_ERR "%s: no DMA mem for firmware\n", tp->name); |
1387 | goto err_out; | 1405 | goto err_out; |
1388 | } | 1406 | } |
@@ -1430,41 +1448,34 @@ typhoon_download_firmware(struct typhoon *tp) | |||
1430 | load_addr = le32_to_cpu(sHdr->startAddr); | 1448 | load_addr = le32_to_cpu(sHdr->startAddr); |
1431 | section_len = le32_to_cpu(sHdr->len); | 1449 | section_len = le32_to_cpu(sHdr->len); |
1432 | 1450 | ||
1433 | while(section_len) { | 1451 | if (typhoon_wait_interrupt(ioaddr) < 0 || |
1434 | len = min_t(u32, section_len, PAGE_SIZE); | 1452 | ioread32(ioaddr + TYPHOON_REG_STATUS) != |
1453 | TYPHOON_STATUS_WAITING_FOR_SEGMENT) { | ||
1454 | printk(KERN_ERR "%s: segment ready timeout\n", | ||
1455 | tp->name); | ||
1456 | goto err_out_irq; | ||
1457 | } | ||
1435 | 1458 | ||
1436 | if(typhoon_wait_interrupt(ioaddr) < 0 || | 1459 | /* Do an pseudo IPv4 checksum on the data -- first |
1437 | ioread32(ioaddr + TYPHOON_REG_STATUS) != | 1460 | * need to convert each u16 to cpu order before |
1438 | TYPHOON_STATUS_WAITING_FOR_SEGMENT) { | 1461 | * summing. Fortunately, due to the properties of |
1439 | printk(KERN_ERR "%s: segment ready timeout\n", | 1462 | * the checksum, we can do this once, at the end. |
1440 | tp->name); | 1463 | */ |
1441 | goto err_out_irq; | 1464 | csum = csum_fold(csum_partial(image_data, section_len, 0)); |
1442 | } | 1465 | |
1466 | iowrite32(section_len, ioaddr + TYPHOON_REG_BOOT_LENGTH); | ||
1467 | iowrite32(le16_to_cpu((__force __le16)csum), | ||
1468 | ioaddr + TYPHOON_REG_BOOT_CHECKSUM); | ||
1469 | iowrite32(load_addr, | ||
1470 | ioaddr + TYPHOON_REG_BOOT_DEST_ADDR); | ||
1471 | iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI); | ||
1472 | iowrite32(image_dma + (image_data - typhoon_fw->data), | ||
1473 | ioaddr + TYPHOON_REG_BOOT_DATA_LO); | ||
1474 | typhoon_post_pci_writes(ioaddr); | ||
1475 | iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE, | ||
1476 | ioaddr + TYPHOON_REG_COMMAND); | ||
1443 | 1477 | ||
1444 | /* Do an pseudo IPv4 checksum on the data -- first | 1478 | image_data += section_len; |
1445 | * need to convert each u16 to cpu order before | ||
1446 | * summing. Fortunately, due to the properties of | ||
1447 | * the checksum, we can do this once, at the end. | ||
1448 | */ | ||
1449 | csum = csum_fold(csum_partial_copy_nocheck(image_data, | ||
1450 | dpage, len, | ||
1451 | 0)); | ||
1452 | |||
1453 | iowrite32(len, ioaddr + TYPHOON_REG_BOOT_LENGTH); | ||
1454 | iowrite32(le16_to_cpu((__force __le16)csum), | ||
1455 | ioaddr + TYPHOON_REG_BOOT_CHECKSUM); | ||
1456 | iowrite32(load_addr, | ||
1457 | ioaddr + TYPHOON_REG_BOOT_DEST_ADDR); | ||
1458 | iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI); | ||
1459 | iowrite32(dpage_dma, ioaddr + TYPHOON_REG_BOOT_DATA_LO); | ||
1460 | typhoon_post_pci_writes(ioaddr); | ||
1461 | iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE, | ||
1462 | ioaddr + TYPHOON_REG_COMMAND); | ||
1463 | |||
1464 | image_data += len; | ||
1465 | load_addr += len; | ||
1466 | section_len -= len; | ||
1467 | } | ||
1468 | } | 1479 | } |
1469 | 1480 | ||
1470 | if(typhoon_wait_interrupt(ioaddr) < 0 || | 1481 | if(typhoon_wait_interrupt(ioaddr) < 0 || |
@@ -1488,7 +1499,7 @@ err_out_irq: | |||
1488 | iowrite32(irqMasked, ioaddr + TYPHOON_REG_INTR_MASK); | 1499 | iowrite32(irqMasked, ioaddr + TYPHOON_REG_INTR_MASK); |
1489 | iowrite32(irqEnabled, ioaddr + TYPHOON_REG_INTR_ENABLE); | 1500 | iowrite32(irqEnabled, ioaddr + TYPHOON_REG_INTR_ENABLE); |
1490 | 1501 | ||
1491 | pci_free_consistent(pdev, PAGE_SIZE, dpage, dpage_dma); | 1502 | pci_unmap_single(pdev, image_dma, typhoon_fw->size, PCI_DMA_TODEVICE); |
1492 | 1503 | ||
1493 | err_out: | 1504 | err_out: |
1494 | return err; | 1505 | return err; |
@@ -2086,6 +2097,10 @@ typhoon_open(struct net_device *dev) | |||
2086 | struct typhoon *tp = netdev_priv(dev); | 2097 | struct typhoon *tp = netdev_priv(dev); |
2087 | int err; | 2098 | int err; |
2088 | 2099 | ||
2100 | err = typhoon_request_firmware(tp); | ||
2101 | if (err) | ||
2102 | goto out; | ||
2103 | |||
2089 | err = typhoon_wakeup(tp, WaitSleep); | 2104 | err = typhoon_wakeup(tp, WaitSleep); |
2090 | if(err < 0) { | 2105 | if(err < 0) { |
2091 | printk(KERN_ERR "%s: unable to wakeup device\n", dev->name); | 2106 | printk(KERN_ERR "%s: unable to wakeup device\n", dev->name); |
@@ -2624,6 +2639,8 @@ typhoon_init(void) | |||
2624 | static void __exit | 2639 | static void __exit |
2625 | typhoon_cleanup(void) | 2640 | typhoon_cleanup(void) |
2626 | { | 2641 | { |
2642 | if (typhoon_fw) | ||
2643 | release_firmware(typhoon_fw); | ||
2627 | pci_unregister_driver(&typhoon_driver); | 2644 | pci_unregister_driver(&typhoon_driver); |
2628 | } | 2645 | } |
2629 | 2646 | ||