// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for the Loongson LS2X APB DMA Controller * * Copyright (C) 2017-2023 Loongson Corporation */ #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/dmapool.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_dma.h> #include <linux/platform_device.h> #include <linux/slab.h> #include "dmaengine.h" #include "virt-dma.h" /* Global Configuration Register */ #define LDMA_ORDER_ERG … /* Bitfield definitions */ /* Bitfields in Global Configuration Register */ #define LDMA_64BIT_EN … #define LDMA_UNCOHERENT_EN … #define LDMA_ASK_VALID … #define LDMA_START … #define LDMA_STOP … #define LDMA_CONFIG_MASK … /* Bitfields in ndesc_addr field of HW descriptor */ #define LDMA_DESC_EN … #define LDMA_DESC_ADDR_LOW … /* Bitfields in cmd field of HW descriptor */ #define LDMA_INT … #define LDMA_DATA_DIRECTION … #define LDMA_SLAVE_BUSWIDTHS … #define LDMA_MAX_TRANS_LEN … /*-- descriptors -----------------------------------------------------*/ /* * struct ls2x_dma_hw_desc - DMA HW descriptor * @ndesc_addr: the next descriptor low address. * @mem_addr: memory low address. * @apb_addr: device buffer address. * @len: length of a piece of carried content, in words. * @step_len: length between two moved memory data blocks. * @step_times: number of blocks to be carried in a single DMA operation. * @cmd: descriptor command or state. * @stats: DMA status. * @high_ndesc_addr: the next descriptor high address. * @high_mem_addr: memory high address. * @reserved: reserved */ struct ls2x_dma_hw_desc { … } __packed; /* * struct ls2x_dma_sg - ls2x dma scatter gather entry * @hw: the pointer to DMA HW descriptor. * @llp: physical address of the DMA HW descriptor. * @phys: destination or source address(mem). * @len: number of Bytes to read. */ struct ls2x_dma_sg { … }; /* * struct ls2x_dma_desc - software descriptor * @vdesc: pointer to the virtual dma descriptor. * @cyclic: flag to dma cyclic * @burst_size: burst size of transaction, in words. * @desc_num: number of sg entries. * @direction: transfer direction, to or from device. * @status: dma controller status. * @sg: array of sgs. */ struct ls2x_dma_desc { … }; /*-- Channels --------------------------------------------------------*/ /* * struct ls2x_dma_chan - internal representation of an LS2X APB DMA channel * @vchan: virtual dma channel entry. * @desc: pointer to the ls2x sw dma descriptor. * @pool: hw desc table * @irq: irq line * @sconfig: configuration for slave transfers, passed via .device_config */ struct ls2x_dma_chan { … }; /*-- Controller ------------------------------------------------------*/ /* * struct ls2x_dma_priv - LS2X APB DMAC specific information * @ddev: dmaengine dma_device object members * @dma_clk: DMAC clock source * @regs: memory mapped register base * @lchan: channel to store ls2x_dma_chan structures */ struct ls2x_dma_priv { … }; /*-- Helper functions ------------------------------------------------*/ static inline struct ls2x_dma_desc *to_ldma_desc(struct virt_dma_desc *vdesc) { … } static inline struct ls2x_dma_chan *to_ldma_chan(struct dma_chan *chan) { … } static inline struct ls2x_dma_priv *to_ldma_priv(struct dma_device *ddev) { … } static struct device *chan2dev(struct dma_chan *chan) { … } static void ls2x_dma_desc_free(struct virt_dma_desc *vdesc) { … } static void ls2x_dma_write_cmd(struct ls2x_dma_chan *lchan, bool cmd) { … } static void ls2x_dma_start_transfer(struct ls2x_dma_chan *lchan) { … } static size_t ls2x_dmac_detect_burst(struct ls2x_dma_chan *lchan) { … } static void ls2x_dma_fill_desc(struct ls2x_dma_chan *lchan, u32 sg_index, struct ls2x_dma_desc *desc) { … } /*-- DMA Engine API --------------------------------------------------*/ /* * ls2x_dma_alloc_chan_resources - allocate resources for DMA channel * @chan: allocate descriptor resources for this channel * * return - the number of allocated descriptors */ static int ls2x_dma_alloc_chan_resources(struct dma_chan *chan) { … } /* * ls2x_dma_free_chan_resources - free all channel resources * @chan: DMA channel */ static void ls2x_dma_free_chan_resources(struct dma_chan *chan) { … } /* * ls2x_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction * @chan: DMA channel * @sgl: scatterlist to transfer to/from * @sg_len: number of entries in @scatterlist * @direction: DMA direction * @flags: tx descriptor status flags * @context: transaction context (ignored) * * Return: Async transaction descriptor on success and NULL on failure */ static struct dma_async_tx_descriptor * ls2x_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, u32 sg_len, enum dma_transfer_direction direction, unsigned long flags, void *context) { … } /* * ls2x_dma_prep_dma_cyclic - prepare the cyclic DMA transfer * @chan: the DMA channel to prepare * @buf_addr: physical DMA address where the buffer starts * @buf_len: total number of bytes for the entire buffer * @period_len: number of bytes for each period * @direction: transfer direction, to or from device * @flags: tx descriptor status flags * * Return: Async transaction descriptor on success and NULL on failure */ static struct dma_async_tx_descriptor * ls2x_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, unsigned long flags) { … } /* * ls2x_slave_config - set slave configuration for channel * @chan: dma channel * @cfg: slave configuration * * Sets slave configuration for channel */ static int ls2x_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *config) { … } /* * ls2x_dma_issue_pending - push pending transactions to the hardware * @chan: channel * * When this function is called, all pending transactions are pushed to the * hardware and executed. */ static void ls2x_dma_issue_pending(struct dma_chan *chan) { … } /* * ls2x_dma_terminate_all - terminate all transactions * @chan: channel * * Stops all DMA transactions. */ static int ls2x_dma_terminate_all(struct dma_chan *chan) { … } /* * ls2x_dma_synchronize - Synchronizes the termination of transfers to the * current context. * @chan: channel */ static void ls2x_dma_synchronize(struct dma_chan *chan) { … } static int ls2x_dma_pause(struct dma_chan *chan) { … } static int ls2x_dma_resume(struct dma_chan *chan) { … } /* * ls2x_dma_isr - LS2X DMA Interrupt handler * @irq: IRQ number * @dev_id: Pointer to ls2x_dma_chan * * Return: IRQ_HANDLED/IRQ_NONE */ static irqreturn_t ls2x_dma_isr(int irq, void *dev_id) { … } static int ls2x_dma_chan_init(struct platform_device *pdev, struct ls2x_dma_priv *priv) { … } /* * ls2x_dma_probe - Driver probe function * @pdev: Pointer to the platform_device structure * * Return: '0' on success and failure value on error */ static int ls2x_dma_probe(struct platform_device *pdev) { … } /* * ls2x_dma_remove - Driver remove function * @pdev: Pointer to the platform_device structure */ static void ls2x_dma_remove(struct platform_device *pdev) { … } static const struct of_device_id ls2x_dma_of_match_table[] = …; MODULE_DEVICE_TABLE(of, ls2x_dma_of_match_table); static struct platform_driver ls2x_dmac_driver = …; module_platform_driver(…) …; MODULE_DESCRIPTION(…) …; MODULE_AUTHOR(…) …; MODULE_LICENSE(…) …;