Commit | Line | Data |
---|---|---|
2f01a1f5 | 1 | /* |
80301cdc | 2 | * This file is part of wl1251 |
2f01a1f5 KV |
3 | * |
4 | * Copyright (C) 2008 Nokia Corporation | |
5 | * | |
2f01a1f5 KV |
6 | * This program is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public License | |
8 | * version 2 as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
18 | * 02110-1301 USA | |
19 | * | |
20 | */ | |
21 | ||
a6b7a407 | 22 | #include <linux/interrupt.h> |
8e639c06 | 23 | #include <linux/irq.h> |
2f01a1f5 | 24 | #include <linux/module.h> |
5a0e3ad6 | 25 | #include <linux/slab.h> |
e757201b | 26 | #include <linux/swab.h> |
2f01a1f5 KV |
27 | #include <linux/crc7.h> |
28 | #include <linux/spi/spi.h> | |
c1f9a095 | 29 | #include <linux/wl12xx.h> |
1d207cd3 | 30 | #include <linux/gpio.h> |
07bbca6f SR |
31 | #include <linux/of.h> |
32 | #include <linux/of_gpio.h> | |
e4c2e09e | 33 | #include <linux/regulator/consumer.h> |
2f01a1f5 | 34 | |
13674118 | 35 | #include "wl1251.h" |
9bc6772e KV |
36 | #include "reg.h" |
37 | #include "spi.h" | |
2f01a1f5 | 38 | |
b5ed9c1b BC |
39 | static irqreturn_t wl1251_irq(int irq, void *cookie) |
40 | { | |
41 | struct wl1251 *wl; | |
42 | ||
43 | wl1251_debug(DEBUG_IRQ, "IRQ"); | |
44 | ||
45 | wl = cookie; | |
46 | ||
16e711f9 | 47 | ieee80211_queue_work(wl->hw, &wl->irq_work); |
b5ed9c1b BC |
48 | |
49 | return IRQ_HANDLED; | |
50 | } | |
51 | ||
af8c78eb BC |
52 | static struct spi_device *wl_to_spi(struct wl1251 *wl) |
53 | { | |
54 | return wl->if_priv; | |
55 | } | |
56 | ||
08d9f572 | 57 | static void wl1251_spi_reset(struct wl1251 *wl) |
2f01a1f5 KV |
58 | { |
59 | u8 *cmd; | |
60 | struct spi_transfer t; | |
61 | struct spi_message m; | |
62 | ||
63 | cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); | |
64 | if (!cmd) { | |
80301cdc | 65 | wl1251_error("could not allocate cmd for spi reset"); |
2f01a1f5 KV |
66 | return; |
67 | } | |
68 | ||
69 | memset(&t, 0, sizeof(t)); | |
70 | spi_message_init(&m); | |
71 | ||
72 | memset(cmd, 0xff, WSPI_INIT_CMD_LEN); | |
73 | ||
74 | t.tx_buf = cmd; | |
75 | t.len = WSPI_INIT_CMD_LEN; | |
76 | spi_message_add_tail(&t, &m); | |
77 | ||
af8c78eb | 78 | spi_sync(wl_to_spi(wl), &m); |
2f01a1f5 | 79 | |
80301cdc | 80 | wl1251_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); |
a859e4d6 GI |
81 | |
82 | kfree(cmd); | |
2f01a1f5 KV |
83 | } |
84 | ||
8e639c06 | 85 | static void wl1251_spi_wake(struct wl1251 *wl) |
2f01a1f5 | 86 | { |
2f01a1f5 KV |
87 | struct spi_transfer t; |
88 | struct spi_message m; | |
e757201b | 89 | u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); |
2f01a1f5 | 90 | |
2f01a1f5 | 91 | if (!cmd) { |
80301cdc | 92 | wl1251_error("could not allocate cmd for spi init"); |
2f01a1f5 KV |
93 | return; |
94 | } | |
95 | ||
2f01a1f5 KV |
96 | memset(&t, 0, sizeof(t)); |
97 | spi_message_init(&m); | |
98 | ||
789e90e5 | 99 | /* Set WSPI_INIT_COMMAND |
2f01a1f5 KV |
100 | * the data is being send from the MSB to LSB |
101 | */ | |
e757201b GS |
102 | cmd[0] = 0xff; |
103 | cmd[1] = 0xff; | |
104 | cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; | |
105 | cmd[3] = 0; | |
106 | cmd[4] = 0; | |
107 | cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3; | |
108 | cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; | |
109 | ||
110 | cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS | |
111 | | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; | |
2f01a1f5 KV |
112 | |
113 | if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) | |
e757201b | 114 | cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; |
2f01a1f5 | 115 | else |
e757201b | 116 | cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY; |
2f01a1f5 | 117 | |
e757201b GS |
118 | cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END; |
119 | /* | |
120 | * The above is the logical order; it must actually be stored | |
121 | * in the buffer byte-swapped. | |
122 | */ | |
123 | __swab32s((u32 *)cmd); | |
124 | __swab32s((u32 *)cmd+1); | |
2f01a1f5 KV |
125 | |
126 | t.tx_buf = cmd; | |
127 | t.len = WSPI_INIT_CMD_LEN; | |
128 | spi_message_add_tail(&t, &m); | |
129 | ||
af8c78eb | 130 | spi_sync(wl_to_spi(wl), &m); |
2f01a1f5 | 131 | |
80301cdc | 132 | wl1251_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); |
a859e4d6 GI |
133 | |
134 | kfree(cmd); | |
2f01a1f5 KV |
135 | } |
136 | ||
08d9f572 BC |
137 | static void wl1251_spi_reset_wake(struct wl1251 *wl) |
138 | { | |
139 | wl1251_spi_reset(wl); | |
8e639c06 | 140 | wl1251_spi_wake(wl); |
08d9f572 BC |
141 | } |
142 | ||
08d9f572 BC |
143 | static void wl1251_spi_read(struct wl1251 *wl, int addr, void *buf, |
144 | size_t len) | |
2f01a1f5 KV |
145 | { |
146 | struct spi_transfer t[3]; | |
147 | struct spi_message m; | |
5262c12d | 148 | u8 *busy_buf; |
56343a3c | 149 | u32 *cmd; |
2f01a1f5 | 150 | |
56343a3c | 151 | cmd = &wl->buffer_cmd; |
5262c12d | 152 | busy_buf = wl->buffer_busyword; |
56343a3c KV |
153 | |
154 | *cmd = 0; | |
155 | *cmd |= WSPI_CMD_READ; | |
156 | *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; | |
157 | *cmd |= addr & WSPI_CMD_BYTE_ADDR; | |
2f01a1f5 KV |
158 | |
159 | spi_message_init(&m); | |
160 | memset(t, 0, sizeof(t)); | |
161 | ||
56343a3c | 162 | t[0].tx_buf = cmd; |
2f01a1f5 KV |
163 | t[0].len = 4; |
164 | spi_message_add_tail(&t[0], &m); | |
165 | ||
166 | /* Busy and non busy words read */ | |
167 | t[1].rx_buf = busy_buf; | |
80301cdc | 168 | t[1].len = WL1251_BUSY_WORD_LEN; |
2f01a1f5 KV |
169 | spi_message_add_tail(&t[1], &m); |
170 | ||
171 | t[2].rx_buf = buf; | |
172 | t[2].len = len; | |
173 | spi_message_add_tail(&t[2], &m); | |
174 | ||
af8c78eb | 175 | spi_sync(wl_to_spi(wl), &m); |
2f01a1f5 KV |
176 | |
177 | /* FIXME: check busy words */ | |
178 | ||
80301cdc KV |
179 | wl1251_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd)); |
180 | wl1251_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); | |
2f01a1f5 KV |
181 | } |
182 | ||
08d9f572 BC |
183 | static void wl1251_spi_write(struct wl1251 *wl, int addr, void *buf, |
184 | size_t len) | |
2f01a1f5 KV |
185 | { |
186 | struct spi_transfer t[2]; | |
187 | struct spi_message m; | |
56343a3c | 188 | u32 *cmd; |
2f01a1f5 | 189 | |
56343a3c KV |
190 | cmd = &wl->buffer_cmd; |
191 | ||
192 | *cmd = 0; | |
193 | *cmd |= WSPI_CMD_WRITE; | |
194 | *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; | |
195 | *cmd |= addr & WSPI_CMD_BYTE_ADDR; | |
2f01a1f5 KV |
196 | |
197 | spi_message_init(&m); | |
198 | memset(t, 0, sizeof(t)); | |
199 | ||
56343a3c KV |
200 | t[0].tx_buf = cmd; |
201 | t[0].len = sizeof(*cmd); | |
2f01a1f5 KV |
202 | spi_message_add_tail(&t[0], &m); |
203 | ||
204 | t[1].tx_buf = buf; | |
205 | t[1].len = len; | |
206 | spi_message_add_tail(&t[1], &m); | |
207 | ||
af8c78eb | 208 | spi_sync(wl_to_spi(wl), &m); |
2f01a1f5 | 209 | |
80301cdc KV |
210 | wl1251_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); |
211 | wl1251_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); | |
2f01a1f5 | 212 | } |
08d9f572 | 213 | |
b5ed9c1b BC |
214 | static void wl1251_spi_enable_irq(struct wl1251 *wl) |
215 | { | |
216 | return enable_irq(wl->irq); | |
217 | } | |
218 | ||
219 | static void wl1251_spi_disable_irq(struct wl1251 *wl) | |
220 | { | |
221 | return disable_irq(wl->irq); | |
222 | } | |
223 | ||
cb7bbc7a GI |
224 | static int wl1251_spi_set_power(struct wl1251 *wl, bool enable) |
225 | { | |
1d207cd3 SR |
226 | if (gpio_is_valid(wl->power_gpio)) |
227 | gpio_set_value(wl->power_gpio, enable); | |
cb7bbc7a GI |
228 | |
229 | return 0; | |
230 | } | |
231 | ||
af8c78eb | 232 | static const struct wl1251_if_operations wl1251_spi_ops = { |
08d9f572 BC |
233 | .read = wl1251_spi_read, |
234 | .write = wl1251_spi_write, | |
235 | .reset = wl1251_spi_reset_wake, | |
b5ed9c1b BC |
236 | .enable_irq = wl1251_spi_enable_irq, |
237 | .disable_irq = wl1251_spi_disable_irq, | |
cb7bbc7a | 238 | .power = wl1251_spi_set_power, |
08d9f572 | 239 | }; |
8e639c06 | 240 | |
b74324d1 | 241 | static int wl1251_spi_probe(struct spi_device *spi) |
8e639c06 | 242 | { |
07bbca6f SR |
243 | struct wl1251_platform_data *pdata = dev_get_platdata(&spi->dev); |
244 | struct device_node *np = spi->dev.of_node; | |
8e639c06 BC |
245 | struct ieee80211_hw *hw; |
246 | struct wl1251 *wl; | |
247 | int ret; | |
248 | ||
07bbca6f | 249 | if (!np && !pdata) { |
8e639c06 BC |
250 | wl1251_error("no platform data"); |
251 | return -ENODEV; | |
252 | } | |
253 | ||
254 | hw = wl1251_alloc_hw(); | |
255 | if (IS_ERR(hw)) | |
256 | return PTR_ERR(hw); | |
257 | ||
258 | wl = hw->priv; | |
259 | ||
260 | SET_IEEE80211_DEV(hw, &spi->dev); | |
c49b05ac | 261 | spi_set_drvdata(spi, wl); |
af8c78eb | 262 | wl->if_priv = spi; |
8e639c06 BC |
263 | wl->if_ops = &wl1251_spi_ops; |
264 | ||
265 | /* This is the only SPI value that we need to set here, the rest | |
789e90e5 SK |
266 | * comes from the board-peripherals file |
267 | */ | |
8e639c06 BC |
268 | spi->bits_per_word = 32; |
269 | ||
270 | ret = spi_setup(spi); | |
271 | if (ret < 0) { | |
272 | wl1251_error("spi_setup failed"); | |
273 | goto out_free; | |
274 | } | |
275 | ||
07bbca6f SR |
276 | if (np) { |
277 | wl->use_eeprom = of_property_read_bool(np, "ti,wl1251-has-eeprom"); | |
278 | wl->power_gpio = of_get_named_gpio(np, "ti,power-gpio", 0); | |
279 | } else if (pdata) { | |
280 | wl->power_gpio = pdata->power_gpio; | |
281 | wl->use_eeprom = pdata->use_eeprom; | |
282 | } | |
283 | ||
284 | if (wl->power_gpio == -EPROBE_DEFER) { | |
285 | ret = -EPROBE_DEFER; | |
286 | goto out_free; | |
287 | } | |
1d207cd3 SR |
288 | |
289 | if (gpio_is_valid(wl->power_gpio)) { | |
290 | ret = devm_gpio_request_one(&spi->dev, wl->power_gpio, | |
291 | GPIOF_OUT_INIT_LOW, "wl1251 power"); | |
292 | if (ret) { | |
293 | wl1251_error("Failed to request gpio: %d\n", ret); | |
294 | goto out_free; | |
295 | } | |
296 | } else { | |
297 | wl1251_error("set power gpio missing in platform data"); | |
298 | ret = -ENODEV; | |
299 | goto out_free; | |
8e639c06 BC |
300 | } |
301 | ||
302 | wl->irq = spi->irq; | |
303 | if (wl->irq < 0) { | |
304 | wl1251_error("irq missing in platform data"); | |
1d207cd3 SR |
305 | ret = -ENODEV; |
306 | goto out_free; | |
8e639c06 BC |
307 | } |
308 | ||
f380f2c4 | 309 | irq_set_status_flags(wl->irq, IRQ_NOAUTOEN); |
1d207cd3 SR |
310 | ret = devm_request_irq(&spi->dev, wl->irq, wl1251_irq, 0, |
311 | DRIVER_NAME, wl); | |
8e639c06 BC |
312 | if (ret < 0) { |
313 | wl1251_error("request_irq() failed: %d", ret); | |
314 | goto out_free; | |
315 | } | |
316 | ||
dced35ae | 317 | irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); |
8e639c06 | 318 | |
e4c2e09e SR |
319 | wl->vio = devm_regulator_get(&spi->dev, "vio"); |
320 | if (IS_ERR(wl->vio)) { | |
321 | ret = PTR_ERR(wl->vio); | |
322 | wl1251_error("vio regulator missing: %d", ret); | |
323 | goto out_free; | |
324 | } | |
325 | ||
326 | ret = regulator_enable(wl->vio); | |
8e639c06 | 327 | if (ret) |
1d207cd3 | 328 | goto out_free; |
8e639c06 | 329 | |
e4c2e09e SR |
330 | ret = wl1251_init_ieee80211(wl); |
331 | if (ret) | |
332 | goto disable_regulator; | |
333 | ||
8e639c06 BC |
334 | return 0; |
335 | ||
e4c2e09e SR |
336 | disable_regulator: |
337 | regulator_disable(wl->vio); | |
338 | out_free: | |
8e639c06 BC |
339 | ieee80211_free_hw(hw); |
340 | ||
341 | return ret; | |
342 | } | |
343 | ||
b74324d1 | 344 | static int wl1251_spi_remove(struct spi_device *spi) |
8e639c06 | 345 | { |
c49b05ac | 346 | struct wl1251 *wl = spi_get_drvdata(spi); |
8e639c06 BC |
347 | |
348 | wl1251_free_hw(wl); | |
e4c2e09e | 349 | regulator_disable(wl->vio); |
8e639c06 BC |
350 | |
351 | return 0; | |
352 | } | |
353 | ||
354 | static struct spi_driver wl1251_spi_driver = { | |
355 | .driver = { | |
7590a550 | 356 | .name = DRIVER_NAME, |
8e639c06 BC |
357 | }, |
358 | ||
359 | .probe = wl1251_spi_probe, | |
b74324d1 | 360 | .remove = wl1251_spi_remove, |
8e639c06 BC |
361 | }; |
362 | ||
19a4e68a | 363 | module_spi_driver(wl1251_spi_driver); |
8e639c06 BC |
364 | |
365 | MODULE_LICENSE("GPL"); | |
31c726f0 | 366 | MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>"); |
f148cfdd | 367 | MODULE_ALIAS("spi:wl1251"); |