Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dmaengine: fsl-edma: add ColdFire mcf5441x edma support

This patch adds support for ColdFire mcf5441x-family edma
module.

The ColdFire edma module is slightly different from fsl-edma,
so a new driver is added. But most of the code is common
between fsl-edma and mcf-edma so it has been collected into a
separate common module fsl-edma-common (patch 1/3).

Signed-off-by: Angelo Dureghello <angelo@sysam.it>
Tested-by: Krzysztof Kozlowski <krzk@kernel.org>
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Angelo Dureghello and committed by
Vinod Koul
e7a3ff92 4d6d3a90

+387 -4
+11
drivers/dma/Kconfig
··· 321 321 Enable support for DMA on NXP LPC18xx/43xx platforms 322 322 with PL080 and multiplexed DMA request lines. 323 323 324 + config MCF_EDMA 325 + tristate "Freescale eDMA engine support, ColdFire mcf5441x SoCs" 326 + depends on M5441x || COMPILE_TEST 327 + select DMA_ENGINE 328 + select DMA_VIRTUAL_CHANNELS 329 + help 330 + Support the Freescale ColdFire eDMA engine, 64-channel 331 + implementation that performs complex data transfers with 332 + minimal intervention from a host processor. 333 + This module can be found on Freescale ColdFire mcf5441x SoCs. 334 + 324 335 config MMP_PDMA 325 336 bool "MMP PDMA support" 326 337 depends on ARCH_MMP || ARCH_PXA || COMPILE_TEST
+1
drivers/dma/Makefile
··· 32 32 obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o 33 33 obj-$(CONFIG_FSL_DMA) += fsldma.o 34 34 obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o 35 + obj-$(CONFIG_MCF_EDMA) += mcf-edma.o fsl-edma-common.o 35 36 obj-$(CONFIG_FSL_RAID) += fsl_raid.o 36 37 obj-$(CONFIG_HSU_DMA) += hsu/ 37 38 obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o
+20 -4
drivers/dma/fsl-edma-common.c
··· 46 46 struct edma_regs *regs = &fsl_chan->edma->regs; 47 47 u32 ch = fsl_chan->vchan.chan.chan_id; 48 48 49 - edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei); 50 - edma_writeb(fsl_chan->edma, ch, regs->serq); 49 + if (fsl_chan->edma->version == v1) { 50 + edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei); 51 + edma_writeb(fsl_chan->edma, ch, regs->serq); 52 + } else { 53 + /* ColdFire is big endian, and accesses natively 54 + * big endian I/O peripherals 55 + */ 56 + iowrite8(EDMA_SEEI_SEEI(ch), regs->seei); 57 + iowrite8(ch, regs->serq); 58 + } 51 59 } 52 60 53 61 void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) ··· 63 55 struct edma_regs *regs = &fsl_chan->edma->regs; 64 56 u32 ch = fsl_chan->vchan.chan.chan_id; 65 57 66 - edma_writeb(fsl_chan->edma, ch, regs->cerq); 67 - edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei); 58 + if (fsl_chan->edma->version == v1) { 59 + edma_writeb(fsl_chan->edma, ch, regs->cerq); 60 + edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei); 61 + } else { 62 + /* ColdFire is big endian, and accesses natively 63 + * big endian I/O peripherals 64 + */ 65 + iowrite8(ch, regs->cerq); 66 + iowrite8(EDMA_CEEI_CEEI(ch), regs->ceei); 67 + } 68 68 } 69 69 EXPORT_SYMBOL_GPL(fsl_edma_disable_request); 70 70
+317
drivers/dma/mcf-edma.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + // 3 + // Copyright (c) 2013-2014 Freescale Semiconductor, Inc 4 + // Copyright (c) 2017 Sysam, Angelo Dureghello <angelo@sysam.it> 5 + 6 + #include <linux/module.h> 7 + #include <linux/interrupt.h> 8 + #include <linux/dmaengine.h> 9 + #include <linux/platform_device.h> 10 + #include <linux/platform_data/dma-mcf-edma.h> 11 + 12 + #include "fsl-edma-common.h" 13 + 14 + #define EDMA_CHANNELS 64 15 + #define EDMA_MASK_CH(x) ((x) & GENMASK(5, 0)) 16 + 17 + static irqreturn_t mcf_edma_tx_handler(int irq, void *dev_id) 18 + { 19 + struct fsl_edma_engine *mcf_edma = dev_id; 20 + struct edma_regs *regs = &mcf_edma->regs; 21 + unsigned int ch; 22 + struct fsl_edma_chan *mcf_chan; 23 + u64 intmap; 24 + 25 + intmap = ioread32(regs->inth); 26 + intmap <<= 32; 27 + intmap |= ioread32(regs->intl); 28 + if (!intmap) 29 + return IRQ_NONE; 30 + 31 + for (ch = 0; ch < mcf_edma->n_chans; ch++) { 32 + if (intmap & BIT(ch)) { 33 + iowrite8(EDMA_MASK_CH(ch), regs->cint); 34 + 35 + mcf_chan = &mcf_edma->chans[ch]; 36 + 37 + spin_lock(&mcf_chan->vchan.lock); 38 + if (!mcf_chan->edesc->iscyclic) { 39 + list_del(&mcf_chan->edesc->vdesc.node); 40 + vchan_cookie_complete(&mcf_chan->edesc->vdesc); 41 + mcf_chan->edesc = NULL; 42 + mcf_chan->status = DMA_COMPLETE; 43 + mcf_chan->idle = true; 44 + } else { 45 + vchan_cyclic_callback(&mcf_chan->edesc->vdesc); 46 + } 47 + 48 + if (!mcf_chan->edesc) 49 + fsl_edma_xfer_desc(mcf_chan); 50 + 51 + spin_unlock(&mcf_chan->vchan.lock); 52 + } 53 + } 54 + 55 + return IRQ_HANDLED; 56 + } 57 + 58 + static irqreturn_t mcf_edma_err_handler(int irq, void *dev_id) 59 + { 60 + struct fsl_edma_engine *mcf_edma = dev_id; 61 + struct edma_regs *regs = &mcf_edma->regs; 62 + unsigned int err, ch; 63 + 64 + err = ioread32(regs->errl); 65 + if (!err) 66 + return IRQ_NONE; 67 + 68 + for (ch = 0; ch < (EDMA_CHANNELS / 2); ch++) { 69 + if (err & BIT(ch)) { 70 + fsl_edma_disable_request(&mcf_edma->chans[ch]); 71 + iowrite8(EDMA_CERR_CERR(ch), regs->cerr); 72 + mcf_edma->chans[ch].status = DMA_ERROR; 73 + mcf_edma->chans[ch].idle = true; 74 + } 75 + } 76 + 77 + err = ioread32(regs->errh); 78 + if (!err) 79 + return IRQ_NONE; 80 + 81 + for (ch = (EDMA_CHANNELS / 2); ch < EDMA_CHANNELS; ch++) { 82 + if (err & (BIT(ch - (EDMA_CHANNELS / 2)))) { 83 + fsl_edma_disable_request(&mcf_edma->chans[ch]); 84 + iowrite8(EDMA_CERR_CERR(ch), regs->cerr); 85 + mcf_edma->chans[ch].status = DMA_ERROR; 86 + mcf_edma->chans[ch].idle = true; 87 + } 88 + } 89 + 90 + return IRQ_HANDLED; 91 + } 92 + 93 + static int mcf_edma_irq_init(struct platform_device *pdev, 94 + struct fsl_edma_engine *mcf_edma) 95 + { 96 + int ret = 0, i; 97 + struct resource *res; 98 + 99 + res = platform_get_resource_byname(pdev, 100 + IORESOURCE_IRQ, "edma-tx-00-15"); 101 + if (!res) 102 + return -1; 103 + 104 + for (ret = 0, i = res->start; i <= res->end; ++i) 105 + ret |= request_irq(i, mcf_edma_tx_handler, 0, "eDMA", mcf_edma); 106 + if (ret) 107 + return ret; 108 + 109 + res = platform_get_resource_byname(pdev, 110 + IORESOURCE_IRQ, "edma-tx-16-55"); 111 + if (!res) 112 + return -1; 113 + 114 + for (ret = 0, i = res->start; i <= res->end; ++i) 115 + ret |= request_irq(i, mcf_edma_tx_handler, 0, "eDMA", mcf_edma); 116 + if (ret) 117 + return ret; 118 + 119 + ret = platform_get_irq_byname(pdev, "edma-tx-56-63"); 120 + if (ret != -ENXIO) { 121 + ret = request_irq(ret, mcf_edma_tx_handler, 122 + 0, "eDMA", mcf_edma); 123 + if (ret) 124 + return ret; 125 + } 126 + 127 + ret = platform_get_irq_byname(pdev, "edma-err"); 128 + if (ret != -ENXIO) { 129 + ret = request_irq(ret, mcf_edma_err_handler, 130 + 0, "eDMA", mcf_edma); 131 + if (ret) 132 + return ret; 133 + } 134 + 135 + return 0; 136 + } 137 + 138 + static void mcf_edma_irq_free(struct platform_device *pdev, 139 + struct fsl_edma_engine *mcf_edma) 140 + { 141 + int irq; 142 + struct resource *res; 143 + 144 + res = platform_get_resource_byname(pdev, 145 + IORESOURCE_IRQ, "edma-tx-00-15"); 146 + if (res) { 147 + for (irq = res->start; irq <= res->end; irq++) 148 + free_irq(irq, mcf_edma); 149 + } 150 + 151 + res = platform_get_resource_byname(pdev, 152 + IORESOURCE_IRQ, "edma-tx-16-55"); 153 + if (res) { 154 + for (irq = res->start; irq <= res->end; irq++) 155 + free_irq(irq, mcf_edma); 156 + } 157 + 158 + irq = platform_get_irq_byname(pdev, "edma-tx-56-63"); 159 + if (irq != -ENXIO) 160 + free_irq(irq, mcf_edma); 161 + 162 + irq = platform_get_irq_byname(pdev, "edma-err"); 163 + if (irq != -ENXIO) 164 + free_irq(irq, mcf_edma); 165 + } 166 + 167 + static int mcf_edma_probe(struct platform_device *pdev) 168 + { 169 + struct mcf_edma_platform_data *pdata; 170 + struct fsl_edma_engine *mcf_edma; 171 + struct fsl_edma_chan *mcf_chan; 172 + struct edma_regs *regs; 173 + struct resource *res; 174 + int ret, i, len, chans; 175 + 176 + pdata = dev_get_platdata(&pdev->dev); 177 + if (!pdata) { 178 + dev_err(&pdev->dev, "no platform data supplied\n"); 179 + return -EINVAL; 180 + } 181 + 182 + chans = pdata->dma_channels; 183 + len = sizeof(*mcf_edma) + sizeof(*mcf_chan) * chans; 184 + mcf_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); 185 + if (!mcf_edma) 186 + return -ENOMEM; 187 + 188 + mcf_edma->n_chans = chans; 189 + 190 + /* Set up version for ColdFire edma */ 191 + mcf_edma->version = v2; 192 + mcf_edma->big_endian = 1; 193 + 194 + if (!mcf_edma->n_chans) { 195 + dev_info(&pdev->dev, "setting default channel number to 64"); 196 + mcf_edma->n_chans = 64; 197 + } 198 + 199 + mutex_init(&mcf_edma->fsl_edma_mutex); 200 + 201 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 202 + 203 + mcf_edma->membase = devm_ioremap_resource(&pdev->dev, res); 204 + if (IS_ERR(mcf_edma->membase)) 205 + return PTR_ERR(mcf_edma->membase); 206 + 207 + fsl_edma_setup_regs(mcf_edma); 208 + regs = &mcf_edma->regs; 209 + 210 + INIT_LIST_HEAD(&mcf_edma->dma_dev.channels); 211 + for (i = 0; i < mcf_edma->n_chans; i++) { 212 + struct fsl_edma_chan *mcf_chan = &mcf_edma->chans[i]; 213 + 214 + mcf_chan->edma = mcf_edma; 215 + mcf_chan->slave_id = i; 216 + mcf_chan->idle = true; 217 + mcf_chan->vchan.desc_free = fsl_edma_free_desc; 218 + vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev); 219 + iowrite32(0x0, &regs->tcd[i].csr); 220 + } 221 + 222 + iowrite32(~0, regs->inth); 223 + iowrite32(~0, regs->intl); 224 + 225 + ret = mcf_edma_irq_init(pdev, mcf_edma); 226 + if (ret) 227 + return ret; 228 + 229 + dma_cap_set(DMA_PRIVATE, mcf_edma->dma_dev.cap_mask); 230 + dma_cap_set(DMA_SLAVE, mcf_edma->dma_dev.cap_mask); 231 + dma_cap_set(DMA_CYCLIC, mcf_edma->dma_dev.cap_mask); 232 + 233 + mcf_edma->dma_dev.dev = &pdev->dev; 234 + mcf_edma->dma_dev.device_alloc_chan_resources = 235 + fsl_edma_alloc_chan_resources; 236 + mcf_edma->dma_dev.device_free_chan_resources = 237 + fsl_edma_free_chan_resources; 238 + mcf_edma->dma_dev.device_config = fsl_edma_slave_config; 239 + mcf_edma->dma_dev.device_prep_dma_cyclic = 240 + fsl_edma_prep_dma_cyclic; 241 + mcf_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg; 242 + mcf_edma->dma_dev.device_tx_status = fsl_edma_tx_status; 243 + mcf_edma->dma_dev.device_pause = fsl_edma_pause; 244 + mcf_edma->dma_dev.device_resume = fsl_edma_resume; 245 + mcf_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all; 246 + mcf_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending; 247 + 248 + mcf_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS; 249 + mcf_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS; 250 + mcf_edma->dma_dev.directions = 251 + BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 252 + 253 + mcf_edma->dma_dev.filter.fn = mcf_edma_filter_fn; 254 + mcf_edma->dma_dev.filter.map = pdata->slave_map; 255 + mcf_edma->dma_dev.filter.mapcnt = pdata->slavecnt; 256 + 257 + platform_set_drvdata(pdev, mcf_edma); 258 + 259 + ret = dma_async_device_register(&mcf_edma->dma_dev); 260 + if (ret) { 261 + dev_err(&pdev->dev, 262 + "Can't register Freescale eDMA engine. (%d)\n", ret); 263 + return ret; 264 + } 265 + 266 + /* Enable round robin arbitration */ 267 + iowrite32(EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); 268 + 269 + return 0; 270 + } 271 + 272 + static int mcf_edma_remove(struct platform_device *pdev) 273 + { 274 + struct fsl_edma_engine *mcf_edma = platform_get_drvdata(pdev); 275 + 276 + mcf_edma_irq_free(pdev, mcf_edma); 277 + fsl_edma_cleanup_vchan(&mcf_edma->dma_dev); 278 + dma_async_device_unregister(&mcf_edma->dma_dev); 279 + 280 + return 0; 281 + } 282 + 283 + static struct platform_driver mcf_edma_driver = { 284 + .driver = { 285 + .name = "mcf-edma", 286 + }, 287 + .probe = mcf_edma_probe, 288 + .remove = mcf_edma_remove, 289 + }; 290 + 291 + bool mcf_edma_filter_fn(struct dma_chan *chan, void *param) 292 + { 293 + if (chan->device->dev->driver == &mcf_edma_driver.driver) { 294 + struct fsl_edma_chan *mcf_chan = to_fsl_edma_chan(chan); 295 + 296 + return (mcf_chan->slave_id == (u32)param); 297 + } 298 + 299 + return false; 300 + } 301 + EXPORT_SYMBOL(mcf_edma_filter_fn); 302 + 303 + static int __init mcf_edma_init(void) 304 + { 305 + return platform_driver_register(&mcf_edma_driver); 306 + } 307 + subsys_initcall(mcf_edma_init); 308 + 309 + static void __exit mcf_edma_exit(void) 310 + { 311 + platform_driver_unregister(&mcf_edma_driver); 312 + } 313 + module_exit(mcf_edma_exit); 314 + 315 + MODULE_ALIAS("platform:mcf-edma"); 316 + MODULE_DESCRIPTION("Freescale eDMA engine driver, ColdFire family"); 317 + MODULE_LICENSE("GPL v2");
+38
include/linux/platform_data/dma-mcf-edma.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Freescale eDMA platform data, ColdFire SoC's family. 4 + * 5 + * Copyright (c) 2017 Angelo Dureghello <angelo@sysam.it> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + */ 16 + 17 + #ifndef __LINUX_PLATFORM_DATA_MCF_EDMA_H__ 18 + #define __LINUX_PLATFORM_DATA_MCF_EDMA_H__ 19 + 20 + struct dma_slave_map; 21 + 22 + bool mcf_edma_filter_fn(struct dma_chan *chan, void *param); 23 + 24 + #define MCF_EDMA_FILTER_PARAM(ch) ((void *)ch) 25 + 26 + /** 27 + * struct mcf_edma_platform_data - platform specific data for eDMA engine 28 + * 29 + * @ver The eDMA module version. 30 + * @dma_channels The number of eDMA channels. 31 + */ 32 + struct mcf_edma_platform_data { 33 + int dma_channels; 34 + const struct dma_slave_map *slave_map; 35 + int slavecnt; 36 + }; 37 + 38 + #endif /* __LINUX_PLATFORM_DATA_MCF_EDMA_H__ */