1 | /* |
2 | * Copyright (c) 2008-2009 Atheros Communications Inc. |
3 | * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org> |
4 | * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org> |
5 | * |
6 | * Permission to use, copy, modify, and/or distribute this software for any |
7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. |
9 | * |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ |
18 | |
19 | #include <linux/module.h> |
20 | #include <linux/nl80211.h> |
21 | #include <linux/platform_device.h> |
22 | #include <linux/etherdevice.h> |
23 | #include <ath25_platform.h> |
24 | #include "ath5k.h" |
25 | #include "debug.h" |
26 | #include "base.h" |
27 | #include "reg.h" |
28 | |
29 | /* return bus cachesize in 4B word units */ |
30 | static void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz) |
31 | { |
32 | *csz = L1_CACHE_BYTES >> 2; |
33 | } |
34 | |
35 | static bool |
36 | ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) |
37 | { |
38 | struct ath5k_hw *ah = common->priv; |
39 | struct platform_device *pdev = to_platform_device(ah->dev); |
40 | struct ar231x_board_config *bcfg = dev_get_platdata(dev: &pdev->dev); |
41 | u16 *eeprom, *eeprom_end; |
42 | |
43 | eeprom = (u16 *) bcfg->radio; |
44 | eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ; |
45 | |
46 | eeprom += off; |
47 | if (eeprom > eeprom_end) |
48 | return false; |
49 | |
50 | *data = *eeprom; |
51 | return true; |
52 | } |
53 | |
54 | int ath5k_hw_read_srev(struct ath5k_hw *ah) |
55 | { |
56 | struct platform_device *pdev = to_platform_device(ah->dev); |
57 | struct ar231x_board_config *bcfg = dev_get_platdata(dev: &pdev->dev); |
58 | ah->ah_mac_srev = bcfg->devid; |
59 | return 0; |
60 | } |
61 | |
62 | static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) |
63 | { |
64 | struct platform_device *pdev = to_platform_device(ah->dev); |
65 | struct ar231x_board_config *bcfg = dev_get_platdata(dev: &pdev->dev); |
66 | u8 *cfg_mac; |
67 | |
68 | if (to_platform_device(ah->dev)->id == 0) |
69 | cfg_mac = bcfg->config->wlan0_mac; |
70 | else |
71 | cfg_mac = bcfg->config->wlan1_mac; |
72 | |
73 | memcpy(mac, cfg_mac, ETH_ALEN); |
74 | return 0; |
75 | } |
76 | |
77 | static const struct ath_bus_ops ath_ahb_bus_ops = { |
78 | .ath_bus_type = ATH_AHB, |
79 | .read_cachesize = ath5k_ahb_read_cachesize, |
80 | .eeprom_read = ath5k_ahb_eeprom_read, |
81 | .eeprom_read_mac = ath5k_ahb_eeprom_read_mac, |
82 | }; |
83 | |
84 | /*Initialization*/ |
85 | static int ath_ahb_probe(struct platform_device *pdev) |
86 | { |
87 | struct ar231x_board_config *bcfg = dev_get_platdata(dev: &pdev->dev); |
88 | struct ath5k_hw *ah; |
89 | struct ieee80211_hw *hw; |
90 | struct resource *res; |
91 | void __iomem *mem; |
92 | int irq; |
93 | int ret = 0; |
94 | u32 reg; |
95 | |
96 | if (!dev_get_platdata(dev: &pdev->dev)) { |
97 | dev_err(&pdev->dev, "no platform data specified\n" ); |
98 | ret = -EINVAL; |
99 | goto err_out; |
100 | } |
101 | |
102 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
103 | if (res == NULL) { |
104 | dev_err(&pdev->dev, "no memory resource found\n" ); |
105 | ret = -ENXIO; |
106 | goto err_out; |
107 | } |
108 | |
109 | mem = ioremap(offset: res->start, size: resource_size(res)); |
110 | if (mem == NULL) { |
111 | dev_err(&pdev->dev, "ioremap failed\n" ); |
112 | ret = -ENOMEM; |
113 | goto err_out; |
114 | } |
115 | |
116 | irq = platform_get_irq(pdev, 0); |
117 | if (irq < 0) { |
118 | ret = irq; |
119 | goto err_iounmap; |
120 | } |
121 | |
122 | hw = ieee80211_alloc_hw(priv_data_len: sizeof(struct ath5k_hw), ops: &ath5k_hw_ops); |
123 | if (hw == NULL) { |
124 | dev_err(&pdev->dev, "no memory for ieee80211_hw\n" ); |
125 | ret = -ENOMEM; |
126 | goto err_iounmap; |
127 | } |
128 | |
129 | ah = hw->priv; |
130 | ah->hw = hw; |
131 | ah->dev = &pdev->dev; |
132 | ah->iobase = mem; |
133 | ah->irq = irq; |
134 | ah->devid = bcfg->devid; |
135 | |
136 | if (bcfg->devid >= AR5K_SREV_AR2315_R6) { |
137 | /* Enable WMAC AHB arbitration */ |
138 | reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); |
139 | reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN; |
140 | iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); |
141 | |
142 | /* Enable global WMAC swapping */ |
143 | reg = ioread32((void __iomem *) AR5K_AR2315_BYTESWAP); |
144 | reg |= AR5K_AR2315_BYTESWAP_WMAC; |
145 | iowrite32(reg, (void __iomem *) AR5K_AR2315_BYTESWAP); |
146 | } else { |
147 | /* Enable WMAC DMA access (assuming 5312 or 231x*/ |
148 | /* TODO: check other platforms */ |
149 | reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE); |
150 | if (to_platform_device(ah->dev)->id == 0) |
151 | reg |= AR5K_AR5312_ENABLE_WLAN0; |
152 | else |
153 | reg |= AR5K_AR5312_ENABLE_WLAN1; |
154 | iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE); |
155 | |
156 | /* |
157 | * On a dual-band AR5312, the multiband radio is only |
158 | * used as pass-through. Disable 2 GHz support in the |
159 | * driver for it |
160 | */ |
161 | if (to_platform_device(ah->dev)->id == 0 && |
162 | (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) == |
163 | (BD_WLAN1 | BD_WLAN0)) |
164 | ah->ah_capabilities.cap_needs_2GHz_ovr = true; |
165 | else |
166 | ah->ah_capabilities.cap_needs_2GHz_ovr = false; |
167 | } |
168 | |
169 | ret = ath5k_init_ah(ah, bus_ops: &ath_ahb_bus_ops); |
170 | if (ret != 0) { |
171 | dev_err(&pdev->dev, "failed to attach device, err=%d\n" , ret); |
172 | ret = -ENODEV; |
173 | goto err_free_hw; |
174 | } |
175 | |
176 | platform_set_drvdata(pdev, data: hw); |
177 | |
178 | return 0; |
179 | |
180 | err_free_hw: |
181 | ieee80211_free_hw(hw); |
182 | err_iounmap: |
183 | iounmap(addr: mem); |
184 | err_out: |
185 | return ret; |
186 | } |
187 | |
188 | static void ath_ahb_remove(struct platform_device *pdev) |
189 | { |
190 | struct ar231x_board_config *bcfg = dev_get_platdata(dev: &pdev->dev); |
191 | struct ieee80211_hw *hw = platform_get_drvdata(pdev); |
192 | struct ath5k_hw *ah; |
193 | u32 reg; |
194 | |
195 | if (!hw) |
196 | return; |
197 | |
198 | ah = hw->priv; |
199 | |
200 | if (bcfg->devid >= AR5K_SREV_AR2315_R6) { |
201 | /* Disable WMAC AHB arbitration */ |
202 | reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); |
203 | reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN; |
204 | iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); |
205 | } else { |
206 | /*Stop DMA access */ |
207 | reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE); |
208 | if (to_platform_device(ah->dev)->id == 0) |
209 | reg &= ~AR5K_AR5312_ENABLE_WLAN0; |
210 | else |
211 | reg &= ~AR5K_AR5312_ENABLE_WLAN1; |
212 | iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE); |
213 | } |
214 | |
215 | ath5k_deinit_ah(ah); |
216 | iounmap(addr: ah->iobase); |
217 | ieee80211_free_hw(hw); |
218 | } |
219 | |
220 | static struct platform_driver ath_ahb_driver = { |
221 | .probe = ath_ahb_probe, |
222 | .remove_new = ath_ahb_remove, |
223 | .driver = { |
224 | .name = "ar231x-wmac" , |
225 | }, |
226 | }; |
227 | |
228 | module_platform_driver(ath_ahb_driver); |
229 | |