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 23 kB image not shown  

Quelle  spi-amlogic-spisg.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * Driver for Amlogic SPI communication Scatter-Gather Controller
 *
 * Copyright (C) 2025 Amlogic, Inc. All rights reserved
 *
 * Author: Sunny Luo <sunny.luo@amlogic.com>
 * Author: Xianwei Zhao <xianwei.zhao@amlogic.com>
 */


#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/reset.h>
#include <linux/regmap.h>

/* Register Map */
#define SPISG_REG_CFG_READY  0x00

#define SPISG_REG_CFG_SPI  0x04
#define CFG_BUS64_EN   BIT(0)
#define CFG_SLAVE_EN   BIT(1)
#define CFG_SLAVE_SELECT  GENMASK(3, 2)
#define CFG_SFLASH_WP   BIT(4)
#define CFG_SFLASH_HD   BIT(5)
/* start on vsync rising */
#define CFG_HW_POS   BIT(6)
/* start on vsync falling */
#define CFG_HW_NEG   BIT(7)

#define SPISG_REG_CFG_START  0x08
#define CFG_BLOCK_NUM   GENMASK(19, 0)
#define CFG_BLOCK_SIZE   GENMASK(22, 20)
#define CFG_DATA_COMMAND  BIT(23)
#define CFG_OP_MODE   GENMASK(25, 24)
#define CFG_RXD_MODE   GENMASK(27, 26)
#define CFG_TXD_MODE   GENMASK(29, 28)
#define CFG_EOC    BIT(30)
#define CFG_PEND   BIT(31)

#define SPISG_REG_CFG_BUS  0x0C
#define CFG_CLK_DIV   GENMASK(7, 0)
#define CLK_DIV_WIDTH   8
#define CFG_RX_TUNING   GENMASK(11, 8)
#define CFG_TX_TUNING   GENMASK(15, 12)
#define CFG_CS_SETUP   GENMASK(19, 16)
#define CFG_LANE   GENMASK(21, 20)
#define CFG_HALF_DUPLEX   BIT(22)
#define CFG_B_L_ENDIAN   BIT(23)
#define CFG_DC_MODE   BIT(24)
#define CFG_NULL_CTL   BIT(25)
#define CFG_DUMMY_CTL   BIT(26)
#define CFG_READ_TURN   GENMASK(28, 27)
#define CFG_KEEP_SS   BIT(29)
#define CFG_CPHA   BIT(30)
#define CFG_CPOL   BIT(31)

#define SPISG_REG_PIO_TX_DATA_L  0x10
#define SPISG_REG_PIO_TX_DATA_H  0x14
#define SPISG_REG_PIO_RX_DATA_L  0x18
#define SPISG_REG_PIO_RX_DATA_H  0x1C
#define SPISG_REG_MEM_TX_ADDR_L  0x10
#define SPISG_REG_MEM_TX_ADDR_H  0x14
#define SPISG_REG_MEM_RX_ADDR_L  0x18
#define SPISG_REG_MEM_RX_ADDR_H  0x1C
#define SPISG_REG_DESC_LIST_L  0x20
#define SPISG_REG_DESC_LIST_H  0x24
#define LIST_DESC_PENDING  BIT(31)
#define SPISG_REG_DESC_CURRENT_L 0x28
#define SPISG_REG_DESC_CURRENT_H 0x2c
#define SPISG_REG_IRQ_STS  0x30
#define SPISG_REG_IRQ_ENABLE  0x34
#define IRQ_RCH_DESC_EOC  BIT(0)
#define IRQ_RCH_DESC_INVALID  BIT(1)
#define IRQ_RCH_DESC_RESP  BIT(2)
#define IRQ_RCH_DATA_RESP  BIT(3)
#define IRQ_WCH_DESC_EOC  BIT(4)
#define IRQ_WCH_DESC_INVALID  BIT(5)
#define IRQ_WCH_DESC_RESP  BIT(6)
#define IRQ_WCH_DATA_RESP  BIT(7)
#define IRQ_DESC_ERR   BIT(8)
#define IRQ_SPI_READY   BIT(9)
#define IRQ_DESC_DONE   BIT(10)
#define IRQ_DESC_CHAIN_DONE  BIT(11)

