Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/spi/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 10 kB image not shown  

Quelle  spi-st-ssc4.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (c) 2008-2014 STMicroelectronics Limited
 *
 *  Author: Angus Clark <Angus.Clark@st.com>
 *          Patrice Chotard <patrice.chotard@st.com>
 *          Lee Jones <lee.jones@linaro.org>
 *
 *  SPI host mode controller driver, used in STMicroelectronics devices.
 */


#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>

/* SSC registers */
#define SSC_BRG    0x000
#define SSC_TBUF   0x004
#define SSC_RBUF   0x008
#define SSC_CTL    0x00C
#define SSC_IEN    0x010
#define SSC_I2C    0x018

/* SSC Control */
#define SSC_CTL_DATA_WIDTH_9  0x8
#define SSC_CTL_DATA_WIDTH_MSK  0xf
#define SSC_CTL_BM   0xf
#define SSC_CTL_HB   BIT(4)
#define SSC_CTL_PH   BIT(5)
#define SSC_CTL_PO   BIT(6)
#define SSC_CTL_SR   BIT(7)
#define SSC_CTL_MS   BIT(8)
#define SSC_CTL_EN   BIT(9)
#define SSC_CTL_LPB   BIT(10)
#define SSC_CTL_EN_TX_FIFO  BIT(11)
#define SSC_CTL_EN_RX_FIFO  BIT(12)
#define SSC_CTL_EN_CLST_RX  BIT(13)

/* SSC Interrupt Enable */
#define SSC_IEN_TEEN   BIT(2)

#define FIFO_SIZE   8

struct spi_st {
 /* SSC SPI Controller */
 void __iomem  *base;
 struct clk  *clk;
 struct device  *dev;

 /* SSC SPI current transaction */
 const u8  *tx_ptr;
 u8   *rx_ptr;
 u16   bytes_per_word;
 unsigned int  words_remaining;
 unsigned int  baud;
 struct completion done;
};

/* Load the TX FIFO */
static void ssc_write_tx_fifo(struct spi_st *spi_st)
{
 unsigned int count, i;
 uint32_t word = 0;

 if (spi_st->words_remaining > FIFO_SIZE)
  count = FIFO_SIZE;
 else
  count = spi_st->words_remaining;

 for (i = 0; i < count; i++) {
  if (spi_st->tx_ptr) {
   if (spi_st->bytes_per_word == 1) {
    word = *spi_st->tx_ptr++;
   } else {
    word = *spi_st->tx_ptr++;
    word = *spi_st->tx_ptr++ | (word << 8);
   }
  }
  writel_relaxed(word, spi_st->base + SSC_TBUF);
 }
}

/* Read the RX FIFO */
static void ssc_read_rx_fifo(struct spi_st *spi_st)
{
 unsigned int count, i;
 uint32_t word = 0;

 if (spi_st->words_remaining > FIFO_SIZE)
  count = FIFO_SIZE;
 else
  count = spi_st->words_remaining;

 for (i = 0; i < count; i++) {
  word = readl_relaxed(spi_st->base + SSC_RBUF);

  if (spi_st->rx_ptr) {
   if (spi_st->bytes_per_word == 1) {
    *spi_st->rx_ptr++ = (uint8_t)word;
   } else {
    *spi_st->rx_ptr++ = (word >> 8);
    *spi_st->rx_ptr++ = word & 0xff;
   }
  }
 }
 spi_st->words_remaining -= count;
}

static int spi_st_transfer_one(struct spi_controller *host,
          struct spi_device *spi, struct spi_transfer *t)
{
 struct spi_st *spi_st = spi_controller_get_devdata(host);
 uint32_t ctl = 0;

 /* Setup transfer */
 spi_st->tx_ptr = t->tx_buf;
 spi_st->rx_ptr = t->rx_buf;

 if (spi->bits_per_word > 8) {
  /*
 * Anything greater than 8 bits-per-word requires 2
 * bytes-per-word in the RX/TX buffers
 */

  spi_st->bytes_per_word = 2;
  spi_st->words_remaining = t->len / 2;

 } else if (spi->bits_per_word == 8 && !(t->len & 0x1)) {
  /*
 * If transfer is even-length, and 8 bits-per-word, then
 * implement as half-length 16 bits-per-word transfer
 */

  spi_st->bytes_per_word = 2;
  spi_st->words_remaining = t->len / 2;

  /* Set SSC_CTL to 16 bits-per-word */
  ctl = readl_relaxed(spi_st->base + SSC_CTL);
  writel_relaxed((ctl | 0xf), spi_st->base + SSC_CTL);

  readl_relaxed(spi_st->base + SSC_RBUF);

 } else {
  spi_st->bytes_per_word = 1;
  spi_st->words_remaining = t->len;
 }

