linux/sound/soc/intel/common/sst-dsp.c
<<
>>
Prefs
   1/*
   2 * Intel Smart Sound Technology (SST) DSP Core Driver
   3 *
   4 * Copyright (C) 2013, Intel Corporation. All rights reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License version
   8 * 2 as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 */
  16
  17#include <linux/slab.h>
  18#include <linux/export.h>
  19#include <linux/interrupt.h>
  20#include <linux/module.h>
  21#include <linux/platform_device.h>
  22#include <linux/io.h>
  23#include <linux/delay.h>
  24
  25#include "sst-dsp.h"
  26#include "sst-dsp-priv.h"
  27
  28#define CREATE_TRACE_POINTS
  29#include <trace/events/intel-sst.h>
  30
  31/* Internal generic low-level SST IO functions - can be overidden */
  32void sst_shim32_write(void __iomem *addr, u32 offset, u32 value)
  33{
  34        writel(value, addr + offset);
  35}
  36EXPORT_SYMBOL_GPL(sst_shim32_write);
  37
  38u32 sst_shim32_read(void __iomem *addr, u32 offset)
  39{
  40        return readl(addr + offset);
  41}
  42EXPORT_SYMBOL_GPL(sst_shim32_read);
  43
  44void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value)
  45{
  46        memcpy_toio(addr + offset, &value, sizeof(value));
  47}
  48EXPORT_SYMBOL_GPL(sst_shim32_write64);
  49
  50u64 sst_shim32_read64(void __iomem *addr, u32 offset)
  51{
  52        u64 val;
  53
  54        memcpy_fromio(&val, addr + offset, sizeof(val));
  55        return val;
  56}
  57EXPORT_SYMBOL_GPL(sst_shim32_read64);
  58
  59static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest,
  60        u32 *src, size_t bytes)
  61{
  62        int i, words = bytes >> 2;
  63
  64        for (i = 0; i < words; i++)
  65                writel(src[i], dest + i);
  66}
  67
  68static inline void _sst_memcpy_fromio_32(u32 *dest,
  69        const volatile __iomem u32 *src, size_t bytes)
  70{
  71        int i, words = bytes >> 2;
  72
  73        for (i = 0; i < words; i++)
  74                dest[i] = readl(src + i);
  75}
  76
  77void sst_memcpy_toio_32(struct sst_dsp *sst,
  78        void __iomem *dest, void *src, size_t bytes)
  79{
  80        _sst_memcpy_toio_32(dest, src, bytes);
  81}
  82EXPORT_SYMBOL_GPL(sst_memcpy_toio_32);
  83
  84void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest,
  85        void __iomem *src, size_t bytes)
  86{
  87        _sst_memcpy_fromio_32(dest, src, bytes);
  88}
  89EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32);
  90
  91/* Public API */
  92void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value)
  93{
  94        unsigned long flags;
  95
  96        spin_lock_irqsave(&sst->spinlock, flags);
  97        sst->ops->write(sst->addr.shim, offset, value);
  98        spin_unlock_irqrestore(&sst->spinlock, flags);
  99}
 100EXPORT_SYMBOL_GPL(sst_dsp_shim_write);
 101
 102u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset)
 103{
 104        unsigned long flags;
 105        u32 val;
 106
 107        spin_lock_irqsave(&sst->spinlock, flags);
 108        val = sst->ops->read(sst->addr.shim, offset);
 109        spin_unlock_irqrestore(&sst->spinlock, flags);
 110
 111        return val;
 112}
 113EXPORT_SYMBOL_GPL(sst_dsp_shim_read);
 114
 115void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value)
 116{
 117        unsigned long flags;
 118
 119        spin_lock_irqsave(&sst->spinlock, flags);
 120        sst->ops->write64(sst->addr.shim, offset, value);
 121        spin_unlock_irqrestore(&sst->spinlock, flags);
 122}
 123EXPORT_SYMBOL_GPL(sst_dsp_shim_write64);
 124
 125u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset)
 126{
 127        unsigned long flags;
 128        u64 val;
 129
 130        spin_lock_irqsave(&sst->spinlock, flags);
 131        val = sst->ops->read64(sst->addr.shim, offset);
 132        spin_unlock_irqrestore(&sst->spinlock, flags);
 133
 134        return val;
 135}
 136EXPORT_SYMBOL_GPL(sst_dsp_shim_read64);
 137
 138void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value)
 139{
 140        sst->ops->write(sst->addr.shim, offset, value);
 141}
 142EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked);
 143
 144u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset)
 145{
 146        return sst->ops->read(sst->addr.shim, offset);
 147}
 148EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked);
 149
 150void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value)
 151{
 152        sst->ops->write64(sst->addr.shim, offset, value);
 153}
 154EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked);
 155
 156u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset)
 157{
 158        return sst->ops->read64(sst->addr.shim, offset);
 159}
 160EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked);
 161
 162int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
 163                                u32 mask, u32 value)
 164{
 165        bool change;
 166        unsigned int old, new;
 167        u32 ret;
 168
 169        ret = sst_dsp_shim_read_unlocked(sst, offset);
 170
 171        old = ret;
 172        new = (old & (~mask)) | (value & mask);
 173
 174        change = (old != new);
 175        if (change)
 176                sst_dsp_shim_write_unlocked(sst, offset, new);
 177
 178        return change;
 179}
 180EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked);
 181
 182int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
 183                                u64 mask, u64 value)
 184{
 185        bool change;
 186        u64 old, new;
 187
 188        old = sst_dsp_shim_read64_unlocked(sst, offset);
 189
 190        new = (old & (~mask)) | (value & mask);
 191
 192        change = (old != new);
 193        if (change)
 194                sst_dsp_shim_write64_unlocked(sst, offset, new);
 195
 196        return change;
 197}
 198EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked);
 199
 200/* This is for registers bits with attribute RWC */
 201void sst_dsp_shim_update_bits_forced_unlocked(struct sst_dsp *sst, u32 offset,
 202                                u32 mask, u32 value)
 203{
 204        unsigned int old, new;
 205        u32 ret;
 206
 207        ret = sst_dsp_shim_read_unlocked(sst, offset);
 208
 209        old = ret;
 210        new = (old & (~mask)) | (value & mask);
 211
 212        sst_dsp_shim_write_unlocked(sst, offset, new);
 213}
 214EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced_unlocked);
 215
 216int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
 217                                u32 mask, u32 value)
 218{
 219        unsigned long flags;
 220        bool change;
 221
 222        spin_lock_irqsave(&sst->spinlock, flags);
 223        change = sst_dsp_shim_update_bits_unlocked(sst, offset, mask, value);
 224        spin_unlock_irqrestore(&sst->spinlock, flags);
 225        return change;
 226}
 227EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits);
 228
 229int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
 230                                u64 mask, u64 value)
 231{
 232        unsigned long flags;
 233        bool change;
 234
 235        spin_lock_irqsave(&sst->spinlock, flags);
 236        change = sst_dsp_shim_update_bits64_unlocked(sst, offset, mask, value);
 237        spin_unlock_irqrestore(&sst->spinlock, flags);
 238        return change;
 239}
 240EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
 241
 242/* This is for registers bits with attribute RWC */
 243void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset,
 244                                u32 mask, u32 value)
 245{
 246        unsigned long flags;
 247
 248        spin_lock_irqsave(&sst->spinlock, flags);
 249        sst_dsp_shim_update_bits_forced_unlocked(sst, offset, mask, value);
 250        spin_unlock_irqrestore(&sst->spinlock, flags);
 251}
 252EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced);
 253
 254int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask,
 255                         u32 target, u32 time, char *operation)
 256{
 257        u32 reg;
 258        unsigned long timeout;
 259        int k = 0, s = 500;
 260
 261        /*
 262         * split the loop into sleeps of varying resolution. more accurately,
 263         * the range of wakeups are:
 264         * Phase 1(first 5ms): min sleep 0.5ms; max sleep 1ms.
 265         * Phase 2:( 5ms to 10ms) : min sleep 0.5ms; max sleep 10ms
 266         * (usleep_range (500, 1000) and usleep_range(5000, 10000) are
 267         * both possible in this phase depending on whether k > 10 or not).
 268         * Phase 3: (beyond 10 ms) min sleep 5ms; max sleep 10ms.
 269         */
 270
 271        timeout = jiffies + msecs_to_jiffies(time);
 272        while ((((reg = sst_dsp_shim_read_unlocked(ctx, offset)) & mask) != target)
 273                && time_before(jiffies, timeout)) {
 274                k++;
 275                if (k > 10)
 276                        s = 5000;
 277
 278                usleep_range(s, 2*s);
 279        }
 280
 281        if ((reg & mask) == target) {
 282                dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s successful\n",
 283                                        reg, operation);
 284
 285                return 0;
 286        }
 287
 288        dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s timedout\n",
 289                                        reg, operation);
 290        return -ETIME;
 291}
 292EXPORT_SYMBOL_GPL(sst_dsp_register_poll);
 293
 294void sst_dsp_dump(struct sst_dsp *sst)
 295{
 296        if (sst->ops->dump)
 297                sst->ops->dump(sst);
 298}
 299EXPORT_SYMBOL_GPL(sst_dsp_dump);
 300
 301void sst_dsp_reset(struct sst_dsp *sst)
 302{
 303        if (sst->ops->reset)
 304                sst->ops->reset(sst);
 305}
 306EXPORT_SYMBOL_GPL(sst_dsp_reset);
 307
 308int sst_dsp_boot(struct sst_dsp *sst)
 309{
 310        if (sst->ops->boot)
 311                sst->ops->boot(sst);
 312
 313        return 0;
 314}
 315EXPORT_SYMBOL_GPL(sst_dsp_boot);
 316
 317int sst_dsp_wake(struct sst_dsp *sst)
 318{
 319        if (sst->ops->wake)
 320                return sst->ops->wake(sst);
 321
 322        return 0;
 323}
 324EXPORT_SYMBOL_GPL(sst_dsp_wake);
 325
 326void sst_dsp_sleep(struct sst_dsp *sst)
 327{
 328        if (sst->ops->sleep)
 329                sst->ops->sleep(sst);
 330}
 331EXPORT_SYMBOL_GPL(sst_dsp_sleep);
 332
 333void sst_dsp_stall(struct sst_dsp *sst)
 334{
 335        if (sst->ops->stall)
 336                sst->ops->stall(sst);
 337}
 338EXPORT_SYMBOL_GPL(sst_dsp_stall);
 339
 340void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
 341{
 342        sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
 343        trace_sst_ipc_msg_tx(msg);
 344}
 345EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx);
 346
 347u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp)
 348{
 349        u32 msg;
 350
 351        msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
 352        trace_sst_ipc_msg_rx(msg);
 353
 354        return msg;
 355}
 356EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx);
 357
 358int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size,
 359        u32 outbox_offset, size_t outbox_size)
 360{
 361        sst->mailbox.in_base = sst->addr.lpe + inbox_offset;
 362        sst->mailbox.out_base = sst->addr.lpe + outbox_offset;
 363        sst->mailbox.in_size = inbox_size;
 364        sst->mailbox.out_size = outbox_size;
 365        return 0;
 366}
 367EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init);
 368
 369void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes)
 370{
 371        u32 i;
 372
 373        trace_sst_ipc_outbox_write(bytes);
 374
 375        memcpy_toio(sst->mailbox.out_base, message, bytes);
 376
 377        for (i = 0; i < bytes; i += 4)
 378                trace_sst_ipc_outbox_wdata(i, *(u32 *)(message + i));
 379}
 380EXPORT_SYMBOL_GPL(sst_dsp_outbox_write);
 381
 382void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes)
 383{
 384        u32 i;
 385
 386        trace_sst_ipc_outbox_read(bytes);
 387
 388        memcpy_fromio(message, sst->mailbox.out_base, bytes);
 389
 390        for (i = 0; i < bytes; i += 4)
 391                trace_sst_ipc_outbox_rdata(i, *(u32 *)(message + i));
 392}
 393EXPORT_SYMBOL_GPL(sst_dsp_outbox_read);
 394
 395void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes)
 396{
 397        u32 i;
 398
 399        trace_sst_ipc_inbox_write(bytes);
 400
 401        memcpy_toio(sst->mailbox.in_base, message, bytes);
 402
 403        for (i = 0; i < bytes; i += 4)
 404                trace_sst_ipc_inbox_wdata(i, *(u32 *)(message + i));
 405}
 406EXPORT_SYMBOL_GPL(sst_dsp_inbox_write);
 407
 408void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
 409{
 410        u32 i;
 411
 412        trace_sst_ipc_inbox_read(bytes);
 413
 414        memcpy_fromio(message, sst->mailbox.in_base, bytes);
 415
 416        for (i = 0; i < bytes; i += 4)
 417                trace_sst_ipc_inbox_rdata(i, *(u32 *)(message + i));
 418}
 419EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
 420
 421/* Module information */
 422MODULE_AUTHOR("Liam Girdwood");
 423MODULE_DESCRIPTION("Intel SST Core");
 424MODULE_LICENSE("GPL v2");
 425