[go: nahoru, domu]

1a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy/*
2a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Mac80211 SPI driver for ST-Ericsson CW1200 device
3a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy *
4a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Copyright (c) 2011, Sagrad Inc.
5a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Author:  Solomon Peachy <speachy@sagrad.com>
6a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy *
7a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Based on cw1200_sdio.c
8a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Copyright (c) 2010, ST-Ericsson
9a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
10a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy *
11a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * This program is free software; you can redistribute it and/or modify
12a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * it under the terms of the GNU General Public License version 2 as
13a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy * published by the Free Software Foundation.
14a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy */
15a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
16a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <linux/module.h>
17a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <linux/gpio.h>
18a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <linux/delay.h>
19a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <linux/spinlock.h>
20a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <linux/interrupt.h>
21a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <net/mac80211.h>
22a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
23a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <linux/spi/spi.h>
24a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include <linux/device.h>
25a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
26a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include "cw1200.h"
27911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy#include "hwbus.h"
284da2a54a842db6921289e3e25b0739531a594deaSolomon Peachy#include <linux/platform_data/net-cw1200.h>
29a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#include "hwio.h"
30a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
31a910e4a94f6923c8c988565525f017f687bf7205Solomon PeachyMODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
32a910e4a94f6923c8c988565525f017f687bf7205Solomon PeachyMODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver");
33a910e4a94f6923c8c988565525f017f687bf7205Solomon PeachyMODULE_LICENSE("GPL");
34a910e4a94f6923c8c988565525f017f687bf7205Solomon PeachyMODULE_ALIAS("spi:cw1200_wlan_spi");
35a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
36a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy/* #define SPI_DEBUG */
37a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
38911373cca1b45571b62938f8f19cec24cb102471Solomon Peachystruct hwbus_priv {
39a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct spi_device	*func;
40a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct cw1200_common	*core;
41a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	const struct cw1200_platform_data_spi *pdata;
42a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spinlock_t		lock; /* Serialize all bus operations */
4385ba8f529c57ac6e2fca9be0d9e17920a1afb2e8Solomon Peachy	wait_queue_head_t       wq;
44a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int claimed;
45a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy};
46a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
47a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
48a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#define SET_WRITE 0x7FFF /* usage: and operation */
49a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#define SET_READ 0x8000  /* usage: or operation */
50a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
518b3e7be437a6b62118d0485ad971e724afe23fdfSolomon Peachy/* Notes on byte ordering:
52a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy   LE:  B0 B1 B2 B3
53a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy   BE:  B3 B2 B1 B0
54a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
55a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy   Hardware expects 32-bit data to be written as 16-bit BE words:
56a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
57a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy   B1 B0 B3 B2
58a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy*/
59a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
60911373cca1b45571b62938f8f19cec24cb102471Solomon Peachystatic int cw1200_spi_memcpy_fromio(struct hwbus_priv *self,
61a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				     unsigned int addr,
62a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				     void *dst, int count)
63a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
64a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int ret, i;
657258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	u16 regaddr;
66a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct spi_message      m;
67a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
68a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct spi_transfer     t_addr = {
69a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.tx_buf         = &regaddr,
70a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.len            = sizeof(regaddr),
71a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	};
72a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct spi_transfer     t_msg = {
73a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.rx_buf         = dst,
74a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.len            = count,
75a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	};
76a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
77a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
78a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	regaddr |= SET_READ;
79a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	regaddr |= (count>>1);
80a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
81a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#ifdef SPI_DEBUG
827258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, regaddr);
83a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#endif
84a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
857258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	/* Header is LE16 */
867258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	regaddr = cpu_to_le16(regaddr);
877258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy
887258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	/* We have to byteswap if the SPI bus is limited to 8b operation
897258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	   or we are running on a Big Endian system
907258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	*/
91a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#if defined(__LITTLE_ENDIAN)
92a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (self->func->bits_per_word == 8)
93a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#endif
94a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		regaddr = swab16(regaddr);
95a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
96a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spi_message_init(&m);
97a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spi_message_add_tail(&t_addr, &m);
98a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spi_message_add_tail(&t_msg, &m);
99a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	ret = spi_sync(self->func, &m);
100a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
101a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#ifdef SPI_DEBUG
102a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_info("READ : ");
103a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	for (i = 0; i < t_addr.len; i++)
104a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
105a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	printk(" : ");
106a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	for (i = 0; i < t_msg.len; i++)
107a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		printk("%02x ", ((u8 *)t_msg.rx_buf)[i]);
108a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	printk("\n");
109a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#endif
110a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
1117258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	/* We have to byteswap if the SPI bus is limited to 8b operation
1127258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	   or we are running on a Big Endian system
1137258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	*/
114a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#if defined(__LITTLE_ENDIAN)
115a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (self->func->bits_per_word == 8)
116a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#endif
117a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	{
118a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		uint16_t *buf = (uint16_t *)dst;
119a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		for (i = 0; i < ((count + 1) >> 1); i++)
120a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			buf[i] = swab16(buf[i]);
121a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
122a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
123a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return ret;
124a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
125a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
126911373cca1b45571b62938f8f19cec24cb102471Solomon Peachystatic int cw1200_spi_memcpy_toio(struct hwbus_priv *self,
127a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				   unsigned int addr,
128a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				   const void *src, int count)
129a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
130a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int rval, i;
1317258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	u16 regaddr;
132a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct spi_transfer     t_addr = {
133a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.tx_buf         = &regaddr,
134a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.len            = sizeof(regaddr),
135a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	};
136a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct spi_transfer     t_msg = {
137a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.tx_buf         = src,
138a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.len            = count,
139a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	};
140a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	struct spi_message      m;
141a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
142a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
143a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	regaddr &= SET_WRITE;
144a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	regaddr |= (count>>1);
145a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
146a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#ifdef SPI_DEBUG
1477258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	pr_info("WRITE: %04d  to  0x%02x (%04x)\n", count, addr, regaddr);
148a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#endif
149a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
1507258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	/* Header is LE16 */
1517258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	regaddr = cpu_to_le16(regaddr);
1527258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy
1537258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	/* We have to byteswap if the SPI bus is limited to 8b operation
1547258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	   or we are running on a Big Endian system
1557258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	*/
156a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#if defined(__LITTLE_ENDIAN)
157a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (self->func->bits_per_word == 8)
158a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#endif
159a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	{
160a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		uint16_t *buf = (uint16_t *)src;
1617258416c517c79b2ebb30b61d8c6807a04dc6b25Solomon Peachy	        regaddr = swab16(regaddr);
162a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		for (i = 0; i < ((count + 1) >> 1); i++)
163a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			buf[i] = swab16(buf[i]);
164a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
165a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
166a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#ifdef SPI_DEBUG
167a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_info("WRITE: ");
168a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	for (i = 0; i < t_addr.len; i++)
169a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
170a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	printk(" : ");
171a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	for (i = 0; i < t_msg.len; i++)
172a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		printk("%02x ", ((u8 *)t_msg.tx_buf)[i]);
173a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	printk("\n");
174a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#endif
175a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
176a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spi_message_init(&m);
177a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spi_message_add_tail(&t_addr, &m);
178a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spi_message_add_tail(&t_msg, &m);
179a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	rval = spi_sync(self->func, &m);
180a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
181a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#ifdef SPI_DEBUG
182a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_info("WROTE: %d\n", m.actual_length);
183a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#endif
184a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
185a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#if defined(__LITTLE_ENDIAN)
186a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* We have to byteswap if the SPI bus is limited to 8b operation */
187a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (self->func->bits_per_word == 8)
188a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy#endif
189a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	{
190a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		uint16_t *buf = (uint16_t *)src;
191a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		for (i = 0; i < ((count + 1) >> 1); i++)
192a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			buf[i] = swab16(buf[i]);
193a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
194a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return rval;
195a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
196a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
197911373cca1b45571b62938f8f19cec24cb102471Solomon Peachystatic void cw1200_spi_lock(struct hwbus_priv *self)
198a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
199a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	unsigned long flags;
200a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
20185ba8f529c57ac6e2fca9be0d9e17920a1afb2e8Solomon Peachy	DECLARE_WAITQUEUE(wait, current);
20285ba8f529c57ac6e2fca9be0d9e17920a1afb2e8Solomon Peachy
203a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	might_sleep();
204a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
20585ba8f529c57ac6e2fca9be0d9e17920a1afb2e8Solomon Peachy	add_wait_queue(&self->wq, &wait);
206a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spin_lock_irqsave(&self->lock, flags);
207a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	while (1) {
208a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		set_current_state(TASK_UNINTERRUPTIBLE);
209a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (!self->claimed)
210a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			break;
211a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		spin_unlock_irqrestore(&self->lock, flags);
212a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		schedule();
213a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		spin_lock_irqsave(&self->lock, flags);
214a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
215a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	set_current_state(TASK_RUNNING);
216a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	self->claimed = 1;
217a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spin_unlock_irqrestore(&self->lock, flags);
21885ba8f529c57ac6e2fca9be0d9e17920a1afb2e8Solomon Peachy	remove_wait_queue(&self->wq, &wait);
219a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
220a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return;
221a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
222a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
223911373cca1b45571b62938f8f19cec24cb102471Solomon Peachystatic void cw1200_spi_unlock(struct hwbus_priv *self)
224a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
225a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	unsigned long flags;
226a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
227a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spin_lock_irqsave(&self->lock, flags);
228a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	self->claimed = 0;
229a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spin_unlock_irqrestore(&self->lock, flags);
23085ba8f529c57ac6e2fca9be0d9e17920a1afb2e8Solomon Peachy	wake_up(&self->wq);
23185ba8f529c57ac6e2fca9be0d9e17920a1afb2e8Solomon Peachy
232a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return;
233a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
234a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
235a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
236a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
237911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	struct hwbus_priv *self = dev_id;
238a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
239a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (self->core) {
2404978705d26149a629b9f50ff221caed6f1ae3048Solomon Peachy		cw1200_spi_lock(self);
241a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		cw1200_irq_handler(self->core);
2424978705d26149a629b9f50ff221caed6f1ae3048Solomon Peachy		cw1200_spi_unlock(self);
243a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return IRQ_HANDLED;
244a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	} else {
245a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return IRQ_NONE;
246a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
247a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
248a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
249911373cca1b45571b62938f8f19cec24cb102471Solomon Peachystatic int cw1200_spi_irq_subscribe(struct hwbus_priv *self)
250a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
251a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int ret;
252a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
253a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_debug("SW IRQ subscribe\n");
254a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
25587421cb6010a2f6494938fbe0a95e1b096b3b7afSolomon Peachy	ret = request_threaded_irq(self->func->irq, NULL,
25687421cb6010a2f6494938fbe0a95e1b096b3b7afSolomon Peachy				   cw1200_spi_irq_handler,
25787421cb6010a2f6494938fbe0a95e1b096b3b7afSolomon Peachy				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
25887421cb6010a2f6494938fbe0a95e1b096b3b7afSolomon Peachy				   "cw1200_wlan_irq", self);
259a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(ret < 0))
260a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		goto exit;
261a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
262a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	ret = enable_irq_wake(self->func->irq);
263a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (WARN_ON(ret))
264a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		goto free_irq;
265a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
266a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return 0;
267a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
268a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyfree_irq:
269a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	free_irq(self->func->irq, self);
270a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachyexit:
271a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return ret;
272a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
273a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
274911373cca1b45571b62938f8f19cec24cb102471Solomon Peachystatic int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self)
275a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
276c4fb19d21b003ec99ec490ba2cb60baffabc73f3Solomon Peachy	int ret = 0;
277c4fb19d21b003ec99ec490ba2cb60baffabc73f3Solomon Peachy
278a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_debug("SW IRQ unsubscribe\n");
279a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	disable_irq_wake(self->func->irq);
280a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	free_irq(self->func->irq, self);
281a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
282c4fb19d21b003ec99ec490ba2cb60baffabc73f3Solomon Peachy	return ret;
283a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
284a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
285a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
286a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
2876dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy	if (pdata->reset) {
2886dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy		gpio_set_value(pdata->reset, 0);
289a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		msleep(30); /* Min is 2 * CLK32K cycles */
2906dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy		gpio_free(pdata->reset);
291a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
292a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
293a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (pdata->power_ctrl)
294a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pdata->power_ctrl(pdata, false);
295a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (pdata->clk_ctrl)
296a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pdata->clk_ctrl(pdata, false);
297a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
298a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return 0;
299a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
300a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
301a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata)
302a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
303a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Ensure I/Os are pulled low */
3046dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy	if (pdata->reset) {
3056dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy		gpio_request(pdata->reset, "cw1200_wlan_reset");
3066dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy		gpio_direction_output(pdata->reset, 0);
307a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
3086dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy	if (pdata->powerup) {
3096dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy		gpio_request(pdata->powerup, "cw1200_wlan_powerup");
3106dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy		gpio_direction_output(pdata->powerup, 0);
311a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
3126dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy	if (pdata->reset || pdata->powerup)
313a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		msleep(10); /* Settle time? */
314a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
315a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Enable 3v3 and 1v8 to hardware */
316a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (pdata->power_ctrl) {
317a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (pdata->power_ctrl(pdata, true)) {
318a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			pr_err("power_ctrl() failed!\n");
319a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			return -1;
320a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		}
321a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
322a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
323a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Enable CLK32K */
324a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (pdata->clk_ctrl) {
325a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (pdata->clk_ctrl(pdata, true)) {
326a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			pr_err("clk_ctrl() failed!\n");
327a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			return -1;
328a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		}
329a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		msleep(10); /* Delay until clock is stable for 2 cycles */
330a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
331a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
332a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Enable POWERUP signal */
3336dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy	if (pdata->powerup) {
3346dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy		gpio_set_value(pdata->powerup, 1);
335a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		msleep(250); /* or more..? */
336a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
337a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Enable RSTn signal */
3386dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy	if (pdata->reset) {
3396dd64a304eff76ca7dd41bf63df55efa965fa9ecSolomon Peachy		gpio_set_value(pdata->reset, 1);
340a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		msleep(50); /* Or more..? */
341a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
342a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return 0;
343a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
344a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
345911373cca1b45571b62938f8f19cec24cb102471Solomon Peachystatic size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size)
346a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
347a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return size & 1 ? size + 1 : size;
348a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
349a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
350911373cca1b45571b62938f8f19cec24cb102471Solomon Peachystatic int cw1200_spi_pm(struct hwbus_priv *self, bool suspend)
351a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
352a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return irq_set_irq_wake(self->func->irq, suspend);
353a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
354a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
355911373cca1b45571b62938f8f19cec24cb102471Solomon Peachystatic struct hwbus_ops cw1200_spi_hwbus_ops = {
356911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	.hwbus_memcpy_fromio	= cw1200_spi_memcpy_fromio,
357911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	.hwbus_memcpy_toio	= cw1200_spi_memcpy_toio,
358a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	.lock			= cw1200_spi_lock,
359a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	.unlock			= cw1200_spi_unlock,
360a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	.align_size		= cw1200_spi_align_size,
361a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	.power_mgmt		= cw1200_spi_pm,
362a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy};
363a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
364a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy/* Probe Function to be called by SPI stack when device is discovered */
365a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_spi_probe(struct spi_device *func)
366a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
367a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	const struct cw1200_platform_data_spi *plat_data =
3683ec8a8d88f762c78caa2d364c111089db81717beJingoo Han		dev_get_platdata(&func->dev);
369911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	struct hwbus_priv *self;
370a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	int status;
371a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
372a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Sanity check speed */
373a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (func->max_speed_hz > 52000000)
374a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		func->max_speed_hz = 52000000;
375a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (func->max_speed_hz < 1000000)
376a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		func->max_speed_hz = 1000000;
377a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
378a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* Fix up transfer size */
379a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (plat_data->spi_bits_per_word)
380a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		func->bits_per_word = plat_data->spi_bits_per_word;
381a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (!func->bits_per_word)
382a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		func->bits_per_word = 16;
383a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
384a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* And finally.. */
385a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	func->mode = SPI_MODE_0;
386a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
387a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n",
388a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		func->chip_select, func->mode, func->bits_per_word,
389a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		func->max_speed_hz);
390a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
391a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (cw1200_spi_on(plat_data)) {
392a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_err("spi_on() failed!\n");
393a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return -1;
394a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
395a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
396a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (spi_setup(func)) {
397a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		pr_err("spi_setup() failed!\n");
398a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return -1;
399a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
400a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
40126c0604628f85d435a664f52fd2ca30aab812266Himangi Saraogi	self = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL);
402a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (!self) {
403911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy		pr_err("Can't allocate SPI hwbus_priv.");
404a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return -ENOMEM;
405a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
406a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
407a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	self->pdata = plat_data;
408a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	self->func = func;
409a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spin_lock_init(&self->lock);
410a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
411a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	spi_set_drvdata(func, self);
412a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
41385ba8f529c57ac6e2fca9be0d9e17920a1afb2e8Solomon Peachy	init_waitqueue_head(&self->wq);
41485ba8f529c57ac6e2fca9be0d9e17920a1afb2e8Solomon Peachy
415a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	status = cw1200_spi_irq_subscribe(self);
416a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
417911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	status = cw1200_core_probe(&cw1200_spi_hwbus_ops,
418a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				   self, &func->dev, &self->core,
419a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				   self->pdata->ref_clk,
420a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				   self->pdata->macaddr,
421a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				   self->pdata->sdd_file,
422a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy				   self->pdata->have_5ghz);
423a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
424a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (status) {
425a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		cw1200_spi_irq_unsubscribe(self);
426a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		cw1200_spi_off(plat_data);
427a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
428a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
429a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return status;
430a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
431a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
432a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy/* Disconnect Function to be called by SPI stack when device is disconnected */
433a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_spi_disconnect(struct spi_device *func)
434a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
435911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	struct hwbus_priv *self = spi_get_drvdata(func);
436a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
437a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (self) {
438a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		cw1200_spi_irq_unsubscribe(self);
439a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		if (self->core) {
440a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			cw1200_core_release(self->core);
441a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy			self->core = NULL;
442a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		}
443a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	}
4443ec8a8d88f762c78caa2d364c111089db81717beJingoo Han	cw1200_spi_off(dev_get_platdata(&func->dev));
445a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
446a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return 0;
447a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
448a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
4494e17b87e792ed19e75a96eea618b90510265120cSolomon Peachy#ifdef CONFIG_PM
450a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_spi_suspend(struct device *dev, pm_message_t state)
451a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
452911373cca1b45571b62938f8f19cec24cb102471Solomon Peachy	struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev));
453a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
454a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	if (!cw1200_can_suspend(self->core))
455a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		return -EAGAIN;
456a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
457a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	/* XXX notify host that we have to keep CW1200 powered on? */
458a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return 0;
459a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
460a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
461a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic int cw1200_spi_resume(struct device *dev)
462a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy{
463a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	return 0;
464a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy}
4654e17b87e792ed19e75a96eea618b90510265120cSolomon Peachy#endif
466a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
467a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachystatic struct spi_driver spi_driver = {
468a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	.probe		= cw1200_spi_probe,
469a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	.remove		= cw1200_spi_disconnect,
470a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	.driver = {
471a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.name		= "cw1200_wlan_spi",
472a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.bus            = &spi_bus_type,
473a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.owner          = THIS_MODULE,
4744e17b87e792ed19e75a96eea618b90510265120cSolomon Peachy#ifdef CONFIG_PM
475a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.suspend        = cw1200_spi_suspend,
476a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy		.resume         = cw1200_spi_resume,
4774e17b87e792ed19e75a96eea618b90510265120cSolomon Peachy#endif
478a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy	},
479a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy};
480a910e4a94f6923c8c988565525f017f687bf7205Solomon Peachy
481d071c0430c6ae42fedec78f1ec2e37603adb8c78Wei Yongjunmodule_spi_driver(spi_driver);
482