xref: /linux/drivers/net/wireless/ath/ath5k/ahb.c (revision 164a974889c03cbc6559923b8c1da8762904c560)
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