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?
Hi Renat
I am afraid spi slave with dma is not supported in nxp linux releases, standard
way is to proceed with extended support of Commercial Support and Engineering Services | NXP
Best regards
igor
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------