linux/drivers/dma/sh/sudmac.c
<<
>>
Prefs
   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);
 418