 reinit_completion(&spi_st->done);

 /* Start transfer by writing to the TX FIFO */
 ssc_write_tx_fifo(spi_st);
 writel_relaxed(SSC_IEN_TEEN, spi_st->base + SSC_IEN);

 /* Wait for transfer to complete */
 wait_for_completion(&spi_st->done);

 /* Restore SSC_CTL if necessary */
 if (ctl)
  writel_relaxed(ctl, spi_st->base + SSC_CTL);

 spi_finalize_current_transfer(spi->controller);

 return t->len;
}

/* the spi->mode bits understood by this driver: */
#define MODEBITS  (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH)
static int spi_st_setup(struct spi_device *spi)
{
 struct spi_st *spi_st = spi_controller_get_devdata(spi->controller);
 u32 spi_st_clk, sscbrg, var;
 u32 hz = spi->max_speed_hz;

 if (!hz)  {
  dev_err(&spi->dev, "max_speed_hz unspecified\n");
  return -EINVAL;
 }

 if (!spi_get_csgpiod(spi, 0)) {
  dev_err(&spi->dev, "no valid gpio assigned\n");
  return -EINVAL;
 }

 spi_st_clk = clk_get_rate(spi_st->clk);

 /* Set SSC_BRF */
 sscbrg = spi_st_clk / (2 * hz);
 if (sscbrg < 0x07 || sscbrg > BIT(16)) {
  dev_err(&spi->dev,
   "baudrate %d outside valid range %d\n", sscbrg, hz);
  return -EINVAL;
 }

 spi_st->baud = spi_st_clk / (2 * sscbrg);
 if (sscbrg == BIT(16)) /* 16-bit counter wraps */
  sscbrg = 0x0;

 writel_relaxed(sscbrg, spi_st->base + SSC_BRG);

 dev_dbg(&spi->dev,
  "setting baudrate:target= %u hz, actual= %u hz, sscbrg= %u\n",
  hz, spi_st->baud, sscbrg);

 /* Set SSC_CTL and enable SSC */
 var = readl_relaxed(spi_st->base + SSC_CTL);
 var |= SSC_CTL_MS;

 if (spi->mode & SPI_CPOL)
  var |= SSC_CTL_PO;
 else
  var &= ~SSC_CTL_PO;

 if (spi->mode & SPI_CPHA)
  var |= SSC_CTL_PH;
 else
  var &= ~SSC_CTL_PH;

 if ((spi->mode & SPI_LSB_FIRST) == 0)
  var |= SSC_CTL_HB;
 else
  var &= ~SSC_CTL_HB;

 if (spi->mode & SPI_LOOP)
  var |= SSC_CTL_LPB;
 else
  var &= ~SSC_CTL_LPB;

 var &= ~SSC_CTL_DATA_WIDTH_MSK;
 var |= (spi->bits_per_word - 1);

 var |= SSC_CTL_EN_TX_FIFO | SSC_CTL_EN_RX_FIFO;
 var |= SSC_CTL_EN;

 writel_relaxed(var, spi_st->base + SSC_CTL);

 /* Clear the status register */
 readl_relaxed(spi_st->base + SSC_RBUF);

 return 0;
}

/* Interrupt fired when TX shift register becomes empty */
static irqreturn_t spi_st_irq(int irq, void *dev_id)
{
 struct spi_st *spi_st = (struct spi_st *)dev_id;

 /* Read RX FIFO */
 ssc_read_rx_fifo(spi_st);

 /* Fill TX FIFO */
 if (spi_st->words_remaining) {
  ssc_write_tx_fifo(spi_st);
 } else {
  /* TX/RX complete */
  writel_relaxed(0x0, spi_st->base + SSC_IEN);
  /*
 * read SSC_IEN to ensure that this bit is set
 * before re-enabling interrupt
 */

  readl(spi_st->base + SSC_IEN);
  complete(&spi_st->done);
 }

 return IRQ_HANDLED;
}

