Linux驅動分析之SPI控制器

前言

    之前對SPI驅動的整體架構做了介紹,現在來分析具體的驅動程序。之前說過,SPI驅動分爲設備驅動和控制器驅動。先來分析控制器驅動。我們以RockChip的控制器來作爲分析。

SPI控制器分析

下面的代碼分析主要都在註釋中,會按照驅動中函數的執行順序分析。

(1) 裝載和卸載函數

//dts匹配表
static const struct of_device_id rockchip_spi_dt_match[] = {
  { .compatible = "rockchip,rv1108-spi", },
  { .compatible = "rockchip,rk3036-spi", },
  { .compatible = "rockchip,rk3066-spi", },
  { .compatible = "rockchip,rk3188-spi", },
  { .compatible = "rockchip,rk3228-spi", },
  { .compatible = "rockchip,rk3288-spi", },
  { .compatible = "rockchip,rk3368-spi", },
  { .compatible = "rockchip,rk3399-spi", },
  { },
};
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);


static struct platform_driver rockchip_spi_driver = {
  .driver = {
    .name  = DRIVER_NAME,
    .pm = &rockchip_spi_pm,
    .of_match_table = of_match_ptr(rockchip_spi_dt_match),
  },
  .probe = rockchip_spi_probe,
  .remove = rockchip_spi_remove,
};
//宏封裝了platform_driver_register和platform_driver_unregister
module_platform_driver(rockchip_spi_driver);

module_platform_driver宏定義在 include/linux/platform_device.h, 具體看一下源碼:

#define module_platform_driver(__platform_driver) \
  module_driver(__platform_driver, platform_driver_register, \
      platform_driver_unregister)


#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
  return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
  __unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

所以其實和我們看到的platform_driver的註冊和卸載時一樣的,只是進行了封裝。

(2) probe()函數

static int rockchip_spi_probe(struct platform_device *pdev)
{
  int ret;
  struct rockchip_spi *rs;
  struct spi_master *master;
  struct resource *mem;
  u32 rsd_nsecs;
    //分配一個spi_master
  master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));
    //保存爲driver_data, 方便其他地方獲取使用
  platform_set_drvdata(pdev, master);
    //獲取設備數據,就是driver_data
  rs = spi_master_get_devdata(master);


  //獲取IO資源
  mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
   //申請IO資源並進行重映射
  rs->regs = devm_ioremap_resource(&pdev->dev, mem);


    //獲取APB時鐘(APB時鐘)
  rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk");
    //獲取spi時鐘(APB提供)
  rs->spiclk = devm_clk_get(&pdev->dev, "spiclk");
    //使能APB時鐘
  ret = clk_prepare_enable(rs->apb_pclk);
    //使能spi時鐘
  ret = clk_prepare_enable(rs->spiclk);
    //關閉spi控制器(設置SSIENR寄存器的值),查看芯片手冊
  spi_enable_chip(rs, 0);


  rs->type = SSI_MOTO_SPI; //摩托羅拉SPI協議
  rs->master = master; //spi_master
  rs->dev = &pdev->dev; //device
  rs->max_freq = clk_get_rate(rs->spiclk); //最大時鐘頻率
    //接收採樣延遲時間
  if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns",
          &rsd_nsecs))
    rs->rsd_nsecs = rsd_nsecs;
    //FIFO大小
  rs->fifo_len = get_fifo_len(rs);


  spin_lock_init(&rs->lock);
    //電源管理
  pm_runtime_set_active(&pdev->dev);
  pm_runtime_enable(&pdev->dev);


  master->auto_runtime_pm = true; //自動電源管理
  master->bus_num = pdev->id; //哪個spi, 比如是SPI1就bus_num=1, SPI2就bus_num=2
  master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; //所支持的模式
  master->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; //片選最大值+1,spi設備的片選值要小於它
  master->dev.of_node = pdev->dev.of_node;
  master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8);//支持8或16bit
    //回調函數
  master->set_cs = rockchip_spi_set_cs; //硬件片選,使用控制器的片選(沒使用可以不實現)
  master->prepare_message = rockchip_spi_prepare_message;//設置spi控制器(傳輸前的準備)
  master->unprepare_message = rockchip_spi_unprepare_message; //釋放prepare的資源
  master->transfer_one = rockchip_spi_transfer_one;//傳輸一個簡單的spi_transfer
  master->max_transfer_size = rockchip_spi_max_transfer_size;
  master->handle_err = rockchip_spi_handle_err;
  master->flags = SPI_MASTER_GPIO_SS;
    //使用DMA
  rs->dma_tx.ch = dma_request_chan(rs->dev, "tx");
  rs->dma_rx.ch = dma_request_chan(rs->dev, "rx");


  if (rs->dma_tx.ch && rs->dma_rx.ch) {
             //FIFO的地址
    rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR);
    rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR);


    master->can_dma = rockchip_spi_can_dma;
    master->dma_tx = rs->dma_tx.ch;
    master->dma_rx = rs->dma_rx.ch;
  }
    //註冊spi_master
  ret = devm_spi_register_master(&pdev->dev, master);


  return 0;
