|
| 1 | +/* |
| 2 | + * Copyright (c) 2018, Intel Corporation |
| 3 | + * All rights reserved. |
| 4 | + * |
| 5 | + * Redistribution and use in source and binary forms, with or without |
| 6 | + * modification, are permitted provided that the following conditions are met: |
| 7 | + * * Redistributions of source code must retain the above copyright |
| 8 | + * notice, this list of conditions and the following disclaimer. |
| 9 | + * * Redistributions in binary form must reproduce the above copyright |
| 10 | + * notice, this list of conditions and the following disclaimer in the |
| 11 | + * documentation and/or other materials provided with the distribution. |
| 12 | + * * Neither the name of the Intel Corporation nor the |
| 13 | + * names of its contributors may be used to endorse or promote products |
| 14 | + * derived from this software without specific prior written permission. |
| 15 | + * |
| 16 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 17 | + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 18 | + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 19 | + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 20 | + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 21 | + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 22 | + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 23 | + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 24 | + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 25 | + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 26 | + * POSSIBILITY OF SUCH DAMAGE. |
| 27 | + * |
| 28 | + * Author: Tomasz Lauda <tomasz.lauda@linux.intel.com> |
| 29 | + */ |
| 30 | + |
| 31 | +/** |
| 32 | + * \file arch/xtensa/smp/include/arch/idc.h |
| 33 | + * \brief Xtensa SMP architecture IDC header file |
| 34 | + * \authors Tomasz Lauda <tomasz.lauda@linux.intel.com> |
| 35 | + */ |
| 36 | + |
| 37 | +#ifndef __ARCH_IDC_H__ |
| 38 | +#define __ARCH_IDC_H__ |
| 39 | + |
| 40 | +#include <xtos-structs.h> |
| 41 | +#include <arch/cpu.h> |
| 42 | +#include <platform/interrupt.h> |
| 43 | +#include <platform/platform.h> |
| 44 | +#include <sof/alloc.h> |
| 45 | +#include <sof/lock.h> |
| 46 | +#include <sof/trace.h> |
| 47 | + |
| 48 | +/** \brief IDC trace function. */ |
| 49 | +#define trace_idc(__e) trace_event(TRACE_CLASS_IDC, __e) |
| 50 | + |
| 51 | +/** \brief IDC trace value function. */ |
| 52 | +#define tracev_idc(__e) tracev_event(TRACE_CLASS_IDC, __e) |
| 53 | + |
| 54 | +/** \brief IDC trace error function. */ |
| 55 | +#define trace_idc_error(__e) trace_error(TRACE_CLASS_IDC, __e) |
| 56 | + |
| 57 | +/** \brief IDC header mask. */ |
| 58 | +#define IDC_HEADER(x) ((x) & 0x7fffffff) |
| 59 | + |
| 60 | +/** \brief IDC extension mask. */ |
| 61 | +#define IDC_EXTENSION(x) ((x) & 0x0fffffff) |
| 62 | + |
| 63 | +/** \brief IDC message. */ |
| 64 | +struct idc_msg { |
| 65 | + uint32_t header; /**< header value */ |
| 66 | + uint32_t extension; /**< extension value */ |
| 67 | + uint32_t core; /**< core id */ |
| 68 | +}; |
| 69 | + |
| 70 | +/** \brief IDC data. */ |
| 71 | +struct idc { |
| 72 | + spinlock_t lock; /**< lock mechanism */ |
| 73 | + uint32_t busy_bit_mask; /**< busy interrupt mask */ |
| 74 | + uint32_t done_bit_mask; /**< done interrupt mask */ |
| 75 | + uint32_t msg_pending; /**< is message pending */ |
| 76 | + struct idc_msg received_msg; /**< received message */ |
| 77 | + struct idc_msg msg_to_send; /**< message to be sent */ |
| 78 | +}; |
| 79 | + |
| 80 | +/** |
| 81 | + * \brief Returns IDC data. |
| 82 | + * \return Pointer to pointer of IDC data. |
| 83 | + */ |
| 84 | +static inline struct idc **idc_get(void) |
| 85 | +{ |
| 86 | + struct core_context *ctx = (struct core_context *)cpu_read_threadptr(); |
| 87 | + |
| 88 | + return &ctx->idc; |
| 89 | +} |
| 90 | + |
| 91 | +/** |
| 92 | + * \brief IDC interrupt handler. |
| 93 | + * \param[in,out] arg Pointer to IDC data. |
| 94 | + */ |
| 95 | +static void idc_irq_handler(void *arg) |
| 96 | +{ |
| 97 | + struct idc *idc = arg; |
| 98 | + int core = cpu_get_id(); |
| 99 | + uint32_t idctfc; |
| 100 | + uint32_t idctefc; |
| 101 | + uint32_t idcietc; |
| 102 | + uint32_t i; |
| 103 | + |
| 104 | + tracev_idc("IRQ"); |
| 105 | + |
| 106 | + for (i = 0; i < PLATFORM_CORE_COUNT; i++) { |
| 107 | + idctfc = idc_read(IPC_IDCTFC(i), core); |
| 108 | + |
| 109 | + if (idctfc & IPC_IDCTFC_BUSY) { |
| 110 | + trace_idc("Nms"); |
| 111 | + |
| 112 | + /* disable BUSY interrupt */ |
| 113 | + idc_write(IPC_IDCCTL, core, idc->done_bit_mask); |
| 114 | + |
| 115 | + idc->received_msg.core = i; |
| 116 | + idc->received_msg.header = |
| 117 | + idctfc & IPC_IDCTFC_MSG_MASK; |
| 118 | + |
| 119 | + idctefc = idc_read(IPC_IDCTEFC(i), core); |
| 120 | + idc->received_msg.extension = |
| 121 | + idctefc & IPC_IDCTEFC_MSG_MASK; |
| 122 | + |
| 123 | + idc->msg_pending = 1; |
| 124 | + |
| 125 | + break; |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + for (i = 0; i < PLATFORM_CORE_COUNT; i++) { |
| 130 | + idcietc = idc_read(IPC_IDCIETC(i), core); |
| 131 | + |
| 132 | + if (idcietc & IPC_IDCIETC_DONE) { |
| 133 | + tracev_idc("Rpy"); |
| 134 | + |
| 135 | + idc_write(IPC_IDCIETC(i), core, |
| 136 | + idcietc | IPC_IDCIETC_DONE); |
| 137 | + |
| 138 | + break; |
| 139 | + } |
| 140 | + } |
| 141 | +} |
| 142 | + |
| 143 | +/** |
| 144 | + * \brief Sends IDC message. |
| 145 | + */ |
| 146 | +static inline void arch_idc_send_msg(void) |
| 147 | +{ |
| 148 | + struct idc *idc = *idc_get(); |
| 149 | + |
| 150 | + struct idc_msg msg = idc->msg_to_send; |
| 151 | + int core = cpu_get_id(); |
| 152 | + uint32_t flags; |
| 153 | + |
| 154 | + tracev_idc("Msg"); |
| 155 | + |
| 156 | + spin_lock_irq(&idc->lock, flags); |
| 157 | + |
| 158 | + idc_write(IPC_IDCIETC(msg.core), core, msg.extension); |
| 159 | + idc_write(IPC_IDCITC(msg.core), core, msg.header | IPC_IDCITC_BUSY); |
| 160 | + |
| 161 | + spin_unlock_irq(&idc->lock, flags); |
| 162 | +} |
| 163 | + |
| 164 | +/** |
| 165 | + * \brief Executes IDC message based on type. |
| 166 | + * \param[in,out] msg Pointer to IDC message. |
| 167 | + * \return Error status. |
| 168 | + */ |
| 169 | +static inline int32_t idc_cmd(struct idc_msg *msg) |
| 170 | +{ |
| 171 | + /* execute message based on type */ |
| 172 | + return 0; |
| 173 | +} |
| 174 | + |
| 175 | +/** |
| 176 | + * \brief Handles received IDC message. |
| 177 | + * \param[in,out] idc Pointer to IDC data. |
| 178 | + */ |
| 179 | +static inline void idc_do_cmd(struct idc *idc) |
| 180 | +{ |
| 181 | + int core = cpu_get_id(); |
| 182 | + int initiator = idc->received_msg.core; |
| 183 | + |
| 184 | + trace_idc("Cmd"); |
| 185 | + |
| 186 | + idc_cmd(&idc->received_msg); |
| 187 | + |
| 188 | + idc->msg_pending = 0; |
| 189 | + |
| 190 | + /* clear BUSY bit */ |
| 191 | + idc_write(IPC_IDCTFC(initiator), core, |
| 192 | + idc_read(IPC_IDCTFC(initiator), core) | IPC_IDCTFC_BUSY); |
| 193 | + |
| 194 | + /* enable BUSY interrupt */ |
| 195 | + idc_write(IPC_IDCCTL, core, idc->busy_bit_mask | idc->done_bit_mask); |
| 196 | +} |
| 197 | + |
| 198 | +/** |
| 199 | + * \brief Checks for pending IDC messages. |
| 200 | + */ |
| 201 | +static inline void arch_idc_process_msg_queue(void) |
| 202 | +{ |
| 203 | + struct idc *idc = *idc_get(); |
| 204 | + |
| 205 | + if (idc->msg_pending) |
| 206 | + idc_do_cmd(idc); |
| 207 | +} |
| 208 | + |
| 209 | +/** |
| 210 | + * \brief Returns BUSY interrupt mask based on core id. |
| 211 | + * \param[in] core Core id. |
| 212 | + * \return BUSY interrupt mask. |
| 213 | + */ |
| 214 | +static inline uint32_t idc_get_busy_bit_mask(int core) |
| 215 | +{ |
| 216 | + uint32_t busy_mask = 0; |
| 217 | + int i; |
| 218 | + |
| 219 | + if (core == PLATFORM_MASTER_CORE_ID) { |
| 220 | + for (i = 0; i < PLATFORM_CORE_COUNT; i++) { |
| 221 | + if (i != PLATFORM_MASTER_CORE_ID) |
| 222 | + busy_mask |= IPC_IDCCTL_IDCTBIE(i); |
| 223 | + } |
| 224 | + } else { |
| 225 | + busy_mask = IPC_IDCCTL_IDCTBIE(PLATFORM_MASTER_CORE_ID); |
| 226 | + } |
| 227 | + |
| 228 | + return busy_mask; |
| 229 | +} |
| 230 | + |
| 231 | +/** |
| 232 | + * \brief Returns DONE interrupt mask based on core id. |
| 233 | + * \param[in] core Core id. |
| 234 | + * \return DONE interrupt mask. |
| 235 | + */ |
| 236 | +static inline uint32_t idc_get_done_bit_mask(int core) |
| 237 | +{ |
| 238 | + uint32_t done_mask = 0; |
| 239 | + int i; |
| 240 | + |
| 241 | + if (core == PLATFORM_MASTER_CORE_ID) { |
| 242 | + for (i = 0; i < PLATFORM_CORE_COUNT; i++) { |
| 243 | + if (i != PLATFORM_MASTER_CORE_ID) |
| 244 | + done_mask |= IPC_IDCCTL_IDCIDIE(i); |
| 245 | + } |
| 246 | + } else { |
| 247 | + done_mask = 0; |
| 248 | + } |
| 249 | + |
| 250 | + return done_mask; |
| 251 | +} |
| 252 | + |
| 253 | +/** |
| 254 | + * \brief Initializes IDC data and registers for interrupt. |
| 255 | + */ |
| 256 | +static inline void arch_idc_init(void) |
| 257 | +{ |
| 258 | + int core = cpu_get_id(); |
| 259 | + |
| 260 | + trace_idc("IDI"); |
| 261 | + |
| 262 | + /* initialize idc data */ |
| 263 | + struct idc **idc = idc_get(); |
| 264 | + *idc = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(**idc)); |
| 265 | + spinlock_init(&((*idc)->lock)); |
| 266 | + (*idc)->busy_bit_mask = idc_get_busy_bit_mask(core); |
| 267 | + (*idc)->done_bit_mask = idc_get_done_bit_mask(core); |
| 268 | + |
| 269 | + /* configure interrupt */ |
| 270 | + interrupt_register(PLATFORM_IDC_INTERRUPT(core), |
| 271 | + idc_irq_handler, *idc); |
| 272 | + interrupt_enable(PLATFORM_IDC_INTERRUPT(core)); |
| 273 | + |
| 274 | + /* enable BUSY and DONE (only for master core) interrupts */ |
| 275 | + idc_write(IPC_IDCCTL, core, |
| 276 | + (*idc)->busy_bit_mask | (*idc)->done_bit_mask); |
| 277 | +} |
| 278 | + |
| 279 | +#endif |
0 commit comments