AnsweredAssumed Answered

I.MX7. Linux SPI Slave with DMA

Question asked by Renat Gabdulhakov on Oct 7, 2019
Latest reply on Oct 7, 2019 by igorpadykov

Dear all,

I am trying to create a custom linux driver with support spi slave dma mode.

 

I changed imx7d-var-som.dtsi.

    custom_spi {
        #address-cells = <1>;
        #size-cells = <0>;
        compatible = "oct, custom_spi";
        reg = <0x30830000 0x10000>;
        pinctrl-names = "default", "sleep";
        pinctrl-0 = <&pinctrl_ecspi2>;       
        interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&clks IMX7D_ECSPI2_ROOT_CLK>,
            <&clks IMX7D_ECSPI2_ROOT_CLK>;
        clock-names = "ipg", "per";
        dmas = <&sdma 2 7 1>, <&sdma 3 7 2>;
        dma-names = "rx", "tx";
        status = "okay";
    };

 

&ecspi2 {
    fsl,spi-num-chipselects = <1>;
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&pinctrl_ecspi2>;
    cs-gpios = <&gpio4 23 0>;
    status = "disable";

 

    spidev0: spi@0 {
        compatible = "spidev";
        reg = <0>;
        spi-max-frequency = <54000000>;
    };
};

My driver custom_spi.c:

static int custom_spi_probe(struct platform_device *pdev){ 
  int ret_val = 0;
  int spi_addr = 0x30830000;  
  u32 temp;
 
  dev = &pdev->dev;  
  ret_val = register_chrdev(MAJOR_NUM_CUSTOM_SPI, DEVICE_NAME, &Fops);
 
  if (ret_val < 0) {
    printk("%s failed with %d\n",
           "Sorry, registering the character device ", ret_val);
    return ret_val;
  }
 
  clk_ipg = devm_clk_get(&pdev->dev, "ipg");
  clk_per = devm_clk_get(&pdev->dev, "per");
 
  clk_prepare_enable(clk_per);
  clk_prepare_enable(clk_ipg);

 

  spi_base = ioremap(0x30830000, 0x1000);
  // Disable SPI
  ctrl = readl(spi_base + MX51_ECSPI_CTRL);
  ctrl &= ~MX51_ECSPI_CTRL_ENABLE;
  //writel(MX51_ECSPI_CTRL_ENABLE, spi_base + MX51_ECSPI_CTRL);
  writel(ctrl, spi_base + MX51_ECSPI_CTRL);
 
  ccm_base = ioremap(0x30380000, 0x10000);    
  gpio_base = ioremap(0x30200000, 0x100);
  temp = readl(gpio_base + 0x04);
  writel(temp | (0x1 << 13), gpio_base + 0x04);
  writel((0x1 << 13), gpio_base);
  iomux_base = ioremap(0x30330000, 0x10000);

 

  return ret_val;
}

 

 

static void dma_callback(void *data){

   enum dma_status dmastat;
   struct dma_tx_state state;
   size_t count;

   printk("dma_callback\n");

   dmastat = dmaengine_tx_status(dma_rx, cookie, &state);
   if (dmastat == DMA_ERROR) {
       printk("dma_callback error\n");
       return;
    }
}

 

int slave_read_dma(unsigned long ioctl_param){
    int res = 0;
    unsigned int ii, jj;
    u32 ctrl, cfg;
    u32 clock, pre, post, length, temp;

    int irq_num;
    int ret_val = 0;
    int spi_addr = 0x30830000;
    dma_cap_mask_t mask;
    struct dma_slave_config slave_config;
    void *data;
    int nent;

    memset(&rx_ring, 0, sizeof(rx_ring));
    data = kmalloc(SPI_MAX_DMA_XFER, GFP_KERNEL);
    rx_ring.buf = data;

    dma_cap_zero(mask);
    dma_cap_set(DMA_CYCLIC, mask);

    dma_rx = dma_request_slave_channel(dev, "rx");
    if (IS_ERR(dma_rx)){
       printk("can't get the RX DMA channel, error %ld\n", PTR_ERR(dma_rx));
    }

    sg_init_table(&sg_rx, 1);
    sg_set_page(&sg_rx, virt_to_page(rx_ring.buf), SPI_MAX_DMA_XFER, offset_in_page(rx_ring.buf));
    nent = dma_map_sg(dev, &sg_rx, 1, DMA_FROM_DEVICE);

    // Configure the slave DMA
    memset(&slave_config, 0, sizeof(slave_config));

   slave_config.direction = DMA_DEV_TO_MEM;
    slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    slave_config.src_addr = spi_addr + MXC_CSPIRXDATA;
    slave_config.src_maxburst = 1;
    //slave_config.device_fc = true;

    ret_val = dmaengine_slave_config(dma_rx, &slave_config);
    if (ret_val)
       printk("RX dma configuration failed with %d\n", ret_val);

   rxdesc = dmaengine_prep_dma_cyclic(dma_rx, sg_dma_address(&sg_rx), sg_dma_len(&sg_rx),    sg_dma_len(&sg_rx)/2, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);

   rxdesc->callback = dma_callback;
    cookie = dmaengine_submit(rxdesc);


    writel(0x00, iomux_base + 0x0184);

   ctrl = MX51_ECSPI_CTRL_ENABLE;
    // set Master or Slave mode
    //ctrl |= MX51_ECSPI_CTRL_MODE_MASK;

   // Enable SPI_RDY handling (falling edge/level triggered).

   // set clock speed
    pre = 0x04;
    post = 0x00;
   clock = (pre << MX51_ECSPI_CTRL_PREDIV_OFFSET) | (post << MX51_ECSPI_CTRL_POSTDIV_OFFSET);
   ctrl |= clock;

   // set chip select to use
    ctrl |= MX51_ECSPI_CTRL_CS(0);

   // set burst length
    length = 32;
    ctrl |= (length - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
    ctrl |= MX51_ECSPI_CTRL_SMC;

   // write CONTROLREG
    writel(ctrl, spi_base + MX51_ECSPI_CTRL);

   udelay(50);

    cfg = readl(spi_base + MX51_ECSPI_CONFIG);
    //cfg |= MX51_ECSPI_CONFIG_SBBCTRL(0);
    cfg |= MX51_ECSPI_CONFIG_SCLKPOL(0);
    cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(0);

    // write CONFIGREG
    writel(cfg, spi_base + MX51_ECSPI_CONFIG);

    //writel(0xBA0B, spi_base + MX51_ECSPI_DMA);
    //writel(0x9190, spi_base + MX51_ECSPI_DMA);
    writel(0xA09F, spi_base + MX51_ECSPI_DMA);
    mdelay(50);

    return res;
}

But I don't received any byte. And the callback function from DMA engine not called.

Without DMA my driver works fine.

Maybe what other settings are needed for DMA?

 

Outcomes