162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2008-2009 Atheros Communications Inc.
362306a36Sopenharmony_ci * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
462306a36Sopenharmony_ci * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
762306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
862306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1162306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1262306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1362306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1462306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1562306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1662306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/nl80211.h>
2162306a36Sopenharmony_ci#include <linux/platform_device.h>
2262306a36Sopenharmony_ci#include <linux/etherdevice.h>
2362306a36Sopenharmony_ci#include <ath25_platform.h>
2462306a36Sopenharmony_ci#include "ath5k.h"
2562306a36Sopenharmony_ci#include "debug.h"
2662306a36Sopenharmony_ci#include "base.h"
2762306a36Sopenharmony_ci#include "reg.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* return bus cachesize in 4B word units */
3062306a36Sopenharmony_cistatic void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	*csz = L1_CACHE_BYTES >> 2;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic bool
3662306a36Sopenharmony_ciath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct ath5k_hw *ah = common->priv;
3962306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(ah->dev);
4062306a36Sopenharmony_ci	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
4162306a36Sopenharmony_ci	u16 *eeprom, *eeprom_end;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	eeprom = (u16 *) bcfg->radio;
4462306a36Sopenharmony_ci	eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	eeprom += off;
4762306a36Sopenharmony_ci	if (eeprom > eeprom_end)
4862306a36Sopenharmony_ci		return false;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	*data = *eeprom;
5162306a36Sopenharmony_ci	return true;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciint ath5k_hw_read_srev(struct ath5k_hw *ah)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(ah->dev);
5762306a36Sopenharmony_ci	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
5862306a36Sopenharmony_ci	ah->ah_mac_srev = bcfg->devid;
5962306a36Sopenharmony_ci	return 0;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(ah->dev);
6562306a36Sopenharmony_ci	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
6662306a36Sopenharmony_ci	u8 *cfg_mac;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (to_platform_device(ah->dev)->id == 0)
6962306a36Sopenharmony_ci		cfg_mac = bcfg->config->wlan0_mac;
7062306a36Sopenharmony_ci	else
7162306a36Sopenharmony_ci		cfg_mac = bcfg->config->wlan1_mac;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	memcpy(mac, cfg_mac, ETH_ALEN);
7462306a36Sopenharmony_ci	return 0;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic const struct ath_bus_ops ath_ahb_bus_ops = {
7862306a36Sopenharmony_ci	.ath_bus_type = ATH_AHB,
7962306a36Sopenharmony_ci	.read_cachesize = ath5k_ahb_read_cachesize,
8062306a36Sopenharmony_ci	.eeprom_read = ath5k_ahb_eeprom_read,
8162306a36Sopenharmony_ci	.eeprom_read_mac = ath5k_ahb_eeprom_read_mac,
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/*Initialization*/
8562306a36Sopenharmony_cistatic int ath_ahb_probe(struct platform_device *pdev)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
8862306a36Sopenharmony_ci	struct ath5k_hw *ah;
8962306a36Sopenharmony_ci	struct ieee80211_hw *hw;
9062306a36Sopenharmony_ci	struct resource *res;
9162306a36Sopenharmony_ci	void __iomem *mem;
9262306a36Sopenharmony_ci	int irq;
9362306a36Sopenharmony_ci	int ret = 0;
9462306a36Sopenharmony_ci	u32 reg;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (!dev_get_platdata(&pdev->dev)) {
9762306a36Sopenharmony_ci		dev_err(&pdev->dev, "no platform data specified\n");
9862306a36Sopenharmony_ci		ret = -EINVAL;
9962306a36Sopenharmony_ci		goto err_out;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
10362306a36Sopenharmony_ci	if (res == NULL) {
10462306a36Sopenharmony_ci		dev_err(&pdev->dev, "no memory resource found\n");
10562306a36Sopenharmony_ci		ret = -ENXIO;
10662306a36Sopenharmony_ci		goto err_out;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	mem = ioremap(res->start, resource_size(res));
11062306a36Sopenharmony_ci	if (mem == NULL) {
11162306a36Sopenharmony_ci		dev_err(&pdev->dev, "ioremap failed\n");
11262306a36Sopenharmony_ci		ret = -ENOMEM;
11362306a36Sopenharmony_ci		goto err_out;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
11762306a36Sopenharmony_ci	if (irq < 0) {
11862306a36Sopenharmony_ci		ret = irq;
11962306a36Sopenharmony_ci		goto err_iounmap;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops);
12362306a36Sopenharmony_ci	if (hw == NULL) {
12462306a36Sopenharmony_ci		dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
12562306a36Sopenharmony_ci		ret = -ENOMEM;
12662306a36Sopenharmony_ci		goto err_iounmap;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	ah = hw->priv;
13062306a36Sopenharmony_ci	ah->hw = hw;
13162306a36Sopenharmony_ci	ah->dev = &pdev->dev;
13262306a36Sopenharmony_ci	ah->iobase = mem;
13362306a36Sopenharmony_ci	ah->irq = irq;
13462306a36Sopenharmony_ci	ah->devid = bcfg->devid;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
13762306a36Sopenharmony_ci		/* Enable WMAC AHB arbitration */
13862306a36Sopenharmony_ci		reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
13962306a36Sopenharmony_ci		reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN;
14062306a36Sopenharmony_ci		iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		/* Enable global WMAC swapping */
14362306a36Sopenharmony_ci		reg = ioread32((void __iomem *) AR5K_AR2315_BYTESWAP);
14462306a36Sopenharmony_ci		reg |= AR5K_AR2315_BYTESWAP_WMAC;
14562306a36Sopenharmony_ci		iowrite32(reg, (void __iomem *) AR5K_AR2315_BYTESWAP);
14662306a36Sopenharmony_ci	} else {
14762306a36Sopenharmony_ci		/* Enable WMAC DMA access (assuming 5312 or 231x*/
14862306a36Sopenharmony_ci		/* TODO: check other platforms */
14962306a36Sopenharmony_ci		reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE);
15062306a36Sopenharmony_ci		if (to_platform_device(ah->dev)->id == 0)
15162306a36Sopenharmony_ci			reg |= AR5K_AR5312_ENABLE_WLAN0;
15262306a36Sopenharmony_ci		else
15362306a36Sopenharmony_ci			reg |= AR5K_AR5312_ENABLE_WLAN1;
15462306a36Sopenharmony_ci		iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		/*
15762306a36Sopenharmony_ci		 * On a dual-band AR5312, the multiband radio is only
15862306a36Sopenharmony_ci		 * used as pass-through. Disable 2 GHz support in the
15962306a36Sopenharmony_ci		 * driver for it
16062306a36Sopenharmony_ci		 */
16162306a36Sopenharmony_ci		if (to_platform_device(ah->dev)->id == 0 &&
16262306a36Sopenharmony_ci		    (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) ==
16362306a36Sopenharmony_ci		     (BD_WLAN1 | BD_WLAN0))
16462306a36Sopenharmony_ci			ah->ah_capabilities.cap_needs_2GHz_ovr = true;
16562306a36Sopenharmony_ci		else
16662306a36Sopenharmony_ci			ah->ah_capabilities.cap_needs_2GHz_ovr = false;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	ret = ath5k_init_ah(ah, &ath_ahb_bus_ops);
17062306a36Sopenharmony_ci	if (ret != 0) {
17162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret);
17262306a36Sopenharmony_ci		ret = -ENODEV;
17362306a36Sopenharmony_ci		goto err_free_hw;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	platform_set_drvdata(pdev, hw);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return 0;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci err_free_hw:
18162306a36Sopenharmony_ci	ieee80211_free_hw(hw);
18262306a36Sopenharmony_ci err_iounmap:
18362306a36Sopenharmony_ci        iounmap(mem);
18462306a36Sopenharmony_ci err_out:
18562306a36Sopenharmony_ci	return ret;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic int ath_ahb_remove(struct platform_device *pdev)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
19162306a36Sopenharmony_ci	struct ieee80211_hw *hw = platform_get_drvdata(pdev);
19262306a36Sopenharmony_ci	struct ath5k_hw *ah;
19362306a36Sopenharmony_ci	u32 reg;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (!hw)
19662306a36Sopenharmony_ci		return 0;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	ah = hw->priv;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
20162306a36Sopenharmony_ci		/* Disable WMAC AHB arbitration */
20262306a36Sopenharmony_ci		reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
20362306a36Sopenharmony_ci		reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN;
20462306a36Sopenharmony_ci		iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
20562306a36Sopenharmony_ci	} else {
20662306a36Sopenharmony_ci		/*Stop DMA access */
20762306a36Sopenharmony_ci		reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE);
20862306a36Sopenharmony_ci		if (to_platform_device(ah->dev)->id == 0)
20962306a36Sopenharmony_ci			reg &= ~AR5K_AR5312_ENABLE_WLAN0;
21062306a36Sopenharmony_ci		else
21162306a36Sopenharmony_ci			reg &= ~AR5K_AR5312_ENABLE_WLAN1;
21262306a36Sopenharmony_ci		iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ath5k_deinit_ah(ah);
21662306a36Sopenharmony_ci	iounmap(ah->iobase);
21762306a36Sopenharmony_ci	ieee80211_free_hw(hw);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return 0;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic struct platform_driver ath_ahb_driver = {
22362306a36Sopenharmony_ci	.probe      = ath_ahb_probe,
22462306a36Sopenharmony_ci	.remove     = ath_ahb_remove,
22562306a36Sopenharmony_ci	.driver		= {
22662306a36Sopenharmony_ci		.name	= "ar231x-wmac",
22762306a36Sopenharmony_ci	},
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cimodule_platform_driver(ath_ahb_driver);
231