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
197ecb6227SRussell King #include <linux/module.h>
200e5d3ab5SSergey Ryazanov #include <linux/nl80211.h>
210e5d3ab5SSergey Ryazanov #include <linux/platform_device.h>
220e5d3ab5SSergey Ryazanov #include <linux/etherdevice.h>
23164a9748SSergey 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 */
ath5k_ahb_read_cachesize(struct ath_common * common,int * csz)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
ath5k_ahb_eeprom_read(struct ath_common * common,u32 off,u16 * data)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
ath5k_hw_read_srev(struct ath5k_hw * ah)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
ath5k_ahb_eeprom_read_mac(struct ath5k_hw * ah,u8 * mac)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*/
ath_ahb_probe(struct platform_device * pdev)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
1094bdc0d67SChristoph Hellwig mem = ioremap(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
11695c95251SDouglas Anderson irq = platform_get_irq(pdev, 0);
11795c95251SDouglas Anderson if (irq < 0) {
11895c95251SDouglas Anderson ret = irq;
1190e5d3ab5SSergey Ryazanov goto err_iounmap;
1200e5d3ab5SSergey Ryazanov }
1210e5d3ab5SSergey Ryazanov
1220e5d3ab5SSergey Ryazanov hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops);
1230e5d3ab5SSergey Ryazanov if (hw == NULL) {
1240e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
1250e5d3ab5SSergey Ryazanov ret = -ENOMEM;
1260e5d3ab5SSergey Ryazanov goto err_iounmap;
1270e5d3ab5SSergey Ryazanov }
1280e5d3ab5SSergey Ryazanov
1290e5d3ab5SSergey Ryazanov ah = hw->priv;
1300e5d3ab5SSergey Ryazanov ah->hw = hw;
1310e5d3ab5SSergey Ryazanov ah->dev = &pdev->dev;
1320e5d3ab5SSergey Ryazanov ah->iobase = mem;
1330e5d3ab5SSergey Ryazanov ah->irq = irq;
1340e5d3ab5SSergey Ryazanov ah->devid = bcfg->devid;
1350e5d3ab5SSergey Ryazanov
1360e5d3ab5SSergey Ryazanov if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
1370e5d3ab5SSergey Ryazanov /* Enable WMAC AHB arbitration */
1380e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
1390e5d3ab5SSergey Ryazanov reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN;
1400e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
1410e5d3ab5SSergey Ryazanov
1420e5d3ab5SSergey Ryazanov /* Enable global WMAC swapping */
1430e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR2315_BYTESWAP);
1440e5d3ab5SSergey Ryazanov reg |= AR5K_AR2315_BYTESWAP_WMAC;
1450e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR2315_BYTESWAP);
1460e5d3ab5SSergey Ryazanov } else {
1470e5d3ab5SSergey Ryazanov /* Enable WMAC DMA access (assuming 5312 or 231x*/
1480e5d3ab5SSergey Ryazanov /* TODO: check other platforms */
1490e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE);
1500e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0)
1510e5d3ab5SSergey Ryazanov reg |= AR5K_AR5312_ENABLE_WLAN0;
1520e5d3ab5SSergey Ryazanov else
1530e5d3ab5SSergey Ryazanov reg |= AR5K_AR5312_ENABLE_WLAN1;
1540e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE);
1550e5d3ab5SSergey Ryazanov
1560e5d3ab5SSergey Ryazanov /*
1570e5d3ab5SSergey Ryazanov * On a dual-band AR5312, the multiband radio is only
1580e5d3ab5SSergey Ryazanov * used as pass-through. Disable 2 GHz support in the
1590e5d3ab5SSergey Ryazanov * driver for it
1600e5d3ab5SSergey Ryazanov */
1610e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0 &&
1620e5d3ab5SSergey Ryazanov (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) ==
1630e5d3ab5SSergey Ryazanov (BD_WLAN1 | BD_WLAN0))
1640e5d3ab5SSergey Ryazanov ah->ah_capabilities.cap_needs_2GHz_ovr = true;
1650e5d3ab5SSergey Ryazanov else
1660e5d3ab5SSergey Ryazanov ah->ah_capabilities.cap_needs_2GHz_ovr = false;
1670e5d3ab5SSergey Ryazanov }
1680e5d3ab5SSergey Ryazanov
1690e5d3ab5SSergey Ryazanov ret = ath5k_init_ah(ah, &ath_ahb_bus_ops);
1700e5d3ab5SSergey Ryazanov if (ret != 0) {
1710e5d3ab5SSergey Ryazanov dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret);
1720e5d3ab5SSergey Ryazanov ret = -ENODEV;
1730e5d3ab5SSergey Ryazanov goto err_free_hw;
1740e5d3ab5SSergey Ryazanov }
1750e5d3ab5SSergey Ryazanov
1760e5d3ab5SSergey Ryazanov platform_set_drvdata(pdev, hw);
1770e5d3ab5SSergey Ryazanov
1780e5d3ab5SSergey Ryazanov return 0;
1790e5d3ab5SSergey Ryazanov
1800e5d3ab5SSergey Ryazanov err_free_hw:
1810e5d3ab5SSergey Ryazanov ieee80211_free_hw(hw);
1820e5d3ab5SSergey Ryazanov err_iounmap:
1830e5d3ab5SSergey Ryazanov iounmap(mem);
1840e5d3ab5SSergey Ryazanov err_out:
1850e5d3ab5SSergey Ryazanov return ret;
1860e5d3ab5SSergey Ryazanov }
1870e5d3ab5SSergey Ryazanov
ath_ahb_remove(struct platform_device * pdev)188*b5418d17SUwe Kleine-König static void ath_ahb_remove(struct platform_device *pdev)
1890e5d3ab5SSergey Ryazanov {
1900e5d3ab5SSergey Ryazanov struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
1910e5d3ab5SSergey Ryazanov struct ieee80211_hw *hw = platform_get_drvdata(pdev);
1920e5d3ab5SSergey Ryazanov struct ath5k_hw *ah;
1930e5d3ab5SSergey Ryazanov u32 reg;
1940e5d3ab5SSergey Ryazanov
1950e5d3ab5SSergey Ryazanov if (!hw)
196*b5418d17SUwe Kleine-König return;
1970e5d3ab5SSergey Ryazanov
1980e5d3ab5SSergey Ryazanov ah = hw->priv;
1990e5d3ab5SSergey Ryazanov
2000e5d3ab5SSergey Ryazanov if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
2010e5d3ab5SSergey Ryazanov /* Disable WMAC AHB arbitration */
2020e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
2030e5d3ab5SSergey Ryazanov reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN;
2040e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
2050e5d3ab5SSergey Ryazanov } else {
2060e5d3ab5SSergey Ryazanov /*Stop DMA access */
2070e5d3ab5SSergey Ryazanov reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE);
2080e5d3ab5SSergey Ryazanov if (to_platform_device(ah->dev)->id == 0)
2090e5d3ab5SSergey Ryazanov reg &= ~AR5K_AR5312_ENABLE_WLAN0;
2100e5d3ab5SSergey Ryazanov else
2110e5d3ab5SSergey Ryazanov reg &= ~AR5K_AR5312_ENABLE_WLAN1;
2120e5d3ab5SSergey Ryazanov iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE);
2130e5d3ab5SSergey Ryazanov }
2140e5d3ab5SSergey Ryazanov
2150e5d3ab5SSergey Ryazanov ath5k_deinit_ah(ah);
2160e5d3ab5SSergey Ryazanov iounmap(ah->iobase);
2170e5d3ab5SSergey Ryazanov ieee80211_free_hw(hw);
2180e5d3ab5SSergey Ryazanov }
2190e5d3ab5SSergey Ryazanov
2200e5d3ab5SSergey Ryazanov static struct platform_driver ath_ahb_driver = {
2210e5d3ab5SSergey Ryazanov .probe = ath_ahb_probe,
222*b5418d17SUwe Kleine-König .remove_new = ath_ahb_remove,
2230e5d3ab5SSergey Ryazanov .driver = {
2240e5d3ab5SSergey Ryazanov .name = "ar231x-wmac",
2250e5d3ab5SSergey Ryazanov },
2260e5d3ab5SSergey Ryazanov };
2270e5d3ab5SSergey Ryazanov
2280e5d3ab5SSergey Ryazanov module_platform_driver(ath_ahb_driver);
229