Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | /*
* Copyright (c) 2021 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SOC_INTEL_ADSP_CAVS_IDC_H_
#define ZEPHYR_SOC_INTEL_ADSP_CAVS_IDC_H_
/*
* (I)ntra (D)SP (C)ommunication is the facility for sending
* interrupts directly between DSP cores. The interface
* is... somewhat needlessly complicated.
*
* Each core has a set of registers its is supposed to use, but all
* registers seem to behave symmetrically regardless of which CPU does
* the access.
*
* Each core has a "ITC" register associated with each other core in
* the system (including itself). When the high bit becomes 1 in an
* ITC register, an IDC interrupt is latched for the target core.
* Data in other bits is stored but otherwise ignored, it's merely
* data to be transmitted along with the interrupt.
*
* On the target core, there is a "TFC" register for each core that
* reflects the same value written to ITC. In fact experimentally
* these seem to be the same register at different addresses. When
* the high bit of TFC is written with a 1, the value becomes ZERO,
* indicating an acknowledgment of the interrupt. This action can
* also latch an interrupt to send back to the originator if unmasked
* (see below).
*
* (There is also an IETC/TEFC register pair that stores 30 bits of
* data but otherwise has no hardware behavior. This is probably best
* ignored for new protocols, as experimentally it seems to provide no
* performance benefit vs. storing a message in RAM. The cAVS 1.5/1.8
* ROM boot protocol uses it to store an entry point address, though.)
*
* So you can send a synchronous message from core "src" (where src is
* the PRID of the CPU, equal to arch_curr_cpu()->id in Zephyr) to
* core "dst" with:
*
* IDC[src].core[dst].itc = BIT(31) | message;
* while (IDC[src].core[dst].itc & BIT(31)) {}
*
* And the other side (on cpu "dst", generally in the IDC interruupt
* handler) will read and acknowledge those same values via:
*
* uint32_t my_msg = IDC[dst].core[src].tfc & 0x7fffffff;
* IDC[dst].core[src].tfc = BIT(31); // clear high bit to signal completion
*
* And for clarity, at all times and for all cores and all pairs of src/dst:
*
* IDC[src].core[dst].itc == IDC[dst].core[src].tfc
*
* Finally note the two control registers at the end of each core's
* register block, which store a bitmask of cores that are allowed to
* send that core an interrupt via either ITC (set high "BUSY" bit) or
* TFC (clear high "DONE" bit). This masking is in ADDITION to the
* level 2 bit for IDC in the per-core INTCTRL DSP register AND the
* Xtensa architectural INTENABLE SR. You must enable IDC interrupts
* form core "src" to core "dst" with:
*
* IDC[dst].busy_int |= BIT(src) // Or disable with "&= ~BIT(src)" of course
*/
struct cavs_idc {
struct {
uint32_t tfc; /* (T)arget (F)rom (C)ore */
uint32_t tefc; /* ^^ + (E)xtension */
uint32_t itc; /* (I)nitiator (T)o (C)ore */
uint32_t ietc; /* ^^ + (E)xtension */
} core[4];
uint32_t unused0[4];
uint8_t busy_int; /* bitmask of cores that can IDC via ITC */
uint8_t done_int; /* bitmask of cores that can IDC via TFC */
uint8_t unused1;
uint8_t unused2;
uint32_t unused3[11];
};
#define IDC ((volatile struct cavs_idc *)DT_REG_ADDR(DT_NODELABEL(idc)))
extern void soc_idc_init(void);
/* cAVS interrupt mask bits. Each core has one of these structs
* indexed in the intctrl[] array. Each external interrupt source
* indexes one bit in one of the state substructs (one each for Xtensa
* level 2-5 interrupts). The "mask" field shows the current masking
* state, with a 1 representing "interrupt disabled". The "status"
* field indicates interrupts that are currently latched and awaiting
* delivery. Write bits to "set" to set the mask bit to 1 and disable
* interrupts. Write a 1 bit to "clear" to force the mask bit to 0
* and enable them. For example, for core "c":
*
* INTCTRL[c].l2.clear = 0x10; // unmask IDC interrupt
*
* INTCTRL[c].l3.set = 0xffffffff; // Mask all L3 interrupts
*
* Note that this interrupt controller is separate from the Xtensa
* architectural interrupt hardware controlled by the
* INTENABLE/INTERRUPT/INTSET/INTCLEAR special registers on each core
* which much also be configured for interrupts to arrive. Note also
* that some hardware (like IDC, see above) implements a third (!)
* layer of interrupt masking.
*/
struct cavs_intctrl {
struct {
uint32_t set, clear, mask, status;
} l2, l3, l4, l5;
};
/* Named interrupt bits in the above registers */
#define CAVS_L2_HPGPDMA BIT(24) /* HP General Purpose DMA */
#define CAVS_L2_DWCT1 BIT(23) /* DSP Wall Clock Timer 1 */
#define CAVS_L2_DWCT0 BIT(22) /* DSP Wall Clock Timer 0 */
#define CAVS_L2_L2ME BIT(21) /* L2 Memory Error */
#define CAVS_L2_DTS BIT(20) /* DSP Timestamping */
#define CAVS_L2_SHA BIT(16) /* SHA-256 */
#define CAVS_L2_DCLC BIT(15) /* Demand Cache Line Command */
#define CAVS_L2_IDC BIT(8) /* IDC */
#define CAVS_L2_HIPC BIT(7) /* Host IPC */
#define CAVS_L2_MIPC BIT(6) /* CSME IPC */
#define CAVS_L2_PIPC BIT(5) /* PMC IPC */
#define CAVS_L2_SIPC BIT(4) /* Sensor Hub IPC */
#define CAVS_L3_DSPGCL BIT(31) /* DSP Gateway Code Loader */
#define CAVS_L3_DSPGHOS(n) BIT(16 + n) /* DSP Gateway Host Output Stream */
#define CAVS_L3_HPGPDMA BIT(15) /* HP General Purpose DMA */
#define CAVS_L3_DSPGHIS(n) BIT(n) /* DSP Gateway Host Input Stream */
#define CAVS_L4_DSPGLOS(n) BIT(16 + n) /* DSP Gateway Link Output Stream */
#define CAVS_L4_LPGPGMA BIT(15) /* LP General Purpose DMA */
#define CAVS_L4_DSPGLIS(n) BIT(n) /* DSP Gateway Link Input Stream */
#define CAVS_L5_LPGPDMA BIT(16) /* LP General Purpose DMA */
#define CAVS_L5_DWCT1 BIT(15) /* DSP Wall CLock Timer 1 */
#define CAVS_L5_DWCT0 BIT(14) /* DSP Wall Clock Timer 0 */
#define CAVS_L5_DMIX BIT(13) /* Digital Mixer */
#define CAVS_L5_ANC BIT(12) /* Active Noise Cancellation */
#define CAVS_L5_SNDW BIT(11) /* SoundWire */
#define CAVS_L5_SLIM BIT(10) /* Slimbus */
#define CAVS_L5_DSPK BIT(9) /* Digital Speaker */
#define CAVS_L5_DMIC BIT(8) /* Digital Mic */
#define CAVS_L5_I2S(n) BIT(n) /* I2S */
#define CAVS_INTCTRL \
((volatile struct cavs_intctrl *)DT_REG_ADDR(DT_NODELABEL(cavs0)))
#endif /* ZEPHYR_SOC_INTEL_ADSP_CAVS_IDC_H_ */
|