18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2008-2009 Atheros Communications Inc.
38c2ecf20Sopenharmony_ci * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
48c2ecf20Sopenharmony_ci * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
78c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
88c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
118c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
128c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
138c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
148c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
158c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
168c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/nl80211.h>
218c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
228c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
238c2ecf20Sopenharmony_ci#include <ath25_platform.h>
248c2ecf20Sopenharmony_ci#include "ath5k.h"
258c2ecf20Sopenharmony_ci#include "debug.h"
268c2ecf20Sopenharmony_ci#include "base.h"
278c2ecf20Sopenharmony_ci#include "reg.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* return bus cachesize in 4B word units */
308c2ecf20Sopenharmony_cistatic void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	*csz = L1_CACHE_BYTES >> 2;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic bool
368c2ecf20Sopenharmony_ciath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = common->priv;
398c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(ah->dev);
408c2ecf20Sopenharmony_ci	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
418c2ecf20Sopenharmony_ci	u16 *eeprom, *eeprom_end;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	eeprom = (u16 *) bcfg->radio;
448c2ecf20Sopenharmony_ci	eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	eeprom += off;
478c2ecf20Sopenharmony_ci	if (eeprom > eeprom_end)
488c2ecf20Sopenharmony_ci		return false;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	*data = *eeprom;
518c2ecf20Sopenharmony_ci	return true;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ciint ath5k_hw_read_srev(struct ath5k_hw *ah)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(ah->dev);
578c2ecf20Sopenharmony_ci	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
588c2ecf20Sopenharmony_ci	ah->ah_mac_srev = bcfg->devid;
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(ah->dev);
658c2ecf20Sopenharmony_ci	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
668c2ecf20Sopenharmony_ci	u8 *cfg_mac;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (to_platform_device(ah->dev)->id == 0)
698c2ecf20Sopenharmony_ci		cfg_mac = bcfg->config->wlan0_mac;
708c2ecf20Sopenharmony_ci	else
718c2ecf20Sopenharmony_ci		cfg_mac = bcfg->config->wlan1_mac;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	memcpy(mac, cfg_mac, ETH_ALEN);
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic const struct ath_bus_ops ath_ahb_bus_ops = {
788c2ecf20Sopenharmony_ci	.ath_bus_type = ATH_AHB,
798c2ecf20Sopenharmony_ci	.read_cachesize = ath5k_ahb_read_cachesize,
808c2ecf20Sopenharmony_ci	.eeprom_read = ath5k_ahb_eeprom_read,
818c2ecf20Sopenharmony_ci	.eeprom_read_mac = ath5k_ahb_eeprom_read_mac,
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/*Initialization*/
858c2ecf20Sopenharmony_cistatic int ath_ahb_probe(struct platform_device *pdev)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
888c2ecf20Sopenharmony_ci	struct ath5k_hw *ah;
898c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw;
908c2ecf20Sopenharmony_ci	struct resource *res;
918c2ecf20Sopenharmony_ci	void __iomem *mem;
928c2ecf20Sopenharmony_ci	int irq;
938c2ecf20Sopenharmony_ci	int ret = 0;
948c2ecf20Sopenharmony_ci	u32 reg;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (!dev_get_platdata(&pdev->dev)) {
978c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no platform data specified\n");
988c2ecf20Sopenharmony_ci		ret = -EINVAL;
998c2ecf20Sopenharmony_ci		goto err_out;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1038c2ecf20Sopenharmony_ci	if (res == NULL) {
1048c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no memory resource found\n");
1058c2ecf20Sopenharmony_ci		ret = -ENXIO;
1068c2ecf20Sopenharmony_ci		goto err_out;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	mem = ioremap(res->start, resource_size(res));
1108c2ecf20Sopenharmony_ci	if (mem == NULL) {
1118c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "ioremap failed\n");
1128c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1138c2ecf20Sopenharmony_ci		goto err_out;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
1178c2ecf20Sopenharmony_ci	if (res == NULL) {
1188c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no IRQ resource found\n");
1198c2ecf20Sopenharmony_ci		ret = -ENXIO;
1208c2ecf20Sopenharmony_ci		goto err_iounmap;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	irq = res->start;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops);
1268c2ecf20Sopenharmony_ci	if (hw == NULL) {
1278c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
1288c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1298c2ecf20Sopenharmony_ci		goto err_iounmap;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ah = hw->priv;
1338c2ecf20Sopenharmony_ci	ah->hw = hw;
1348c2ecf20Sopenharmony_ci	ah->dev = &pdev->dev;
1358c2ecf20Sopenharmony_ci	ah->iobase = mem;
1368c2ecf20Sopenharmony_ci	ah->irq = irq;
1378c2ecf20Sopenharmony_ci	ah->devid = bcfg->devid;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
1408c2ecf20Sopenharmony_ci		/* Enable WMAC AHB arbitration */
1418c2ecf20Sopenharmony_ci		reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
1428c2ecf20Sopenharmony_ci		reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN;
1438c2ecf20Sopenharmony_ci		iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		/* Enable global WMAC swapping */
1468c2ecf20Sopenharmony_ci		reg = ioread32((void __iomem *) AR5K_AR2315_BYTESWAP);
1478c2ecf20Sopenharmony_ci		reg |= AR5K_AR2315_BYTESWAP_WMAC;
1488c2ecf20Sopenharmony_ci		iowrite32(reg, (void __iomem *) AR5K_AR2315_BYTESWAP);
1498c2ecf20Sopenharmony_ci	} else {
1508c2ecf20Sopenharmony_ci		/* Enable WMAC DMA access (assuming 5312 or 231x*/
1518c2ecf20Sopenharmony_ci		/* TODO: check other platforms */
1528c2ecf20Sopenharmony_ci		reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE);
1538c2ecf20Sopenharmony_ci		if (to_platform_device(ah->dev)->id == 0)
1548c2ecf20Sopenharmony_ci			reg |= AR5K_AR5312_ENABLE_WLAN0;
1558c2ecf20Sopenharmony_ci		else
1568c2ecf20Sopenharmony_ci			reg |= AR5K_AR5312_ENABLE_WLAN1;
1578c2ecf20Sopenharmony_ci		iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		/*
1608c2ecf20Sopenharmony_ci		 * On a dual-band AR5312, the multiband radio is only
1618c2ecf20Sopenharmony_ci		 * used as pass-through. Disable 2 GHz support in the
1628c2ecf20Sopenharmony_ci		 * driver for it
1638c2ecf20Sopenharmony_ci		 */
1648c2ecf20Sopenharmony_ci		if (to_platform_device(ah->dev)->id == 0 &&
1658c2ecf20Sopenharmony_ci		    (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) ==
1668c2ecf20Sopenharmony_ci		     (BD_WLAN1 | BD_WLAN0))
1678c2ecf20Sopenharmony_ci			ah->ah_capabilities.cap_needs_2GHz_ovr = true;
1688c2ecf20Sopenharmony_ci		else
1698c2ecf20Sopenharmony_ci			ah->ah_capabilities.cap_needs_2GHz_ovr = false;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	ret = ath5k_init_ah(ah, &ath_ahb_bus_ops);
1738c2ecf20Sopenharmony_ci	if (ret != 0) {
1748c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret);
1758c2ecf20Sopenharmony_ci		ret = -ENODEV;
1768c2ecf20Sopenharmony_ci		goto err_free_hw;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, hw);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return 0;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci err_free_hw:
1848c2ecf20Sopenharmony_ci	ieee80211_free_hw(hw);
1858c2ecf20Sopenharmony_ci err_iounmap:
1868c2ecf20Sopenharmony_ci        iounmap(mem);
1878c2ecf20Sopenharmony_ci err_out:
1888c2ecf20Sopenharmony_ci	return ret;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int ath_ahb_remove(struct platform_device *pdev)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
1948c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = platform_get_drvdata(pdev);
1958c2ecf20Sopenharmony_ci	struct ath5k_hw *ah;
1968c2ecf20Sopenharmony_ci	u32 reg;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (!hw)
1998c2ecf20Sopenharmony_ci		return 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	ah = hw->priv;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
2048c2ecf20Sopenharmony_ci		/* Disable WMAC AHB arbitration */
2058c2ecf20Sopenharmony_ci		reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
2068c2ecf20Sopenharmony_ci		reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN;
2078c2ecf20Sopenharmony_ci		iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
2088c2ecf20Sopenharmony_ci	} else {
2098c2ecf20Sopenharmony_ci		/*Stop DMA access */
2108c2ecf20Sopenharmony_ci		reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE);
2118c2ecf20Sopenharmony_ci		if (to_platform_device(ah->dev)->id == 0)
2128c2ecf20Sopenharmony_ci			reg &= ~AR5K_AR5312_ENABLE_WLAN0;
2138c2ecf20Sopenharmony_ci		else
2148c2ecf20Sopenharmony_ci			reg &= ~AR5K_AR5312_ENABLE_WLAN1;
2158c2ecf20Sopenharmony_ci		iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	ath5k_deinit_ah(ah);
2198c2ecf20Sopenharmony_ci	iounmap(ah->iobase);
2208c2ecf20Sopenharmony_ci	ieee80211_free_hw(hw);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return 0;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic struct platform_driver ath_ahb_driver = {
2268c2ecf20Sopenharmony_ci	.probe      = ath_ahb_probe,
2278c2ecf20Sopenharmony_ci	.remove     = ath_ahb_remove,
2288c2ecf20Sopenharmony_ci	.driver		= {
2298c2ecf20Sopenharmony_ci		.name	= "ar231x-wmac",
2308c2ecf20Sopenharmony_ci	},
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cimodule_platform_driver(ath_ahb_driver);
234