#define SPISG_MAX_REG   0x40

#define SPISG_BLOCK_MAX   0x100000

#define SPISG_OP_MODE_WRITE_CMD  0
#define SPISG_OP_MODE_READ_STS  1
#define SPISG_OP_MODE_WRITE  2
#define SPISG_OP_MODE_READ  3

#define SPISG_DATA_MODE_NONE  0
#define SPISG_DATA_MODE_PIO  1
#define SPISG_DATA_MODE_MEM  2
#define SPISG_DATA_MODE_SG  3

#define SPISG_CLK_DIV_MAX  256
/* recommended by specification */
#define SPISG_CLK_DIV_MIN  4
#define DIV_NUM (SPISG_CLK_DIV_MAX - SPISG_CLK_DIV_MIN + 1)

#define SPISG_PCLK_RATE_MIN  24000000

#define SPISG_SINGLE_SPI  0
#define SPISG_DUAL_SPI   1
#define SPISG_QUAD_SPI   2

struct spisg_sg_link {
#define LINK_ADDR_VALID  BIT(0)
#define LINK_ADDR_EOC  BIT(1)
#define LINK_ADDR_IRQ  BIT(2)
#define LINK_ADDR_ACT  GENMASK(5, 3)
#define LINK_ADDR_RING  BIT(6)
#define LINK_ADDR_LEN  GENMASK(31, 8)
 u32   addr;
 u32   addr1;
};

struct spisg_descriptor {
 u32    cfg_start;
 u32    cfg_bus;
 u64    tx_paddr;
 u64    rx_paddr;
};

struct spisg_descriptor_extra {
 struct spisg_sg_link  *tx_ccsg;
 struct spisg_sg_link  *rx_ccsg;
 int    tx_ccsg_len;
 int    rx_ccsg_len;
};

struct spisg_device {
 struct spi_controller  *controller;
 struct platform_device  *pdev;
 struct regmap   *map;
 struct clk   *core;
 struct clk   *pclk;
 struct clk   *sclk;
 struct clk_div_table  *tbl;
 struct completion  completion;
 u32    status;
 u32    speed_hz;
 u32    effective_speed_hz;
 u32    bytes_per_word;
 u32    cfg_spi;
 u32    cfg_start;
 u32    cfg_bus;
};

static int spi_delay_to_sclk(u32 slck_speed_hz, struct spi_delay *delay)
{
 s32 ns;

 if (!delay)
  return 0;

 if (delay->unit == SPI_DELAY_UNIT_SCK)
  return delay->value;

 ns = spi_delay_to_ns(delay, NULL);
 if (ns < 0)
  return 0;

 return DIV_ROUND_UP_ULL(slck_speed_hz * ns, NSEC_PER_SEC);
}

static inline u32 aml_spisg_sem_down_read(struct spisg_device *spisg)
{
 u32 ret;

 regmap_read(spisg->map, SPISG_REG_CFG_READY, &ret);
 if (ret)
  regmap_write(spisg->map, SPISG_REG_CFG_READY, 0);

 return ret;
}

static inline void aml_spisg_sem_up_write(struct spisg_device *spisg)
{
 regmap_write(spisg->map, SPISG_REG_CFG_READY, 1);
}

static int aml_spisg_set_speed(struct spisg_device *spisg, uint speed_hz)
{
 u32 cfg_bus;

 if (!speed_hz || speed_hz == spisg->speed_hz)
  return 0;

 spisg->speed_hz = speed_hz;
 clk_set_rate(spisg->sclk, speed_hz);
 /* Store the div for the descriptor mode */
 regmap_read(spisg->map, SPISG_REG_CFG_BUS, &cfg_bus);
 spisg->cfg_bus &= ~CFG_CLK_DIV;
 spisg->cfg_bus |= cfg_bus & CFG_CLK_DIV;
 spisg->effective_speed_hz = clk_get_rate(spisg->sclk);
 dev_dbg(&spisg->pdev->dev,
  "desired speed %dHz, effective speed %dHz\n",
  speed_hz, spisg->effective_speed_hz);

 return 0;
}

static bool aml_spisg_can_dma(struct spi_controller *ctlr,
         struct spi_device *spi,
         struct spi_transfer *xfer)
{
 return true;
}

