1// SPDX-License-Identifier: GPL-2.0 2// 3// Copyright (C) 2019 Linaro Ltd. 4// Copyright (C) 2019 Socionext Inc. 5 6#include <linux/bits.h> 7#include <linux/dma-mapping.h> 8#include <linux/dmaengine.h> 9#include <linux/interrupt.h> 10#include <linux/iopoll.h> 11#include <linux/list.h> 12#include <linux/module.h> 13#include <linux/of_dma.h> 14#include <linux/platform_device.h> 15#include <linux/slab.h> 16#include <linux/types.h> 17#include <linux/bitfield.h> 18 19#include "virt-dma.h" 20 21/* global register */ 22#define M10V_XDACS 0x00 23 24/* channel local register */ 25#define M10V_XDTBC 0x10 26#define M10V_XDSSA 0x14 27#define M10V_XDDSA 0x18 28#define M10V_XDSAC 0x1C 29#define M10V_XDDAC 0x20 30#define M10V_XDDCC 0x24 31#define M10V_XDDES 0x28 32#define M10V_XDDPC 0x2C 33#define M10V_XDDSD 0x30 34 35#define M10V_XDACS_XE BIT(28) 36 37#define M10V_DEFBS 0x3 38#define M10V_DEFBL 0xf 39 40#define M10V_XDSAC_SBS GENMASK(17, 16) 41#define M10V_XDSAC_SBL GENMASK(11, 8) 42 43#define M10V_XDDAC_DBS GENMASK(17, 16) 44#define M10V_XDDAC_DBL GENMASK(11, 8) 45 46#define M10V_XDDES_CE BIT(28) 47#define M10V_XDDES_SE BIT(24) 48#define M10V_XDDES_SA BIT(15) 49#define M10V_XDDES_TF GENMASK(23, 20) 50#define M10V_XDDES_EI BIT(1) 51#define M10V_XDDES_TI BIT(0) 52 53#define M10V_XDDSD_IS_MASK GENMASK(3, 0) 54#define M10V_XDDSD_IS_NORMAL 0x8 55 56#define MLB_XDMAC_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ 57 BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ 58 BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ 59 BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)) 60 61struct milbeaut_xdmac_desc { 62 struct virt_dma_desc vd; 63 size_t len; 64 dma_addr_t src; 65 dma_addr_t dst; 66}; 67 68struct milbeaut_xdmac_chan { 69 struct virt_dma_chan vc; 70 struct milbeaut_xdmac_desc *md; 71 void __iomem *reg_ch_base; 72}; 73 74struct milbeaut_xdmac_device { 75 struct dma_device ddev; 76 void __iomem *reg_base; 77 struct milbeaut_xdmac_chan channels[]; 78}; 79 80static struct milbeaut_xdmac_chan * 81to_milbeaut_xdmac_chan(struct virt_dma_chan *vc) 82{ 83 return container_of(vc, struct milbeaut_xdmac_chan, vc); 84} 85 86static struct milbeaut_xdmac_desc * 87to_milbeaut_xdmac_desc(struct virt_dma_desc *vd) 88{ 89 return container_of(vd, struct milbeaut_xdmac_desc, vd); 90} 91 92/* mc->vc.lock must be held by caller */ 93static struct milbeaut_xdmac_desc * 94milbeaut_xdmac_next_desc(struct milbeaut_xdmac_chan *mc) 95{ 96 struct virt_dma_desc *vd; 97 98 vd = vchan_next_desc(&mc->vc); 99 if (!vd) { 100 mc->md = NULL; 101 return NULL; 102 } 103 104 list_del(&vd->node); 105 106 mc->md = to_milbeaut_xdmac_desc(vd); 107 108 return mc->md; 109} 110 111/* mc->vc.lock must be held by caller */ 112static void milbeaut_chan_start(struct milbeaut_xdmac_chan *mc, 113 struct milbeaut_xdmac_desc *md) 114{ 115 u32 val; 116 117 /* Setup the channel */ 118 val = md->len - 1; 119 writel_relaxed(val, mc->reg_ch_base + M10V_XDTBC); 120 121 val = md->src; 122 writel_relaxed(val, mc->reg_ch_base + M10V_XDSSA); 123 124 val = md->dst; 125 writel_relaxed(val, mc->reg_ch_base + M10V_XDDSA); 126 127 val = readl_relaxed(mc->reg_ch_base + M10V_XDSAC); 128 val &= ~(M10V_XDSAC_SBS | M10V_XDSAC_SBL); 129 val |= FIELD_PREP(M10V_XDSAC_SBS, M10V_DEFBS) | 130 FIELD_PREP(M10V_XDSAC_SBL, M10V_DEFBL); 131 writel_relaxed(val, mc->reg_ch_base + M10V_XDSAC); 132 133 val = readl_relaxed(mc->reg_ch_base + M10V_XDDAC); 134 val &= ~(M10V_XDDAC_DBS | M10V_XDDAC_DBL); 135 val |= FIELD_PREP(M10V_XDDAC_DBS, M10V_DEFBS) | 136 FIELD_PREP(M10V_XDDAC_DBL, M10V_DEFBL); 137 writel_relaxed(val, mc->reg_ch_base + M10V_XDDAC); 138 139 /* Start the channel */ 140 val = readl_relaxed(mc->reg_ch_base + M10V_XDDES); 141 val &= ~(M10V_XDDES_CE | M10V_XDDES_SE | M10V_XDDES_TF | 142 M10V_XDDES_EI | M10V_XDDES_TI); 143 val |= FIELD_PREP(M10V_XDDES_CE, 1) | FIELD_PREP(M10V_XDDES_SE, 1) | 144 FIELD_PREP(M10V_XDDES_TF, 1) | FIELD_PREP(M10V_XDDES_EI, 1) | 145 FIELD_PREP(M10V_XDDES_TI, 1); 146 writel_relaxed(val, mc->reg_ch_base + M10V_XDDES); 147} 148 149/* mc->vc.lock must be held by caller */ 150static void milbeaut_xdmac_start(struct milbeaut_xdmac_chan *mc) 151{ 152 struct milbeaut_xdmac_desc *md; 153 154 md = milbeaut_xdmac_next_desc(mc); 155 if (md) 156 milbeaut_chan_start(mc, md); 157} 158 159static irqreturn_t milbeaut_xdmac_interrupt(int irq, void *dev_id) 160{ 161 struct milbeaut_xdmac_chan *mc = dev_id; 162 struct milbeaut_xdmac_desc *md; 163 unsigned long flags; 164 u32 val; 165 166 spin_lock_irqsave(&mc->vc.lock, flags); 167 168 /* Ack and Stop */ 169 val = FIELD_PREP(M10V_XDDSD_IS_MASK, 0x0); 170 writel_relaxed(val, mc->reg_ch_base + M10V_XDDSD); 171 172 md = mc->md; 173 if (!md) 174 goto out; 175 176 vchan_cookie_complete(&md->vd); 177 178 milbeaut_xdmac_start(mc); 179out: 180 spin_unlock_irqrestore(&mc->vc.lock, flags); 181 return IRQ_HANDLED; 182} 183 184static void milbeaut_xdmac_free_chan_resources(struct dma_chan *chan) 185{ 186 vchan_free_chan_resources(to_virt_chan(chan)); 187} 188 189static struct dma_async_tx_descriptor * 190milbeaut_xdmac_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, 191 dma_addr_t src, size_t len, unsigned long flags) 192{ 193 struct virt_dma_chan *vc = to_virt_chan(chan); 194 struct milbeaut_xdmac_desc *md; 195 196 md = kzalloc(sizeof(*md), GFP_NOWAIT); 197 if (!md) 198 return NULL; 199 200 md->len = len; 201 md->src = src; 202 md->dst = dst; 203 204 return vchan_tx_prep(vc, &md->vd, flags); 205} 206 207static int milbeaut_xdmac_terminate_all(struct dma_chan *chan) 208{ 209 struct virt_dma_chan *vc = to_virt_chan(chan); 210 struct milbeaut_xdmac_chan *mc = to_milbeaut_xdmac_chan(vc); 211 unsigned long flags; 212 u32 val; 213 214 LIST_HEAD(head); 215 216 spin_lock_irqsave(&vc->lock, flags); 217 218 /* Halt the channel */ 219 val = readl(mc->reg_ch_base + M10V_XDDES); 220 val &= ~M10V_XDDES_CE; 221 val |= FIELD_PREP(M10V_XDDES_CE, 0); 222 writel(val, mc->reg_ch_base + M10V_XDDES); 223 224 if (mc->md) { 225 vchan_terminate_vdesc(&mc->md->vd); 226 mc->md = NULL; 227 } 228 229 vchan_get_all_descriptors(vc, &head); 230 231 spin_unlock_irqrestore(&vc->lock, flags); 232 233 vchan_dma_desc_free_list(vc, &head); 234 235 return 0; 236} 237 238static void milbeaut_xdmac_synchronize(struct dma_chan *chan) 239{ 240 vchan_synchronize(to_virt_chan(chan)); 241} 242 243static void milbeaut_xdmac_issue_pending(struct dma_chan *chan) 244{ 245 struct virt_dma_chan *vc = to_virt_chan(chan); 246 struct milbeaut_xdmac_chan *mc = to_milbeaut_xdmac_chan(vc); 247 unsigned long flags; 248 249 spin_lock_irqsave(&vc->lock, flags); 250 251 if (vchan_issue_pending(vc) && !mc->md) 252 milbeaut_xdmac_start(mc); 253 254 spin_unlock_irqrestore(&vc->lock, flags); 255} 256 257static void milbeaut_xdmac_desc_free(struct virt_dma_desc *vd) 258{ 259 kfree(to_milbeaut_xdmac_desc(vd)); 260} 261 262static int milbeaut_xdmac_chan_init(struct platform_device *pdev, 263 struct milbeaut_xdmac_device *mdev, 264 int chan_id) 265{ 266 struct device *dev = &pdev->dev; 267 struct milbeaut_xdmac_chan *mc = &mdev->channels[chan_id]; 268 char *irq_name; 269 int irq, ret; 270 271 irq = platform_get_irq(pdev, chan_id); 272 if (irq < 0) 273 return irq; 274 275 irq_name = devm_kasprintf(dev, GFP_KERNEL, "milbeaut-xdmac-%d", 276 chan_id); 277 if (!irq_name) 278 return -ENOMEM; 279 280 ret = devm_request_irq(dev, irq, milbeaut_xdmac_interrupt, 281 IRQF_SHARED, irq_name, mc); 282 if (ret) 283 return ret; 284 285 mc->reg_ch_base = mdev->reg_base + chan_id * 0x30; 286 287 mc->vc.desc_free = milbeaut_xdmac_desc_free; 288 vchan_init(&mc->vc, &mdev->ddev); 289 290 return 0; 291} 292 293static void enable_xdmac(struct milbeaut_xdmac_device *mdev) 294{ 295 unsigned int val; 296 297 val = readl(mdev->reg_base + M10V_XDACS); 298 val |= M10V_XDACS_XE; 299 writel(val, mdev->reg_base + M10V_XDACS); 300} 301 302static void disable_xdmac(struct milbeaut_xdmac_device *mdev) 303{ 304 unsigned int val; 305 306 val = readl(mdev->reg_base + M10V_XDACS); 307 val &= ~M10V_XDACS_XE; 308 writel(val, mdev->reg_base + M10V_XDACS); 309} 310 311static int milbeaut_xdmac_probe(struct platform_device *pdev) 312{ 313 struct device *dev = &pdev->dev; 314 struct milbeaut_xdmac_device *mdev; 315 struct dma_device *ddev; 316 int nr_chans, ret, i; 317 318 nr_chans = platform_irq_count(pdev); 319 if (nr_chans < 0) 320 return nr_chans; 321 322 mdev = devm_kzalloc(dev, struct_size(mdev, channels, nr_chans), 323 GFP_KERNEL); 324 if (!mdev) 325 return -ENOMEM; 326 327 mdev->reg_base = devm_platform_ioremap_resource(pdev, 0); 328 if (IS_ERR(mdev->reg_base)) 329 return PTR_ERR(mdev->reg_base); 330 331 ddev = &mdev->ddev; 332 ddev->dev = dev; 333 dma_cap_set(DMA_MEMCPY, ddev->cap_mask); 334 ddev->src_addr_widths = MLB_XDMAC_BUSWIDTHS; 335 ddev->dst_addr_widths = MLB_XDMAC_BUSWIDTHS; 336 ddev->device_free_chan_resources = milbeaut_xdmac_free_chan_resources; 337 ddev->device_prep_dma_memcpy = milbeaut_xdmac_prep_memcpy; 338 ddev->device_terminate_all = milbeaut_xdmac_terminate_all; 339 ddev->device_synchronize = milbeaut_xdmac_synchronize; 340 ddev->device_tx_status = dma_cookie_status; 341 ddev->device_issue_pending = milbeaut_xdmac_issue_pending; 342 INIT_LIST_HEAD(&ddev->channels); 343 344 for (i = 0; i < nr_chans; i++) { 345 ret = milbeaut_xdmac_chan_init(pdev, mdev, i); 346 if (ret) 347 return ret; 348 } 349 350 enable_xdmac(mdev); 351 352 ret = dma_async_device_register(ddev); 353 if (ret) 354 goto disable_xdmac; 355 356 ret = of_dma_controller_register(dev->of_node, 357 of_dma_simple_xlate, mdev); 358 if (ret) 359 goto unregister_dmac; 360 361 platform_set_drvdata(pdev, mdev); 362 363 return 0; 364 365unregister_dmac: 366 dma_async_device_unregister(ddev); 367disable_xdmac: 368 disable_xdmac(mdev); 369 return ret; 370} 371 372static int milbeaut_xdmac_remove(struct platform_device *pdev) 373{ 374 struct milbeaut_xdmac_device *mdev = platform_get_drvdata(pdev); 375 struct dma_chan *chan; 376 int ret; 377 378 /* 379 * Before reaching here, almost all descriptors have been freed by the 380 * ->device_free_chan_resources() hook. However, each channel might 381 * be still holding one descriptor that was on-flight at that moment. 382 * Terminate it to make sure this hardware is no longer running. Then, 383 * free the channel resources once again to avoid memory leak. 384 */ 385 list_for_each_entry(chan, &mdev->ddev.channels, device_node) { 386 ret = dmaengine_terminate_sync(chan); 387 if (ret) 388 return ret; 389 milbeaut_xdmac_free_chan_resources(chan); 390 } 391 392 of_dma_controller_free(pdev->dev.of_node); 393 dma_async_device_unregister(&mdev->ddev); 394 395 disable_xdmac(mdev); 396 397 return 0; 398} 399 400static const struct of_device_id milbeaut_xdmac_match[] = { 401 { .compatible = "socionext,milbeaut-m10v-xdmac" }, 402 { /* sentinel */ } 403}; 404MODULE_DEVICE_TABLE(of, milbeaut_xdmac_match); 405 406static struct platform_driver milbeaut_xdmac_driver = { 407 .probe = milbeaut_xdmac_probe, 408 .remove = milbeaut_xdmac_remove, 409 .driver = { 410 .name = "milbeaut-m10v-xdmac", 411 .of_match_table = milbeaut_xdmac_match, 412 }, 413}; 414module_platform_driver(milbeaut_xdmac_driver); 415 416MODULE_DESCRIPTION("Milbeaut XDMAC DmaEngine driver"); 417MODULE_LICENSE("GPL v2"); 418