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 = ®addr, 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 = ®addr, 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