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