static void aml_spisg_sg_xlate(struct sg_table *sgt, struct spisg_sg_link *ccsg)
{
 struct scatterlist *sg;
 int i;

 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
  ccsg->addr = FIELD_PREP(LINK_ADDR_VALID, 1) |
        FIELD_PREP(LINK_ADDR_RING, 0) |
        FIELD_PREP(LINK_ADDR_EOC, sg_is_last(sg)) |
        FIELD_PREP(LINK_ADDR_LEN, sg_dma_len(sg));
  ccsg->addr1 = (u32)sg_dma_address(sg);
  ccsg++;
 }
}

static int nbits_to_lane[] = {
 SPISG_SINGLE_SPI,
 SPISG_SINGLE_SPI,
 SPISG_DUAL_SPI,
 -EINVAL,
 SPISG_QUAD_SPI
};

static int aml_spisg_setup_transfer(struct spisg_device *spisg,
        struct spi_transfer *xfer,
        struct spisg_descriptor *desc,
        struct spisg_descriptor_extra *exdesc)
{
 int block_size, blocks;
 struct device *dev = &spisg->pdev->dev;
 struct spisg_sg_link *ccsg;
 int ccsg_len;
 dma_addr_t paddr;
 int ret;

 memset(desc, 0, sizeof(*desc));
 if (exdesc)
  memset(exdesc, 0, sizeof(*exdesc));
 aml_spisg_set_speed(spisg, xfer->speed_hz);
 xfer->effective_speed_hz = spisg->effective_speed_hz;

 desc->cfg_start = spisg->cfg_start;
 desc->cfg_bus = spisg->cfg_bus;

 block_size = xfer->bits_per_word >> 3;
 blocks = xfer->len / block_size;

 desc->cfg_start |= FIELD_PREP(CFG_EOC, 0);
 desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, !xfer->cs_change);
 desc->cfg_bus |= FIELD_PREP(CFG_NULL_CTL, 0);

 if (xfer->tx_buf || xfer->tx_dma) {
  desc->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->tx_nbits]);
  desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE);
 }
 if (xfer->rx_buf || xfer->rx_dma) {
  desc->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->rx_nbits]);
  desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_READ);
 }

 if (FIELD_GET(CFG_OP_MODE, desc->cfg_start) == SPISG_OP_MODE_READ_STS) {
  desc->cfg_start |= FIELD_PREP(CFG_BLOCK_SIZE, blocks) |
       FIELD_PREP(CFG_BLOCK_NUM, 1);
 } else {
  blocks = min_t(int, blocks, SPISG_BLOCK_MAX);
  desc->cfg_start |= FIELD_PREP(CFG_BLOCK_SIZE, block_size & 0x7) |
       FIELD_PREP(CFG_BLOCK_NUM, blocks);
 }

 if (xfer->tx_sg.nents && xfer->tx_sg.sgl) {
  ccsg_len = xfer->tx_sg.nents * sizeof(struct spisg_sg_link);
  ccsg = kzalloc(ccsg_len, GFP_KERNEL | GFP_DMA);
  if (!ccsg) {
   dev_err(dev, "alloc tx_ccsg failed\n");
   return -ENOMEM;
  }

  aml_spisg_sg_xlate(&xfer->tx_sg, ccsg);
  paddr = dma_map_single(dev, (void *)ccsg,
           ccsg_len, DMA_TO_DEVICE);
  ret = dma_mapping_error(dev, paddr);
  if (ret) {
   kfree(ccsg);
   dev_err(dev, "tx ccsg map failed\n");
   return ret;
  }

  desc->tx_paddr = paddr;
  desc->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_SG);
  exdesc->tx_ccsg = ccsg;
  exdesc->tx_ccsg_len = ccsg_len;
  dma_sync_sgtable_for_device(spisg->controller->cur_tx_dma_dev,
         &xfer->tx_sg, DMA_TO_DEVICE);
 } else if (xfer->tx_buf || xfer->tx_dma) {
  paddr = xfer->tx_dma;
  if (!paddr) {
   paddr = dma_map_single(dev, (void *)xfer->tx_buf,
            xfer->len, DMA_TO_DEVICE);
   ret = dma_mapping_error(dev, paddr);
   if (ret) {
    dev_err(dev, "tx buf map failed\n");
    return ret;
   }
  }
  desc->tx_paddr = paddr;
  desc->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_MEM);
 }

 if (xfer->rx_sg.nents && xfer->rx_sg.sgl) {
  ccsg_len = xfer->rx_sg.nents * sizeof(struct spisg_sg_link);
  ccsg = kzalloc(ccsg_len, GFP_KERNEL | GFP_DMA);
  if (!ccsg) {
   dev_err(dev, "alloc rx_ccsg failed\n");
   return -ENOMEM;
  }

  aml_spisg_sg_xlate(&xfer->rx_sg, ccsg);
  paddr = dma_map_single(dev, (void *)ccsg,
           ccsg_len, DMA_TO_DEVICE);
  ret = dma_mapping_error(dev, paddr);
  if (ret) {
   kfree(ccsg);
   dev_err(dev, "rx ccsg map failed\n");
   return ret;
  }

  desc->rx_paddr = paddr;
  desc->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_SG);
  exdesc->rx_ccsg = ccsg;
  exdesc->rx_ccsg_len = ccsg_len;
  dma_sync_sgtable_for_device(spisg->controller->cur_rx_dma_dev,
         &xfer->rx_sg, DMA_FROM_DEVICE);
 } else if (xfer->rx_buf || xfer->rx_dma) {
  paddr = xfer->rx_dma;
  if (!paddr) {
   paddr = dma_map_single(dev, xfer->rx_buf,
            xfer->len, DMA_FROM_DEVICE);
   ret = dma_mapping_error(dev, paddr);
   if (ret) {
    dev_err(dev, "rx buf map failed\n");
    return ret;
   }
  }

  desc->rx_paddr = paddr;
  desc->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_MEM);
 }

 return 0;
}

