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