| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Mac80211 SPI driver for ST-Ericsson CW1200 device | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2011, Sagrad Inc. | 
					
						
							|  |  |  |  * Author:  Solomon Peachy <speachy@sagrad.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Based on cw1200_sdio.c | 
					
						
							|  |  |  |  * Copyright (c) 2010, ST-Ericsson | 
					
						
							|  |  |  |  * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 as | 
					
						
							|  |  |  |  * published by the Free Software Foundation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/gpio.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <linux/spinlock.h>
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <net/mac80211.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/spi/spi.h>
 | 
					
						
							|  |  |  | #include <linux/device.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "cw1200.h"
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | #include "hwbus.h"
 | 
					
						
							| 
									
										
										
										
											2013-06-02 11:35:32 -04:00
										 |  |  | #include <linux/platform_data/net-cw1200.h>
 | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | #include "hwio.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | MODULE_ALIAS("spi:cw1200_wlan_spi"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* #define SPI_DEBUG */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | struct hwbus_priv { | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	struct spi_device	*func; | 
					
						
							|  |  |  | 	struct cw1200_common	*core; | 
					
						
							|  |  |  | 	const struct cw1200_platform_data_spi *pdata; | 
					
						
							|  |  |  | 	spinlock_t		lock; /* Serialize all bus operations */ | 
					
						
							| 
									
										
										
										
											2013-08-27 20:29:47 -04:00
										 |  |  | 	wait_queue_head_t       wq; | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	int claimed; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
 | 
					
						
							|  |  |  | #define SET_WRITE 0x7FFF /* usage: and operation */
 | 
					
						
							|  |  |  | #define SET_READ 0x8000  /* usage: or operation */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-11 09:49:40 -04:00
										 |  |  | /* Notes on byte ordering:
 | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  |    LE:  B0 B1 B2 B3 | 
					
						
							|  |  |  |    BE:  B3 B2 B1 B0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    Hardware expects 32-bit data to be written as 16-bit BE words: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    B1 B0 B3 B2 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self, | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 				     unsigned int addr, | 
					
						
							|  |  |  | 				     void *dst, int count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret, i; | 
					
						
							| 
									
										
										
										
											2013-06-20 23:03:12 -04:00
										 |  |  | 	u16 regaddr; | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	struct spi_message      m; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct spi_transfer     t_addr = { | 
					
						
							|  |  |  | 		.tx_buf         = ®addr, | 
					
						
							|  |  |  | 		.len            = sizeof(regaddr), | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	struct spi_transfer     t_msg = { | 
					
						
							|  |  |  | 		.rx_buf         = dst, | 
					
						
							|  |  |  | 		.len            = count, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; | 
					
						
							|  |  |  | 	regaddr |= SET_READ; | 
					
						
							|  |  |  | 	regaddr |= (count>>1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef SPI_DEBUG
 | 
					
						
							| 
									
										
										
										
											2013-06-20 23:03:12 -04:00
										 |  |  | 	pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, regaddr); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-20 23:03:12 -04:00
										 |  |  | 	/* Header is LE16 */ | 
					
						
							|  |  |  | 	regaddr = cpu_to_le16(regaddr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* We have to byteswap if the SPI bus is limited to 8b operation
 | 
					
						
							|  |  |  | 	   or we are running on a Big Endian system | 
					
						
							|  |  |  | 	*/ | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | #if defined(__LITTLE_ENDIAN)
 | 
					
						
							|  |  |  | 	if (self->func->bits_per_word == 8) | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 		regaddr = swab16(regaddr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spi_message_init(&m); | 
					
						
							|  |  |  | 	spi_message_add_tail(&t_addr, &m); | 
					
						
							|  |  |  | 	spi_message_add_tail(&t_msg, &m); | 
					
						
							|  |  |  | 	ret = spi_sync(self->func, &m); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef SPI_DEBUG
 | 
					
						
							|  |  |  | 	pr_info("READ : "); | 
					
						
							|  |  |  | 	for (i = 0; i < t_addr.len; i++) | 
					
						
							|  |  |  | 		printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); | 
					
						
							|  |  |  | 	printk(" : "); | 
					
						
							|  |  |  | 	for (i = 0; i < t_msg.len; i++) | 
					
						
							|  |  |  | 		printk("%02x ", ((u8 *)t_msg.rx_buf)[i]); | 
					
						
							|  |  |  | 	printk("\n"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-20 23:03:12 -04:00
										 |  |  | 	/* We have to byteswap if the SPI bus is limited to 8b operation
 | 
					
						
							|  |  |  | 	   or we are running on a Big Endian system | 
					
						
							|  |  |  | 	*/ | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | #if defined(__LITTLE_ENDIAN)
 | 
					
						
							|  |  |  | 	if (self->func->bits_per_word == 8) | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		uint16_t *buf = (uint16_t *)dst; | 
					
						
							|  |  |  | 		for (i = 0; i < ((count + 1) >> 1); i++) | 
					
						
							|  |  |  | 			buf[i] = swab16(buf[i]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | static int cw1200_spi_memcpy_toio(struct hwbus_priv *self, | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 				   unsigned int addr, | 
					
						
							|  |  |  | 				   const void *src, int count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int rval, i; | 
					
						
							| 
									
										
										
										
											2013-06-20 23:03:12 -04:00
										 |  |  | 	u16 regaddr; | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	struct spi_transfer     t_addr = { | 
					
						
							|  |  |  | 		.tx_buf         = ®addr, | 
					
						
							|  |  |  | 		.len            = sizeof(regaddr), | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	struct spi_transfer     t_msg = { | 
					
						
							|  |  |  | 		.tx_buf         = src, | 
					
						
							|  |  |  | 		.len            = count, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	struct spi_message      m; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; | 
					
						
							|  |  |  | 	regaddr &= SET_WRITE; | 
					
						
							|  |  |  | 	regaddr |= (count>>1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef SPI_DEBUG
 | 
					
						
							| 
									
										
										
										
											2013-06-20 23:03:12 -04:00
										 |  |  | 	pr_info("WRITE: %04d  to  0x%02x (%04x)\n", count, addr, regaddr); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-20 23:03:12 -04:00
										 |  |  | 	/* Header is LE16 */ | 
					
						
							|  |  |  | 	regaddr = cpu_to_le16(regaddr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* We have to byteswap if the SPI bus is limited to 8b operation
 | 
					
						
							|  |  |  | 	   or we are running on a Big Endian system | 
					
						
							|  |  |  | 	*/ | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | #if defined(__LITTLE_ENDIAN)
 | 
					
						
							|  |  |  | 	if (self->func->bits_per_word == 8) | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		uint16_t *buf = (uint16_t *)src; | 
					
						
							| 
									
										
										
										
											2013-06-20 23:03:12 -04:00
										 |  |  | 	        regaddr = swab16(regaddr); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 		for (i = 0; i < ((count + 1) >> 1); i++) | 
					
						
							|  |  |  | 			buf[i] = swab16(buf[i]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef SPI_DEBUG
 | 
					
						
							|  |  |  | 	pr_info("WRITE: "); | 
					
						
							|  |  |  | 	for (i = 0; i < t_addr.len; i++) | 
					
						
							|  |  |  | 		printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); | 
					
						
							|  |  |  | 	printk(" : "); | 
					
						
							|  |  |  | 	for (i = 0; i < t_msg.len; i++) | 
					
						
							|  |  |  | 		printk("%02x ", ((u8 *)t_msg.tx_buf)[i]); | 
					
						
							|  |  |  | 	printk("\n"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spi_message_init(&m); | 
					
						
							|  |  |  | 	spi_message_add_tail(&t_addr, &m); | 
					
						
							|  |  |  | 	spi_message_add_tail(&t_msg, &m); | 
					
						
							|  |  |  | 	rval = spi_sync(self->func, &m); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef SPI_DEBUG
 | 
					
						
							|  |  |  | 	pr_info("WROTE: %d\n", m.actual_length); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if defined(__LITTLE_ENDIAN)
 | 
					
						
							|  |  |  | 	/* We have to byteswap if the SPI bus is limited to 8b operation */ | 
					
						
							|  |  |  | 	if (self->func->bits_per_word == 8) | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		uint16_t *buf = (uint16_t *)src; | 
					
						
							|  |  |  | 		for (i = 0; i < ((count + 1) >> 1); i++) | 
					
						
							|  |  |  | 			buf[i] = swab16(buf[i]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return rval; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | static void cw1200_spi_lock(struct hwbus_priv *self) | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-27 20:29:47 -04:00
										 |  |  | 	DECLARE_WAITQUEUE(wait, current); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	might_sleep(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-27 20:29:47 -04:00
										 |  |  | 	add_wait_queue(&self->wq, &wait); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	spin_lock_irqsave(&self->lock, flags); | 
					
						
							|  |  |  | 	while (1) { | 
					
						
							|  |  |  | 		set_current_state(TASK_UNINTERRUPTIBLE); | 
					
						
							|  |  |  | 		if (!self->claimed) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		spin_unlock_irqrestore(&self->lock, flags); | 
					
						
							|  |  |  | 		schedule(); | 
					
						
							|  |  |  | 		spin_lock_irqsave(&self->lock, flags); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	set_current_state(TASK_RUNNING); | 
					
						
							|  |  |  | 	self->claimed = 1; | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&self->lock, flags); | 
					
						
							| 
									
										
										
										
											2013-08-27 20:29:47 -04:00
										 |  |  | 	remove_wait_queue(&self->wq, &wait); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | static void cw1200_spi_unlock(struct hwbus_priv *self) | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&self->lock, flags); | 
					
						
							|  |  |  | 	self->claimed = 0; | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&self->lock, flags); | 
					
						
							| 
									
										
										
										
											2013-08-27 20:29:47 -04:00
										 |  |  | 	wake_up(&self->wq); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | 	struct hwbus_priv *self = dev_id; | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (self->core) { | 
					
						
							| 
									
										
										
										
											2013-10-09 12:15:11 -04:00
										 |  |  | 		cw1200_spi_lock(self); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 		cw1200_irq_handler(self->core); | 
					
						
							| 
									
										
										
										
											2013-10-09 12:15:11 -04:00
										 |  |  | 		cw1200_spi_unlock(self); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 		return IRQ_HANDLED; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return IRQ_NONE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | static int cw1200_spi_irq_subscribe(struct hwbus_priv *self) | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pr_debug("SW IRQ subscribe\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-23 16:00:04 -04:00
										 |  |  | 	ret = request_threaded_irq(self->func->irq, NULL, | 
					
						
							|  |  |  | 				   cw1200_spi_irq_handler, | 
					
						
							|  |  |  | 				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT, | 
					
						
							|  |  |  | 				   "cw1200_wlan_irq", self); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	if (WARN_ON(ret < 0)) | 
					
						
							|  |  |  | 		goto exit; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = enable_irq_wake(self->func->irq); | 
					
						
							|  |  |  | 	if (WARN_ON(ret)) | 
					
						
							|  |  |  | 		goto free_irq; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | free_irq: | 
					
						
							|  |  |  | 	free_irq(self->func->irq, self); | 
					
						
							|  |  |  | exit: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self) | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-09-23 16:00:03 -04:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	pr_debug("SW IRQ unsubscribe\n"); | 
					
						
							|  |  |  | 	disable_irq_wake(self->func->irq); | 
					
						
							|  |  |  | 	free_irq(self->func->irq, self); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-23 16:00:03 -04:00
										 |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-06-02 09:53:03 -04:00
										 |  |  | 	if (pdata->reset) { | 
					
						
							|  |  |  | 		gpio_set_value(pdata->reset, 0); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 		msleep(30); /* Min is 2 * CLK32K cycles */ | 
					
						
							| 
									
										
										
										
											2013-06-02 09:53:03 -04:00
										 |  |  | 		gpio_free(pdata->reset); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pdata->power_ctrl) | 
					
						
							|  |  |  | 		pdata->power_ctrl(pdata, false); | 
					
						
							|  |  |  | 	if (pdata->clk_ctrl) | 
					
						
							|  |  |  | 		pdata->clk_ctrl(pdata, false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Ensure I/Os are pulled low */ | 
					
						
							| 
									
										
										
										
											2013-06-02 09:53:03 -04:00
										 |  |  | 	if (pdata->reset) { | 
					
						
							|  |  |  | 		gpio_request(pdata->reset, "cw1200_wlan_reset"); | 
					
						
							|  |  |  | 		gpio_direction_output(pdata->reset, 0); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-06-02 09:53:03 -04:00
										 |  |  | 	if (pdata->powerup) { | 
					
						
							|  |  |  | 		gpio_request(pdata->powerup, "cw1200_wlan_powerup"); | 
					
						
							|  |  |  | 		gpio_direction_output(pdata->powerup, 0); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-06-02 09:53:03 -04:00
										 |  |  | 	if (pdata->reset || pdata->powerup) | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 		msleep(10); /* Settle time? */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Enable 3v3 and 1v8 to hardware */ | 
					
						
							|  |  |  | 	if (pdata->power_ctrl) { | 
					
						
							|  |  |  | 		if (pdata->power_ctrl(pdata, true)) { | 
					
						
							|  |  |  | 			pr_err("power_ctrl() failed!\n"); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Enable CLK32K */ | 
					
						
							|  |  |  | 	if (pdata->clk_ctrl) { | 
					
						
							|  |  |  | 		if (pdata->clk_ctrl(pdata, true)) { | 
					
						
							|  |  |  | 			pr_err("clk_ctrl() failed!\n"); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		msleep(10); /* Delay until clock is stable for 2 cycles */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Enable POWERUP signal */ | 
					
						
							| 
									
										
										
										
											2013-06-02 09:53:03 -04:00
										 |  |  | 	if (pdata->powerup) { | 
					
						
							|  |  |  | 		gpio_set_value(pdata->powerup, 1); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 		msleep(250); /* or more..? */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* Enable RSTn signal */ | 
					
						
							| 
									
										
										
										
											2013-06-02 09:53:03 -04:00
										 |  |  | 	if (pdata->reset) { | 
					
						
							|  |  |  | 		gpio_set_value(pdata->reset, 1); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 		msleep(50); /* Or more..? */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size) | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	return size & 1 ? size + 1 : size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend) | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	return irq_set_irq_wake(self->func->irq, suspend); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | static struct hwbus_ops cw1200_spi_hwbus_ops = { | 
					
						
							|  |  |  | 	.hwbus_memcpy_fromio	= cw1200_spi_memcpy_fromio, | 
					
						
							|  |  |  | 	.hwbus_memcpy_toio	= cw1200_spi_memcpy_toio, | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	.lock			= cw1200_spi_lock, | 
					
						
							|  |  |  | 	.unlock			= cw1200_spi_unlock, | 
					
						
							|  |  |  | 	.align_size		= cw1200_spi_align_size, | 
					
						
							|  |  |  | 	.power_mgmt		= cw1200_spi_pm, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Probe Function to be called by SPI stack when device is discovered */ | 
					
						
							|  |  |  | static int cw1200_spi_probe(struct spi_device *func) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const struct cw1200_platform_data_spi *plat_data = | 
					
						
							| 
									
										
										
										
											2013-09-10 17:55:41 +09:00
										 |  |  | 		dev_get_platdata(&func->dev); | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | 	struct hwbus_priv *self; | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	int status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Sanity check speed */ | 
					
						
							|  |  |  | 	if (func->max_speed_hz > 52000000) | 
					
						
							|  |  |  | 		func->max_speed_hz = 52000000; | 
					
						
							|  |  |  | 	if (func->max_speed_hz < 1000000) | 
					
						
							|  |  |  | 		func->max_speed_hz = 1000000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Fix up transfer size */ | 
					
						
							|  |  |  | 	if (plat_data->spi_bits_per_word) | 
					
						
							|  |  |  | 		func->bits_per_word = plat_data->spi_bits_per_word; | 
					
						
							|  |  |  | 	if (!func->bits_per_word) | 
					
						
							|  |  |  | 		func->bits_per_word = 16; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* And finally.. */ | 
					
						
							|  |  |  | 	func->mode = SPI_MODE_0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n", | 
					
						
							|  |  |  | 		func->chip_select, func->mode, func->bits_per_word, | 
					
						
							|  |  |  | 		func->max_speed_hz); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (cw1200_spi_on(plat_data)) { | 
					
						
							|  |  |  | 		pr_err("spi_on() failed!\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (spi_setup(func)) { | 
					
						
							|  |  |  | 		pr_err("spi_setup() failed!\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	self = kzalloc(sizeof(*self), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!self) { | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | 		pr_err("Can't allocate SPI hwbus_priv."); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	self->pdata = plat_data; | 
					
						
							|  |  |  | 	self->func = func; | 
					
						
							|  |  |  | 	spin_lock_init(&self->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spi_set_drvdata(func, self); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-27 20:29:47 -04:00
										 |  |  | 	init_waitqueue_head(&self->wq); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	status = cw1200_spi_irq_subscribe(self); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | 	status = cw1200_core_probe(&cw1200_spi_hwbus_ops, | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 				   self, &func->dev, &self->core, | 
					
						
							|  |  |  | 				   self->pdata->ref_clk, | 
					
						
							|  |  |  | 				   self->pdata->macaddr, | 
					
						
							|  |  |  | 				   self->pdata->sdd_file, | 
					
						
							|  |  |  | 				   self->pdata->have_5ghz); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (status) { | 
					
						
							|  |  |  | 		cw1200_spi_irq_unsubscribe(self); | 
					
						
							|  |  |  | 		cw1200_spi_off(plat_data); | 
					
						
							|  |  |  | 		kfree(self); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return status; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Disconnect Function to be called by SPI stack when device is disconnected */ | 
					
						
							|  |  |  | static int cw1200_spi_disconnect(struct spi_device *func) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | 	struct hwbus_priv *self = spi_get_drvdata(func); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (self) { | 
					
						
							|  |  |  | 		cw1200_spi_irq_unsubscribe(self); | 
					
						
							|  |  |  | 		if (self->core) { | 
					
						
							|  |  |  | 			cw1200_core_release(self->core); | 
					
						
							|  |  |  | 			self->core = NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		kfree(self); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-09-10 17:55:41 +09:00
										 |  |  | 	cw1200_spi_off(dev_get_platdata(&func->dev)); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-29 22:22:05 -04:00
										 |  |  | #ifdef CONFIG_PM
 | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | static int cw1200_spi_suspend(struct device *dev, pm_message_t state) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-06-01 08:08:42 -04:00
										 |  |  | 	struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev)); | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!cw1200_can_suspend(self->core)) | 
					
						
							|  |  |  | 		return -EAGAIN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* XXX notify host that we have to keep CW1200 powered on? */ | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int cw1200_spi_resume(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-05-29 22:22:05 -04:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | static struct spi_driver spi_driver = { | 
					
						
							|  |  |  | 	.probe		= cw1200_spi_probe, | 
					
						
							|  |  |  | 	.remove		= cw1200_spi_disconnect, | 
					
						
							|  |  |  | 	.driver = { | 
					
						
							|  |  |  | 		.name		= "cw1200_wlan_spi", | 
					
						
							|  |  |  | 		.bus            = &spi_bus_type, | 
					
						
							|  |  |  | 		.owner          = THIS_MODULE, | 
					
						
							| 
									
										
										
										
											2013-05-29 22:22:05 -04:00
										 |  |  | #ifdef CONFIG_PM
 | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 		.suspend        = cw1200_spi_suspend, | 
					
						
							|  |  |  | 		.resume         = cw1200_spi_resume, | 
					
						
							| 
									
										
										
										
											2013-05-29 22:22:05 -04:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2013-05-24 20:04:38 -04:00
										 |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 19:42:54 +08:00
										 |  |  | module_spi_driver(spi_driver); |