static void aml_spisg_cleanup_transfer(struct spisg_device *spisg,
           struct spi_transfer *xfer,
           struct spisg_descriptor *desc,
           struct spisg_descriptor_extra *exdesc)
{
 struct device *dev = &spisg->pdev->dev;

 if (desc->tx_paddr) {
  if (FIELD_GET(CFG_TXD_MODE, desc->cfg_start) == SPISG_DATA_MODE_SG) {
   dma_unmap_single(dev, (dma_addr_t)desc->tx_paddr,
      exdesc->tx_ccsg_len, DMA_TO_DEVICE);
   kfree(exdesc->tx_ccsg);
   dma_sync_sgtable_for_cpu(spisg->controller->cur_tx_dma_dev,
       &xfer->tx_sg, DMA_TO_DEVICE);
  } else if (!xfer->tx_dma) {
   dma_unmap_single(dev, (dma_addr_t)desc->tx_paddr,
      xfer->len, DMA_TO_DEVICE);
  }
 }

 if (desc->rx_paddr) {
  if (FIELD_GET(CFG_RXD_MODE, desc->cfg_start) == SPISG_DATA_MODE_SG) {
   dma_unmap_single(dev, (dma_addr_t)desc->rx_paddr,
      exdesc->rx_ccsg_len, DMA_TO_DEVICE);
   kfree(exdesc->rx_ccsg);
   dma_sync_sgtable_for_cpu(spisg->controller->cur_rx_dma_dev,
       &xfer->rx_sg, DMA_FROM_DEVICE);
  } else if (!xfer->rx_dma) {
   dma_unmap_single(dev, (dma_addr_t)desc->rx_paddr,
      xfer->len, DMA_FROM_DEVICE);
  }
 }
}

static void aml_spisg_setup_null_desc(struct spisg_device *spisg,
          struct spisg_descriptor *desc,
          u32 n_sclk)
{
 /* unit is the last xfer sclk */
 desc->cfg_start = spisg->cfg_start;
 desc->cfg_bus = spisg->cfg_bus;

 desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE) |
      FIELD_PREP(CFG_BLOCK_SIZE, 1) |
      FIELD_PREP(CFG_BLOCK_NUM, DIV_ROUND_UP(n_sclk, 8));

 desc->cfg_bus |= FIELD_PREP(CFG_NULL_CTL, 1);
}

