📄 if_ath_ahb.c
字号:
/*- * Copyright (c) 2004 Atheros Communications, Inc. * All rights reserved * * $Id: if_ath_ahb.c 1590 2006-05-22 04:39:55Z mrenzmann $ */#include "opt_ah.h"#ifndef EXPORT_SYMTAB#define EXPORT_SYMTAB#endif#include <linux/config.h>#include <linux/version.h>#include <linux/module.h>#include <linux/init.h>#include <linux/if.h>#include <linux/netdevice.h>#include <linux/cache.h>#include <asm/io.h>#include <asm/uaccess.h>#include "if_media.h"#include <net80211/ieee80211_var.h>#include "if_athvar.h"#include "ah_devid.h"#include "if_ath_ahb.h"struct ath_ahb_softc { struct ath_softc aps_sc;#ifdef CONFIG_PM u32 aps_pmstate[16];#endif};static struct ath_ahb_softc *sclist[2] = {NULL, NULL};static u_int8_t num_activesc = 0;static struct ar531x_boarddata *ar5312_boardConfig = NULL;static char *radioConfig = NULL;static intar5312_get_board_config(void){ int dataFound; char *bd_config; /* * Find start of Board Configuration data, using heuristics: * Search back from the (aliased) end of flash by 0x1000 bytes * at a time until we find the string "5311", which marks the * start of Board Configuration. Give up if we've searched * more than 500KB. */ dataFound = 0; for (bd_config = (char *)0xbffff000; bd_config > (char *)0xbff80000; bd_config -= 0x1000) { if ( *(int *)bd_config == AR531X_BD_MAGIC) { dataFound = 1; break; } } if (!dataFound) { printk("Could not find Board Configuration Data\n"); bd_config = NULL; } ar5312_boardConfig = (struct ar531x_boarddata *) bd_config; return dataFound;}static intar5312_get_radio_config(void){ int dataFound; char *radio_config; /* * Now find the start of Radio Configuration data, using heuristics: * Search forward from Board Configuration data by 0x1000 bytes * at a time until we find non-0xffffffff. */ dataFound = 0; for (radio_config = ((char *) ar5312_boardConfig) + 0x1000; radio_config < (char *)0xbffff000; radio_config += 0x1000) { if (*(int *)radio_config != 0xffffffff) { dataFound = 1; break; } } if (!dataFound) { /* AR2316 relocates radio config to new location */ dataFound = 0; for (radio_config = ((char *) ar5312_boardConfig) + 0xf8; radio_config < (char *)0xbffff0f8; radio_config += 0x1000) { if (*(int *)radio_config != 0xffffffff) { dataFound = 1; break; } } } if (!dataFound) { printk("Could not find Radio Configuration data\n"); radio_config = NULL; } radioConfig = radio_config; return dataFound;}static intar5312SetupFlash(void){ if (ar5312_get_board_config()) if (ar5312_get_radio_config()) return 1; return 0;} /* * Read 16 bits of data from offset into *data */static voidar5312BspEepromRead(u_int32_t off, u_int32_t nbytes, u_int8_t *data){ int i; char *eepromAddr = radioConfig; for (i = 0; i < nbytes; i++, off++) data[i] = eepromAddr[off];}/* set bus cachesize in 4B word units */voidbus_read_cachesize(struct ath_softc *sc, u_int8_t *csz){ /* XXX: get the appropriate value! PCI case reads from config space, * and I think this is the data cache line-size. */ *csz = L1_CACHE_BYTES / sizeof(u_int32_t);}/* NOTE: returns uncached (kseg1) address. */void *bus_alloc_consistent(void *hwdev, size_t size, dma_addr_t *dma_handle){ void *ret; int gfp = GFP_ATOMIC; ret = (void *) __get_free_pages(gfp, get_order(size)); if (ret != NULL) { memset(ret, 0, size); *dma_handle = __pa(ret); dma_cache_wback_inv((unsigned long) ret, size); ret = UNCAC_ADDR(ret); } return ret;}voidbus_free_consistent(void *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle){ unsigned long addr = (unsigned long) vaddr; addr = CAC_ADDR(addr); free_pages(addr, get_order(size));}intahb_enable_wmac(u_int16_t devid, u_int16_t wlanNum){ if ((devid & AR5315_REV_MAJ_M) == AR5315_REV_MAJ) { u_int32_t reg; u_int32_t *en = (u_int32_t *) AR5315_AHB_ARB_CTL; KASSERT(wlanNum == 0, ("invalid wlan # %d", wlanNum)); /* Enable Arbitration for WLAN */ *en |= AR5315_ARB_WLAN; /* Enable global swapping so this looks like a normal BE system */ reg = REG_READ(AR5315_ENDIAN_CTL); reg |= AR5315_CONFIG_WLAN; REG_WRITE(AR5315_ENDIAN_CTL, reg); /* wake up the MAC */ /* NOTE: for the following write to succeed the * RST_AHB_ARB_CTL should be set to 0. This driver * assumes that the register has been set to 0 by boot loader */ reg = REG_READ(AR5315_PCI_MAC_SCR); reg = (reg & ~AR5315_PCI_MAC_SCR_SLMODE_M) | (AR5315_PCI_MAC_SCR_SLM_FWAKE << AR5315_PCI_MAC_SCR_SLMODE_S); REG_WRITE(AR5315_PCI_MAC_SCR, reg); /* wait for the MAC to wakeup */ while (REG_READ(AR5315_PCI_MAC_PCICFG) & AR5315_PCI_MAC_PCICFG_SPWR_DN); } else { u_int32_t *en = (u_int32_t *)AR531X_ENABLE; switch (wlanNum) { case AR531X_WLAN0_NUM: *en |= AR531X_ENABLE_WLAN0; break; case AR531X_WLAN1_NUM: *en |= AR531X_ENABLE_WLAN1; break; default: return -ENODEV; } } return 0;}intahb_disable_wmac(u_int16_t devid, u_int16_t wlanNum){ if ((devid & AR5315_REV_MAJ_M) == AR5315_REV_MAJ) { u_int32_t *en = (u_int32_t *) AR5315_AHB_ARB_CTL; KASSERT(wlanNum == 0, ("invalid wlan # %d", wlanNum) ); /* Enable Arbitration for WLAN */ *en &= ~AR5315_ARB_WLAN; } else { u_int32_t *en = (u_int32_t *)AR531X_ENABLE; switch (wlanNum) { case AR531X_WLAN0_NUM: *en &= ~AR531X_ENABLE_WLAN0; break; case AR531X_WLAN1_NUM: *en &= ~AR531X_ENABLE_WLAN1; break; default: return -ENODEV; } } return 0;}intexit_ath_wmac(u_int16_t wlanNum){ struct ath_ahb_softc *sc = sclist[wlanNum]; struct net_device *dev; const char *sysType; u_int16_t devid; if (sc == NULL) return -ENODEV; /* XXX: correct return value? */ dev = sc->aps_sc.sc_dev; ath_detach(dev); if (dev->irq) free_irq(dev->irq, dev); sysType = get_system_type(); if (!strcmp(sysType, "Atheros AR5315")) devid = (u_int16_t) (sysRegRead(AR5315_SREV) & (AR5315_REV_MAJ_M | AR5315_REV_MIN_M)); else devid = (u_int16_t) ((sysRegRead(AR531X_REV) >> 8) & (AR531X_REV_MAJ | AR531X_REV_MIN)); ahb_disable_wmac(devid, wlanNum); free_netdev(dev); sclist[wlanNum] = NULL; return 0;}intinit_ath_wmac(u_int16_t devid, u_int16_t wlanNum){ const char *athname; struct net_device *dev; struct ath_ahb_softc *sc; if (((wlanNum != 0) && (wlanNum != 1)) || (sclist[wlanNum] != NULL)) goto bad; ahb_enable_wmac(devid, wlanNum); dev = alloc_netdev(sizeof(struct ath_ahb_softc), "wifi%d", ether_setup); if (dev == NULL) { printk(KERN_ERR "ath_dev_probe: no memory for device state\n"); goto bad2; } sc = dev->priv; sc->aps_sc.sc_dev = dev; /* * Mark the device as detached to avoid processing * interrupts until setup is complete. */ sc->aps_sc.sc_invalid = 1;#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,41) dev->owner = THIS_MODULE;#else SET_MODULE_OWNER(dev);#endif sclist[wlanNum] = sc; switch (wlanNum) { case AR531X_WLAN0_NUM: if ((devid & AR5315_REV_MAJ_M) == AR5315_REV_MAJ) { dev->irq = AR5315_IRQ_WLAN0_INTRS; dev->mem_start = AR5315_WLAN0; } else { dev->irq = AR531X_IRQ_WLAN0_INTRS; dev->mem_start = AR531X_WLAN0; } break; case AR531X_WLAN1_NUM: dev->irq = AR531X_IRQ_WLAN1_INTRS; dev->mem_start = KSEG1ADDR(AR531X_WLAN1); break; default: goto bad3; } dev->mem_end = dev->mem_start + AR531X_WLANX_LEN; sc->aps_sc.sc_bdev = NULL; if (request_irq(dev->irq, ath_intr, SA_SHIRQ, dev->name, dev)) { printk(KERN_WARNING "%s: request_irq failed\n", dev->name); goto bad3; } if (ath_attach(devid, dev) != 0) goto bad4; athname = ath_hal_probe(ATHEROS_VENDOR_ID, devid); printk(KERN_INFO "%s: %s: mem=0x%lx, irq=%d\n", dev->name, athname ? athname : "Atheros ???", dev->mem_start, dev->irq); num_activesc++; /* Ready to process interrupts */ sc->aps_sc.sc_invalid = 0; return 0; bad4: free_irq(dev->irq, dev); bad3: free_netdev(dev); sclist[wlanNum] = NULL; bad2: ahb_disable_wmac(devid, wlanNum); bad: return -ENODEV;}intinit_ahb(void){ int ret; u_int16_t devid, radioMask; const char *sysType; sysType = get_system_type(); if (!strcmp(sysType,"Atheros AR5315")) { devid = (u_int16_t) (sysRegRead(AR5315_SREV) & (AR5315_REV_MAJ_M | AR5315_REV_MIN_M)); if ((devid & AR5315_REV_MAJ_M) == AR5315_REV_MAJ) return init_ath_wmac(devid, 0); } /* Probe to find out the silicon revision and enable the correct number of macs */ if (!ar5312SetupFlash()) return -ENODEV; devid = (u_int16_t) ((sysRegRead(AR531X_REV) >>8) & (AR531X_REV_MAJ | AR531X_REV_MIN)); switch (devid) { case AR5212_AR5312_REV2: case AR5212_AR5312_REV7: /* Need to determine if we have a 5312 or a 2312 since they * have the same Silicon Rev ID */ ar5312BspEepromRead(2 * AR531X_RADIO_MASK_OFF, 2, (char *) &radioMask); if ((radioMask & AR531X_RADIO0_MASK) != 0) if ((ret = init_ath_wmac(devid, 0)) !=0 ) return ret; /* XXX: Fall through?! */ case AR5212_AR2313_REV8: if ((ret = init_ath_wmac(devid, 1)) != 0) return ret; break; default: return -ENODEV; } return 0;}/* * Module glue. */#include "version.h"#include "release.h"static char *version = ATH_PCI_VERSION " (" RELEASE_VERSION ")";static char *dev_info = "ath_ahb";#include <linux/ethtool.h>intath_ioctl_ethtool(struct ath_softc *sc, int cmd, void __user *addr){ struct ethtool_drvinfo info; if (cmd != ETHTOOL_GDRVINFO) return -EOPNOTSUPP; memset(&info, 0, sizeof(info)); info.cmd = cmd; strncpy(info.driver, dev_info, sizeof(info.driver) - 1); strncpy(info.version, version, sizeof(info.version) - 1); return copy_to_user(addr, &info, sizeof(info)) ? -EFAULT : 0;}MODULE_AUTHOR("Atheros Communications, Inc.");MODULE_DESCRIPTION("Support for Atheros 802.11 wireless LAN cards.");#ifdef MODULE_VERSIONMODULE_VERSION(RELEASE_VERSION);#endifMODULE_SUPPORTED_DEVICE("Atheros WLAN cards");#ifdef MODULE_LICENSEMODULE_LICENSE("Dual BSD/GPL");#endifstatic int __initinit_ath_ahb(void){ printk(KERN_INFO "%s: %s\n", dev_info, version); if (init_ahb() != 0) { printk("ath_ahb: No devices found, driver not installed.\n"); return (-ENODEV); }#ifdef CONFIG_SYSCTL ath_sysctl_register();#endif return 0;}module_init(init_ath_ahb);static void __exitexit_ath_ahb(void){#ifdef CONFIG_SYSCTL ath_sysctl_unregister();#endif exit_ath_wmac(AR531X_WLAN0_NUM); exit_ath_wmac(AR531X_WLAN1_NUM); printk(KERN_INFO "%s: driver unloaded\n", dev_info);}module_exit(exit_ath_ahb);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -