1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // Copyright(c) 2021-2022 Intel Corporation. All rights reserved. |
4 | // |
5 | // Authors: Cezary Rojewski <cezary.rojewski@intel.com> |
6 | // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> |
7 | // |
8 | |
9 | #include <linux/devcoredump.h> |
10 | #include <linux/slab.h> |
11 | #include <sound/hdaudio_ext.h> |
12 | #include "avs.h" |
13 | #include "messages.h" |
14 | |
15 | irqreturn_t avs_skl_irq_thread(struct avs_dev *adev) |
16 | { |
17 | union avs_reply_msg msg; |
18 | u32 hipct, hipcte; |
19 | |
20 | hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); |
21 | hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE); |
22 | |
23 | /* Ensure DSP sent new response to process. */ |
24 | if (!(hipct & SKL_ADSP_HIPCT_BUSY)) |
25 | return IRQ_NONE; |
26 | |
27 | msg.primary = hipct; |
28 | msg.ext.val = hipcte; |
29 | avs_dsp_process_response(adev, header: msg.val); |
30 | |
31 | /* Tell DSP we accepted its message. */ |
32 | snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY); |
33 | /* Unmask busy interrupt. */ |
34 | snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, AVS_ADSP_HIPCCTL_BUSY, |
35 | AVS_ADSP_HIPCCTL_BUSY); |
36 | |
37 | return IRQ_HANDLED; |
38 | } |
39 | |
40 | static int __maybe_unused |
41 | avs_skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, |
42 | u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) |
43 | { |
44 | struct avs_skl_log_state_info *info; |
45 | u32 size, num_cores = adev->hw_cfg.dsp_cores; |
46 | int ret, i; |
47 | |
48 | if (fls_long(l: resource_mask) > num_cores) |
49 | return -EINVAL; |
50 | size = struct_size(info, logs_core, num_cores); |
51 | info = kzalloc(size, GFP_KERNEL); |
52 | if (!info) |
53 | return -ENOMEM; |
54 | |
55 | info->core_mask = resource_mask; |
56 | if (enable) |
57 | for_each_set_bit(i, &resource_mask, num_cores) { |
58 | info->logs_core[i].enable = enable; |
59 | info->logs_core[i].min_priority = *priorities++; |
60 | } |
61 | else |
62 | for_each_set_bit(i, &resource_mask, num_cores) |
63 | info->logs_core[i].enable = enable; |
64 | |
65 | ret = avs_ipc_set_enable_logs(adev, log_info: (u8 *)info, size); |
66 | kfree(objp: info); |
67 | if (ret) |
68 | return AVS_IPC_RET(ret); |
69 | |
70 | return 0; |
71 | } |
72 | |
73 | int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core) |
74 | { |
75 | return core * avs_log_buffer_size(adev); |
76 | } |
77 | |
78 | /* fw DbgLogWp registers */ |
79 | #define FW_REGS_DBG_LOG_WP(core) (0x30 + 0x4 * core) |
80 | |
81 | static int avs_skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) |
82 | { |
83 | void __iomem *buf; |
84 | u16 size, write, offset; |
85 | |
86 | if (!avs_logging_fw(adev)) |
87 | return 0; |
88 | |
89 | size = avs_log_buffer_size(adev) / 2; |
90 | write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core)); |
91 | /* determine buffer half */ |
92 | offset = (write < size) ? size : 0; |
93 | |
94 | /* Address is guaranteed to exist in SRAM2. */ |
95 | buf = avs_log_buffer_addr(adev, msg->log.core) + offset; |
96 | avs_dump_fw_log_wakeup(adev, src: buf, len: size); |
97 | |
98 | return 0; |
99 | } |
100 | |
101 | static int avs_skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) |
102 | { |
103 | u8 *dump; |
104 | |
105 | dump = vzalloc(AVS_FW_REGS_SIZE); |
106 | if (!dump) |
107 | return -ENOMEM; |
108 | |
109 | memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE); |
110 | dev_coredumpv(dev: adev->dev, data: dump, AVS_FW_REGS_SIZE, GFP_KERNEL); |
111 | |
112 | return 0; |
113 | } |
114 | |
115 | static bool avs_skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) |
116 | { |
117 | /* unsupported on cAVS 1.5 hw */ |
118 | return false; |
119 | } |
120 | |
121 | static int avs_skl_set_d0ix(struct avs_dev *adev, bool enable) |
122 | { |
123 | /* unsupported on cAVS 1.5 hw */ |
124 | return 0; |
125 | } |
126 | |
127 | const struct avs_dsp_ops avs_skl_dsp_ops = { |
128 | .power = avs_dsp_core_power, |
129 | .reset = avs_dsp_core_reset, |
130 | .stall = avs_dsp_core_stall, |
131 | .irq_handler = avs_irq_handler, |
132 | .irq_thread = avs_skl_irq_thread, |
133 | .int_control = avs_dsp_interrupt_control, |
134 | .load_basefw = avs_cldma_load_basefw, |
135 | .load_lib = avs_cldma_load_library, |
136 | .transfer_mods = avs_cldma_transfer_modules, |
137 | .log_buffer_offset = avs_skl_log_buffer_offset, |
138 | .log_buffer_status = avs_skl_log_buffer_status, |
139 | .coredump = avs_skl_coredump, |
140 | .d0ix_toggle = avs_skl_d0ix_toggle, |
141 | .set_d0ix = avs_skl_set_d0ix, |
142 | AVS_SET_ENABLE_LOGS_OP(skl) |
143 | }; |
144 | |