//錯誤處理
//.....
  return ret;
}

上面將一些錯誤判斷及Log信息去掉了,只留下關鍵的部分。

(3) 傳輸函數 -- rockchip_spi_transfer_one

static int rockchip_spi_transfer_one(
    struct spi_master *master,
    struct spi_device *spi,
    struct spi_transfer *xfer)
{
    //獲取rockchip_spi
  struct rockchip_spi *rs = spi_master_get_devdata(master);
    //判斷spi當前狀態
  WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) &&
    (readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY));


  if (!xfer->tx_buf && !xfer->rx_buf) {
    dev_err(rs->dev, "No buffer for transfer\n");
    return -EINVAL;
  }


  if (xfer->len > ROCKCHIP_SPI_MAX_TRANLEN) {
    dev_err(rs->dev, "Transfer is too long (%d)\n", xfer->len);
    return -EINVAL;
  }
    
  rs->speed = xfer->speed_hz; //傳輸速率
  rs->bpw = xfer->bits_per_word; //8bit或16bit
  rs->n_bytes = rs->bpw >> 3;
    //傳輸的數據
  rs->tx = xfer->tx_buf;
  rs->tx_end = rs->tx + xfer->len;
  rs->rx = xfer->rx_buf;
  rs->rx_end = rs->rx + xfer->len;
  rs->len = xfer->len;


  rs->tx_sg = xfer->tx_sg;
  rs->rx_sg = xfer->rx_sg;


  if (rs->tx && rs->rx)
    rs->tmode = CR0_XFM_TR; //發送並接收
  else if (rs->tx)
    rs->tmode = CR0_XFM_TO; //只發送
  else if (rs->rx)
    rs->tmode = CR0_XFM_RO; //只接收


  /* we need prepare dma before spi was enabled */
     //是否使用DMA
  if (master->can_dma && master->can_dma(master, spi, xfer))
    rs->use_dma = true;
  else
    rs->use_dma = false;
    //配置spi,對寄存器進行配置
  rockchip_spi_config(rs);


  if (rs->use_dma)
    return rockchip_spi_prepare_dma(rs);
    //數據傳輸
  return rockchip_spi_pio_transfer(rs);
}
  • rockchip_spi_pio_transfer

static int rockchip_spi_pio_transfer(struct rockchip_spi *rs)
{
  int remain = 0;
    //使能片選
  spi_enable_chip(rs, 1);


  do {
    if (rs->tx) {
      remain = rs->tx_end - rs->tx;
      rockchip_spi_pio_writer(rs); //發送
    }


    if (rs->rx) {
      remain = rs->rx_end - rs->rx;
      rockchip_spi_pio_reader(rs); //讀取
    }


    cpu_relax();
  } while (remain);


  /* If tx, wait until the FIFO data completely. */
  if (rs->tx)
    wait_for_idle(rs);
    //關閉片選使能
  spi_enable_chip(rs, 0);


  return 0;
}
  • rockchip_spi_pio_writer

//發送
static void rockchip_spi_pio_writer(struct rockchip_spi *rs)
{
  u32 max = tx_max(rs);
  u32 txw = 0;


  while (max--) {
    if (rs->n_bytes == 1)
      txw = *(u8 *)(rs->tx);
    else
      txw = *(u16 *)(rs->tx);
            //寫到數據發送寄存器
    writel_relaxed(txw, rs->regs + ROCKCHIP_SPI_TXDR);
    rs->tx += rs->n_bytes;
  }
}
//讀取
static void rockchip_spi_pio_reader(struct rockchip_spi *rs)
{
  u32 max = rx_max(rs);
  u32 rxw;


  while (max--) {
    //讀取數據接收寄存器中的數據
    rxw = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
    if (rs->n_bytes == 1)
      *(u8 *)(rs->rx) = (u8)rxw;
    else
      *(u16 *)(rs->rx) = (u16)rxw;
    rs->rx += rs->n_bytes;
  }
}

總結

    每次看到這麼多代碼分析,很多人肯定都不怎麼想看,但是多看幾份,你就會發現都是套路。每個Linux版本的結構體可能都會變,但是基本的東西都是不變的。大家可以和之前的SPI驅動架構分析那篇文章一起看。

更多好文,掃碼關注公衆號

 

精品文章

Linux驅動分析之SPI驅動架構

簡單上手GDB調試

Linux程序如何加載動態庫

C編程修養

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章