Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Solomon Peachy | 1971 | 97.91% | 9 | 50.00% |
Lars-Peter Clausen | 12 | 0.60% | 1 | 5.56% |
Arnd Bergmann | 9 | 0.45% | 1 | 5.56% |
Jingoo Han | 8 | 0.40% | 1 | 5.56% |
Himangi Saraogi | 6 | 0.30% | 1 | 5.56% |
Wei Yongjun | 2 | 0.10% | 1 | 5.56% |
Thomas Gleixner | 2 | 0.10% | 1 | 5.56% |
Bhumika Goyal | 1 | 0.05% | 1 | 5.56% |
Jason Yan | 1 | 0.05% | 1 | 5.56% |
Uwe Kleine-König | 1 | 0.05% | 1 | 5.56% |
Total | 2013 | 18 |
// SPDX-License-Identifier: GPL-2.0-only /* * 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> */ #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 <speachy@sagrad.com>"); 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 void 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)); } 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);
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1