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 (((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        reg = sst_dsp_shim_read_unlocked(ctx, offset);
 282
 283        if ((reg & mask) == target) {
 284                dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s successful\n",
 285                                        reg, operation);
 286
 287                return 0;
 288        }
 289
 290        dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s timedout\n",
 291                                        reg, operation);
 292        return -ETIME;
 293}
 294EXPORT_SYMBOL_GPL(sst_dsp_register_poll);
 295
 296void sst_dsp_dump(struct sst_dsp *sst)
 297{
 298        if (sst->ops->dump)
 299                sst->ops->dump(sst);
 300}
 301EXPORT_SYMBOL_GPL(sst_dsp_dump);
 302
 303void sst_dsp_reset(struct sst_dsp *sst)
 304{
 305        if (sst->ops->reset)
 306                sst->ops->reset(sst);
 307}
 308EXPORT_SYMBOL_GPL(sst_dsp_reset);
 309
 310int sst_dsp_boot(struct sst_dsp *sst)
 311{
 312        if (sst->ops->boot)
 313                sst->ops->boot(sst);
 314
 315        return 0;
 316}
 317EXPORT_SYMBOL_GPL(sst_dsp_boot);
 318
 319int sst_dsp_wake(struct sst_dsp *sst)
 320{
 321        if (sst->ops->wake)
 322                return sst->ops->wake(sst);
 323
 324        return 0;
 325}
 326EXPORT_SYMBOL_GPL(sst_dsp_wake);
 327
 328void sst_dsp_sleep(struct sst_dsp *sst)
 329{
 330        if (sst->ops->sleep)
 331                sst->ops->sleep(sst);
 332}
 333EXPORT_SYMBOL_GPL(sst_dsp_sleep);
 334
 335void sst_dsp_stall(struct sst_dsp *sst)
 336{
 337        if (sst->ops->stall)
 338                sst->ops->stall(sst);
 339}
 340EXPORT_SYMBOL_GPL(sst_dsp_stall);
 341
 342void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
 343{
 344        sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
 345        trace_sst_ipc_msg_tx(msg);
 346}
 347EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx);
 348
 349u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp)
 350{
 351        u32 msg;
 352
 353        msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
 354        trace_sst_ipc_msg_rx(msg);
 355
 356        return msg;
 357}
 358EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx);
 359
 360int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size,
 361        u32 outbox_offset, size_t outbox_size)
 362{
 363        sst->mailbox.in_base = sst->addr.lpe + inbox_offset;
 364        sst->mailbox.out_base = sst->addr.lpe + outbox_offset;
 365        sst->mailbox.in_size = inbox_size;
 366        sst->mailbox.out_size = outbox_size;
 367        return 0;
 368}
 369EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init);
 370
 371void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes)
 372{
 373        u32 i;
 374
 375        trace_sst_ipc_outbox_write(bytes);
 376
 377        memcpy_toio(sst->mailbox.out_base, message, bytes);
 378
 379        for (i = 0; i < bytes; i += 4)
 380                trace_sst_ipc_outbox_wdata(i, *(u32 *)(message + i));
 381}
 382EXPORT_SYMBOL_GPL(sst_dsp_outbox_write);
 383
 384void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes)
 385{
 386        u32 i;
 387
 388        trace_sst_ipc_outbox_read(bytes);
 389
 390        memcpy_fromio(message, sst->mailbox.out_base, bytes);
 391
 392        for (i = 0; i < bytes; i += 4)
 393                trace_sst_ipc_outbox_rdata(i, *(u32 *)(message + i));
 394}
 395EXPORT_SYMBOL_GPL(sst_dsp_outbox_read);
 396
 397void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes)
 398{
 399        u32 i;
 400
 401        trace_sst_ipc_inbox_write(bytes);
 402
 403        memcpy_toio(sst->mailbox.in_base, message, bytes);
 404
 405        for (i = 0; i < bytes; i += 4)
 406                trace_sst_ipc_inbox_wdata(i, *(u32 *)(message + i));
 407}
 408EXPORT_SYMBOL_GPL(sst_dsp_inbox_write);
 409
 410void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
 411{
 412        u32 i;
 413
 414        trace_sst_ipc_inbox_read(bytes);
 415
 416        memcpy_fromio(message, sst->mailbox.in_base, bytes);
 417
 418        for (i = 0; i < bytes; i += 4)
 419                trace_sst_ipc_inbox_rdata(i, *(u32 *)(message + i));
 420}
 421EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
 422
 423/* Module information */
 424MODULE_AUTHOR("Liam Girdwood");
 425MODULE_DESCRIPTION("Intel SST Core");
 426MODULE_LICENSE("GPL v2");
 427