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