static int spi_st_probe(struct platform_device *pdev)
{
 struct device_node *np = pdev->dev.of_node;
 struct spi_controller *host;
 struct spi_st *spi_st;
 int irq, ret = 0;
 u32 var;

 host = spi_alloc_host(&pdev->dev, sizeof(*spi_st));
 if (!host)
  return -ENOMEM;

 host->dev.of_node  = np;
 host->mode_bits   = MODEBITS;
 host->setup   = spi_st_setup;
 host->transfer_one  = spi_st_transfer_one;
 host->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
 host->auto_runtime_pm  = true;
 host->bus_num   = pdev->id;
 host->use_gpio_descriptors = true;
 spi_st    = spi_controller_get_devdata(host);

 spi_st->clk = devm_clk_get(&pdev->dev, "ssc");
 if (IS_ERR(spi_st->clk)) {
  dev_err(&pdev->dev, "Unable to request clock\n");
  ret = PTR_ERR(spi_st->clk);
  goto put_host;
 }

 ret = clk_prepare_enable(spi_st->clk);
 if (ret)
  goto put_host;

 init_completion(&spi_st->done);

 /* Get resources */
 spi_st->base = devm_platform_ioremap_resource(pdev, 0);
 if (IS_ERR(spi_st->base)) {
  ret = PTR_ERR(spi_st->base);
  goto clk_disable;
 }

 /* Disable I2C and Reset SSC */
 writel_relaxed(0x0, spi_st->base + SSC_I2C);
 var = readw_relaxed(spi_st->base + SSC_CTL);
 var |= SSC_CTL_SR;
 writel_relaxed(var, spi_st->base + SSC_CTL);

 udelay(1);
 var = readl_relaxed(spi_st->base + SSC_CTL);
 var &= ~SSC_CTL_SR;
 writel_relaxed(var, spi_st->base + SSC_CTL);

 /* Set SSC into target mode before reconfiguring PIO pins */
 var = readl_relaxed(spi_st->base + SSC_CTL);
 var &= ~SSC_CTL_MS;
 writel_relaxed(var, spi_st->base + SSC_CTL);

 irq = irq_of_parse_and_map(np, 0);
 if (!irq) {
  dev_err(&pdev->dev, "IRQ missing or invalid\n");
  ret = -EINVAL;
  goto clk_disable;
 }

 ret = devm_request_irq(&pdev->dev, irq, spi_st_irq, 0,
          pdev->name, spi_st);
 if (ret) {
  dev_err(&pdev->dev, "Failed to request irq %d\n", irq);
  goto clk_disable;
 }

 /* by default the device is on */
 pm_runtime_set_active(&pdev->dev);
 pm_runtime_enable(&pdev->dev);

 platform_set_drvdata(pdev, host);

 ret = devm_spi_register_controller(&pdev->dev, host);
 if (ret) {
  dev_err(&pdev->dev, "Failed to register host\n");
  goto rpm_disable;
 }

 return 0;

rpm_disable:
 pm_runtime_disable(&pdev->dev);
clk_disable:
 clk_disable_unprepare(spi_st->clk);
put_host:
 spi_controller_put(host);
 return ret;
}

static void spi_st_remove(struct platform_device *pdev)
{
 struct spi_controller *host = platform_get_drvdata(pdev);
 struct spi_st *spi_st = spi_controller_get_devdata(host);

 pm_runtime_disable(&pdev->dev);

 clk_disable_unprepare(spi_st->clk);

 pinctrl_pm_select_sleep_state(&pdev->dev);
}

static int spi_st_runtime_suspend(struct device *dev)
{
 struct spi_controller *host = dev_get_drvdata(dev);
 struct spi_st *spi_st = spi_controller_get_devdata(host);

 writel_relaxed(0, spi_st->base + SSC_IEN);
 pinctrl_pm_select_sleep_state(dev);

 clk_disable_unprepare(spi_st->clk);

 return 0;
}

static int spi_st_runtime_resume(struct device *dev)
{
 struct spi_controller *host = dev_get_drvdata(dev);
 struct spi_st *spi_st = spi_controller_get_devdata(host);
 int ret;

 ret = clk_prepare_enable(spi_st->clk);
 pinctrl_pm_select_default_state(dev);

 return ret;
}

static int __maybe_unused spi_st_suspend(struct device *dev)
{
 struct spi_controller *host = dev_get_drvdata(dev);
 int ret;

 ret = spi_controller_suspend(host);
 if (ret)
  return ret;

 return pm_runtime_force_suspend(dev);
}

static int __maybe_unused spi_st_resume(struct device *dev)
{
 struct spi_controller *host = dev_get_drvdata(dev);
 int ret;

 ret = spi_controller_resume(host);
 if (ret)
  return ret;

 return pm_runtime_force_resume(dev);
}

static const struct dev_pm_ops spi_st_pm = {
 SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume)
 RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL)
};

static const struct of_device_id stm_spi_match[] = {
 { .compatible = "st,comms-ssc4-spi", },
 {},
};
MODULE_DEVICE_TABLE(of, stm_spi_match);

static struct platform_driver spi_st_driver = {
 .driver = {
  .name = "spi-st",
  .pm = pm_ptr(&spi_st_pm),
  .of_match_table = of_match_ptr(stm_spi_match),
 },
 .probe = spi_st_probe,
 .remove = spi_st_remove,
};
module_platform_driver(spi_st_driver);

MODULE_AUTHOR("Patrice Chotard ");
MODULE_DESCRIPTION("STM SSC SPI driver");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.