forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
464 lines
10 KiB
464 lines
10 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* Mac80211 SPI driver for ST-Ericsson CW1200 device |
|
* |
|
* Copyright (c) 2011, Sagrad Inc. |
|
* Author: Solomon Peachy <[email protected]> |
|
* |
|
* Based on cw1200_sdio.c |
|
* Copyright (c) 2010, ST-Ericsson |
|
* Author: Dmitry Tarnyagin <[email protected]> |
|
*/ |
|
|
|
#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" |
|
#include "hwbus.h" |
|
#include <linux/platform_data/net-cw1200.h> |
|
#include "hwio.h" |
|
|
|
MODULE_AUTHOR("Solomon Peachy <[email protected]>"); |
|
MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver"); |
|
MODULE_LICENSE("GPL"); |
|
MODULE_ALIAS("spi:cw1200_wlan_spi"); |
|
|
|
/* #define SPI_DEBUG */ |
|
|
|
struct hwbus_priv { |
|
struct spi_device *func; |
|
struct cw1200_common *core; |
|
const struct cw1200_platform_data_spi *pdata; |
|
spinlock_t lock; /* Serialize all bus operations */ |
|
wait_queue_head_t wq; |
|
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 */ |
|
|
|
/* Notes on byte ordering: |
|
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 |
|
*/ |
|
|
|
static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self, |
|
unsigned int addr, |
|
void *dst, int count) |
|
{ |
|
int ret, i; |
|
u16 regaddr; |
|
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 |
|
pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, regaddr); |
|
#endif |
|
|
|
/* 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 |
|
*/ |
|
#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 |
|
|
|
/* We have to byteswap if the SPI bus is limited to 8b operation |
|
or we are running on a Big Endian system |
|
*/ |
|
#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; |
|
} |
|
|
|
static int cw1200_spi_memcpy_toio(struct hwbus_priv *self, |
|
unsigned int addr, |
|
const void *src, int count) |
|
{ |
|
int rval, i; |
|
u16 regaddr; |
|
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 |
|
pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr, regaddr); |
|
#endif |
|
|
|
/* 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 |
|
*/ |
|
#if defined(__LITTLE_ENDIAN) |
|
if (self->func->bits_per_word == 8) |
|
#endif |
|
{ |
|
uint16_t *buf = (uint16_t *)src; |
|
regaddr = swab16(regaddr); |
|
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; |
|
} |
|
|
|
static void cw1200_spi_lock(struct hwbus_priv *self) |
|
{ |
|
unsigned long flags; |
|
|
|
DECLARE_WAITQUEUE(wait, current); |
|
|
|
might_sleep(); |
|
|
|
add_wait_queue(&self->wq, &wait); |
|
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); |
|
remove_wait_queue(&self->wq, &wait); |
|
|
|
return; |
|
} |
|
|
|
static void cw1200_spi_unlock(struct hwbus_priv *self) |
|
{ |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&self->lock, flags); |
|
self->claimed = 0; |
|
spin_unlock_irqrestore(&self->lock, flags); |
|
wake_up(&self->wq); |
|
|
|
return; |
|
} |
|
|
|
static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id) |
|
{ |
|
struct hwbus_priv *self = dev_id; |
|
|
|
if (self->core) { |
|
cw1200_spi_lock(self); |
|
cw1200_irq_handler(self->core); |
|
cw1200_spi_unlock(self); |
|
return IRQ_HANDLED; |
|
} else { |
|
return IRQ_NONE; |
|
} |
|
} |
|
|
|
static int cw1200_spi_irq_subscribe(struct hwbus_priv *self) |
|
{ |
|
int ret; |
|
|
|
pr_debug("SW IRQ subscribe\n"); |
|
|
|
ret = request_threaded_irq(self->func->irq, NULL, |
|
cw1200_spi_irq_handler, |
|
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, |
|
"cw1200_wlan_irq", self); |
|
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; |
|
} |
|
|
|
static void cw1200_spi_irq_unsubscribe(struct hwbus_priv *self) |
|
{ |
|
pr_debug("SW IRQ unsubscribe\n"); |
|
disable_irq_wake(self->func->irq); |
|
free_irq(self->func->irq, self); |
|
} |
|
|
|
static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) |
|
{ |
|
if (pdata->reset) { |
|
gpio_set_value(pdata->reset, 0); |
|
msleep(30); /* Min is 2 * CLK32K cycles */ |
|
gpio_free(pdata->reset); |
|
} |
|
|
|
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 */ |
|
if (pdata->reset) { |
|
gpio_request(pdata->reset, "cw1200_wlan_reset"); |
|
gpio_direction_output(pdata->reset, 0); |
|
} |
|
if (pdata->powerup) { |
|
gpio_request(pdata->powerup, "cw1200_wlan_powerup"); |
|
gpio_direction_output(pdata->powerup, 0); |
|
} |
|
if (pdata->reset || pdata->powerup) |
|
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 */ |
|
if (pdata->powerup) { |
|
gpio_set_value(pdata->powerup, 1); |
|
msleep(250); /* or more..? */ |
|
} |
|
/* Enable RSTn signal */ |
|
if (pdata->reset) { |
|
gpio_set_value(pdata->reset, 1); |
|
msleep(50); /* Or more..? */ |
|
} |
|
return 0; |
|
} |
|
|
|
static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size) |
|
{ |
|
return size & 1 ? size + 1 : size; |
|
} |
|
|
|
static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend) |
|
{ |
|
return irq_set_irq_wake(self->func->irq, suspend); |
|
} |
|
|
|
static const struct hwbus_ops cw1200_spi_hwbus_ops = { |
|
.hwbus_memcpy_fromio = cw1200_spi_memcpy_fromio, |
|
.hwbus_memcpy_toio = cw1200_spi_memcpy_toio, |
|
.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 = |
|
dev_get_platdata(&func->dev); |
|
struct hwbus_priv *self; |
|
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 = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL); |
|
if (!self) { |
|
pr_err("Can't allocate SPI hwbus_priv."); |
|
return -ENOMEM; |
|
} |
|
|
|
self->pdata = plat_data; |
|
self->func = func; |
|
spin_lock_init(&self->lock); |
|
|
|
spi_set_drvdata(func, self); |
|
|
|
init_waitqueue_head(&self->wq); |
|
|
|
status = cw1200_spi_irq_subscribe(self); |
|
|
|
status = cw1200_core_probe(&cw1200_spi_hwbus_ops, |
|
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); |
|
} |
|
|
|
return status; |
|
} |
|
|
|
/* Disconnect Function to be called by SPI stack when device is disconnected */ |
|
static int cw1200_spi_disconnect(struct spi_device *func) |
|
{ |
|
struct hwbus_priv *self = spi_get_drvdata(func); |
|
|
|
if (self) { |
|
cw1200_spi_irq_unsubscribe(self); |
|
if (self->core) { |
|
cw1200_core_release(self->core); |
|
self->core = NULL; |
|
} |
|
} |
|
cw1200_spi_off(dev_get_platdata(&func->dev)); |
|
|
|
return 0; |
|
} |
|
|
|
static int __maybe_unused cw1200_spi_suspend(struct device *dev) |
|
{ |
|
struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev)); |
|
|
|
if (!cw1200_can_suspend(self->core)) |
|
return -EAGAIN; |
|
|
|
/* XXX notify host that we have to keep CW1200 powered on? */ |
|
return 0; |
|
} |
|
|
|
static SIMPLE_DEV_PM_OPS(cw1200_pm_ops, cw1200_spi_suspend, NULL); |
|
|
|
static struct spi_driver spi_driver = { |
|
.probe = cw1200_spi_probe, |
|
.remove = cw1200_spi_disconnect, |
|
.driver = { |
|
.name = "cw1200_wlan_spi", |
|
.pm = IS_ENABLED(CONFIG_PM) ? &cw1200_pm_ops : NULL, |
|
}, |
|
}; |
|
|
|
module_spi_driver(spi_driver);
|
|
|