static void aml_spisg_pending(struct spisg_device *spisg,
         dma_addr_t desc_paddr,
         bool trig,
         bool irq_en)
{
 u32 desc_l, desc_h, cfg_spi, irq_enable;

#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
 desc_l = (u64)desc_paddr & 0xffffffff;
 desc_h = (u64)desc_paddr >> 32;
#else
 desc_l = desc_paddr & 0xffffffff;
 desc_h = 0;
#endif

 cfg_spi = spisg->cfg_spi;
 if (trig)
  cfg_spi |= CFG_HW_POS;
 else
  desc_h |= LIST_DESC_PENDING;

 irq_enable = IRQ_RCH_DESC_INVALID | IRQ_RCH_DESC_RESP |
       IRQ_RCH_DATA_RESP | IRQ_WCH_DESC_INVALID |
       IRQ_WCH_DESC_RESP | IRQ_WCH_DATA_RESP |
       IRQ_DESC_ERR | IRQ_DESC_CHAIN_DONE;
 regmap_write(spisg->map, SPISG_REG_IRQ_ENABLE, irq_en ? irq_enable : 0);
 regmap_write(spisg->map, SPISG_REG_CFG_SPI, cfg_spi);
 regmap_write(spisg->map, SPISG_REG_DESC_LIST_L, desc_l);
 regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, desc_h);
}

static irqreturn_t aml_spisg_irq(int irq, void *data)
{
 struct spisg_device *spisg = (void *)data;
 u32 sts;

 spisg->status = 0;
 regmap_read(spisg->map, SPISG_REG_IRQ_STS, &sts);
 regmap_write(spisg->map, SPISG_REG_IRQ_STS, sts);
 if (sts & (IRQ_RCH_DESC_INVALID |
     IRQ_RCH_DESC_RESP |
     IRQ_RCH_DATA_RESP |
     IRQ_WCH_DESC_INVALID |
     IRQ_WCH_DESC_RESP |
     IRQ_WCH_DATA_RESP |
     IRQ_DESC_ERR))
  spisg->status = sts;
 else if (sts & IRQ_DESC_CHAIN_DONE)
  spisg->status = 0;
 else
  return IRQ_NONE;

 complete(&spisg->completion);

 return IRQ_HANDLED;
}

static int aml_spisg_transfer_one_message(struct spi_controller *ctlr,
       struct spi_message *msg)
{
 struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
 struct device *dev = &spisg->pdev->dev;
 unsigned long long ms = 0;
 struct spi_transfer *xfer;
 struct spisg_descriptor *descs, *desc;
 struct spisg_descriptor_extra *exdescs, *exdesc;
 dma_addr_t descs_paddr;
 int desc_num = 1, descs_len;
 u32 cs_hold_in_sclk = 0;
 int ret = -EIO;

 if (!aml_spisg_sem_down_read(spisg)) {
  spi_finalize_current_message(ctlr);
  dev_err(dev, "controller busy\n");
  return -EBUSY;
 }

 /* calculate the desc num for all xfer */
 list_for_each_entry(xfer, &msg->transfers, transfer_list)
  desc_num++;

 /* alloc descriptor/extra-descriptor table */
 descs = kcalloc(desc_num, sizeof(*desc) + sizeof(*exdesc),
   GFP_KERNEL | GFP_DMA);
 if (!descs) {
  spi_finalize_current_message(ctlr);
  aml_spisg_sem_up_write(spisg);
  return -ENOMEM;
 }
 descs_len = sizeof(*desc) * desc_num;
 exdescs = (struct spisg_descriptor_extra *)(descs + desc_num);

 /* config descriptor for each xfer */
 desc = descs;
 exdesc = exdescs;
 list_for_each_entry(xfer, &msg->transfers, transfer_list) {
  ret = aml_spisg_setup_transfer(spisg, xfer, desc, exdesc);
  if (ret) {
   dev_err(dev, "config descriptor failed\n");
   goto end;
  }

  /* calculate cs-setup delay with the first xfer speed */
  if (list_is_first(&xfer->transfer_list, &msg->transfers))
   desc->cfg_bus |= FIELD_PREP(CFG_CS_SETUP,
    spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_setup));

