123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- /*
- * Intel Smart Sound Technology (SST) DSP Core Driver
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #include <linux/slab.h>
- #include <linux/export.h>
- #include <linux/interrupt.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/io.h>
- #include <linux/delay.h>
- #include "sst-dsp.h"
- #include "sst-dsp-priv.h"
- #define CREATE_TRACE_POINTS
- #include <trace/events/intel-sst.h>
- /* Internal generic low-level SST IO functions - can be overidden */
- void sst_shim32_write(void __iomem *addr, u32 offset, u32 value)
- {
- writel(value, addr + offset);
- }
- EXPORT_SYMBOL_GPL(sst_shim32_write);
- u32 sst_shim32_read(void __iomem *addr, u32 offset)
- {
- return readl(addr + offset);
- }
- EXPORT_SYMBOL_GPL(sst_shim32_read);
- void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value)
- {
- memcpy_toio(addr + offset, &value, sizeof(value));
- }
- EXPORT_SYMBOL_GPL(sst_shim32_write64);
- u64 sst_shim32_read64(void __iomem *addr, u32 offset)
- {
- u64 val;
- memcpy_fromio(&val, addr + offset, sizeof(val));
- return val;
- }
- EXPORT_SYMBOL_GPL(sst_shim32_read64);
- static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest,
- u32 *src, size_t bytes)
- {
- int i, words = bytes >> 2;
- for (i = 0; i < words; i++)
- writel(src[i], dest + i);
- }
- static inline void _sst_memcpy_fromio_32(u32 *dest,
- const volatile __iomem u32 *src, size_t bytes)
- {
- int i, words = bytes >> 2;
- for (i = 0; i < words; i++)
- dest[i] = readl(src + i);
- }
- void sst_memcpy_toio_32(struct sst_dsp *sst,
- void __iomem *dest, void *src, size_t bytes)
- {
- _sst_memcpy_toio_32(dest, src, bytes);
- }
- EXPORT_SYMBOL_GPL(sst_memcpy_toio_32);
- void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest,
- void __iomem *src, size_t bytes)
- {
- _sst_memcpy_fromio_32(dest, src, bytes);
- }
- EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32);
- /* Public API */
- void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value)
- {
- unsigned long flags;
- spin_lock_irqsave(&sst->spinlock, flags);
- sst->ops->write(sst->addr.shim, offset, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_write);
- u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset)
- {
- unsigned long flags;
- u32 val;
- spin_lock_irqsave(&sst->spinlock, flags);
- val = sst->ops->read(sst->addr.shim, offset);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return val;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_read);
- void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value)
- {
- unsigned long flags;
- spin_lock_irqsave(&sst->spinlock, flags);
- sst->ops->write64(sst->addr.shim, offset, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_write64);
- u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset)
- {
- unsigned long flags;
- u64 val;
- spin_lock_irqsave(&sst->spinlock, flags);
- val = sst->ops->read64(sst->addr.shim, offset);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return val;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_read64);
- void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value)
- {
- sst->ops->write(sst->addr.shim, offset, value);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked);
- u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset)
- {
- return sst->ops->read(sst->addr.shim, offset);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked);
- void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value)
- {
- sst->ops->write64(sst->addr.shim, offset, value);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked);
- u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset)
- {
- return sst->ops->read64(sst->addr.shim, offset);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked);
- int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value)
- {
- bool change;
- unsigned int old, new;
- u32 ret;
- ret = sst_dsp_shim_read_unlocked(sst, offset);
- old = ret;
- new = (old & (~mask)) | (value & mask);
- change = (old != new);
- if (change)
- sst_dsp_shim_write_unlocked(sst, offset, new);
- return change;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked);
- int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
- u64 mask, u64 value)
- {
- bool change;
- u64 old, new;
- old = sst_dsp_shim_read64_unlocked(sst, offset);
- new = (old & (~mask)) | (value & mask);
- change = (old != new);
- if (change)
- sst_dsp_shim_write64_unlocked(sst, offset, new);
- return change;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked);
- /* This is for registers bits with attribute RWC */
- void sst_dsp_shim_update_bits_forced_unlocked(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value)
- {
- unsigned int old, new;
- u32 ret;
- ret = sst_dsp_shim_read_unlocked(sst, offset);
- old = ret;
- new = (old & (~mask)) | (value & mask);
- sst_dsp_shim_write_unlocked(sst, offset, new);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced_unlocked);
- int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value)
- {
- unsigned long flags;
- bool change;
- spin_lock_irqsave(&sst->spinlock, flags);
- change = sst_dsp_shim_update_bits_unlocked(sst, offset, mask, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return change;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits);
- int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
- u64 mask, u64 value)
- {
- unsigned long flags;
- bool change;
- spin_lock_irqsave(&sst->spinlock, flags);
- change = sst_dsp_shim_update_bits64_unlocked(sst, offset, mask, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return change;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
- /* This is for registers bits with attribute RWC */
- void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value)
- {
- unsigned long flags;
- spin_lock_irqsave(&sst->spinlock, flags);
- sst_dsp_shim_update_bits_forced_unlocked(sst, offset, mask, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced);
- int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask,
- u32 target, u32 timeout, char *operation)
- {
- int time, ret;
- u32 reg;
- bool done = false;
- /*
- * we will poll for couple of ms using mdelay, if not successful
- * then go to longer sleep using usleep_range
- */
- /* check if set state successful */
- for (time = 0; time < 5; time++) {
- if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target) {
- done = true;
- break;
- }
- mdelay(1);
- }
- if (done == false) {
- /* sleeping in 10ms steps so adjust timeout value */
- timeout /= 10;
- for (time = 0; time < timeout; time++) {
- if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target)
- break;
- usleep_range(5000, 10000);
- }
- }
- reg = sst_dsp_shim_read_unlocked(ctx, offset);
- dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
- (time < timeout) ? "successful" : "timedout");
- ret = time < timeout ? 0 : -ETIME;
- return ret;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_register_poll);
- void sst_dsp_dump(struct sst_dsp *sst)
- {
- if (sst->ops->dump)
- sst->ops->dump(sst);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_dump);
- void sst_dsp_reset(struct sst_dsp *sst)
- {
- if (sst->ops->reset)
- sst->ops->reset(sst);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_reset);
- int sst_dsp_boot(struct sst_dsp *sst)
- {
- if (sst->ops->boot)
- sst->ops->boot(sst);
- return 0;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_boot);
- int sst_dsp_wake(struct sst_dsp *sst)
- {
- if (sst->ops->wake)
- return sst->ops->wake(sst);
- return 0;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_wake);
- void sst_dsp_sleep(struct sst_dsp *sst)
- {
- if (sst->ops->sleep)
- sst->ops->sleep(sst);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_sleep);
- void sst_dsp_stall(struct sst_dsp *sst)
- {
- if (sst->ops->stall)
- sst->ops->stall(sst);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_stall);
- void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
- {
- sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
- trace_sst_ipc_msg_tx(msg);
- }
- EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx);
- u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp)
- {
- u32 msg;
- msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
- trace_sst_ipc_msg_rx(msg);
- return msg;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx);
- int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size,
- u32 outbox_offset, size_t outbox_size)
- {
- sst->mailbox.in_base = sst->addr.lpe + inbox_offset;
- sst->mailbox.out_base = sst->addr.lpe + outbox_offset;
- sst->mailbox.in_size = inbox_size;
- sst->mailbox.out_size = outbox_size;
- return 0;
- }
- EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init);
- void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes)
- {
- u32 i;
- trace_sst_ipc_outbox_write(bytes);
- memcpy_toio(sst->mailbox.out_base, message, bytes);
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_outbox_wdata(i, *(u32 *)(message + i));
- }
- EXPORT_SYMBOL_GPL(sst_dsp_outbox_write);
- void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes)
- {
- u32 i;
- trace_sst_ipc_outbox_read(bytes);
- memcpy_fromio(message, sst->mailbox.out_base, bytes);
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_outbox_rdata(i, *(u32 *)(message + i));
- }
- EXPORT_SYMBOL_GPL(sst_dsp_outbox_read);
- void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes)
- {
- u32 i;
- trace_sst_ipc_inbox_write(bytes);
- memcpy_toio(sst->mailbox.in_base, message, bytes);
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_inbox_wdata(i, *(u32 *)(message + i));
- }
- EXPORT_SYMBOL_GPL(sst_dsp_inbox_write);
- void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
- {
- u32 i;
- trace_sst_ipc_inbox_read(bytes);
- memcpy_fromio(message, sst->mailbox.in_base, bytes);
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_inbox_rdata(i, *(u32 *)(message + i));
- }
- EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
- /* Module information */
- MODULE_AUTHOR("Liam Girdwood");
- MODULE_DESCRIPTION("Intel SST Core");
- MODULE_LICENSE("GPL v2");
|