linux/arch/arm/plat-stmp3xxx/dma.c
<<
>>
Prefs
   1/*
   2 * DMA helper routines for Freescale STMP37XX/STMP378X
   3 *
   4 * Author: dmitry pervushin <dpervushin@embeddedalley.com>
   5 *
   6 * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
   7 * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
   8 */
   9
  10/*
  11 * The code contained herein is licensed under the GNU General Public
  12 * License. You may obtain a copy of the GNU General Public License
  13 * Version 2 or later at the following locations:
  14 *
  15 * http://www.opensource.org/licenses/gpl-license.html
  16 * http://www.gnu.org/copyleft/gpl.html
  17 */
  18#include <linux/kernel.h>
  19#include <linux/device.h>
  20#include <linux/dmapool.h>
  21#include <linux/sysdev.h>
  22#include <linux/cpufreq.h>
  23
  24#include <asm/page.h>
  25
  26#include <mach/platform.h>
  27#include <mach/dma.h>
  28#include <mach/regs-apbx.h>
  29#include <mach/regs-apbh.h>
  30
  31static const size_t pool_item_size = sizeof(struct stmp3xxx_dma_command);
  32static const size_t pool_alignment = 8;
  33static struct stmp3xxx_dma_user {
  34        void *pool;
  35        int inuse;
  36        const char *name;
  37} channels[MAX_DMA_CHANNELS];
  38
  39#define IS_VALID_CHANNEL(ch) ((ch) >= 0 && (ch) < MAX_DMA_CHANNELS)
  40#define IS_USED(ch) (channels[ch].inuse)
  41
  42int stmp3xxx_dma_request(int ch, struct device *dev, const char *name)
  43{
  44        struct stmp3xxx_dma_user *user;
  45        int err = 0;
  46
  47        user = channels + ch;
  48        if (!IS_VALID_CHANNEL(ch)) {
  49                err = -ENODEV;
  50                goto out;
  51        }
  52        if (IS_USED(ch)) {
  53                err = -EBUSY;
  54                goto out;
  55        }
  56        /* Create a pool to allocate dma commands from */
  57        user->pool = dma_pool_create(name, dev, pool_item_size,
  58                                     pool_alignment, PAGE_SIZE);
  59        if (user->pool == NULL) {
  60                err = -ENOMEM;
  61                goto out;
  62        }
  63        user->name = name;
  64        user->inuse++;
  65out:
  66        return err;
  67}
  68EXPORT_SYMBOL(stmp3xxx_dma_request);
  69
  70int stmp3xxx_dma_release(int ch)
  71{
  72        struct stmp3xxx_dma_user *user = channels + ch;
  73        int err = 0;
  74
  75        if (!IS_VALID_CHANNEL(ch)) {
  76                err = -ENODEV;
  77                goto out;
  78        }
  79        if (!IS_USED(ch)) {
  80                err = -EBUSY;
  81                goto out;
  82        }
  83        BUG_ON(user->pool == NULL);
  84        dma_pool_destroy(user->pool);
  85        user->inuse--;
  86out:
  87        return err;
  88}
  89EXPORT_SYMBOL(stmp3xxx_dma_release);
  90
  91int stmp3xxx_dma_read_semaphore(int channel)
  92{
  93        int sem = -1;
  94
  95        switch (STMP3XXX_DMA_BUS(channel)) {
  96        case STMP3XXX_BUS_APBH:
  97                sem = __raw_readl(REGS_APBH_BASE + HW_APBH_CHn_SEMA +
  98                                STMP3XXX_DMA_CHANNEL(channel) * 0x70);
  99                sem &= BM_APBH_CHn_SEMA_PHORE;
 100                sem >>= BP_APBH_CHn_SEMA_PHORE;
 101                break;
 102
 103        case STMP3XXX_BUS_APBX:
 104                sem = __raw_readl(REGS_APBX_BASE + HW_APBX_CHn_SEMA +
 105                                STMP3XXX_DMA_CHANNEL(channel) * 0x70);
 106                sem &= BM_APBX_CHn_SEMA_PHORE;
 107                sem >>= BP_APBX_CHn_SEMA_PHORE;
 108                break;
 109        default:
 110                BUG();
 111        }
 112        return sem;
 113}
 114EXPORT_SYMBOL(stmp3xxx_dma_read_semaphore);
 115
 116int stmp3xxx_dma_allocate_command(int channel,
 117                                  struct stmp3xxx_dma_descriptor *descriptor)
 118{
 119        struct stmp3xxx_dma_user *user = channels + channel;
 120        int err = 0;
 121
 122        if (!IS_VALID_CHANNEL(channel)) {
 123                err = -ENODEV;
 124                goto out;
 125        }
 126        if (!IS_USED(channel)) {
 127                err = -EBUSY;
 128                goto out;
 129        }
 130        if (descriptor == NULL) {
 131                err = -EINVAL;
 132                goto out;
 133        }
 134
 135        /* Allocate memory for a command from the buffer */
 136        descriptor->command =
 137            dma_pool_alloc(user->pool, GFP_KERNEL, &descriptor->handle);
 138
 139        /* Check it worked */
 140        if (!descriptor->command) {
 141                err = -ENOMEM;
 142                goto out;
 143        }
 144
 145        memset(descriptor->command, 0, pool_item_size);
 146out:
 147        WARN_ON(err);
 148        return err;
 149}
 150EXPORT_SYMBOL(stmp3xxx_dma_allocate_command);
 151
 152int stmp3xxx_dma_free_command(int channel,
 153                              struct stmp3xxx_dma_descriptor *descriptor)
 154{
 155        int err = 0;
 156
 157        if (!IS_VALID_CHANNEL(channel)) {
 158                err = -ENODEV;
 159                goto out;
 160        }
 161        if (!IS_USED(channel)) {
 162                err = -EBUSY;
 163                goto out;
 164        }
 165
 166        /* Return the command memory to the pool */
 167        dma_pool_free(channels[channel].pool, descriptor->command,
 168                      descriptor->handle);
 169
 170        /* Initialise descriptor so we're not tempted to use it */
 171        descriptor->command = NULL;
 172        descriptor->handle = 0;
 173        descriptor->virtual_buf_ptr = NULL;
 174        descriptor->next_descr = NULL;
 175
 176        WARN_ON(err);
 177out:
 178        return err;
 179}
 180EXPORT_SYMBOL(stmp3xxx_dma_free_command);
 181
 182void stmp3xxx_dma_go(int channel,
 183                     struct stmp3xxx_dma_descriptor *head, u32 semaphore)
 184{
 185        int ch = STMP3XXX_DMA_CHANNEL(channel);
 186        void __iomem *c, *s;
 187
 188        switch (STMP3XXX_DMA_BUS(channel)) {
 189        case STMP3XXX_BUS_APBH:
 190                c = REGS_APBH_BASE + HW_APBH_CHn_NXTCMDAR + 0x70 * ch;
 191                s = REGS_APBH_BASE + HW_APBH_CHn_SEMA + 0x70 * ch;
 192                break;
 193
 194        case STMP3XXX_BUS_APBX:
 195                c = REGS_APBX_BASE + HW_APBX_CHn_NXTCMDAR + 0x70 * ch;
 196                s = REGS_APBX_BASE + HW_APBX_CHn_SEMA + 0x70 * ch;
 197                break;
 198
 199        default:
 200                return;
 201        }
 202
 203        /* Set next command */
 204        __raw_writel(head->handle, c);
 205        /* Set counting semaphore (kicks off transfer). Assumes
 206           peripheral has been set up correctly */
 207        __raw_writel(semaphore, s);
 208}
 209EXPORT_SYMBOL(stmp3xxx_dma_go);
 210
 211int stmp3xxx_dma_running(int channel)
 212{
 213        switch (STMP3XXX_DMA_BUS(channel)) {
 214        case STMP3XXX_BUS_APBH:
 215                return (__raw_readl(REGS_APBH_BASE + HW_APBH_CHn_SEMA +
 216                        0x70 * STMP3XXX_DMA_CHANNEL(channel))) &
 217                            BM_APBH_CHn_SEMA_PHORE;
 218
 219        case STMP3XXX_BUS_APBX:
 220                return (__raw_readl(REGS_APBX_BASE + HW_APBX_CHn_SEMA +
 221                        0x70 * STMP3XXX_DMA_CHANNEL(channel))) &
 222                            BM_APBX_CHn_SEMA_PHORE;
 223        default:
 224                BUG();
 225                return 0;
 226        }
 227}
 228EXPORT_SYMBOL(stmp3xxx_dma_running);
 229
 230/*
 231 * Circular dma chain management
 232 */
 233void stmp3xxx_dma_free_chain(struct stmp37xx_circ_dma_chain *chain)
 234{
 235        int i;
 236
 237        for (i = 0; i < chain->total_count; i++)
 238                stmp3xxx_dma_free_command(
 239                        STMP3XXX_DMA(chain->channel, chain->bus),
 240                        &chain->chain[i]);
 241}
 242EXPORT_SYMBOL(stmp3xxx_dma_free_chain);
 243
 244int stmp3xxx_dma_make_chain(int ch, struct stmp37xx_circ_dma_chain *chain,
 245                            struct stmp3xxx_dma_descriptor descriptors[],
 246                            unsigned items)
 247{
 248        int i;
 249        int err = 0;
 250
 251        if (items == 0)
 252                return err;
 253
 254        for (i = 0; i < items; i++) {
 255                err = stmp3xxx_dma_allocate_command(ch, &descriptors[i]);
 256                if (err) {
 257                        WARN_ON(err);
 258                        /*
 259                         * Couldn't allocate the whole chain.
 260                         * deallocate what has been allocated
 261                         */
 262                        if (i) {
 263                                do {
 264                                        stmp3xxx_dma_free_command(ch,
 265                                                                  &descriptors
 266                                                                  [i]);
 267                                } while (i-- > 0);
 268                        }
 269                        return err;
 270                }
 271
 272                /* link them! */
 273                if (i > 0) {
 274                        descriptors[i - 1].next_descr = &descriptors[i];
 275                        descriptors[i - 1].command->next =
 276                                                descriptors[i].handle;
 277                }
 278        }
 279
 280        /* make list circular */
 281        descriptors[items - 1].next_descr = &descriptors[0];
 282        descriptors[items - 1].command->next = descriptors[0].handle;
 283
 284        chain->total_count = items;
 285        chain->chain = descriptors;
 286        chain->free_index = 0;
 287        chain->active_index = 0;
 288        chain->cooked_index = 0;
 289        chain->free_count = items;
 290        chain->active_count = 0;
 291        chain->cooked_count = 0;
 292        chain->bus = STMP3XXX_DMA_BUS(ch);
 293        chain->channel = STMP3XXX_DMA_CHANNEL(ch);
 294        return err;
 295}
 296EXPORT_SYMBOL(stmp3xxx_dma_make_chain);
 297
 298void stmp37xx_circ_clear_chain(struct stmp37xx_circ_dma_chain *chain)
 299{
 300        BUG_ON(stmp3xxx_dma_running(STMP3XXX_DMA(chain->channel, chain->bus)));
 301        chain->free_index = 0;
 302        chain->active_index = 0;
 303        chain->cooked_index = 0;
 304        chain->free_count = chain->total_count;
 305        chain->active_count = 0;
 306        chain->cooked_count = 0;
 307}
 308EXPORT_SYMBOL(stmp37xx_circ_clear_chain);
 309
 310void stmp37xx_circ_advance_free(struct stmp37xx_circ_dma_chain *chain,
 311                unsigned count)
 312{
 313        BUG_ON(chain->cooked_count < count);
 314
 315        chain->cooked_count -= count;
 316        chain->cooked_index += count;
 317        chain->cooked_index %= chain->total_count;
 318        chain->free_count += count;
 319}
 320EXPORT_SYMBOL(stmp37xx_circ_advance_free);
 321
 322void stmp37xx_circ_advance_active(struct stmp37xx_circ_dma_chain *chain,
 323                unsigned count)
 324{
 325        void __iomem *c;
 326        u32 mask_clr, mask;
 327        BUG_ON(chain->free_count < count);
 328
 329        chain->free_count -= count;
 330        chain->free_index += count;
 331        chain->free_index %= chain->total_count;
 332        chain->active_count += count;
 333
 334        switch (chain->bus) {
 335        case STMP3XXX_BUS_APBH:
 336                c = REGS_APBH_BASE + HW_APBH_CHn_SEMA + 0x70 * chain->channel;
 337                mask_clr = BM_APBH_CHn_SEMA_INCREMENT_SEMA;
 338                mask = BF(count, APBH_CHn_SEMA_INCREMENT_SEMA);
 339                break;
 340        case STMP3XXX_BUS_APBX:
 341                c = REGS_APBX_BASE + HW_APBX_CHn_SEMA + 0x70 * chain->channel;
 342                mask_clr = BM_APBX_CHn_SEMA_INCREMENT_SEMA;
 343                mask = BF(count, APBX_CHn_SEMA_INCREMENT_SEMA);
 344                break;
 345        default:
 346                BUG();
 347                return;
 348        }
 349
 350        /* Set counting semaphore (kicks off transfer). Assumes
 351           peripheral has been set up correctly */
 352        stmp3xxx_clearl(mask_clr, c);
 353        stmp3xxx_setl(mask, c);
 354}
 355EXPORT_SYMBOL(stmp37xx_circ_advance_active);
 356
 357unsigned stmp37xx_circ_advance_cooked(struct stmp37xx_circ_dma_chain *chain)
 358{
 359        unsigned cooked;
 360
 361        cooked = chain->active_count -
 362          stmp3xxx_dma_read_semaphore(STMP3XXX_DMA(chain->channel, chain->bus));
 363
 364        chain->active_count -= cooked;
 365        chain->active_index += cooked;
 366        chain->active_index %= chain->total_count;
 367
 368        chain->cooked_count += cooked;
 369
 370        return cooked;
 371}
 372EXPORT_SYMBOL(stmp37xx_circ_advance_cooked);
 373
 374void stmp3xxx_dma_set_alt_target(int channel, int function)
 375{
 376#if defined(CONFIG_ARCH_STMP37XX)
 377        unsigned bits = 4;
 378#elif defined(CONFIG_ARCH_STMP378X)
 379        unsigned bits = 2;
 380#else
 381#error wrong arch
 382#endif
 383        int shift = STMP3XXX_DMA_CHANNEL(channel) * bits;
 384        unsigned mask = (1<<bits) - 1;
 385        void __iomem *c;
 386
 387        BUG_ON(function < 0 || function >= (1<<bits));
 388        pr_debug("%s: channel = %d, using mask %x, "
 389                 "shift = %d\n", __func__, channel, mask, shift);
 390
 391        switch (STMP3XXX_DMA_BUS(channel)) {
 392        case STMP3XXX_BUS_APBH:
 393                c = REGS_APBH_BASE + HW_APBH_DEVSEL;
 394                break;
 395        case STMP3XXX_BUS_APBX:
 396                c = REGS_APBX_BASE + HW_APBX_DEVSEL;
 397                break;
 398        default:
 399                BUG();
 400        }
 401        stmp3xxx_clearl(mask << shift, c);
 402        stmp3xxx_setl(mask << shift, c);
 403}
 404EXPORT_SYMBOL(stmp3xxx_dma_set_alt_target);
 405
 406void stmp3xxx_dma_suspend(void)
 407{
 408        stmp3xxx_setl(BM_APBH_CTRL0_CLKGATE, REGS_APBH_BASE + HW_APBH_CTRL0);
 409        stmp3xxx_setl(BM_APBX_CTRL0_CLKGATE, REGS_APBX_BASE + HW_APBX_CTRL0);
 410}
 411
 412void stmp3xxx_dma_resume(void)
 413{
 414        stmp3xxx_clearl(BM_APBH_CTRL0_CLKGATE | BM_APBH_CTRL0_SFTRST,
 415                        REGS_APBH_BASE + HW_APBH_CTRL0);
 416        stmp3xxx_clearl(BM_APBX_CTRL0_CLKGATE | BM_APBX_CTRL0_SFTRST,
 417                        REGS_APBX_BASE + HW_APBX_CTRL0);
 418}
 419
 420#ifdef CONFIG_CPU_FREQ
 421
 422struct dma_notifier_block {
 423        struct notifier_block nb;
 424        void *data;
 425};
 426
 427static int dma_cpufreq_notifier(struct notifier_block *self,
 428                                unsigned long phase, void *p)
 429{
 430        switch (phase) {
 431        case CPUFREQ_POSTCHANGE:
 432                stmp3xxx_dma_resume();
 433                break;
 434
 435        case CPUFREQ_PRECHANGE:
 436                stmp3xxx_dma_suspend();
 437                break;
 438
 439        default:
 440                break;
 441        }
 442
 443        return NOTIFY_DONE;
 444}
 445
 446static struct dma_notifier_block dma_cpufreq_nb = {
 447        .nb = {
 448                .notifier_call = dma_cpufreq_notifier,
 449        },
 450};
 451#endif /* CONFIG_CPU_FREQ */
 452
 453void __init stmp3xxx_dma_init(void)
 454{
 455        stmp3xxx_clearl(BM_APBH_CTRL0_CLKGATE | BM_APBH_CTRL0_SFTRST,
 456                        REGS_APBH_BASE + HW_APBH_CTRL0);
 457        stmp3xxx_clearl(BM_APBX_CTRL0_CLKGATE | BM_APBX_CTRL0_SFTRST,
 458                        REGS_APBX_BASE + HW_APBX_CTRL0);
 459#ifdef CONFIG_CPU_FREQ
 460        cpufreq_register_notifier(&dma_cpufreq_nb.nb,
 461                                CPUFREQ_TRANSITION_NOTIFIER);
 462#endif /* CONFIG_CPU_FREQ */
 463}
 464