  /* calculate cs-hold delay with the last xfer speed */
  if (list_is_last(&xfer->transfer_list, &msg->transfers))
   cs_hold_in_sclk =
    spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_hold);

  desc++;
  exdesc++;
  ms += DIV_ROUND_UP_ULL(8LL * MSEC_PER_SEC * xfer->len,
           xfer->effective_speed_hz);
 }

 if (cs_hold_in_sclk)
  /* additional null-descriptor to achieve the cs-hold delay */
  aml_spisg_setup_null_desc(spisg, desc, cs_hold_in_sclk);
 else
  desc--;

 desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, 0);
 desc->cfg_start |= FIELD_PREP(CFG_EOC, 1);

 /* some tolerances */
 ms += ms + 20;
 if (ms > UINT_MAX)
  ms = UINT_MAX;

 descs_paddr = dma_map_single(dev, (void *)descs,
         descs_len, DMA_TO_DEVICE);
 ret = dma_mapping_error(dev, descs_paddr);
 if (ret) {
  dev_err(dev, "desc table map failed\n");
  goto end;
 }

 reinit_completion(&spisg->completion);
 aml_spisg_pending(spisg, descs_paddr, falsetrue);
 if (wait_for_completion_timeout(&spisg->completion,
     spi_controller_is_target(spisg->controller) ?
     MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(ms)))
  ret = spisg->status ? -EIO : 0;
 else
  ret = -ETIMEDOUT;

 dma_unmap_single(dev, descs_paddr, descs_len, DMA_TO_DEVICE);
end:
 desc = descs;
 exdesc = exdescs;
 list_for_each_entry(xfer, &msg->transfers, transfer_list)
  aml_spisg_cleanup_transfer(spisg, xfer, desc++, exdesc++);
 kfree(descs);

 if (!ret)
  msg->actual_length = msg->frame_length;
 msg->status = ret;
 spi_finalize_current_message(ctlr);
 aml_spisg_sem_up_write(spisg);

 return ret;
}

static int aml_spisg_prepare_message(struct spi_controller *ctlr,
         struct spi_message *message)
{
 struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
 struct spi_device *spi = message->spi;

 if (!spi->bits_per_word || spi->bits_per_word % 8) {
  dev_err(&spisg->pdev->dev, "invalid wordlen %d\n", spi->bits_per_word);
  return -EINVAL;
 }

 spisg->bytes_per_word = spi->bits_per_word >> 3;

 spisg->cfg_spi &= ~CFG_SLAVE_SELECT;
 spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_SELECT, spi_get_chipselect(spi, 0));

 spisg->cfg_bus &= ~(CFG_CPOL | CFG_CPHA | CFG_B_L_ENDIAN | CFG_HALF_DUPLEX);
 spisg->cfg_bus |= FIELD_PREP(CFG_CPOL, !!(spi->mode & SPI_CPOL)) |
     FIELD_PREP(CFG_CPHA, !!(spi->mode & SPI_CPHA)) |
     FIELD_PREP(CFG_B_L_ENDIAN, !!(spi->mode & SPI_LSB_FIRST)) |
     FIELD_PREP(CFG_HALF_DUPLEX, !!(spi->mode & SPI_3WIRE));

 return 0;
}

static int aml_spisg_setup(struct spi_device *spi)
{
 if (!spi->controller_state)
  spi->controller_state = spi_controller_get_devdata(spi->controller);

 return 0;
}

static void aml_spisg_cleanup(struct spi_device *spi)
{
 spi->controller_state = NULL;
}

static int aml_spisg_target_abort(struct spi_controller *ctlr)
{
 struct spisg_device *spisg = spi_controller_get_devdata(ctlr);

 spisg->status = 0;
 regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, 0);
 complete(&spisg->completion);

 return 0;
}

