Subject: [PATCH] firmware: convert acenic driver to request_firmware() From: David Woodhouse To: Jes Sorensen Cc: netdev@vger.kernel.org, jaswinder@infradead.org Again with help from Jaswinder Singh. Omitting the large part of the patch which actually moves the firmware around, since you'll need to pull from the git tree to get the preceding patches if you want to test it anyway. We store the firmware in little-endian form now, and thus use __raw_writel() to write it to the device, to avoid byteswapping by writel(). I've revamped that loop in ace_copy() a little bit so it could probably do with being tested. I've dropped the information about SBSS and BSS sections of the firmware -- we were clearing the whole of the device's memory in advance anyway, so clearing the BSS sections for a _second_ time seems pointless. And since the text,rodata,data sections were (almost) contiguous, we now just load those as a single blob rather than keeping them separate. Although it probably isn't necessary, we do preserve the ability to change the load and start addresses from 0x4000, by putting them into a header at the beginning of the firmware blob, along with the version number. [ ported to debian -maks ] diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f80c839..7677680 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1912,8 +1912,8 @@ if NETDEV_1000 config ACENIC tristate "Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support" - depends on BROKEN depends on PCI + select FW_LOADER ---help--- Say Y here if you have an Alteon AceNIC, 3Com 3C985(B), NetGear GA620, SGI Gigabit or Farallon PN9000-SX PCI Gigabit Ethernet diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index 6c19265..1e42734 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -67,6 +67,7 @@ #include #include #include +#include #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) #include @@ -187,8 +188,6 @@ MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); #define MAX_RODATA_LEN 8*1024 #define MAX_DATA_LEN 2*1024 -#include "acenic_firmware.h" - #ifndef tigon2FwReleaseLocal #define tigon2FwReleaseLocal 0 #endif @@ -418,6 +417,8 @@ static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1}; MODULE_AUTHOR("Jes Sorensen "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver"); +MODULE_FIRMWARE("acenic_tg1.bin"); +MODULE_FIRMWARE("acenic_tg2.bin"); module_param_array_named(link, link_state, int, NULL, 0); module_param_array(trace, int, NULL, 0); @@ -939,8 +940,8 @@ static int __devinit ace_init(struct net_device *dev) case 4: case 5: printk(KERN_INFO " Tigon I (Rev. %i), Firmware: %i.%i.%i, ", - tig_ver, tigonFwReleaseMajor, tigonFwReleaseMinor, - tigonFwReleaseFix); + tig_ver, ap->firmware_major, ap->firmware_minor, + ap->firmware_fix); writel(0, ®s->LocalCtrl); ap->version = 1; ap->tx_ring_entries = TIGON_I_TX_RING_ENTRIES; @@ -948,8 +949,8 @@ static int __devinit ace_init(struct net_device *dev) #endif case 6: printk(KERN_INFO " Tigon II (Rev. %i), Firmware: %i.%i.%i, ", - tig_ver, tigon2FwReleaseMajor, tigon2FwReleaseMinor, - tigon2FwReleaseFix); + tig_ver, ap->firmware_major, ap->firmware_minor, + ap->firmware_fix); writel(readl(®s->CpuBCtrl) | CPU_HALT, ®s->CpuBCtrl); readl(®s->CpuBCtrl); /* PCI write posting */ /* @@ -1201,7 +1202,9 @@ static int __devinit ace_init(struct net_device *dev) memset(ap->info, 0, sizeof(struct ace_info)); memset(ap->skb, 0, sizeof(struct ace_skb)); - ace_load_firmware(dev); + if (ace_load_firmware(dev)) + goto init_error; + ap->fw_running = 0; tmp_ptr = ap->info_dma; @@ -1437,10 +1440,7 @@ static int __devinit ace_init(struct net_device *dev) if (ap->version >= 2) writel(tmp, ®s->TuneFastLink); - if (ACE_IS_TIGON_I(ap)) - writel(tigonFwStartAddr, ®s->Pc); - if (ap->version == 2) - writel(tigon2FwStartAddr, ®s->Pc); + writel(ap->firmware_start, ®s->Pc); writel(0, ®s->Mb0Lo); @@ -2763,8 +2763,8 @@ static void ace_get_drvinfo(struct net_device *dev, strlcpy(info->driver, "acenic", sizeof(info->driver)); snprintf(info->version, sizeof(info->version), "%i.%i.%i", - tigonFwReleaseMajor, tigonFwReleaseMinor, - tigonFwReleaseFix); + ap->firmware_major, ap->firmware_minor, + ap->firmware_fix); if (ap->pdev) strlcpy(info->bus_info, pci_name(ap->pdev), @@ -2871,11 +2871,10 @@ static struct net_device_stats *ace_get_stats(struct net_device *dev) } -static void __devinit ace_copy(struct ace_regs __iomem *regs, void *src, - u32 dest, int size) +static void __devinit ace_copy(struct ace_regs __iomem *regs, const __le32 *src, + u32 dest, int size) { void __iomem *tdest; - u32 *wsrc; short tsize, i; if (size <= 0) @@ -2887,20 +2886,15 @@ static void __devinit ace_copy(struct ace_regs __iomem *regs, void *src, tdest = (void __iomem *) ®s->Window + (dest & (ACE_WINDOW_SIZE - 1)); writel(dest & ~(ACE_WINDOW_SIZE - 1), ®s->WinBase); - /* - * This requires byte swapping on big endian, however - * writel does that for us - */ - wsrc = src; for (i = 0; i < (tsize / 4); i++) { - writel(wsrc[i], tdest + i*4); + /* Firmware is stored as little-endian */ + __raw_writel(*src, tdest); + src++; + tdest += 4; + dest += 4; + size -= 4; } - dest += tsize; - src += tsize; - size -= tsize; } - - return; } @@ -2939,8 +2933,13 @@ static void __devinit ace_clear(struct ace_regs __iomem *regs, u32 dest, int siz */ static int __devinit ace_load_firmware(struct net_device *dev) { + const struct firmware *fw; + const char *fw_name = "acenic_tg2.bin"; struct ace_private *ap = netdev_priv(dev); struct ace_regs __iomem *regs = ap->regs; + const __le32 *fw_data; + u32 load_addr; + int ret; if (!(readl(®s->CpuCtrl) & CPU_HALTED)) { printk(KERN_ERR "%s: trying to download firmware while the " @@ -2948,28 +2947,52 @@ static int __devinit ace_load_firmware(struct net_device *dev) return -EFAULT; } + if (ACE_IS_TIGON_I(ap)) + fw_name = "acenic_tg1.bin"; + + ret = request_firmware(&fw, fw_name, &ap->pdev->dev); + if (ret) { + printk(KERN_ERR "%s: Failed to load firmware \"%s\"\n", + ap->name, fw_name); + return ret; + } + + fw_data = (void *)fw->data; + + /* Firmware blob starts with version numbers, followed by + load and start address. Remainder is the blob to be loaded + contiguously from load address. We don't bother to represent + the BSS/SBSS sections any more, since we were clearing the + whole thing anyway. */ + ap->firmware_major = fw->data[0]; + ap->firmware_minor = fw->data[1]; + ap->firmware_fix = fw->data[2]; + + ap->firmware_start = le32_to_cpu(fw_data[1]); + if (ap->firmware_start < 0x4000 || ap->firmware_start >= 0x80000) { + printk(KERN_ERR "%s: bogus load address %08x in \"%s\"\n", + ap->name, ap->firmware_start, fw_name); + ret = -EINVAL; + goto out; + } + + load_addr = le32_to_cpu(fw_data[2]); + if (load_addr < 0x4000 || load_addr >= 0x80000) { + printk(KERN_ERR "%s: bogus load address %08x in \"%s\"\n", + ap->name, load_addr, fw_name); + ret = -EINVAL; + goto out; + } + /* - * Do not try to clear more than 512KB or we end up seeing - * funny things on NICs with only 512KB SRAM + * Do not try to clear more than 512KiB or we end up seeing + * funny things on NICs with only 512KiB SRAM */ ace_clear(regs, 0x2000, 0x80000-0x2000); - if (ACE_IS_TIGON_I(ap)) { - ace_copy(regs, tigonFwText, tigonFwTextAddr, tigonFwTextLen); - ace_copy(regs, tigonFwData, tigonFwDataAddr, tigonFwDataLen); - ace_copy(regs, tigonFwRodata, tigonFwRodataAddr, - tigonFwRodataLen); - ace_clear(regs, tigonFwBssAddr, tigonFwBssLen); - ace_clear(regs, tigonFwSbssAddr, tigonFwSbssLen); - }else if (ap->version == 2) { - ace_clear(regs, tigon2FwBssAddr, tigon2FwBssLen); - ace_clear(regs, tigon2FwSbssAddr, tigon2FwSbssLen); - ace_copy(regs, tigon2FwText, tigon2FwTextAddr,tigon2FwTextLen); - ace_copy(regs, tigon2FwRodata, tigon2FwRodataAddr, - tigon2FwRodataLen); - ace_copy(regs, tigon2FwData, tigon2FwDataAddr,tigon2FwDataLen); - } - - return 0; + ace_copy(regs, &fw_data[3], load_addr, fw->size-12); + out: + release_firmware(fw); + return ret; } diff --git a/drivers/net/acenic.h b/drivers/net/acenic.h index 60ed183..cf7e80e 100644 --- a/drivers/net/acenic.h +++ b/drivers/net/acenic.h @@ -695,6 +695,10 @@ struct ace_private #endif struct net_device_stats stats; int pci_using_dac; + u8 firmware_major; + u8 firmware_minor; + u8 firmware_fix; + u32 firmware_start; };