1*0e5d3ab5SSergey Ryazanov /* 2*0e5d3ab5SSergey Ryazanov * Copyright (c) 2008-2009 Atheros Communications Inc. 3*0e5d3ab5SSergey Ryazanov * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org> 4*0e5d3ab5SSergey Ryazanov * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org> 5*0e5d3ab5SSergey Ryazanov * 6*0e5d3ab5SSergey Ryazanov * Permission to use, copy, modify, and/or distribute this software for any 7*0e5d3ab5SSergey Ryazanov * purpose with or without fee is hereby granted, provided that the above 8*0e5d3ab5SSergey Ryazanov * copyright notice and this permission notice appear in all copies. 9*0e5d3ab5SSergey Ryazanov * 10*0e5d3ab5SSergey Ryazanov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11*0e5d3ab5SSergey Ryazanov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12*0e5d3ab5SSergey Ryazanov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13*0e5d3ab5SSergey Ryazanov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14*0e5d3ab5SSergey Ryazanov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15*0e5d3ab5SSergey Ryazanov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16*0e5d3ab5SSergey Ryazanov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17*0e5d3ab5SSergey Ryazanov */ 18*0e5d3ab5SSergey Ryazanov 19*0e5d3ab5SSergey Ryazanov #include <linux/nl80211.h> 20*0e5d3ab5SSergey Ryazanov #include <linux/platform_device.h> 21*0e5d3ab5SSergey Ryazanov #include <linux/etherdevice.h> 22*0e5d3ab5SSergey Ryazanov #include <linux/export.h> 23*0e5d3ab5SSergey Ryazanov #include <ar231x_platform.h> 24*0e5d3ab5SSergey Ryazanov #include "ath5k.h" 25*0e5d3ab5SSergey Ryazanov #include "debug.h" 26*0e5d3ab5SSergey Ryazanov #include "base.h" 27*0e5d3ab5SSergey Ryazanov #include "reg.h" 28*0e5d3ab5SSergey Ryazanov 29*0e5d3ab5SSergey Ryazanov /* return bus cachesize in 4B word units */ 30*0e5d3ab5SSergey Ryazanov static void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz) 31*0e5d3ab5SSergey Ryazanov { 32*0e5d3ab5SSergey Ryazanov *csz = L1_CACHE_BYTES >> 2; 33*0e5d3ab5SSergey Ryazanov } 34*0e5d3ab5SSergey Ryazanov 35*0e5d3ab5SSergey Ryazanov static bool 36*0e5d3ab5SSergey Ryazanov ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) 37*0e5d3ab5SSergey Ryazanov { 38*0e5d3ab5SSergey Ryazanov struct ath5k_hw *ah = common->priv; 39*0e5d3ab5SSergey Ryazanov struct platform_device *pdev = to_platform_device(ah->dev); 40*0e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); 41*0e5d3ab5SSergey Ryazanov u16 *eeprom, *eeprom_end; 42*0e5d3ab5SSergey Ryazanov 43*0e5d3ab5SSergey Ryazanov eeprom = (u16 *) bcfg->radio; 44*0e5d3ab5SSergey Ryazanov eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ; 45*0e5d3ab5SSergey Ryazanov 46*0e5d3ab5SSergey Ryazanov eeprom += off; 47*0e5d3ab5SSergey Ryazanov if (eeprom > eeprom_end) 48*0e5d3ab5SSergey Ryazanov return false; 49*0e5d3ab5SSergey Ryazanov 50*0e5d3ab5SSergey Ryazanov *data = *eeprom; 51*0e5d3ab5SSergey Ryazanov return true; 52*0e5d3ab5SSergey Ryazanov } 53*0e5d3ab5SSergey Ryazanov 54*0e5d3ab5SSergey Ryazanov int ath5k_hw_read_srev(struct ath5k_hw *ah) 55*0e5d3ab5SSergey Ryazanov { 56*0e5d3ab5SSergey Ryazanov struct platform_device *pdev = to_platform_device(ah->dev); 57*0e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); 58*0e5d3ab5SSergey Ryazanov ah->ah_mac_srev = bcfg->devid; 59*0e5d3ab5SSergey Ryazanov return 0; 60*0e5d3ab5SSergey Ryazanov } 61*0e5d3ab5SSergey Ryazanov 62*0e5d3ab5SSergey Ryazanov static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) 63*0e5d3ab5SSergey Ryazanov { 64*0e5d3ab5SSergey Ryazanov struct platform_device *pdev = to_platform_device(ah->dev); 65*0e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); 66*0e5d3ab5SSergey Ryazanov u8 *cfg_mac; 67*0e5d3ab5SSergey Ryazanov 68*0e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0) 69*0e5d3ab5SSergey Ryazanov cfg_mac = bcfg->config->wlan0_mac; 70*0e5d3ab5SSergey Ryazanov else 71*0e5d3ab5SSergey Ryazanov cfg_mac = bcfg->config->wlan1_mac; 72*0e5d3ab5SSergey Ryazanov 73*0e5d3ab5SSergey Ryazanov memcpy(mac, cfg_mac, ETH_ALEN); 74*0e5d3ab5SSergey Ryazanov return 0; 75*0e5d3ab5SSergey Ryazanov } 76*0e5d3ab5SSergey Ryazanov 77*0e5d3ab5SSergey Ryazanov static const struct ath_bus_ops ath_ahb_bus_ops = { 78*0e5d3ab5SSergey Ryazanov .ath_bus_type = ATH_AHB, 79*0e5d3ab5SSergey Ryazanov .read_cachesize = ath5k_ahb_read_cachesize, 80*0e5d3ab5SSergey Ryazanov .eeprom_read = ath5k_ahb_eeprom_read, 81*0e5d3ab5SSergey Ryazanov .eeprom_read_mac = ath5k_ahb_eeprom_read_mac, 82*0e5d3ab5SSergey Ryazanov }; 83*0e5d3ab5SSergey Ryazanov 84*0e5d3ab5SSergey Ryazanov /*Initialization*/ 85*0e5d3ab5SSergey Ryazanov static int ath_ahb_probe(struct platform_device *pdev) 86*0e5d3ab5SSergey Ryazanov { 87*0e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); 88*0e5d3ab5SSergey Ryazanov struct ath5k_hw *ah; 89*0e5d3ab5SSergey Ryazanov struct ieee80211_hw *hw; 90*0e5d3ab5SSergey Ryazanov struct resource *res; 91*0e5d3ab5SSergey Ryazanov void __iomem *mem; 92*0e5d3ab5SSergey Ryazanov int irq; 93*0e5d3ab5SSergey Ryazanov int ret = 0; 94*0e5d3ab5SSergey Ryazanov u32 reg; 95*0e5d3ab5SSergey Ryazanov 96*0e5d3ab5SSergey Ryazanov if (!dev_get_platdata(&pdev->dev)) { 97*0e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "no platform data specified\n"); 98*0e5d3ab5SSergey Ryazanov ret = -EINVAL; 99*0e5d3ab5SSergey Ryazanov goto err_out; 100*0e5d3ab5SSergey Ryazanov } 101*0e5d3ab5SSergey Ryazanov 102*0e5d3ab5SSergey Ryazanov res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 103*0e5d3ab5SSergey Ryazanov if (res == NULL) { 104*0e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "no memory resource found\n"); 105*0e5d3ab5SSergey Ryazanov ret = -ENXIO; 106*0e5d3ab5SSergey Ryazanov goto err_out; 107*0e5d3ab5SSergey Ryazanov } 108*0e5d3ab5SSergey Ryazanov 109*0e5d3ab5SSergey Ryazanov mem = ioremap_nocache(res->start, resource_size(res)); 110*0e5d3ab5SSergey Ryazanov if (mem == NULL) { 111*0e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "ioremap failed\n"); 112*0e5d3ab5SSergey Ryazanov ret = -ENOMEM; 113*0e5d3ab5SSergey Ryazanov goto err_out; 114*0e5d3ab5SSergey Ryazanov } 115*0e5d3ab5SSergey Ryazanov 116*0e5d3ab5SSergey Ryazanov res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 117*0e5d3ab5SSergey Ryazanov if (res == NULL) { 118*0e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "no IRQ resource found\n"); 119*0e5d3ab5SSergey Ryazanov ret = -ENXIO; 120*0e5d3ab5SSergey Ryazanov goto err_iounmap; 121*0e5d3ab5SSergey Ryazanov } 122*0e5d3ab5SSergey Ryazanov 123*0e5d3ab5SSergey Ryazanov irq = res->start; 124*0e5d3ab5SSergey Ryazanov 125*0e5d3ab5SSergey Ryazanov hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops); 126*0e5d3ab5SSergey Ryazanov if (hw == NULL) { 127*0e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); 128*0e5d3ab5SSergey Ryazanov ret = -ENOMEM; 129*0e5d3ab5SSergey Ryazanov goto err_iounmap; 130*0e5d3ab5SSergey Ryazanov } 131*0e5d3ab5SSergey Ryazanov 132*0e5d3ab5SSergey Ryazanov ah = hw->priv; 133*0e5d3ab5SSergey Ryazanov ah->hw = hw; 134*0e5d3ab5SSergey Ryazanov ah->dev = &pdev->dev; 135*0e5d3ab5SSergey Ryazanov ah->iobase = mem; 136*0e5d3ab5SSergey Ryazanov ah->irq = irq; 137*0e5d3ab5SSergey Ryazanov ah->devid = bcfg->devid; 138*0e5d3ab5SSergey Ryazanov 139*0e5d3ab5SSergey Ryazanov if (bcfg->devid >= AR5K_SREV_AR2315_R6) { 140*0e5d3ab5SSergey Ryazanov /* Enable WMAC AHB arbitration */ 141*0e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); 142*0e5d3ab5SSergey Ryazanov reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN; 143*0e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); 144*0e5d3ab5SSergey Ryazanov 145*0e5d3ab5SSergey Ryazanov /* Enable global WMAC swapping */ 146*0e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR2315_BYTESWAP); 147*0e5d3ab5SSergey Ryazanov reg |= AR5K_AR2315_BYTESWAP_WMAC; 148*0e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR2315_BYTESWAP); 149*0e5d3ab5SSergey Ryazanov } else { 150*0e5d3ab5SSergey Ryazanov /* Enable WMAC DMA access (assuming 5312 or 231x*/ 151*0e5d3ab5SSergey Ryazanov /* TODO: check other platforms */ 152*0e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE); 153*0e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0) 154*0e5d3ab5SSergey Ryazanov reg |= AR5K_AR5312_ENABLE_WLAN0; 155*0e5d3ab5SSergey Ryazanov else 156*0e5d3ab5SSergey Ryazanov reg |= AR5K_AR5312_ENABLE_WLAN1; 157*0e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE); 158*0e5d3ab5SSergey Ryazanov 159*0e5d3ab5SSergey Ryazanov /* 160*0e5d3ab5SSergey Ryazanov * On a dual-band AR5312, the multiband radio is only 161*0e5d3ab5SSergey Ryazanov * used as pass-through. Disable 2 GHz support in the 162*0e5d3ab5SSergey Ryazanov * driver for it 163*0e5d3ab5SSergey Ryazanov */ 164*0e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0 && 165*0e5d3ab5SSergey Ryazanov (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) == 166*0e5d3ab5SSergey Ryazanov (BD_WLAN1 | BD_WLAN0)) 167*0e5d3ab5SSergey Ryazanov ah->ah_capabilities.cap_needs_2GHz_ovr = true; 168*0e5d3ab5SSergey Ryazanov else 169*0e5d3ab5SSergey Ryazanov ah->ah_capabilities.cap_needs_2GHz_ovr = false; 170*0e5d3ab5SSergey Ryazanov } 171*0e5d3ab5SSergey Ryazanov 172*0e5d3ab5SSergey Ryazanov ret = ath5k_init_ah(ah, &ath_ahb_bus_ops); 173*0e5d3ab5SSergey Ryazanov if (ret != 0) { 174*0e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret); 175*0e5d3ab5SSergey Ryazanov ret = -ENODEV; 176*0e5d3ab5SSergey Ryazanov goto err_free_hw; 177*0e5d3ab5SSergey Ryazanov } 178*0e5d3ab5SSergey Ryazanov 179*0e5d3ab5SSergey Ryazanov platform_set_drvdata(pdev, hw); 180*0e5d3ab5SSergey Ryazanov 181*0e5d3ab5SSergey Ryazanov return 0; 182*0e5d3ab5SSergey Ryazanov 183*0e5d3ab5SSergey Ryazanov err_free_hw: 184*0e5d3ab5SSergey Ryazanov ieee80211_free_hw(hw); 185*0e5d3ab5SSergey Ryazanov err_iounmap: 186*0e5d3ab5SSergey Ryazanov iounmap(mem); 187*0e5d3ab5SSergey Ryazanov err_out: 188*0e5d3ab5SSergey Ryazanov return ret; 189*0e5d3ab5SSergey Ryazanov } 190*0e5d3ab5SSergey Ryazanov 191*0e5d3ab5SSergey Ryazanov static int ath_ahb_remove(struct platform_device *pdev) 192*0e5d3ab5SSergey Ryazanov { 193*0e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); 194*0e5d3ab5SSergey Ryazanov struct ieee80211_hw *hw = platform_get_drvdata(pdev); 195*0e5d3ab5SSergey Ryazanov struct ath5k_hw *ah; 196*0e5d3ab5SSergey Ryazanov u32 reg; 197*0e5d3ab5SSergey Ryazanov 198*0e5d3ab5SSergey Ryazanov if (!hw) 199*0e5d3ab5SSergey Ryazanov return 0; 200*0e5d3ab5SSergey Ryazanov 201*0e5d3ab5SSergey Ryazanov ah = hw->priv; 202*0e5d3ab5SSergey Ryazanov 203*0e5d3ab5SSergey Ryazanov if (bcfg->devid >= AR5K_SREV_AR2315_R6) { 204*0e5d3ab5SSergey Ryazanov /* Disable WMAC AHB arbitration */ 205*0e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); 206*0e5d3ab5SSergey Ryazanov reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN; 207*0e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); 208*0e5d3ab5SSergey Ryazanov } else { 209*0e5d3ab5SSergey Ryazanov /*Stop DMA access */ 210*0e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE); 211*0e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0) 212*0e5d3ab5SSergey Ryazanov reg &= ~AR5K_AR5312_ENABLE_WLAN0; 213*0e5d3ab5SSergey Ryazanov else 214*0e5d3ab5SSergey Ryazanov reg &= ~AR5K_AR5312_ENABLE_WLAN1; 215*0e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE); 216*0e5d3ab5SSergey Ryazanov } 217*0e5d3ab5SSergey Ryazanov 218*0e5d3ab5SSergey Ryazanov ath5k_deinit_ah(ah); 219*0e5d3ab5SSergey Ryazanov iounmap(ah->iobase); 220*0e5d3ab5SSergey Ryazanov ieee80211_free_hw(hw); 221*0e5d3ab5SSergey Ryazanov 222*0e5d3ab5SSergey Ryazanov return 0; 223*0e5d3ab5SSergey Ryazanov } 224*0e5d3ab5SSergey Ryazanov 225*0e5d3ab5SSergey Ryazanov static struct platform_driver ath_ahb_driver = { 226*0e5d3ab5SSergey Ryazanov .probe = ath_ahb_probe, 227*0e5d3ab5SSergey Ryazanov .remove = ath_ahb_remove, 228*0e5d3ab5SSergey Ryazanov .driver = { 229*0e5d3ab5SSergey Ryazanov .name = "ar231x-wmac", 230*0e5d3ab5SSergey Ryazanov .owner = THIS_MODULE, 231*0e5d3ab5SSergey Ryazanov }, 232*0e5d3ab5SSergey Ryazanov }; 233*0e5d3ab5SSergey Ryazanov 234*0e5d3ab5SSergey Ryazanov module_platform_driver(ath_ahb_driver); 235