zephyr/soc/xtensa/intel_adsp/common/trace_out.c

139 lines
3.3 KiB
C

/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <adsp/cache.h>
#include <soc/shim.h>
/* Simple output driver for the trace window of an ADSP device used
* for communication with the host processor as a shared memory
* region. The protocol uses an array of 64-byte "slots", each of
* which is prefixed by a 16 bit magic number followed by a sequential
* ID number. The remaining bytes are a (potentially nul-terminated)
* string containing output data.
*
* IMPORTANT NOTE on cache coherence: the shared memory window is in
* HP-SRAM. Each DSP core has an L1 cache that is incoherent (!) from
* the perspective of the other cores. To handle this, we take care
* to access all memory through the uncached window into HP-SRAM at
* 0x9xxxxxxx and not the L1-cached mapping of the same memory at
* 0xBxxxxxxx.
*/
#define SLOT_SIZE 64
#define SLOT_MAGIC 0x55aa
#define NSLOTS (SRAM_TRACE_SIZE / SLOT_SIZE)
#define MSGSZ (SLOT_SIZE - sizeof(struct slot_hdr))
struct slot_hdr {
uint16_t magic;
uint16_t id;
};
struct slot {
struct slot_hdr hdr;
char msg[MSGSZ];
};
struct metadata {
struct k_spinlock lock;
bool initialized;
uint32_t curr_slot; /* To which slot are we writing? */
uint32_t n_bytes; /* How many bytes buffered in curr_slot */
};
/* Give it a cache line all its own! */
static __aligned(64) union {
struct metadata meta;
uint32_t cache_pad[16];
} data_rec;
#define data ((volatile struct metadata *)z_soc_uncached_ptr(&data_rec.meta))
static inline struct slot *slot(int i)
{
struct slot *slots = z_soc_uncached_ptr((void *)SRAM_TRACE_BASE);
return &slots[i];
}
static int slot_incr(int s)
{
return (s + 1) % NSLOTS;
}
void intel_adsp_trace_out(int8_t *str, size_t len)
{
if (len == 0) {
return;
}
k_spinlock_key_t key = k_spin_lock((void *)&data->lock);
if (!data->initialized) {
slot(0)->hdr.magic = 0;
slot(0)->hdr.id = 0;
data->curr_slot = data->n_bytes = 0;
data->initialized = 1;
}
/* We work with a local copy of the global data for
* performance reasons (The memory behind the "data" pointer
* is uncached and volatile!) and put it back at the end.
*/
uint32_t curr_slot = data->curr_slot;
uint32_t n_bytes = data->n_bytes;
for (size_t i = 0; i < len; i++) {
int8_t c = str[i];
struct slot *s = slot(curr_slot);
s->msg[n_bytes++] = c;
/* Are we done with this slot? Terminate it and flag
* it for consumption on the other side
*/
if (c == '\n' || n_bytes >= MSGSZ) {
if (n_bytes < MSGSZ) {
s->msg[n_bytes] = 0;
}
/* Make sure the next slot has a magic number
* (so the reader can distinguish between
* no-new-data and system-reset), but does NOT
* have the correct successor ID (so can never
* be picked up as valid data). We'll
* increment it later when we terminate that
* slot.
*/
int next_slot = slot_incr(curr_slot);
uint16_t new_id = s->hdr.id + 1;
slot(next_slot)->hdr.id = new_id;
slot(next_slot)->hdr.magic = SLOT_MAGIC;
slot(next_slot)->msg[0] = 0;
s->hdr.id = new_id;
s->hdr.magic = SLOT_MAGIC;
curr_slot = next_slot;
n_bytes = 0;
}
}
data->curr_slot = curr_slot;
data->n_bytes = n_bytes;
k_spin_unlock((void *)&data->lock, key);
}
int arch_printk_char_out(int c)
{
int8_t s = c;
intel_adsp_trace_out(&s, 1);
return 0;
}