10e5d3ab5SSergey Ryazanov /* 20e5d3ab5SSergey Ryazanov * Copyright (c) 2008-2009 Atheros Communications Inc. 30e5d3ab5SSergey Ryazanov * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org> 40e5d3ab5SSergey Ryazanov * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org> 50e5d3ab5SSergey Ryazanov * 60e5d3ab5SSergey Ryazanov * Permission to use, copy, modify, and/or distribute this software for any 70e5d3ab5SSergey Ryazanov * purpose with or without fee is hereby granted, provided that the above 80e5d3ab5SSergey Ryazanov * copyright notice and this permission notice appear in all copies. 90e5d3ab5SSergey Ryazanov * 100e5d3ab5SSergey Ryazanov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 110e5d3ab5SSergey Ryazanov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 120e5d3ab5SSergey Ryazanov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 130e5d3ab5SSergey Ryazanov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 140e5d3ab5SSergey Ryazanov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 150e5d3ab5SSergey Ryazanov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 160e5d3ab5SSergey Ryazanov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 170e5d3ab5SSergey Ryazanov */ 180e5d3ab5SSergey Ryazanov 190e5d3ab5SSergey Ryazanov #include <linux/nl80211.h> 200e5d3ab5SSergey Ryazanov #include <linux/platform_device.h> 210e5d3ab5SSergey Ryazanov #include <linux/etherdevice.h> 220e5d3ab5SSergey Ryazanov #include <linux/export.h> 23*164a9748SSergey Ryazanov #include <ath25_platform.h> 240e5d3ab5SSergey Ryazanov #include "ath5k.h" 250e5d3ab5SSergey Ryazanov #include "debug.h" 260e5d3ab5SSergey Ryazanov #include "base.h" 270e5d3ab5SSergey Ryazanov #include "reg.h" 280e5d3ab5SSergey Ryazanov 290e5d3ab5SSergey Ryazanov /* return bus cachesize in 4B word units */ 300e5d3ab5SSergey Ryazanov static void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz) 310e5d3ab5SSergey Ryazanov { 320e5d3ab5SSergey Ryazanov *csz = L1_CACHE_BYTES >> 2; 330e5d3ab5SSergey Ryazanov } 340e5d3ab5SSergey Ryazanov 350e5d3ab5SSergey Ryazanov static bool 360e5d3ab5SSergey Ryazanov ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) 370e5d3ab5SSergey Ryazanov { 380e5d3ab5SSergey Ryazanov struct ath5k_hw *ah = common->priv; 390e5d3ab5SSergey Ryazanov struct platform_device *pdev = to_platform_device(ah->dev); 400e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); 410e5d3ab5SSergey Ryazanov u16 *eeprom, *eeprom_end; 420e5d3ab5SSergey Ryazanov 430e5d3ab5SSergey Ryazanov eeprom = (u16 *) bcfg->radio; 440e5d3ab5SSergey Ryazanov eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ; 450e5d3ab5SSergey Ryazanov 460e5d3ab5SSergey Ryazanov eeprom += off; 470e5d3ab5SSergey Ryazanov if (eeprom > eeprom_end) 480e5d3ab5SSergey Ryazanov return false; 490e5d3ab5SSergey Ryazanov 500e5d3ab5SSergey Ryazanov *data = *eeprom; 510e5d3ab5SSergey Ryazanov return true; 520e5d3ab5SSergey Ryazanov } 530e5d3ab5SSergey Ryazanov 540e5d3ab5SSergey Ryazanov int ath5k_hw_read_srev(struct ath5k_hw *ah) 550e5d3ab5SSergey Ryazanov { 560e5d3ab5SSergey Ryazanov struct platform_device *pdev = to_platform_device(ah->dev); 570e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); 580e5d3ab5SSergey Ryazanov ah->ah_mac_srev = bcfg->devid; 590e5d3ab5SSergey Ryazanov return 0; 600e5d3ab5SSergey Ryazanov } 610e5d3ab5SSergey Ryazanov 620e5d3ab5SSergey Ryazanov static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) 630e5d3ab5SSergey Ryazanov { 640e5d3ab5SSergey Ryazanov struct platform_device *pdev = to_platform_device(ah->dev); 650e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); 660e5d3ab5SSergey Ryazanov u8 *cfg_mac; 670e5d3ab5SSergey Ryazanov 680e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0) 690e5d3ab5SSergey Ryazanov cfg_mac = bcfg->config->wlan0_mac; 700e5d3ab5SSergey Ryazanov else 710e5d3ab5SSergey Ryazanov cfg_mac = bcfg->config->wlan1_mac; 720e5d3ab5SSergey Ryazanov 730e5d3ab5SSergey Ryazanov memcpy(mac, cfg_mac, ETH_ALEN); 740e5d3ab5SSergey Ryazanov return 0; 750e5d3ab5SSergey Ryazanov } 760e5d3ab5SSergey Ryazanov 770e5d3ab5SSergey Ryazanov static const struct ath_bus_ops ath_ahb_bus_ops = { 780e5d3ab5SSergey Ryazanov .ath_bus_type = ATH_AHB, 790e5d3ab5SSergey Ryazanov .read_cachesize = ath5k_ahb_read_cachesize, 800e5d3ab5SSergey Ryazanov .eeprom_read = ath5k_ahb_eeprom_read, 810e5d3ab5SSergey Ryazanov .eeprom_read_mac = ath5k_ahb_eeprom_read_mac, 820e5d3ab5SSergey Ryazanov }; 830e5d3ab5SSergey Ryazanov 840e5d3ab5SSergey Ryazanov /*Initialization*/ 850e5d3ab5SSergey Ryazanov static int ath_ahb_probe(struct platform_device *pdev) 860e5d3ab5SSergey Ryazanov { 870e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); 880e5d3ab5SSergey Ryazanov struct ath5k_hw *ah; 890e5d3ab5SSergey Ryazanov struct ieee80211_hw *hw; 900e5d3ab5SSergey Ryazanov struct resource *res; 910e5d3ab5SSergey Ryazanov void __iomem *mem; 920e5d3ab5SSergey Ryazanov int irq; 930e5d3ab5SSergey Ryazanov int ret = 0; 940e5d3ab5SSergey Ryazanov u32 reg; 950e5d3ab5SSergey Ryazanov 960e5d3ab5SSergey Ryazanov if (!dev_get_platdata(&pdev->dev)) { 970e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "no platform data specified\n"); 980e5d3ab5SSergey Ryazanov ret = -EINVAL; 990e5d3ab5SSergey Ryazanov goto err_out; 1000e5d3ab5SSergey Ryazanov } 1010e5d3ab5SSergey Ryazanov 1020e5d3ab5SSergey Ryazanov res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1030e5d3ab5SSergey Ryazanov if (res == NULL) { 1040e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "no memory resource found\n"); 1050e5d3ab5SSergey Ryazanov ret = -ENXIO; 1060e5d3ab5SSergey Ryazanov goto err_out; 1070e5d3ab5SSergey Ryazanov } 1080e5d3ab5SSergey Ryazanov 1090e5d3ab5SSergey Ryazanov mem = ioremap_nocache(res->start, resource_size(res)); 1100e5d3ab5SSergey Ryazanov if (mem == NULL) { 1110e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "ioremap failed\n"); 1120e5d3ab5SSergey Ryazanov ret = -ENOMEM; 1130e5d3ab5SSergey Ryazanov goto err_out; 1140e5d3ab5SSergey Ryazanov } 1150e5d3ab5SSergey Ryazanov 1160e5d3ab5SSergey Ryazanov res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 1170e5d3ab5SSergey Ryazanov if (res == NULL) { 1180e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "no IRQ resource found\n"); 1190e5d3ab5SSergey Ryazanov ret = -ENXIO; 1200e5d3ab5SSergey Ryazanov goto err_iounmap; 1210e5d3ab5SSergey Ryazanov } 1220e5d3ab5SSergey Ryazanov 1230e5d3ab5SSergey Ryazanov irq = res->start; 1240e5d3ab5SSergey Ryazanov 1250e5d3ab5SSergey Ryazanov hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops); 1260e5d3ab5SSergey Ryazanov if (hw == NULL) { 1270e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); 1280e5d3ab5SSergey Ryazanov ret = -ENOMEM; 1290e5d3ab5SSergey Ryazanov goto err_iounmap; 1300e5d3ab5SSergey Ryazanov } 1310e5d3ab5SSergey Ryazanov 1320e5d3ab5SSergey Ryazanov ah = hw->priv; 1330e5d3ab5SSergey Ryazanov ah->hw = hw; 1340e5d3ab5SSergey Ryazanov ah->dev = &pdev->dev; 1350e5d3ab5SSergey Ryazanov ah->iobase = mem; 1360e5d3ab5SSergey Ryazanov ah->irq = irq; 1370e5d3ab5SSergey Ryazanov ah->devid = bcfg->devid; 1380e5d3ab5SSergey Ryazanov 1390e5d3ab5SSergey Ryazanov if (bcfg->devid >= AR5K_SREV_AR2315_R6) { 1400e5d3ab5SSergey Ryazanov /* Enable WMAC AHB arbitration */ 1410e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); 1420e5d3ab5SSergey Ryazanov reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN; 1430e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); 1440e5d3ab5SSergey Ryazanov 1450e5d3ab5SSergey Ryazanov /* Enable global WMAC swapping */ 1460e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR2315_BYTESWAP); 1470e5d3ab5SSergey Ryazanov reg |= AR5K_AR2315_BYTESWAP_WMAC; 1480e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR2315_BYTESWAP); 1490e5d3ab5SSergey Ryazanov } else { 1500e5d3ab5SSergey Ryazanov /* Enable WMAC DMA access (assuming 5312 or 231x*/ 1510e5d3ab5SSergey Ryazanov /* TODO: check other platforms */ 1520e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE); 1530e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0) 1540e5d3ab5SSergey Ryazanov reg |= AR5K_AR5312_ENABLE_WLAN0; 1550e5d3ab5SSergey Ryazanov else 1560e5d3ab5SSergey Ryazanov reg |= AR5K_AR5312_ENABLE_WLAN1; 1570e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE); 1580e5d3ab5SSergey Ryazanov 1590e5d3ab5SSergey Ryazanov /* 1600e5d3ab5SSergey Ryazanov * On a dual-band AR5312, the multiband radio is only 1610e5d3ab5SSergey Ryazanov * used as pass-through. Disable 2 GHz support in the 1620e5d3ab5SSergey Ryazanov * driver for it 1630e5d3ab5SSergey Ryazanov */ 1640e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0 && 1650e5d3ab5SSergey Ryazanov (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) == 1660e5d3ab5SSergey Ryazanov (BD_WLAN1 | BD_WLAN0)) 1670e5d3ab5SSergey Ryazanov ah->ah_capabilities.cap_needs_2GHz_ovr = true; 1680e5d3ab5SSergey Ryazanov else 1690e5d3ab5SSergey Ryazanov ah->ah_capabilities.cap_needs_2GHz_ovr = false; 1700e5d3ab5SSergey Ryazanov } 1710e5d3ab5SSergey Ryazanov 1720e5d3ab5SSergey Ryazanov ret = ath5k_init_ah(ah, &ath_ahb_bus_ops); 1730e5d3ab5SSergey Ryazanov if (ret != 0) { 1740e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret); 1750e5d3ab5SSergey Ryazanov ret = -ENODEV; 1760e5d3ab5SSergey Ryazanov goto err_free_hw; 1770e5d3ab5SSergey Ryazanov } 1780e5d3ab5SSergey Ryazanov 1790e5d3ab5SSergey Ryazanov platform_set_drvdata(pdev, hw); 1800e5d3ab5SSergey Ryazanov 1810e5d3ab5SSergey Ryazanov return 0; 1820e5d3ab5SSergey Ryazanov 1830e5d3ab5SSergey Ryazanov err_free_hw: 1840e5d3ab5SSergey Ryazanov ieee80211_free_hw(hw); 1850e5d3ab5SSergey Ryazanov err_iounmap: 1860e5d3ab5SSergey Ryazanov iounmap(mem); 1870e5d3ab5SSergey Ryazanov err_out: 1880e5d3ab5SSergey Ryazanov return ret; 1890e5d3ab5SSergey Ryazanov } 1900e5d3ab5SSergey Ryazanov 1910e5d3ab5SSergey Ryazanov static int ath_ahb_remove(struct platform_device *pdev) 1920e5d3ab5SSergey Ryazanov { 1930e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); 1940e5d3ab5SSergey Ryazanov struct ieee80211_hw *hw = platform_get_drvdata(pdev); 1950e5d3ab5SSergey Ryazanov struct ath5k_hw *ah; 1960e5d3ab5SSergey Ryazanov u32 reg; 1970e5d3ab5SSergey Ryazanov 1980e5d3ab5SSergey Ryazanov if (!hw) 1990e5d3ab5SSergey Ryazanov return 0; 2000e5d3ab5SSergey Ryazanov 2010e5d3ab5SSergey Ryazanov ah = hw->priv; 2020e5d3ab5SSergey Ryazanov 2030e5d3ab5SSergey Ryazanov if (bcfg->devid >= AR5K_SREV_AR2315_R6) { 2040e5d3ab5SSergey Ryazanov /* Disable WMAC AHB arbitration */ 2050e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); 2060e5d3ab5SSergey Ryazanov reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN; 2070e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); 2080e5d3ab5SSergey Ryazanov } else { 2090e5d3ab5SSergey Ryazanov /*Stop DMA access */ 2100e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE); 2110e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0) 2120e5d3ab5SSergey Ryazanov reg &= ~AR5K_AR5312_ENABLE_WLAN0; 2130e5d3ab5SSergey Ryazanov else 2140e5d3ab5SSergey Ryazanov reg &= ~AR5K_AR5312_ENABLE_WLAN1; 2150e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE); 2160e5d3ab5SSergey Ryazanov } 2170e5d3ab5SSergey Ryazanov 2180e5d3ab5SSergey Ryazanov ath5k_deinit_ah(ah); 2190e5d3ab5SSergey Ryazanov iounmap(ah->iobase); 2200e5d3ab5SSergey Ryazanov ieee80211_free_hw(hw); 2210e5d3ab5SSergey Ryazanov 2220e5d3ab5SSergey Ryazanov return 0; 2230e5d3ab5SSergey Ryazanov } 2240e5d3ab5SSergey Ryazanov 2250e5d3ab5SSergey Ryazanov static struct platform_driver ath_ahb_driver = { 2260e5d3ab5SSergey Ryazanov .probe = ath_ahb_probe, 2270e5d3ab5SSergey Ryazanov .remove = ath_ahb_remove, 2280e5d3ab5SSergey Ryazanov .driver = { 2290e5d3ab5SSergey Ryazanov .name = "ar231x-wmac", 2300e5d3ab5SSergey Ryazanov .owner = THIS_MODULE, 2310e5d3ab5SSergey Ryazanov }, 2320e5d3ab5SSergey Ryazanov }; 2330e5d3ab5SSergey Ryazanov 2340e5d3ab5SSergey Ryazanov module_platform_driver(ath_ahb_driver); 235