Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  cw1200_spi.c   Sprache: unbekannt

 
// 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/consumer.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;
 struct gpio_desc *reset;
 struct gpio_desc *powerup;
 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 = (__force u16)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 = (__force u16)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(struct hwbus_priv *self, const struct cw1200_platform_data_spi *pdata)
{
 if (self->reset) {
  /* Assert RESET, note active low */
  gpiod_set_value(self->reset, 1);
  msleep(30); /* Min is 2 * CLK32K cycles */
 }

 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(struct hwbus_priv *self, const struct cw1200_platform_data_spi *pdata)
{
 /* Ensure I/Os are pulled low */
 gpiod_direction_output(self->reset, 1); /* Active low */
 gpiod_direction_output(self->powerup, 0);
 if (self->reset || self->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 (self->powerup) {
  gpiod_set_value(self->powerup, 1);
  msleep(250); /* or more..? */
 }
 /* Assert RSTn signal, note active low */
 if (self->reset) {
  gpiod_set_value(self->reset, 0);
  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",
  spi_get_chipselect(func, 0), func->mode, func->bits_per_word,
  func->max_speed_hz);

 self = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL);
 if (!self) {
  pr_err("Can't allocate SPI hwbus_priv.");
  return -ENOMEM;
 }

 /* Request reset asserted */
 self->reset = devm_gpiod_get_optional(&func->dev, "reset", GPIOD_OUT_HIGH);
 if (IS_ERR(self->reset))
  return dev_err_probe(&func->dev, PTR_ERR(self->reset),
         "could not get reset GPIO\n");
 gpiod_set_consumer_name(self->reset, "cw1200_wlan_reset");

 self->powerup = devm_gpiod_get_optional(&func->dev, "powerup", GPIOD_OUT_LOW);
 if (IS_ERR(self->powerup))
  return dev_err_probe(&func->dev, PTR_ERR(self->powerup),
         "could not get powerup GPIO\n");
 gpiod_set_consumer_name(self->reset, "cw1200_wlan_powerup");

 if (cw1200_spi_on(self, plat_data)) {
  pr_err("spi_on() failed!\n");
  return -ENODEV;
 }

 if (spi_setup(func)) {
  pr_err("spi_setup() failed!\n");
  return -ENODEV;
 }

 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(self, 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(self, 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 (self && !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);

Messung V0.5 in Prozent
C=95 H=91 G=92

[zur Elbe Produktseite wechseln0.12QuellennavigatorsAnalyse erneut starten2026-06-07]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=434850
#Domains=655579