1 | /* |
2 | * Copyright (c) 2008-2011 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/nl80211.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/module.h> |
22 | #include <linux/mod_devicetable.h> |
23 | #include "ath9k.h" |
24 | |
25 | static const struct platform_device_id ath9k_platform_id_table[] = { |
26 | { |
27 | .name = "ath9k" , |
28 | .driver_data = AR5416_AR9100_DEVID, |
29 | }, |
30 | { |
31 | .name = "ar933x_wmac" , |
32 | .driver_data = AR9300_DEVID_AR9330, |
33 | }, |
34 | { |
35 | .name = "ar934x_wmac" , |
36 | .driver_data = AR9300_DEVID_AR9340, |
37 | }, |
38 | { |
39 | .name = "qca955x_wmac" , |
40 | .driver_data = AR9300_DEVID_QCA955X, |
41 | }, |
42 | { |
43 | .name = "qca953x_wmac" , |
44 | .driver_data = AR9300_DEVID_AR953X, |
45 | }, |
46 | { |
47 | .name = "qca956x_wmac" , |
48 | .driver_data = AR9300_DEVID_QCA956X, |
49 | }, |
50 | {}, |
51 | }; |
52 | |
53 | /* return bus cachesize in 4B word units */ |
54 | static void ath_ahb_read_cachesize(struct ath_common *common, int *csz) |
55 | { |
56 | *csz = L1_CACHE_BYTES >> 2; |
57 | } |
58 | |
59 | static bool ath_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) |
60 | { |
61 | ath_err(common, "%s: eeprom data has to be provided externally\n" , |
62 | __func__); |
63 | return false; |
64 | } |
65 | |
66 | static const struct ath_bus_ops ath_ahb_bus_ops = { |
67 | .ath_bus_type = ATH_AHB, |
68 | .read_cachesize = ath_ahb_read_cachesize, |
69 | .eeprom_read = ath_ahb_eeprom_read, |
70 | }; |
71 | |
72 | static int ath_ahb_probe(struct platform_device *pdev) |
73 | { |
74 | void __iomem *mem; |
75 | struct ath_softc *sc; |
76 | struct ieee80211_hw *hw; |
77 | struct resource *res; |
78 | const struct platform_device_id *id = platform_get_device_id(pdev); |
79 | int irq; |
80 | int ret = 0; |
81 | struct ath_hw *ah; |
82 | char hw_name[64]; |
83 | |
84 | if (!dev_get_platdata(dev: &pdev->dev)) { |
85 | dev_err(&pdev->dev, "no platform data specified\n" ); |
86 | return -EINVAL; |
87 | } |
88 | |
89 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
90 | if (res == NULL) { |
91 | dev_err(&pdev->dev, "no memory resource found\n" ); |
92 | return -ENXIO; |
93 | } |
94 | |
95 | mem = devm_ioremap(dev: &pdev->dev, offset: res->start, size: resource_size(res)); |
96 | if (mem == NULL) { |
97 | dev_err(&pdev->dev, "ioremap failed\n" ); |
98 | return -ENOMEM; |
99 | } |
100 | |
101 | irq = platform_get_irq(pdev, 0); |
102 | if (irq < 0) |
103 | return irq; |
104 | |
105 | ath9k_fill_chanctx_ops(); |
106 | hw = ieee80211_alloc_hw(priv_data_len: sizeof(struct ath_softc), ops: &ath9k_ops); |
107 | if (hw == NULL) { |
108 | dev_err(&pdev->dev, "no memory for ieee80211_hw\n" ); |
109 | return -ENOMEM; |
110 | } |
111 | |
112 | SET_IEEE80211_DEV(hw, dev: &pdev->dev); |
113 | platform_set_drvdata(pdev, data: hw); |
114 | |
115 | sc = hw->priv; |
116 | sc->hw = hw; |
117 | sc->dev = &pdev->dev; |
118 | sc->mem = mem; |
119 | sc->irq = irq; |
120 | |
121 | ret = request_irq(irq, handler: ath_isr, IRQF_SHARED, name: "ath9k" , dev: sc); |
122 | if (ret) { |
123 | dev_err(&pdev->dev, "request_irq failed\n" ); |
124 | goto err_free_hw; |
125 | } |
126 | |
127 | ret = ath9k_init_device(devid: id->driver_data, sc, bus_ops: &ath_ahb_bus_ops); |
128 | if (ret) { |
129 | dev_err(&pdev->dev, "failed to initialize device\n" ); |
130 | goto err_irq; |
131 | } |
132 | |
133 | ah = sc->sc_ah; |
134 | ath9k_hw_name(ah, hw_name, len: sizeof(hw_name)); |
135 | wiphy_info(hw->wiphy, "%s mem=0x%p, irq=%d\n" , |
136 | hw_name, mem, irq); |
137 | |
138 | return 0; |
139 | |
140 | err_irq: |
141 | free_irq(irq, sc); |
142 | err_free_hw: |
143 | ieee80211_free_hw(hw); |
144 | return ret; |
145 | } |
146 | |
147 | static void ath_ahb_remove(struct platform_device *pdev) |
148 | { |
149 | struct ieee80211_hw *hw = platform_get_drvdata(pdev); |
150 | |
151 | if (hw) { |
152 | struct ath_softc *sc = hw->priv; |
153 | |
154 | ath9k_deinit_device(sc); |
155 | free_irq(sc->irq, sc); |
156 | ieee80211_free_hw(hw: sc->hw); |
157 | } |
158 | } |
159 | |
160 | static struct platform_driver ath_ahb_driver = { |
161 | .probe = ath_ahb_probe, |
162 | .remove_new = ath_ahb_remove, |
163 | .driver = { |
164 | .name = "ath9k" , |
165 | }, |
166 | .id_table = ath9k_platform_id_table, |
167 | }; |
168 | |
169 | MODULE_DEVICE_TABLE(platform, ath9k_platform_id_table); |
170 | |
171 | int ath_ahb_init(void) |
172 | { |
173 | return platform_driver_register(&ath_ahb_driver); |
174 | } |
175 | |
176 | void ath_ahb_exit(void) |
177 | { |
178 | platform_driver_unregister(&ath_ahb_driver); |
179 | } |
180 | |