139 lines
3.3 KiB
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;
|
|
}
|