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 */ 32 void sst_shim32_write(void __iomem *addr, u32 offset, u32 value) 33 { 34 writel(value, addr + offset); 35 } 36 EXPORT_SYMBOL_GPL(sst_shim32_write); 37 38 u32 sst_shim32_read(void __iomem *addr, u32 offset) 39 { 40 return readl(addr + offset); 41 } 42 EXPORT_SYMBOL_GPL(sst_shim32_read); 43 44 void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value) 45 { 46 memcpy_toio(addr + offset, &value, sizeof(value)); 47 } 48 EXPORT_SYMBOL_GPL(sst_shim32_write64); 49 50 u64 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 } 57 EXPORT_SYMBOL_GPL(sst_shim32_read64); 58 59 static 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 68 static 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 77 void 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 } 82 EXPORT_SYMBOL_GPL(sst_memcpy_toio_32); 83 84 void 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 } 89 EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32); 90 91 /* Public API */ 92 void 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 } 100 EXPORT_SYMBOL_GPL(sst_dsp_shim_write); 101 102 u32 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 } 113 EXPORT_SYMBOL_GPL(sst_dsp_shim_read); 114 115 void 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 } 123 EXPORT_SYMBOL_GPL(sst_dsp_shim_write64); 124 125 u64 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 } 136 EXPORT_SYMBOL_GPL(sst_dsp_shim_read64); 137 138 void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value) 139 { 140 sst->ops->write(sst->addr.shim, offset, value); 141 } 142 EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked); 143 144 u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset) 145 { 146 return sst->ops->read(sst->addr.shim, offset); 147 } 148 EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked); 149 150 void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value) 151 { 152 sst->ops->write64(sst->addr.shim, offset, value); 153 } 154 EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked); 155 156 u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset) 157 { 158 return sst->ops->read64(sst->addr.shim, offset); 159 } 160 EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked); 161 162 int 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 } 180 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked); 181 182 int 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 } 198 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked); 199 200 /* This is for registers bits with attribute RWC */ 201 void 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 } 214 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced_unlocked); 215 216 int 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 } 227 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits); 228 229 int 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 } 240 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64); 241 242 /* This is for registers bits with attribute RWC */ 243 void 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 } 252 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced); 253 254 int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask, 255 u32 target, u32 timeout, char *operation) 256 { 257 int time, ret; 258 u32 reg; 259 bool done = false; 260 261 /* 262 * we will poll for couple of ms using mdelay, if not successful 263 * then go to longer sleep using usleep_range 264 */ 265 266 /* check if set state successful */ 267 for (time = 0; time < 5; time++) { 268 if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target) { 269 done = true; 270 break; 271 } 272 mdelay(1); 273 } 274 275 if (done == false) { 276 /* sleeping in 10ms steps so adjust timeout value */ 277 timeout /= 10; 278 279 for (time = 0; time < timeout; time++) { 280 if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target) 281 break; 282 283 usleep_range(5000, 10000); 284 } 285 } 286 287 reg = sst_dsp_shim_read_unlocked(ctx, offset); 288 dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation, 289 (time < timeout) ? "successful" : "timedout"); 290 ret = time < timeout ? 0 : -ETIME; 291 292 return ret; 293 } 294 EXPORT_SYMBOL_GPL(sst_dsp_register_poll); 295 296 void sst_dsp_dump(struct sst_dsp *sst) 297 { 298 if (sst->ops->dump) 299 sst->ops->dump(sst); 300 } 301 EXPORT_SYMBOL_GPL(sst_dsp_dump); 302 303 void sst_dsp_reset(struct sst_dsp *sst) 304 { 305 if (sst->ops->reset) 306 sst->ops->reset(sst); 307 } 308 EXPORT_SYMBOL_GPL(sst_dsp_reset); 309 310 int sst_dsp_boot(struct sst_dsp *sst) 311 { 312 if (sst->ops->boot) 313 sst->ops->boot(sst); 314 315 return 0; 316 } 317 EXPORT_SYMBOL_GPL(sst_dsp_boot); 318 319 int sst_dsp_wake(struct sst_dsp *sst) 320 { 321 if (sst->ops->wake) 322 return sst->ops->wake(sst); 323 324 return 0; 325 } 326 EXPORT_SYMBOL_GPL(sst_dsp_wake); 327 328 void sst_dsp_sleep(struct sst_dsp *sst) 329 { 330 if (sst->ops->sleep) 331 sst->ops->sleep(sst); 332 } 333 EXPORT_SYMBOL_GPL(sst_dsp_sleep); 334 335 void sst_dsp_stall(struct sst_dsp *sst) 336 { 337 if (sst->ops->stall) 338 sst->ops->stall(sst); 339 } 340 EXPORT_SYMBOL_GPL(sst_dsp_stall); 341 342 void 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 } 347 EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx); 348 349 u32 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 } 358 EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx); 359 360 int 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 } 369 EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init); 370 371 void 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 } 382 EXPORT_SYMBOL_GPL(sst_dsp_outbox_write); 383 384 void 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 } 395 EXPORT_SYMBOL_GPL(sst_dsp_outbox_read); 396 397 void 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 } 408 EXPORT_SYMBOL_GPL(sst_dsp_inbox_write); 409 410 void 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 } 421 EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); 422 423 /* Module information */ 424 MODULE_AUTHOR("Liam Girdwood"); 425 MODULE_DESCRIPTION("Intel SST Core"); 426 MODULE_LICENSE("GPL v2"); 427