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