Path: blob/master/drivers/dma/loongson/loongson2-apb-cmc-dma.c
170890 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Loongson-2 Chain Multi-Channel DMA Controller driver3*4* Copyright (C) 2024-2026 Loongson Technology Corporation Limited5*/67#include <linux/acpi.h>8#include <linux/acpi_dma.h>9#include <linux/bitfield.h>10#include <linux/clk.h>11#include <linux/dma-mapping.h>12#include <linux/dmapool.h>13#include <linux/interrupt.h>14#include <linux/io.h>15#include <linux/module.h>16#include <linux/of.h>17#include <linux/of_dma.h>18#include <linux/platform_device.h>19#include <linux/slab.h>2021#include "../dmaengine.h"22#include "../virt-dma.h"2324#define LOONGSON2_CMCDMA_ISR 0x0 /* DMA Interrupt Status Register */25#define LOONGSON2_CMCDMA_IFCR 0x4 /* DMA Interrupt Flag Clear Register */26#define LOONGSON2_CMCDMA_CCR 0x8 /* DMA Channel Configuration Register */27#define LOONGSON2_CMCDMA_CNDTR 0xc /* DMA Channel Transmit Count Register */28#define LOONGSON2_CMCDMA_CPAR 0x10 /* DMA Channel Peripheral Address Register */29#define LOONGSON2_CMCDMA_CMAR 0x14 /* DMA Channel Memory Address Register */3031/* Bitfields of DMA interrupt status register */32#define LOONGSON2_CMCDMA_TCI BIT(1) /* Transfer Complete Interrupt */33#define LOONGSON2_CMCDMA_HTI BIT(2) /* Half Transfer Interrupt */34#define LOONGSON2_CMCDMA_TEI BIT(3) /* Transfer Error Interrupt */3536#define LOONGSON2_CMCDMA_MASKI \37(LOONGSON2_CMCDMA_TCI | LOONGSON2_CMCDMA_HTI | LOONGSON2_CMCDMA_TEI)3839/* Bitfields of DMA channel x Configuration Register */40#define LOONGSON2_CMCDMA_CCR_EN BIT(0) /* Stream Enable */41#define LOONGSON2_CMCDMA_CCR_TCIE BIT(1) /* Transfer Complete Interrupt Enable */42#define LOONGSON2_CMCDMA_CCR_HTIE BIT(2) /* Half Transfer Complete Interrupt Enable */43#define LOONGSON2_CMCDMA_CCR_TEIE BIT(3) /* Transfer Error Interrupt Enable */44#define LOONGSON2_CMCDMA_CCR_DIR BIT(4) /* Data Transfer Direction */45#define LOONGSON2_CMCDMA_CCR_CIRC BIT(5) /* Circular mode */46#define LOONGSON2_CMCDMA_CCR_PINC BIT(6) /* Peripheral increment mode */47#define LOONGSON2_CMCDMA_CCR_MINC BIT(7) /* Memory increment mode */48#define LOONGSON2_CMCDMA_CCR_PSIZE_MASK GENMASK(9, 8)49#define LOONGSON2_CMCDMA_CCR_MSIZE_MASK GENMASK(11, 10)50#define LOONGSON2_CMCDMA_CCR_PL_MASK GENMASK(13, 12)51#define LOONGSON2_CMCDMA_CCR_M2M BIT(14)5253#define LOONGSON2_CMCDMA_CCR_CFG_MASK \54(LOONGSON2_CMCDMA_CCR_PINC | LOONGSON2_CMCDMA_CCR_MINC | LOONGSON2_CMCDMA_CCR_PL_MASK)5556#define LOONGSON2_CMCDMA_CCR_IRQ_MASK \57(LOONGSON2_CMCDMA_CCR_TCIE | LOONGSON2_CMCDMA_CCR_HTIE | LOONGSON2_CMCDMA_CCR_TEIE)5859#define LOONGSON2_CMCDMA_STREAM_MASK \60(LOONGSON2_CMCDMA_CCR_CFG_MASK | LOONGSON2_CMCDMA_CCR_IRQ_MASK)6162#define LOONGSON2_CMCDMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \63BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \64BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))6566#define LOONSON2_CMCDMA_MAX_DATA_ITEMS SZ_64K6768struct loongson2_cmc_dma_chan_reg {69u32 ccr;70u32 cndtr;71u32 cpar;72u32 cmar;73};7475struct loongson2_cmc_dma_sg_req {76u32 len;77struct loongson2_cmc_dma_chan_reg chan_reg;78};7980struct loongson2_cmc_dma_desc {81struct virt_dma_desc vdesc;82bool cyclic;83u32 num_sgs;84struct loongson2_cmc_dma_sg_req sg_req[] __counted_by(num_sgs);85};8687struct loongson2_cmc_dma_chan {88struct virt_dma_chan vchan;89struct dma_slave_config dma_sconfig;90struct loongson2_cmc_dma_desc *desc;91u32 id;92u32 irq;93u32 next_sg;94struct loongson2_cmc_dma_chan_reg chan_reg;95};9697struct loongson2_cmc_dma_dev {98struct dma_device ddev;99struct clk *dma_clk;100void __iomem *base;101u32 nr_channels;102u32 chan_reg_offset;103struct loongson2_cmc_dma_chan chan[] __counted_by(nr_channels);104};105106struct loongson2_cmc_dma_config {107u32 max_channels;108u32 chan_reg_offset;109};110111static const struct loongson2_cmc_dma_config ls2k0300_cmc_dma_config = {112.max_channels = 8,113.chan_reg_offset = 0x14,114};115116static const struct loongson2_cmc_dma_config ls2k3000_cmc_dma_config = {117.max_channels = 4,118.chan_reg_offset = 0x18,119};120121static struct loongson2_cmc_dma_dev *lmdma_get_dev(struct loongson2_cmc_dma_chan *lchan)122{123return container_of(lchan->vchan.chan.device, struct loongson2_cmc_dma_dev, ddev);124}125126static struct loongson2_cmc_dma_chan *to_lmdma_chan(struct dma_chan *chan)127{128return container_of(chan, struct loongson2_cmc_dma_chan, vchan.chan);129}130131static struct loongson2_cmc_dma_desc *to_lmdma_desc(struct virt_dma_desc *vdesc)132{133return container_of(vdesc, struct loongson2_cmc_dma_desc, vdesc);134}135136static struct device *chan2dev(struct loongson2_cmc_dma_chan *lchan)137{138return &lchan->vchan.chan.dev->device;139}140141static u32 loongson2_cmc_dma_read(struct loongson2_cmc_dma_dev *lddev, u32 reg, u32 id)142{143return readl(lddev->base + (reg + lddev->chan_reg_offset * id));144}145146static void loongson2_cmc_dma_write(struct loongson2_cmc_dma_dev *lddev, u32 reg, u32 id, u32 val)147{148writel(val, lddev->base + (reg + lddev->chan_reg_offset * id));149}150151static int loongson2_cmc_dma_get_width(enum dma_slave_buswidth width)152{153switch (width) {154case DMA_SLAVE_BUSWIDTH_1_BYTE:155case DMA_SLAVE_BUSWIDTH_2_BYTES:156case DMA_SLAVE_BUSWIDTH_4_BYTES:157return ffs(width) - 1;158default:159return -EINVAL;160}161}162163static int loongson2_cmc_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *config)164{165struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);166167memcpy(&lchan->dma_sconfig, config, sizeof(*config));168169return 0;170}171172static void loongson2_cmc_dma_irq_clear(struct loongson2_cmc_dma_chan *lchan, u32 flags)173{174struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);175u32 ifcr;176177ifcr = flags << (4 * lchan->id);178loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_IFCR, 0, ifcr);179}180181static void loongson2_cmc_dma_stop(struct loongson2_cmc_dma_chan *lchan)182{183struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);184u32 ccr;185186ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id);187ccr &= ~(LOONGSON2_CMCDMA_CCR_IRQ_MASK | LOONGSON2_CMCDMA_CCR_EN);188loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, ccr);189190loongson2_cmc_dma_irq_clear(lchan, LOONGSON2_CMCDMA_MASKI);191}192193static int loongson2_cmc_dma_terminate_all(struct dma_chan *chan)194{195struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);196197LIST_HEAD(head);198199scoped_guard(spinlock_irqsave, &lchan->vchan.lock) {200if (lchan->desc) {201vchan_terminate_vdesc(&lchan->desc->vdesc);202loongson2_cmc_dma_stop(lchan);203lchan->desc = NULL;204}205vchan_get_all_descriptors(&lchan->vchan, &head);206}207208vchan_dma_desc_free_list(&lchan->vchan, &head);209210return 0;211}212213static void loongson2_cmc_dma_synchronize(struct dma_chan *chan)214{215struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);216217vchan_synchronize(&lchan->vchan);218}219220static void loongson2_cmc_dma_start_transfer(struct loongson2_cmc_dma_chan *lchan)221{222struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);223struct loongson2_cmc_dma_sg_req *sg_req;224struct loongson2_cmc_dma_chan_reg *reg;225struct virt_dma_desc *vdesc;226227loongson2_cmc_dma_stop(lchan);228229if (!lchan->desc) {230vdesc = vchan_next_desc(&lchan->vchan);231if (!vdesc)232return;233234list_del(&vdesc->node);235lchan->desc = to_lmdma_desc(vdesc);236lchan->next_sg = 0;237}238239if (lchan->next_sg == lchan->desc->num_sgs)240lchan->next_sg = 0;241242sg_req = &lchan->desc->sg_req[lchan->next_sg];243reg = &sg_req->chan_reg;244245loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, reg->ccr);246loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CNDTR, lchan->id, reg->cndtr);247loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CPAR, lchan->id, reg->cpar);248loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CMAR, lchan->id, reg->cmar);249250lchan->next_sg++;251252/* Start DMA */253reg->ccr |= LOONGSON2_CMCDMA_CCR_EN;254loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, reg->ccr);255}256257static void loongson2_cmc_dma_configure_next_sg(struct loongson2_cmc_dma_chan *lchan)258{259struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);260struct loongson2_cmc_dma_sg_req *sg_req;261u32 ccr, id = lchan->id;262263if (lchan->next_sg == lchan->desc->num_sgs)264lchan->next_sg = 0;265266/* Stop to update mem addr */267ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, id);268ccr &= ~LOONGSON2_CMCDMA_CCR_EN;269loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, id, ccr);270271sg_req = &lchan->desc->sg_req[lchan->next_sg];272loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CMAR, id, sg_req->chan_reg.cmar);273274/* Start transition */275ccr |= LOONGSON2_CMCDMA_CCR_EN;276loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, id, ccr);277}278279static void loongson2_cmc_dma_handle_chan_done(struct loongson2_cmc_dma_chan *lchan)280{281if (!lchan->desc)282return;283284if (lchan->desc->cyclic) {285vchan_cyclic_callback(&lchan->desc->vdesc);286/* LOONGSON2_CMCDMA_CCR_CIRC mode don't need update register */287if (lchan->desc->num_sgs == 1)288return;289loongson2_cmc_dma_configure_next_sg(lchan);290lchan->next_sg++;291} else {292if (lchan->next_sg == lchan->desc->num_sgs) {293vchan_cookie_complete(&lchan->desc->vdesc);294lchan->desc = NULL;295}296loongson2_cmc_dma_start_transfer(lchan);297}298}299300static irqreturn_t loongson2_cmc_dma_chan_irq(int irq, void *devid)301{302struct loongson2_cmc_dma_chan *lchan = devid;303struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);304struct device *dev = chan2dev(lchan);305u32 ists, status, ccr;306307scoped_guard(spinlock, &lchan->vchan.lock) {308ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id);309ists = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_ISR, 0);310status = (ists >> (4 * lchan->id)) & LOONGSON2_CMCDMA_MASKI;311312loongson2_cmc_dma_irq_clear(lchan, status);313314if (status & LOONGSON2_CMCDMA_TCI) {315loongson2_cmc_dma_handle_chan_done(lchan);316status &= ~LOONGSON2_CMCDMA_TCI;317}318319if (status & LOONGSON2_CMCDMA_HTI)320status &= ~LOONGSON2_CMCDMA_HTI;321322if (status & LOONGSON2_CMCDMA_TEI) {323dev_err(dev, "DMA Transform Error.\n");324if (!(ccr & LOONGSON2_CMCDMA_CCR_EN))325dev_err(dev, "Channel disabled by HW.\n");326}327}328329return IRQ_HANDLED;330}331332static void loongson2_cmc_dma_issue_pending(struct dma_chan *chan)333{334struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);335336guard(spinlock_irqsave)(&lchan->vchan.lock);337338if (vchan_issue_pending(&lchan->vchan) && !lchan->desc) {339dev_dbg(chan2dev(lchan), "vchan %pK: issued\n", &lchan->vchan);340loongson2_cmc_dma_start_transfer(lchan);341}342}343344static int loongson2_cmc_dma_set_xfer_param(struct loongson2_cmc_dma_chan *lchan,345enum dma_transfer_direction direction,346enum dma_slave_buswidth *buswidth, u32 buf_len)347{348struct dma_slave_config sconfig = lchan->dma_sconfig;349struct device *dev = chan2dev(lchan);350int dev_width;351u32 ccr;352353switch (direction) {354case DMA_MEM_TO_DEV:355dev_width = loongson2_cmc_dma_get_width(sconfig.dst_addr_width);356if (dev_width < 0) {357dev_err(dev, "DMA_MEM_TO_DEV bus width not supported\n");358return dev_width;359}360lchan->chan_reg.cpar = sconfig.dst_addr;361ccr = LOONGSON2_CMCDMA_CCR_DIR;362*buswidth = sconfig.dst_addr_width;363break;364case DMA_DEV_TO_MEM:365dev_width = loongson2_cmc_dma_get_width(sconfig.src_addr_width);366if (dev_width < 0) {367dev_err(dev, "DMA_DEV_TO_MEM bus width not supported\n");368return dev_width;369}370lchan->chan_reg.cpar = sconfig.src_addr;371ccr = LOONGSON2_CMCDMA_CCR_MINC;372*buswidth = sconfig.src_addr_width;373break;374default:375return -EINVAL;376}377378ccr |= FIELD_PREP(LOONGSON2_CMCDMA_CCR_PSIZE_MASK, dev_width) |379FIELD_PREP(LOONGSON2_CMCDMA_CCR_MSIZE_MASK, dev_width);380381/* Set DMA control register */382lchan->chan_reg.ccr &= ~(LOONGSON2_CMCDMA_CCR_PSIZE_MASK | LOONGSON2_CMCDMA_CCR_MSIZE_MASK);383lchan->chan_reg.ccr |= ccr;384385return 0;386}387388static struct dma_async_tx_descriptor *389loongson2_cmc_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, u32 sg_len,390enum dma_transfer_direction direction,391unsigned long flags, void *context)392{393struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);394struct loongson2_cmc_dma_desc *desc;395enum dma_slave_buswidth buswidth;396struct scatterlist *sg;397u32 num_items, i;398int ret;399400desc = kzalloc_flex(*desc, sg_req, sg_len, GFP_NOWAIT);401if (!desc)402return ERR_PTR(-ENOMEM);403404for_each_sg(sgl, sg, sg_len, i) {405ret = loongson2_cmc_dma_set_xfer_param(lchan, direction, &buswidth, sg_dma_len(sg));406if (ret)407return ERR_PTR(ret);408409num_items = DIV_ROUND_UP(sg_dma_len(sg), buswidth);410if (num_items >= LOONSON2_CMCDMA_MAX_DATA_ITEMS) {411dev_err(chan2dev(lchan), "Number of items not supported\n");412kfree(desc);413return ERR_PTR(-EINVAL);414}415416desc->sg_req[i].len = sg_dma_len(sg);417desc->sg_req[i].chan_reg.ccr = lchan->chan_reg.ccr;418desc->sg_req[i].chan_reg.cpar = lchan->chan_reg.cpar;419desc->sg_req[i].chan_reg.cmar = sg_dma_address(sg);420desc->sg_req[i].chan_reg.cndtr = num_items;421}422423desc->num_sgs = sg_len;424desc->cyclic = false;425426return vchan_tx_prep(&lchan->vchan, &desc->vdesc, flags);427}428429static struct dma_async_tx_descriptor *430loongson2_cmc_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,431size_t period_len, enum dma_transfer_direction direction,432unsigned long flags)433{434struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);435struct loongson2_cmc_dma_desc *desc;436enum dma_slave_buswidth buswidth;437u32 num_periods, num_items, i;438int ret;439440if (unlikely(buf_len % period_len))441return ERR_PTR(-EINVAL);442443ret = loongson2_cmc_dma_set_xfer_param(lchan, direction, &buswidth, period_len);444if (ret)445return ERR_PTR(ret);446447num_items = DIV_ROUND_UP(period_len, buswidth);448if (num_items >= LOONSON2_CMCDMA_MAX_DATA_ITEMS) {449dev_err(chan2dev(lchan), "Number of items not supported\n");450return ERR_PTR(-EINVAL);451}452453/* Enable Circular mode */454if (buf_len == period_len)455lchan->chan_reg.ccr |= LOONGSON2_CMCDMA_CCR_CIRC;456457num_periods = DIV_ROUND_UP(buf_len, period_len);458desc = kzalloc_flex(*desc, sg_req, num_periods, GFP_NOWAIT);459if (!desc)460return ERR_PTR(-ENOMEM);461462for (i = 0; i < num_periods; i++) {463desc->sg_req[i].len = period_len;464desc->sg_req[i].chan_reg.ccr = lchan->chan_reg.ccr;465desc->sg_req[i].chan_reg.cpar = lchan->chan_reg.cpar;466desc->sg_req[i].chan_reg.cmar = buf_addr;467desc->sg_req[i].chan_reg.cndtr = num_items;468buf_addr += period_len;469}470471desc->num_sgs = num_periods;472desc->cyclic = true;473474return vchan_tx_prep(&lchan->vchan, &desc->vdesc, flags);475}476477static size_t loongson2_cmc_dma_desc_residue(struct loongson2_cmc_dma_chan *lchan,478struct loongson2_cmc_dma_desc *desc, u32 next_sg)479{480struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);481u32 residue, width, ndtr, ccr, i;482483ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id);484width = FIELD_GET(LOONGSON2_CMCDMA_CCR_PSIZE_MASK, ccr);485486ndtr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CNDTR, lchan->id);487residue = ndtr << width;488489if (lchan->desc->cyclic && next_sg == 0)490return residue;491492for (i = next_sg; i < desc->num_sgs; i++)493residue += desc->sg_req[i].len;494495return residue;496}497498static enum dma_status loongson2_cmc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,499struct dma_tx_state *state)500{501struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);502struct virt_dma_desc *vdesc;503enum dma_status status;504505status = dma_cookie_status(chan, cookie, state);506if (status == DMA_COMPLETE || !state)507return status;508509scoped_guard(spinlock_irqsave, &lchan->vchan.lock) {510vdesc = vchan_find_desc(&lchan->vchan, cookie);511if (lchan->desc && cookie == lchan->desc->vdesc.tx.cookie)512state->residue = loongson2_cmc_dma_desc_residue(lchan, lchan->desc,513lchan->next_sg);514else if (vdesc)515state->residue = loongson2_cmc_dma_desc_residue(lchan,516to_lmdma_desc(vdesc), 0);517}518519return status;520}521522static void loongson2_cmc_dma_free_chan_resources(struct dma_chan *chan)523{524vchan_free_chan_resources(to_virt_chan(chan));525}526527static void loongson2_cmc_dma_desc_free(struct virt_dma_desc *vdesc)528{529kfree(to_lmdma_desc(vdesc));530}531532static bool loongson2_cmc_dma_acpi_filter(struct dma_chan *chan, void *param)533{534struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);535struct acpi_dma_spec *dma_spec = param;536537memset(&lchan->chan_reg, 0, sizeof(struct loongson2_cmc_dma_chan_reg));538lchan->chan_reg.ccr = dma_spec->chan_id & LOONGSON2_CMCDMA_STREAM_MASK;539540return true;541}542543static int loongson2_cmc_dma_acpi_controller_register(struct loongson2_cmc_dma_dev *lddev)544{545struct device *dev = lddev->ddev.dev;546struct acpi_dma_filter_info *info;547548if (!is_acpi_node(dev_fwnode(dev)))549return 0;550551info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);552if (!info)553return -ENOMEM;554555dma_cap_zero(info->dma_cap);556info->dma_cap = lddev->ddev.cap_mask;557info->filter_fn = loongson2_cmc_dma_acpi_filter;558559return devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate, info);560}561562static struct dma_chan *loongson2_cmc_dma_of_xlate(struct of_phandle_args *dma_spec,563struct of_dma *ofdma)564{565struct loongson2_cmc_dma_dev *lddev = ofdma->of_dma_data;566struct device *dev = lddev->ddev.dev;567struct loongson2_cmc_dma_chan *lchan;568struct dma_chan *chan;569570if (dma_spec->args_count < 2)571return ERR_PTR(-EINVAL);572573if (dma_spec->args[0] >= lddev->nr_channels) {574dev_err(dev, "Invalid channel id.\n");575return ERR_PTR(-EINVAL);576}577578lchan = &lddev->chan[dma_spec->args[0]];579chan = dma_get_slave_channel(&lchan->vchan.chan);580if (!chan) {581dev_err(dev, "No more channels available.\n");582return ERR_PTR(-EINVAL);583}584585memset(&lchan->chan_reg, 0, sizeof(struct loongson2_cmc_dma_chan_reg));586lchan->chan_reg.ccr = dma_spec->args[1] & LOONGSON2_CMCDMA_STREAM_MASK;587588return chan;589}590591static int loongson2_cmc_dma_of_controller_register(struct loongson2_cmc_dma_dev *lddev)592{593struct device *dev = lddev->ddev.dev;594595if (!is_of_node(dev_fwnode(dev)))596return 0;597598return of_dma_controller_register(dev->of_node, loongson2_cmc_dma_of_xlate, lddev);599}600601static int loongson2_cmc_dma_probe(struct platform_device *pdev)602{603const struct loongson2_cmc_dma_config *config;604struct loongson2_cmc_dma_chan *lchan;605struct loongson2_cmc_dma_dev *lddev;606struct device *dev = &pdev->dev;607struct dma_device *ddev;608u32 nr_chans, i;609int ret;610611config = (const struct loongson2_cmc_dma_config *)device_get_match_data(dev);612if (!config)613return -EINVAL;614615ret = device_property_read_u32(dev, "dma-channels", &nr_chans);616if (ret || nr_chans > config->max_channels) {617dev_err(dev, "missing or invalid dma-channels property\n");618nr_chans = config->max_channels;619}620621lddev = devm_kzalloc(dev, struct_size(lddev, chan, nr_chans), GFP_KERNEL);622if (!lddev)623return -ENOMEM;624625lddev->base = devm_platform_ioremap_resource(pdev, 0);626if (IS_ERR(lddev->base))627return PTR_ERR(lddev->base);628629platform_set_drvdata(pdev, lddev);630lddev->nr_channels = nr_chans;631lddev->chan_reg_offset = config->chan_reg_offset;632633lddev->dma_clk = devm_clk_get_optional_enabled(dev, NULL);634if (IS_ERR(lddev->dma_clk))635return dev_err_probe(dev, PTR_ERR(lddev->dma_clk), "Failed to get dma clock\n");636637ddev = &lddev->ddev;638ddev->dev = dev;639640dma_cap_zero(ddev->cap_mask);641dma_cap_set(DMA_SLAVE, ddev->cap_mask);642dma_cap_set(DMA_PRIVATE, ddev->cap_mask);643dma_cap_set(DMA_CYCLIC, ddev->cap_mask);644645ddev->device_free_chan_resources = loongson2_cmc_dma_free_chan_resources;646ddev->device_config = loongson2_cmc_dma_slave_config;647ddev->device_prep_slave_sg = loongson2_cmc_dma_prep_slave_sg;648ddev->device_prep_dma_cyclic = loongson2_cmc_dma_prep_dma_cyclic;649ddev->device_issue_pending = loongson2_cmc_dma_issue_pending;650ddev->device_synchronize = loongson2_cmc_dma_synchronize;651ddev->device_tx_status = loongson2_cmc_dma_tx_status;652ddev->device_terminate_all = loongson2_cmc_dma_terminate_all;653654ddev->max_sg_burst = LOONSON2_CMCDMA_MAX_DATA_ITEMS;655ddev->src_addr_widths = LOONGSON2_CMCDMA_BUSWIDTHS;656ddev->dst_addr_widths = LOONGSON2_CMCDMA_BUSWIDTHS;657ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);658INIT_LIST_HEAD(&ddev->channels);659660for (i = 0; i < nr_chans; i++) {661lchan = &lddev->chan[i];662663lchan->id = i;664lchan->vchan.desc_free = loongson2_cmc_dma_desc_free;665vchan_init(&lchan->vchan, ddev);666}667668ret = dmaenginem_async_device_register(ddev);669if (ret)670return dev_err_probe(dev, ret, "Failed to register DMA engine device.\n");671672for (i = 0; i < nr_chans; i++) {673lchan = &lddev->chan[i];674675lchan->irq = platform_get_irq(pdev, i);676if (lchan->irq < 0)677return lchan->irq;678679ret = devm_request_irq(dev, lchan->irq, loongson2_cmc_dma_chan_irq, IRQF_SHARED,680dev_name(chan2dev(lchan)), lchan);681if (ret)682return ret;683}684685ret = loongson2_cmc_dma_acpi_controller_register(lddev);686if (ret)687return dev_err_probe(dev, ret, "Failed to register dma controller with ACPI.\n");688689ret = loongson2_cmc_dma_of_controller_register(lddev);690if (ret)691return dev_err_probe(dev, ret, "Failed to register dma controller with FDT.\n");692693dev_info(dev, "Loongson-2 Multi-Channel DMA Controller registered successfully.\n");694695return 0;696}697698static void loongson2_cmc_dma_remove(struct platform_device *pdev)699{700of_dma_controller_free(pdev->dev.of_node);701}702703static const struct of_device_id loongson2_cmc_dma_of_match[] = {704{ .compatible = "loongson,ls2k0300-dma", .data = &ls2k0300_cmc_dma_config },705{ .compatible = "loongson,ls2k3000-dma", .data = &ls2k3000_cmc_dma_config },706{ /* sentinel */ }707};708MODULE_DEVICE_TABLE(of, loongson2_cmc_dma_of_match);709710static const struct acpi_device_id loongson2_cmc_dma_acpi_match[] = {711{ "LOON0014", .driver_data = (kernel_ulong_t)&ls2k3000_cmc_dma_config },712{ /* sentinel */ }713};714MODULE_DEVICE_TABLE(acpi, loongson2_cmc_dma_acpi_match);715716static struct platform_driver loongson2_cmc_dma_driver = {717.driver = {718.name = "loongson2-apb-cmc-dma",719.of_match_table = loongson2_cmc_dma_of_match,720.acpi_match_table = loongson2_cmc_dma_acpi_match,721},722.probe = loongson2_cmc_dma_probe,723.remove = loongson2_cmc_dma_remove,724};725module_platform_driver(loongson2_cmc_dma_driver);726727MODULE_DESCRIPTION("Loongson-2 Chain Multi-Channel DMA Controller driver");728MODULE_AUTHOR("Loongson Technology Corporation Limited");729MODULE_LICENSE("GPL");730731732