static int aml_spisg_clk_init(struct spisg_device *spisg, void __iomem *base)
{
 struct device *dev = &spisg->pdev->dev;
 struct clk_init_data init;
 struct clk_divider *div;
 struct clk_div_table *tbl;
 char name[32];
 int ret, i;

 spisg->core = devm_clk_get_enabled(dev, "core");
 if (IS_ERR_OR_NULL(spisg->core)) {
  dev_err(dev, "core clock request failed\n");
  return PTR_ERR(spisg->core);
 }

 spisg->pclk = devm_clk_get_enabled(dev, "pclk");
 if (IS_ERR_OR_NULL(spisg->pclk)) {
  dev_err(dev, "pclk clock request failed\n");
  return PTR_ERR(spisg->pclk);
 }

 clk_set_min_rate(spisg->pclk, SPISG_PCLK_RATE_MIN);

 clk_disable_unprepare(spisg->pclk);

 tbl = devm_kzalloc(dev, sizeof(struct clk_div_table) * (DIV_NUM + 1), GFP_KERNEL);
 if (!tbl)
  return -ENOMEM;

 for (i = 0; i < DIV_NUM; i++) {
  tbl[i].val = i + SPISG_CLK_DIV_MIN - 1;
  tbl[i].div = i + SPISG_CLK_DIV_MIN;
 }
 spisg->tbl = tbl;

 div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
 if (!div)
  return -ENOMEM;

 div->flags = CLK_DIVIDER_ROUND_CLOSEST;
 div->reg = base + SPISG_REG_CFG_BUS;
 div->shift = __bf_shf(CFG_CLK_DIV);
 div->width = CLK_DIV_WIDTH;
 div->table = tbl;

 /* Register value should not be outside of the table */
 regmap_update_bits(spisg->map, SPISG_REG_CFG_BUS, CFG_CLK_DIV,
      FIELD_PREP(CFG_CLK_DIV, SPISG_CLK_DIV_MIN - 1));

 /* Register clk-divider */
 snprintf(name, sizeof(name), "%s_div", dev_name(dev));
 init.name = name;
 init.ops = &clk_divider_ops;
 init.flags = CLK_SET_RATE_PARENT;
 init.parent_data = &(const struct clk_parent_data) {
    .fw_name = "pclk",
      };
 init.num_parents = 1;
 div->hw.init = &init;
 ret = devm_clk_hw_register(dev, &div->hw);
 if (ret) {
  dev_err(dev, "clock registration failed\n");
  return ret;
 }

 spisg->sclk = devm_clk_hw_get_clk(dev, &div->hw, NULL);
 if (IS_ERR_OR_NULL(spisg->sclk)) {
  dev_err(dev, "get clock failed\n");
  return PTR_ERR(spisg->sclk);
 }

 clk_prepare_enable(spisg->sclk);

 return 0;
}

