1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * This file is part of wl1251 |
4 | * |
5 | * Copyright (C) 2008 Nokia Corporation |
6 | */ |
7 | |
8 | #include <linux/err.h> |
9 | #include <linux/interrupt.h> |
10 | #include <linux/irq.h> |
11 | #include <linux/module.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/swab.h> |
14 | #include <linux/crc7.h> |
15 | #include <linux/spi/spi.h> |
16 | #include <linux/gpio/consumer.h> |
17 | #include <linux/of.h> |
18 | #include <linux/regulator/consumer.h> |
19 | |
20 | #include "wl1251.h" |
21 | #include "reg.h" |
22 | #include "spi.h" |
23 | |
24 | struct wl1251_spi { |
25 | struct spi_device *spi; |
26 | struct gpio_desc *power_gpio; |
27 | }; |
28 | |
29 | static irqreturn_t wl1251_irq(int irq, void *cookie) |
30 | { |
31 | struct wl1251 *wl; |
32 | |
33 | wl1251_debug(DEBUG_IRQ, "IRQ" ); |
34 | |
35 | wl = cookie; |
36 | |
37 | ieee80211_queue_work(hw: wl->hw, work: &wl->irq_work); |
38 | |
39 | return IRQ_HANDLED; |
40 | } |
41 | |
42 | static void wl1251_spi_reset(struct wl1251 *wl) |
43 | { |
44 | struct wl1251_spi *wl_spi = wl->if_priv; |
45 | u8 *cmd; |
46 | struct spi_transfer t; |
47 | struct spi_message m; |
48 | |
49 | cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); |
50 | if (!cmd) { |
51 | wl1251_error("could not allocate cmd for spi reset" ); |
52 | return; |
53 | } |
54 | |
55 | memset(&t, 0, sizeof(t)); |
56 | spi_message_init(m: &m); |
57 | |
58 | memset(cmd, 0xff, WSPI_INIT_CMD_LEN); |
59 | |
60 | t.tx_buf = cmd; |
61 | t.len = WSPI_INIT_CMD_LEN; |
62 | spi_message_add_tail(t: &t, m: &m); |
63 | |
64 | spi_sync(spi: wl_spi->spi, message: &m); |
65 | |
66 | wl1251_dump(DEBUG_SPI, "spi reset -> " , cmd, WSPI_INIT_CMD_LEN); |
67 | |
68 | kfree(objp: cmd); |
69 | } |
70 | |
71 | static void wl1251_spi_wake(struct wl1251 *wl) |
72 | { |
73 | struct wl1251_spi *wl_spi = wl->if_priv; |
74 | struct spi_transfer t; |
75 | struct spi_message m; |
76 | u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); |
77 | |
78 | if (!cmd) { |
79 | wl1251_error("could not allocate cmd for spi init" ); |
80 | return; |
81 | } |
82 | |
83 | memset(&t, 0, sizeof(t)); |
84 | spi_message_init(m: &m); |
85 | |
86 | /* Set WSPI_INIT_COMMAND |
87 | * the data is being send from the MSB to LSB |
88 | */ |
89 | cmd[0] = 0xff; |
90 | cmd[1] = 0xff; |
91 | cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; |
92 | cmd[3] = 0; |
93 | cmd[4] = 0; |
94 | cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3; |
95 | cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; |
96 | |
97 | cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS |
98 | | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; |
99 | |
100 | if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) |
101 | cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; |
102 | else |
103 | cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY; |
104 | |
105 | cmd[7] = crc7_be(crc: 0, buffer: cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END; |
106 | /* |
107 | * The above is the logical order; it must actually be stored |
108 | * in the buffer byte-swapped. |
109 | */ |
110 | __swab32s(p: (u32 *)cmd); |
111 | __swab32s(p: (u32 *)cmd+1); |
112 | |
113 | t.tx_buf = cmd; |
114 | t.len = WSPI_INIT_CMD_LEN; |
115 | spi_message_add_tail(t: &t, m: &m); |
116 | |
117 | spi_sync(spi: wl_spi->spi, message: &m); |
118 | |
119 | wl1251_dump(DEBUG_SPI, "spi init -> " , cmd, WSPI_INIT_CMD_LEN); |
120 | |
121 | kfree(objp: cmd); |
122 | } |
123 | |
124 | static void wl1251_spi_reset_wake(struct wl1251 *wl) |
125 | { |
126 | wl1251_spi_reset(wl); |
127 | wl1251_spi_wake(wl); |
128 | } |
129 | |
130 | static void wl1251_spi_read(struct wl1251 *wl, int addr, void *buf, |
131 | size_t len) |
132 | { |
133 | struct wl1251_spi *wl_spi = wl->if_priv; |
134 | struct spi_transfer t[3]; |
135 | struct spi_message m; |
136 | u8 *busy_buf; |
137 | u32 *cmd; |
138 | |
139 | cmd = &wl->buffer_cmd; |
140 | busy_buf = wl->buffer_busyword; |
141 | |
142 | *cmd = 0; |
143 | *cmd |= WSPI_CMD_READ; |
144 | *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; |
145 | *cmd |= addr & WSPI_CMD_BYTE_ADDR; |
146 | |
147 | spi_message_init(m: &m); |
148 | memset(t, 0, sizeof(t)); |
149 | |
150 | t[0].tx_buf = cmd; |
151 | t[0].len = 4; |
152 | spi_message_add_tail(t: &t[0], m: &m); |
153 | |
154 | /* Busy and non busy words read */ |
155 | t[1].rx_buf = busy_buf; |
156 | t[1].len = WL1251_BUSY_WORD_LEN; |
157 | spi_message_add_tail(t: &t[1], m: &m); |
158 | |
159 | t[2].rx_buf = buf; |
160 | t[2].len = len; |
161 | spi_message_add_tail(t: &t[2], m: &m); |
162 | |
163 | spi_sync(spi: wl_spi->spi, message: &m); |
164 | |
165 | /* FIXME: check busy words */ |
166 | |
167 | wl1251_dump(DEBUG_SPI, "spi_read cmd -> " , cmd, sizeof(*cmd)); |
168 | wl1251_dump(DEBUG_SPI, "spi_read buf <- " , buf, len); |
169 | } |
170 | |
171 | static void wl1251_spi_write(struct wl1251 *wl, int addr, void *buf, |
172 | size_t len) |
173 | { |
174 | struct wl1251_spi *wl_spi = wl->if_priv; |
175 | struct spi_transfer t[2]; |
176 | struct spi_message m; |
177 | u32 *cmd; |
178 | |
179 | cmd = &wl->buffer_cmd; |
180 | |
181 | *cmd = 0; |
182 | *cmd |= WSPI_CMD_WRITE; |
183 | *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; |
184 | *cmd |= addr & WSPI_CMD_BYTE_ADDR; |
185 | |
186 | spi_message_init(m: &m); |
187 | memset(t, 0, sizeof(t)); |
188 | |
189 | t[0].tx_buf = cmd; |
190 | t[0].len = sizeof(*cmd); |
191 | spi_message_add_tail(t: &t[0], m: &m); |
192 | |
193 | t[1].tx_buf = buf; |
194 | t[1].len = len; |
195 | spi_message_add_tail(t: &t[1], m: &m); |
196 | |
197 | spi_sync(spi: wl_spi->spi, message: &m); |
198 | |
199 | wl1251_dump(DEBUG_SPI, "spi_write cmd -> " , cmd, sizeof(*cmd)); |
200 | wl1251_dump(DEBUG_SPI, "spi_write buf -> " , buf, len); |
201 | } |
202 | |
203 | static void wl1251_spi_enable_irq(struct wl1251 *wl) |
204 | { |
205 | return enable_irq(irq: wl->irq); |
206 | } |
207 | |
208 | static void wl1251_spi_disable_irq(struct wl1251 *wl) |
209 | { |
210 | return disable_irq(irq: wl->irq); |
211 | } |
212 | |
213 | static int wl1251_spi_set_power(struct wl1251 *wl, bool enable) |
214 | { |
215 | struct wl1251_spi *wl_spi = wl->if_priv; |
216 | |
217 | if (wl_spi->power_gpio) |
218 | gpiod_set_value_cansleep(desc: wl_spi->power_gpio, value: enable); |
219 | |
220 | return 0; |
221 | } |
222 | |
223 | static const struct wl1251_if_operations wl1251_spi_ops = { |
224 | .read = wl1251_spi_read, |
225 | .write = wl1251_spi_write, |
226 | .reset = wl1251_spi_reset_wake, |
227 | .enable_irq = wl1251_spi_enable_irq, |
228 | .disable_irq = wl1251_spi_disable_irq, |
229 | .power = wl1251_spi_set_power, |
230 | }; |
231 | |
232 | static int wl1251_spi_probe(struct spi_device *spi) |
233 | { |
234 | struct device_node *np = spi->dev.of_node; |
235 | struct ieee80211_hw *hw; |
236 | struct wl1251_spi *wl_spi; |
237 | struct wl1251 *wl; |
238 | int ret; |
239 | |
240 | if (!np) |
241 | return -ENODEV; |
242 | |
243 | wl_spi = devm_kzalloc(dev: &spi->dev, size: sizeof(*wl_spi), GFP_KERNEL); |
244 | if (!wl_spi) |
245 | return -ENOMEM; |
246 | |
247 | wl_spi->spi = spi; |
248 | |
249 | hw = wl1251_alloc_hw(); |
250 | if (IS_ERR(ptr: hw)) |
251 | return PTR_ERR(ptr: hw); |
252 | |
253 | wl = hw->priv; |
254 | |
255 | SET_IEEE80211_DEV(hw, dev: &spi->dev); |
256 | spi_set_drvdata(spi, data: wl); |
257 | wl->if_priv = wl_spi; |
258 | wl->if_ops = &wl1251_spi_ops; |
259 | |
260 | /* This is the only SPI value that we need to set here, the rest |
261 | * comes from the board-peripherals file |
262 | */ |
263 | spi->bits_per_word = 32; |
264 | |
265 | ret = spi_setup(spi); |
266 | if (ret < 0) { |
267 | wl1251_error("spi_setup failed" ); |
268 | goto out_free; |
269 | } |
270 | |
271 | wl->use_eeprom = of_property_read_bool(np, propname: "ti,wl1251-has-eeprom" ); |
272 | |
273 | wl_spi->power_gpio = devm_gpiod_get_optional(dev: &spi->dev, con_id: "ti,power" , |
274 | flags: GPIOD_OUT_LOW); |
275 | ret = PTR_ERR_OR_ZERO(ptr: wl_spi->power_gpio); |
276 | if (ret) { |
277 | if (ret != -EPROBE_DEFER) |
278 | wl1251_error("Failed to request gpio: %d\n" , ret); |
279 | goto out_free; |
280 | } |
281 | |
282 | gpiod_set_consumer_name(desc: wl_spi->power_gpio, name: "wl1251 power" ); |
283 | |
284 | wl->irq = spi->irq; |
285 | if (wl->irq < 0) { |
286 | wl1251_error("irq missing in platform data" ); |
287 | ret = -ENODEV; |
288 | goto out_free; |
289 | } |
290 | |
291 | irq_set_status_flags(irq: wl->irq, set: IRQ_NOAUTOEN); |
292 | ret = devm_request_irq(dev: &spi->dev, irq: wl->irq, handler: wl1251_irq, irqflags: 0, |
293 | DRIVER_NAME, dev_id: wl); |
294 | if (ret < 0) { |
295 | wl1251_error("request_irq() failed: %d" , ret); |
296 | goto out_free; |
297 | } |
298 | |
299 | irq_set_irq_type(irq: wl->irq, type: IRQ_TYPE_EDGE_RISING); |
300 | |
301 | wl->vio = devm_regulator_get(dev: &spi->dev, id: "vio" ); |
302 | if (IS_ERR(ptr: wl->vio)) { |
303 | ret = PTR_ERR(ptr: wl->vio); |
304 | wl1251_error("vio regulator missing: %d" , ret); |
305 | goto out_free; |
306 | } |
307 | |
308 | ret = regulator_enable(regulator: wl->vio); |
309 | if (ret) |
310 | goto out_free; |
311 | |
312 | ret = wl1251_init_ieee80211(wl); |
313 | if (ret) |
314 | goto disable_regulator; |
315 | |
316 | return 0; |
317 | |
318 | disable_regulator: |
319 | regulator_disable(regulator: wl->vio); |
320 | out_free: |
321 | ieee80211_free_hw(hw); |
322 | |
323 | return ret; |
324 | } |
325 | |
326 | static void wl1251_spi_remove(struct spi_device *spi) |
327 | { |
328 | struct wl1251 *wl = spi_get_drvdata(spi); |
329 | |
330 | wl1251_free_hw(wl); |
331 | regulator_disable(regulator: wl->vio); |
332 | } |
333 | |
334 | static struct spi_driver wl1251_spi_driver = { |
335 | .driver = { |
336 | .name = DRIVER_NAME, |
337 | }, |
338 | |
339 | .probe = wl1251_spi_probe, |
340 | .remove = wl1251_spi_remove, |
341 | }; |
342 | |
343 | module_spi_driver(wl1251_spi_driver); |
344 | |
345 | MODULE_DESCRIPTION("TI WL1251 SPI helpers" ); |
346 | MODULE_LICENSE("GPL" ); |
347 | MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>" ); |
348 | MODULE_ALIAS("spi:wl1251" ); |
349 | |