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

Configure Feed

Select the types of activity you want to include in your feed.

at v4.20-rc6 414 lines 11 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Renesas SUDMAC support 4 * 5 * Copyright (C) 2013 Renesas Solutions Corp. 6 * 7 * based on drivers/dma/sh/shdma.c: 8 * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de> 9 * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com> 10 * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. 11 * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. 12 */ 13 14#include <linux/dmaengine.h> 15#include <linux/err.h> 16#include <linux/init.h> 17#include <linux/interrupt.h> 18#include <linux/module.h> 19#include <linux/platform_device.h> 20#include <linux/slab.h> 21#include <linux/sudmac.h> 22 23struct sudmac_chan { 24 struct shdma_chan shdma_chan; 25 void __iomem *base; 26 char dev_id[16]; /* unique name per DMAC of channel */ 27 28 u32 offset; /* for CFG, BA, BBC, CA, CBC, DEN */ 29 u32 cfg; 30 u32 dint_end_bit; 31}; 32 33struct sudmac_device { 34 struct shdma_dev shdma_dev; 35 struct sudmac_pdata *pdata; 36 void __iomem *chan_reg; 37}; 38 39struct sudmac_regs { 40 u32 base_addr; 41 u32 base_byte_count; 42}; 43 44struct sudmac_desc { 45 struct sudmac_regs hw; 46 struct shdma_desc shdma_desc; 47}; 48 49#define to_chan(schan) container_of(schan, struct sudmac_chan, shdma_chan) 50#define to_desc(sdesc) container_of(sdesc, struct sudmac_desc, shdma_desc) 51#define to_sdev(sc) container_of(sc->shdma_chan.dma_chan.device, \ 52 struct sudmac_device, shdma_dev.dma_dev) 53 54/* SUDMAC register */ 55#define SUDMAC_CH0CFG 0x00 56#define SUDMAC_CH0BA 0x10 57#define SUDMAC_CH0BBC 0x18 58#define SUDMAC_CH0CA 0x20 59#define SUDMAC_CH0CBC 0x28 60#define SUDMAC_CH0DEN 0x30 61#define SUDMAC_DSTSCLR 0x38 62#define SUDMAC_DBUFCTRL 0x3C 63#define SUDMAC_DINTCTRL 0x40 64#define SUDMAC_DINTSTS 0x44 65#define SUDMAC_DINTSTSCLR 0x48 66#define SUDMAC_CH0SHCTRL 0x50 67 68/* Definitions for the sudmac_channel.config */ 69#define SUDMAC_SENDBUFM 0x1000 /* b12: Transmit Buffer Mode */ 70#define SUDMAC_RCVENDM 0x0100 /* b8: Receive Data Transfer End Mode */ 71#define SUDMAC_LBA_WAIT 0x0030 /* b5-4: Local Bus Access Wait */ 72 73/* Definitions for the sudmac_channel.dint_end_bit */ 74#define SUDMAC_CH1ENDE 0x0002 /* b1: Ch1 DMA Transfer End Int Enable */ 75#define SUDMAC_CH0ENDE 0x0001 /* b0: Ch0 DMA Transfer End Int Enable */ 76 77#define SUDMAC_DRV_NAME "sudmac" 78 79static void sudmac_writel(struct sudmac_chan *sc, u32 data, u32 reg) 80{ 81 iowrite32(data, sc->base + reg); 82} 83 84static u32 sudmac_readl(struct sudmac_chan *sc, u32 reg) 85{ 86 return ioread32(sc->base + reg); 87} 88 89static bool sudmac_is_busy(struct sudmac_chan *sc) 90{ 91 u32 den = sudmac_readl(sc, SUDMAC_CH0DEN + sc->offset); 92 93 if (den) 94 return true; /* working */ 95 96 return false; /* waiting */ 97} 98 99static void sudmac_set_reg(struct sudmac_chan *sc, struct sudmac_regs *hw, 100 struct shdma_desc *sdesc) 101{ 102 sudmac_writel(sc, sc->cfg, SUDMAC_CH0CFG + sc->offset); 103 sudmac_writel(sc, hw->base_addr, SUDMAC_CH0BA + sc->offset); 104 sudmac_writel(sc, hw->base_byte_count, SUDMAC_CH0BBC + sc->offset); 105} 106 107static void sudmac_start(struct sudmac_chan *sc) 108{ 109 u32 dintctrl = sudmac_readl(sc, SUDMAC_DINTCTRL); 110 111 sudmac_writel(sc, dintctrl | sc->dint_end_bit, SUDMAC_DINTCTRL); 112 sudmac_writel(sc, 1, SUDMAC_CH0DEN + sc->offset); 113} 114 115static void sudmac_start_xfer(struct shdma_chan *schan, 116 struct shdma_desc *sdesc) 117{ 118 struct sudmac_chan *sc = to_chan(schan); 119 struct sudmac_desc *sd = to_desc(sdesc); 120 121 sudmac_set_reg(sc, &sd->hw, sdesc); 122 sudmac_start(sc); 123} 124 125static bool sudmac_channel_busy(struct shdma_chan *schan) 126{ 127 struct sudmac_chan *sc = to_chan(schan); 128 129 return sudmac_is_busy(sc); 130} 131 132static void sudmac_setup_xfer(struct shdma_chan *schan, int slave_id) 133{ 134} 135 136static const struct sudmac_slave_config *sudmac_find_slave( 137 struct sudmac_chan *sc, int slave_id) 138{ 139 struct sudmac_device *sdev = to_sdev(sc); 140 struct sudmac_pdata *pdata = sdev->pdata; 141 const struct sudmac_slave_config *cfg; 142 int i; 143 144 for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) 145 if (cfg->slave_id == slave_id) 146 return cfg; 147 148 return NULL; 149} 150 151static int sudmac_set_slave(struct shdma_chan *schan, int slave_id, 152 dma_addr_t slave_addr, bool try) 153{ 154 struct sudmac_chan *sc = to_chan(schan); 155 const struct sudmac_slave_config *cfg = sudmac_find_slave(sc, slave_id); 156 157 if (!cfg) 158 return -ENODEV; 159 160 return 0; 161} 162 163static inline void sudmac_dma_halt(struct sudmac_chan *sc) 164{ 165 u32 dintctrl = sudmac_readl(sc, SUDMAC_DINTCTRL); 166 167 sudmac_writel(sc, 0, SUDMAC_CH0DEN + sc->offset); 168 sudmac_writel(sc, dintctrl & ~sc->dint_end_bit, SUDMAC_DINTCTRL); 169 sudmac_writel(sc, sc->dint_end_bit, SUDMAC_DINTSTSCLR); 170} 171 172static int sudmac_desc_setup(struct shdma_chan *schan, 173 struct shdma_desc *sdesc, 174 dma_addr_t src, dma_addr_t dst, size_t *len) 175{ 176 struct sudmac_chan *sc = to_chan(schan); 177 struct sudmac_desc *sd = to_desc(sdesc); 178 179 dev_dbg(sc->shdma_chan.dev, "%s: src=%pad, dst=%pad, len=%zu\n", 180 __func__, &src, &dst, *len); 181 182 if (*len > schan->max_xfer_len) 183 *len = schan->max_xfer_len; 184 185 if (dst) 186 sd->hw.base_addr = dst; 187 else if (src) 188 sd->hw.base_addr = src; 189 sd->hw.base_byte_count = *len; 190 191 return 0; 192} 193 194static void sudmac_halt(struct shdma_chan *schan) 195{ 196 struct sudmac_chan *sc = to_chan(schan); 197 198 sudmac_dma_halt(sc); 199} 200 201static bool sudmac_chan_irq(struct shdma_chan *schan, int irq) 202{ 203 struct sudmac_chan *sc = to_chan(schan); 204 u32 dintsts = sudmac_readl(sc, SUDMAC_DINTSTS); 205 206 if (!(dintsts & sc->dint_end_bit)) 207 return false; 208 209 /* DMA stop */ 210 sudmac_dma_halt(sc); 211 212 return true; 213} 214 215static size_t sudmac_get_partial(struct shdma_chan *schan, 216 struct shdma_desc *sdesc) 217{ 218 struct sudmac_chan *sc = to_chan(schan); 219 struct sudmac_desc *sd = to_desc(sdesc); 220 u32 current_byte_count = sudmac_readl(sc, SUDMAC_CH0CBC + sc->offset); 221 222 return sd->hw.base_byte_count - current_byte_count; 223} 224 225static bool sudmac_desc_completed(struct shdma_chan *schan, 226 struct shdma_desc *sdesc) 227{ 228 struct sudmac_chan *sc = to_chan(schan); 229 struct sudmac_desc *sd = to_desc(sdesc); 230 u32 current_addr = sudmac_readl(sc, SUDMAC_CH0CA + sc->offset); 231 232 return sd->hw.base_addr + sd->hw.base_byte_count == current_addr; 233} 234 235static int sudmac_chan_probe(struct sudmac_device *su_dev, int id, int irq, 236 unsigned long flags) 237{ 238 struct shdma_dev *sdev = &su_dev->shdma_dev; 239 struct platform_device *pdev = to_platform_device(sdev->dma_dev.dev); 240 struct sudmac_chan *sc; 241 struct shdma_chan *schan; 242 int err; 243 244 sc = devm_kzalloc(&pdev->dev, sizeof(struct sudmac_chan), GFP_KERNEL); 245 if (!sc) 246 return -ENOMEM; 247 248 schan = &sc->shdma_chan; 249 schan->max_xfer_len = 64 * 1024 * 1024 - 1; 250 251 shdma_chan_probe(sdev, schan, id); 252 253 sc->base = su_dev->chan_reg; 254 255 /* get platform_data */ 256 sc->offset = su_dev->pdata->channel->offset; 257 if (su_dev->pdata->channel->config & SUDMAC_TX_BUFFER_MODE) 258 sc->cfg |= SUDMAC_SENDBUFM; 259 if (su_dev->pdata->channel->config & SUDMAC_RX_END_MODE) 260 sc->cfg |= SUDMAC_RCVENDM; 261 sc->cfg |= (su_dev->pdata->channel->wait << 4) & SUDMAC_LBA_WAIT; 262 263 if (su_dev->pdata->channel->dint_end_bit & SUDMAC_DMA_BIT_CH0) 264 sc->dint_end_bit |= SUDMAC_CH0ENDE; 265 if (su_dev->pdata->channel->dint_end_bit & SUDMAC_DMA_BIT_CH1) 266 sc->dint_end_bit |= SUDMAC_CH1ENDE; 267 268 /* set up channel irq */ 269 if (pdev->id >= 0) 270 snprintf(sc->dev_id, sizeof(sc->dev_id), "sudmac%d.%d", 271 pdev->id, id); 272 else 273 snprintf(sc->dev_id, sizeof(sc->dev_id), "sudmac%d", id); 274 275 err = shdma_request_irq(schan, irq, flags, sc->dev_id); 276 if (err) { 277 dev_err(sdev->dma_dev.dev, 278 "DMA channel %d request_irq failed %d\n", id, err); 279 goto err_no_irq; 280 } 281 282 return 0; 283 284err_no_irq: 285 /* remove from dmaengine device node */ 286 shdma_chan_remove(schan); 287 return err; 288} 289 290static void sudmac_chan_remove(struct sudmac_device *su_dev) 291{ 292 struct shdma_chan *schan; 293 int i; 294 295 shdma_for_each_chan(schan, &su_dev->shdma_dev, i) { 296 BUG_ON(!schan); 297 298 shdma_chan_remove(schan); 299 } 300} 301 302static dma_addr_t sudmac_slave_addr(struct shdma_chan *schan) 303{ 304 /* SUDMAC doesn't need the address */ 305 return 0; 306} 307 308static struct shdma_desc *sudmac_embedded_desc(void *buf, int i) 309{ 310 return &((struct sudmac_desc *)buf)[i].shdma_desc; 311} 312 313static const struct shdma_ops sudmac_shdma_ops = { 314 .desc_completed = sudmac_desc_completed, 315 .halt_channel = sudmac_halt, 316 .channel_busy = sudmac_channel_busy, 317 .slave_addr = sudmac_slave_addr, 318 .desc_setup = sudmac_desc_setup, 319 .set_slave = sudmac_set_slave, 320 .setup_xfer = sudmac_setup_xfer, 321 .start_xfer = sudmac_start_xfer, 322 .embedded_desc = sudmac_embedded_desc, 323 .chan_irq = sudmac_chan_irq, 324 .get_partial = sudmac_get_partial, 325}; 326 327static int sudmac_probe(struct platform_device *pdev) 328{ 329 struct sudmac_pdata *pdata = dev_get_platdata(&pdev->dev); 330 int err, i; 331 struct sudmac_device *su_dev; 332 struct dma_device *dma_dev; 333 struct resource *chan, *irq_res; 334 335 /* get platform data */ 336 if (!pdata) 337 return -ENODEV; 338 339 irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 340 if (!irq_res) 341 return -ENODEV; 342 343 err = -ENOMEM; 344 su_dev = devm_kzalloc(&pdev->dev, sizeof(struct sudmac_device), 345 GFP_KERNEL); 346 if (!su_dev) 347 return err; 348 349 dma_dev = &su_dev->shdma_dev.dma_dev; 350 351 chan = platform_get_resource(pdev, IORESOURCE_MEM, 0); 352 su_dev->chan_reg = devm_ioremap_resource(&pdev->dev, chan); 353 if (IS_ERR(su_dev->chan_reg)) 354 return PTR_ERR(su_dev->chan_reg); 355 356 dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); 357 358 su_dev->shdma_dev.ops = &sudmac_shdma_ops; 359 su_dev->shdma_dev.desc_size = sizeof(struct sudmac_desc); 360 err = shdma_init(&pdev->dev, &su_dev->shdma_dev, pdata->channel_num); 361 if (err < 0) 362 return err; 363 364 /* platform data */ 365 su_dev->pdata = dev_get_platdata(&pdev->dev); 366 367 platform_set_drvdata(pdev, su_dev); 368 369 /* Create DMA Channel */ 370 for (i = 0; i < pdata->channel_num; i++) { 371 err = sudmac_chan_probe(su_dev, i, irq_res->start, IRQF_SHARED); 372 if (err) 373 goto chan_probe_err; 374 } 375 376 err = dma_async_device_register(&su_dev->shdma_dev.dma_dev); 377 if (err < 0) 378 goto chan_probe_err; 379 380 return err; 381 382chan_probe_err: 383 sudmac_chan_remove(su_dev); 384 385 shdma_cleanup(&su_dev->shdma_dev); 386 387 return err; 388} 389 390static int sudmac_remove(struct platform_device *pdev) 391{ 392 struct sudmac_device *su_dev = platform_get_drvdata(pdev); 393 struct dma_device *dma_dev = &su_dev->shdma_dev.dma_dev; 394 395 dma_async_device_unregister(dma_dev); 396 sudmac_chan_remove(su_dev); 397 shdma_cleanup(&su_dev->shdma_dev); 398 399 return 0; 400} 401 402static struct platform_driver sudmac_driver = { 403 .driver = { 404 .name = SUDMAC_DRV_NAME, 405 }, 406 .probe = sudmac_probe, 407 .remove = sudmac_remove, 408}; 409module_platform_driver(sudmac_driver); 410 411MODULE_AUTHOR("Yoshihiro Shimoda"); 412MODULE_DESCRIPTION("Renesas SUDMAC driver"); 413MODULE_LICENSE("GPL v2"); 414MODULE_ALIAS("platform:" SUDMAC_DRV_NAME);