static int aml_spisg_probe(struct platform_device *pdev)
{
 struct spi_controller *ctlr;
 struct spisg_device *spisg;
 struct device *dev = &pdev->dev;
 void __iomem *base;
 int ret, irq;

 const struct regmap_config aml_regmap_config = {
  .reg_bits = 32,
  .val_bits = 32,
  .reg_stride = 4,
  .max_register = SPISG_MAX_REG,
 };

 if (of_property_read_bool(dev->of_node, "spi-slave"))
  ctlr = spi_alloc_target(dev, sizeof(*spisg));
 else
  ctlr = spi_alloc_host(dev, sizeof(*spisg));
 if (!ctlr)
  return dev_err_probe(dev, -ENOMEM, "controller allocation failed\n");

 spisg = spi_controller_get_devdata(ctlr);
 spisg->controller = ctlr;

 spisg->pdev = pdev;
 platform_set_drvdata(pdev, spisg);

 base = devm_platform_ioremap_resource(pdev, 0);
 if (IS_ERR(base))
  return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n");

 spisg->map = devm_regmap_init_mmio(dev, base, &aml_regmap_config);
 if (IS_ERR(spisg->map))
  return dev_err_probe(dev, PTR_ERR(spisg->map), "regmap init failed\n");

 irq = platform_get_irq(pdev, 0);
 if (irq < 0) {
  ret = irq;
  goto out_controller;
 }

 ret = device_reset_optional(dev);
 if (ret)
  return dev_err_probe(dev, ret, "reset dev failed\n");

 ret = aml_spisg_clk_init(spisg, base);
 if (ret)
  return dev_err_probe(dev, ret, "clock init failed\n");

 spisg->cfg_spi = 0;
 spisg->cfg_start = 0;
 spisg->cfg_bus = 0;

 spisg->cfg_spi = FIELD_PREP(CFG_SFLASH_WP, 1) |
    FIELD_PREP(CFG_SFLASH_HD, 1);
 if (spi_controller_is_target(ctlr)) {
  spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_EN, 1);
  spisg->cfg_bus = FIELD_PREP(CFG_TX_TUNING, 0xf);
 }
 /* default pending */
 spisg->cfg_start = FIELD_PREP(CFG_PEND, 1);

 pm_runtime_set_active(&spisg->pdev->dev);
 pm_runtime_enable(&spisg->pdev->dev);
 pm_runtime_resume_and_get(&spisg->pdev->dev);

 ctlr->num_chipselect = 4;
 ctlr->dev.of_node = pdev->dev.of_node;
 ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST |
     SPI_3WIRE | SPI_TX_QUAD | SPI_RX_QUAD;
 ctlr->max_speed_hz = 1000 * 1000 * 100;
 ctlr->min_speed_hz = 1000 * 10;
 ctlr->setup = aml_spisg_setup;
 ctlr->cleanup = aml_spisg_cleanup;
 ctlr->prepare_message = aml_spisg_prepare_message;
 ctlr->transfer_one_message = aml_spisg_transfer_one_message;
 ctlr->target_abort = aml_spisg_target_abort;
 ctlr->can_dma = aml_spisg_can_dma;
 ctlr->max_dma_len = SPISG_BLOCK_MAX;
 ctlr->auto_runtime_pm = true;

 dma_set_max_seg_size(&pdev->dev, SPISG_BLOCK_MAX);

 ret = devm_request_irq(&pdev->dev, irq, aml_spisg_irq, 0, NULL, spisg);
 if (ret) {
  dev_err(&pdev->dev, "irq request failed\n");
  goto out_clk;
 }

 ret = devm_spi_register_controller(dev, ctlr);
 if (ret) {
  dev_err(&pdev->dev, "spi controller registration failed\n");
  goto out_clk;
 }

 init_completion(&spisg->completion);

 pm_runtime_put(&spisg->pdev->dev);

 return 0;
out_clk:
 if (spisg->core)
  clk_disable_unprepare(spisg->core);
 clk_disable_unprepare(spisg->pclk);
out_controller:
 spi_controller_put(ctlr);

 return ret;
}

static void aml_spisg_remove(struct platform_device *pdev)
{
 struct spisg_device *spisg = platform_get_drvdata(pdev);

 if (!pm_runtime_suspended(&pdev->dev)) {
  pinctrl_pm_select_sleep_state(&spisg->pdev->dev);
  clk_disable_unprepare(spisg->core);
  clk_disable_unprepare(spisg->pclk);
 }
}

static int spisg_suspend_runtime(struct device *dev)
{
 struct spisg_device *spisg = dev_get_drvdata(dev);

 pinctrl_pm_select_sleep_state(&spisg->pdev->dev);
 clk_disable_unprepare(spisg->sclk);
 clk_disable_unprepare(spisg->core);

 return 0;
}

static int spisg_resume_runtime(struct device *dev)
{
 struct spisg_device *spisg = dev_get_drvdata(dev);

 clk_prepare_enable(spisg->core);
 clk_prepare_enable(spisg->sclk);
 pinctrl_pm_select_default_state(&spisg->pdev->dev);

 return 0;
}

static const struct dev_pm_ops amlogic_spisg_pm_ops = {
 .runtime_suspend = spisg_suspend_runtime,
 .runtime_resume  = spisg_resume_runtime,
};

static const struct of_device_id amlogic_spisg_of_match[] = {
 {
  .compatible = "amlogic,a4-spisg",
 },

 { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, amlogic_spisg_of_match);

static struct platform_driver amlogic_spisg_driver = {
 .probe = aml_spisg_probe,
 .remove = aml_spisg_remove,
 .driver  = {
  .name = "amlogic-spisg",
  .of_match_table = amlogic_spisg_of_match,
  .pm = &amlogic_spisg_pm_ops,
 },
};

module_platform_driver(amlogic_spisg_driver);

MODULE_DESCRIPTION("Amlogic SPI Scatter-Gather Controller driver");
MODULE_AUTHOR("Sunny Luo ");
MODULE_LICENSE("GPL");

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

¤ Dauer der Verarbeitung: 0.14 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.