coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/ish/ipc_heci.c

730 lines
19 KiB
C

/* Copyright 2018 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* IPC module for ISH */
/**
* IPC - Inter Processor Communication
* -----------------------------------
*
* IPC is a bi-directional doorbell based message passing interface sans
* session and transport layers, between hardware blocks. ISH uses IPC to
* communicate with the Host, PMC (Power Management Controller), CSME
* (Converged Security and Manageability Engine), Audio, Graphics and ISP.
*
* Both the initiator and target ends each have a 32-bit doorbell register and
* 128-byte message regions. In addition, the following register pairs help in
* synchronizing IPC.
*
* - Peripheral Interrupt Status Register (PISR)
* - Peripheral Interrupt Mask Register (PIMR)
* - Doorbell Clear Status Register (DB CSR)
*/
#include "registers.h"
#include "console.h"
#include "task.h"
#include "util.h"
#include "ipc_heci.h"
#include "ish_fwst.h"
#include "queue.h"
#include "hooks.h"
#include "hwtimer.h"
#define CPUTS(outstr) cputs(CC_LPC, outstr)
#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_LPC, format, ## args)
/*
* comminucation protocol is defined in Linux Documentation
* <kernel_root>/Documentation/hid/intel-ish-hid.txt
*/
/* MNG commands */
/* The ipc_mng_task manages IPC link. It should be the highest priority */
#define MNG_RX_CMPL_ENABLE 0
#define MNG_RX_CMPL_DISABLE 1
#define MNG_RX_CMPL_INDICATION 2
#define MNG_RESET_NOTIFY 3
#define MNG_RESET_NOTIFY_ACK 4
#define MNG_SYNC_FW_CLOCK 5
#define MNG_ILLEGAL_CMD 0xFF
/* Doorbell */
#define IPC_DB_MSG_LENGTH_FIELD 0x3FF
#define IPC_DB_MSG_LENGTH_SHIFT 0
#define IPC_DB_MSG_LENGTH_MASK \
(IPC_DB_MSG_LENGTH_FIELD << IPC_DB_MSG_LENGTH_SHIFT)
#define IPC_DB_PROTOCOL_FIELD 0x0F
#define IPC_DB_PROTOCOL_SHIFT 10
#define IPC_DB_PROTOCOL_MASK (IPC_DB_PROTOCOL_FIELD << IPC_DB_PROTOCOL_SHIFT)
#define IPC_DB_CMD_FIELD 0x0F
#define IPC_DB_CMD_SHIFT 16
#define IPC_DB_CMD_MASK (IPC_DB_CMD_FIELD << IPC_DB_CMD_SHIFT)
#define IPC_DB_BUSY_SHIFT 31
#define IPC_DB_BUSY_MASK BIT(IPC_DB_BUSY_SHIFT)
#define IPC_DB_MSG_LENGTH(drbl) \
(((drbl) & IPC_DB_MSG_LENGTH_MASK) >> IPC_DB_MSG_LENGTH_SHIFT)
#define IPC_DB_PROTOCOL(drbl) \
(((drbl) & IPC_DB_PROTOCOL_MASK) >> IPC_DB_PROTOCOL_SHIFT)
#define IPC_DB_CMD(drbl) \
(((drbl) & IPC_DB_CMD_MASK) >> IPC_DB_CMD_SHIFT)
#define IPC_DB_BUSY(drbl) (!!((drbl) & IPC_DB_BUSY_MASK))
#define IPC_BUILD_DB(length, proto, cmd, busy) \
(((busy) << IPC_DB_BUSY_SHIFT) | ((cmd) << IPC_DB_CMD_SHIFT) | \
((proto) << IPC_DB_PROTOCOL_SHIFT) | \
((length) << IPC_DB_MSG_LENGTH_SHIFT))
#define IPC_BUILD_MNG_DB(cmd, length) \
IPC_BUILD_DB(length, IPC_PROTOCOL_MNG, cmd, 1)
#define IPC_BUILD_HECI_DB(length) \
IPC_BUILD_DB(length, IPC_PROTOCOL_HECI, 0, 1)
#define IPC_MSG_MAX_SIZE 0x80
#define IPC_HOST_MSG_QUEUE_SIZE 8
#define IPC_PMC_MSG_QUEUE_SIZE 2
#define IPC_HANDLE_PEER_ID_SHIFT 4
#define IPC_HANDLE_PROTOCOL_SHIFT 0
#define IPC_HANDLE_PROTOCOL_MASK 0x0F
#define IPC_BUILD_HANDLE(peer_id, protocol) \
((ipc_handle_t)(((peer_id) << IPC_HANDLE_PEER_ID_SHIFT) | (protocol)))
#define IPC_BUILD_MNG_HANDLE(peer_id) \
IPC_BUILD_HANDLE((peer_id), IPC_PROTOCOL_MNG)
#define IPC_BUILD_HOST_MNG_HANDLE() IPC_BUILD_MNG_HANDLE(IPC_PEER_ID_HOST)
#define IPC_HANDLE_PEER_ID(handle) \
((uint32_t)(handle) >> IPC_HANDLE_PEER_ID_SHIFT)
#define IPC_HANDLE_PROTOCOL(handle) \
((uint32_t)(handle) & IPC_HANDLE_PROTOCOL_MASK)
#define IPC_IS_VALID_HANDLE(handle) \
(IPC_HANDLE_PEER_ID(handle) < IPC_PEERS_COUNT && \
IPC_HANDLE_PROTOCOL(handle) < IPC_PROTOCOL_COUNT)
struct ipc_msg {
uint32_t drbl;
uint32_t *timestamp_of_outgoing_doorbell;
uint8_t payload[IPC_MSG_MAX_SIZE];
} __packed;
struct ipc_rst_payload {
uint16_t reset_id;
uint16_t reserved;
};
struct ipc_oob_msg {
uint32_t address;
uint32_t length;
};
struct ipc_msg_event {
task_id_t task_id;
uint32_t event;
uint8_t enabled;
};
/*
* IPC interface context
* This is per-IPC context.
*/
struct ipc_if_ctx {
volatile uint8_t *in_msg_reg;
volatile uint8_t *out_msg_reg;
volatile uint32_t *in_drbl_reg;
volatile uint32_t *out_drbl_reg;
uint32_t clr_busy_bit;
uint32_t pimr_2ish_bit;
uint32_t pimr_2host_clearing_bit;
uint8_t irq_in;
uint8_t irq_clr;
uint16_t reset_id;
struct ipc_msg_event msg_events[IPC_PROTOCOL_COUNT];
struct mutex lock;
struct mutex write_lock;
struct queue tx_queue;
uint8_t is_tx_ipc_busy;
uint8_t initialized;
};
/* list of peer contexts */
static struct ipc_if_ctx ipc_peer_ctxs[IPC_PEERS_COUNT] = {
[IPC_PEER_ID_HOST] = {
.in_msg_reg = IPC_HOST2ISH_MSG_BASE,
.out_msg_reg = IPC_ISH2HOST_MSG_BASE,
.in_drbl_reg = IPC_HOST2ISH_DOORBELL_ADDR,
.out_drbl_reg = IPC_ISH2HOST_DOORBELL_ADDR,
.clr_busy_bit = IPC_DB_CLR_STS_ISH2HOST_BIT,
.pimr_2ish_bit = IPC_PIMR_HOST2ISH_BIT,
.pimr_2host_clearing_bit = IPC_PIMR_ISH2HOST_CLR_BIT,
.irq_in = ISH_IPC_HOST2ISH_IRQ,
.irq_clr = ISH_IPC_ISH2HOST_CLR_IRQ,
.tx_queue = QUEUE_NULL(IPC_HOST_MSG_QUEUE_SIZE, struct ipc_msg),
},
/* Other peers (PMC, CSME, etc) to be added when required */
};
static inline struct ipc_if_ctx *ipc_get_if_ctx(const uint32_t peer_id)
{
return &ipc_peer_ctxs[peer_id];
}
static inline struct ipc_if_ctx *ipc_handle_to_if_ctx(const ipc_handle_t handle)
{
return ipc_get_if_ctx(IPC_HANDLE_PEER_ID(handle));
}
static inline void ipc_enable_pimr_db_interrupt(const struct ipc_if_ctx *ctx)
{
IPC_PIMR |= ctx->pimr_2ish_bit;
}
static inline void ipc_disable_pimr_db_interrupt(const struct ipc_if_ctx *ctx)
{
IPC_PIMR &= ~ctx->pimr_2ish_bit;
}
static inline void ipc_enable_pimr_clearing_interrupt(
const struct ipc_if_ctx *ctx)
{
IPC_PIMR |= ctx->pimr_2host_clearing_bit;
}
static inline void ipc_disable_pimr_clearing_interrupt(
const struct ipc_if_ctx *ctx)
{
IPC_PIMR &= ~ctx->pimr_2host_clearing_bit;
}
static void write_payload_and_ring_drbl(const struct ipc_if_ctx *ctx,
uint32_t drbl,
const uint8_t *payload,
size_t payload_size)
{
memcpy((void *)(ctx->out_msg_reg), payload, payload_size);
*(ctx->out_drbl_reg) = drbl;
}
static int ipc_write_raw_timestamp(struct ipc_if_ctx *ctx, uint32_t drbl,
const uint8_t *payload, size_t payload_size,
uint32_t *timestamp)
{
struct queue *q = &ctx->tx_queue;
struct ipc_msg *msg;
size_t tail, space;
int res = 0;
mutex_lock(&ctx->write_lock);
ipc_disable_pimr_clearing_interrupt(ctx);
if (ctx->is_tx_ipc_busy) {
space = queue_space(q);
if (space) {
tail = q->state->tail & (q->buffer_units - 1);
msg = (struct ipc_msg *)q->buffer + tail;
msg->drbl = drbl;
msg->timestamp_of_outgoing_doorbell = timestamp;
memcpy(msg->payload, payload, payload_size);
queue_advance_tail(q, 1);
} else {
CPRINTS("tx queue is full");
res = -IPC_ERR_TX_QUEUE_FULL;
}
ipc_enable_pimr_clearing_interrupt(ctx);
goto write_unlock;
}
ctx->is_tx_ipc_busy = 1;
ipc_enable_pimr_clearing_interrupt(ctx);
write_payload_and_ring_drbl(ctx, drbl, payload, payload_size);
/* We wrote inline, take timestamp now */
if (timestamp)
*timestamp = __hw_clock_source_read();
write_unlock:
mutex_unlock(&ctx->write_lock);
return res;
}
static int ipc_write_raw(struct ipc_if_ctx *ctx, uint32_t drbl,
const uint8_t *payload, size_t payload_size)
{
return ipc_write_raw_timestamp(ctx, drbl, payload, payload_size, NULL);
}
static int ipc_send_reset_notify(const ipc_handle_t handle)
{
struct ipc_rst_payload *ipc_rst;
struct ipc_if_ctx *ctx;
struct ipc_msg msg;
ctx = ipc_handle_to_if_ctx(handle);
ctx->reset_id = (uint16_t)ish_fwst_get_reset_id();
ipc_rst = (struct ipc_rst_payload *)msg.payload;
ipc_rst->reset_id = ctx->reset_id;
msg.drbl = IPC_BUILD_MNG_DB(MNG_RESET_NOTIFY, sizeof(*ipc_rst));
ipc_write_raw(ctx, msg.drbl, msg.payload, IPC_DB_MSG_LENGTH(msg.drbl));
return 0;
}
static int ipc_send_cmpl_indication(struct ipc_if_ctx *ctx)
{
struct ipc_msg msg;
msg.drbl = IPC_BUILD_MNG_DB(MNG_RX_CMPL_INDICATION, 0);
ipc_write_raw(ctx, msg.drbl, msg.payload, IPC_DB_MSG_LENGTH(msg.drbl));
return 0;
}
static int ipc_get_protocol_data(const struct ipc_if_ctx *ctx,
const uint32_t protocol,
uint8_t *buf, const size_t buf_size)
{
int len = 0, payload_size;
uint8_t *src = NULL, *dest = NULL;
struct ipc_msg *msg;
uint32_t drbl_val;
drbl_val = *(ctx->in_drbl_reg);
payload_size = IPC_DB_MSG_LENGTH(drbl_val);
if (payload_size > IPC_MAX_PAYLOAD_SIZE) {
CPRINTS("invalid msg : payload is too big");
return -IPC_ERR_INVALID_MSG;
}
switch (protocol) {
case IPC_PROTOCOL_HECI:
/* copy only payload which is a heci packet */
len = payload_size;
break;
case IPC_PROTOCOL_MNG:
/* copy including doorbell which forms a ipc packet */
len = payload_size + sizeof(drbl_val);
break;
default:
CPRINTS("protocol %d not supported yet", protocol);
break;
}
if (len > buf_size) {
CPRINTS("buffer is smaller than payload");
return -IPC_ERR_TOO_SMALL_BUFFER;
}
if (IS_ENABLED(IPC_HECI_DEBUG))
CPRINTF("ipc p=%d, db=0x%0x, payload_size=%d\n",
protocol, drbl_val,
IPC_DB_MSG_LENGTH(drbl_val));
switch (protocol) {
case IPC_PROTOCOL_HECI:
src = (uint8_t *)ctx->in_msg_reg;
dest = buf;
break;
case IPC_PROTOCOL_MNG:
src = (uint8_t *)ctx->in_msg_reg;
msg = (struct ipc_msg *)buf;
msg->drbl = drbl_val;
dest = msg->payload;
break;
default :
break;
}
if (src && dest)
memcpy(dest, src, payload_size);
return len;
}
static void set_pimr_and_send_rx_complete(struct ipc_if_ctx *ctx)
{
ipc_enable_pimr_db_interrupt(ctx);
ipc_send_cmpl_indication(ctx);
}
static void handle_msg_recv_interrupt(const uint32_t peer_id)
{
struct ipc_if_ctx *ctx;
uint32_t drbl_val, payload_size, protocol, invalid_msg = 0;
ctx = ipc_get_if_ctx(peer_id);
ipc_disable_pimr_db_interrupt(ctx);
drbl_val = *(ctx->in_drbl_reg);
protocol = IPC_DB_PROTOCOL(drbl_val);
payload_size = IPC_DB_MSG_LENGTH(drbl_val);
if (payload_size > IPC_MSG_MAX_SIZE)
invalid_msg = 1;
if (!ctx->msg_events[protocol].enabled)
invalid_msg = 2;
if (!invalid_msg) {
/* send event to task */
task_set_event(ctx->msg_events[protocol].task_id,
ctx->msg_events[protocol].event, 0);
} else {
CPRINTS("discard msg (%d) : %d", protocol, invalid_msg);
*(ctx->in_drbl_reg) = 0;
set_pimr_and_send_rx_complete(ctx);
}
}
static void handle_busy_clear_interrupt(const uint32_t peer_id)
{
struct ipc_if_ctx *ctx;
struct ipc_msg *msg;
struct queue *q;
size_t head;
ctx = ipc_get_if_ctx(peer_id);
/*
* Resetting interrupt status bit should be done
* before sending an item in tx_queue.
*/
IPC_BUSY_CLEAR = ctx->clr_busy_bit;
/*
* No need to use sync mechanism here since the accesing the queue
* happens only when either this IRQ is disabled or
* in ISR context(here) of this IRQ.
*/
if (!queue_is_empty(&ctx->tx_queue)) {
q = &ctx->tx_queue;
head = q->state->head & (q->buffer_units - 1);
msg = (struct ipc_msg *)(q->buffer + head * q->unit_bytes);
write_payload_and_ring_drbl(ctx, msg->drbl, msg->payload,
IPC_DB_MSG_LENGTH(msg->drbl));
if (msg->timestamp_of_outgoing_doorbell)
*msg->timestamp_of_outgoing_doorbell =
__hw_clock_source_read();
queue_advance_head(q, 1);
} else {
ctx->is_tx_ipc_busy = 0;
}
}
/**
* IPC interrupts are received by the FW when a) Host SW rings doorbell and
* b) when Host SW clears doorbell busy bit [31].
*
* Doorbell Register (DB) bits
* ----+-------+--------+-----------+--------+------------+--------------------
* 31 | 30 29 | 28-20 |19 18 17 16| 15 14 | 13 12 11 10| 9 8 7 6 5 4 3 2 1 0
* ----+-------+--------+-----------+--------+------------+--------------------
* Busy|Options|Reserved| Command |Reserved| Protocol | Message Length
* ----+-------+--------+-----------+--------+------------+--------------------
*
* ISH Peripheral Interrupt Status Register:
* Bit 0 - If set, indicates interrupt was caused by setting Host2ISH DB
*
* ISH Peripheral Interrupt Mask Register
* Bit 0 - If set, mask interrupt caused by Host2ISH DB
*
* ISH Peripheral DB Clear Status Register
* Bit 0 - If set, indicates interrupt was caused by clearing Host2ISH DB
*/
static void ipc_host2ish_isr(void)
{
uint32_t pisr = IPC_PISR;
uint32_t pimr = IPC_PIMR;
/*
* Ensure that the host IPC write power is requested after getting an
* interrupt otherwise the resume message will never get delivered (via
* host ipc communication). Resume is where we would like to restore all
* power settings, but that is too late for this power request.
*/
if (IS_ENABLED(CHIP_FAMILY_ISH5))
PMU_VNN_REQ = VNN_REQ_IPC_HOST_WRITE & ~PMU_VNN_REQ;
if ((pisr & IPC_PISR_HOST2ISH_BIT) && (pimr & IPC_PIMR_HOST2ISH_BIT))
handle_msg_recv_interrupt(IPC_PEER_ID_HOST);
}
DECLARE_IRQ(ISH_IPC_HOST2ISH_IRQ, ipc_host2ish_isr);
static void ipc_host2ish_busy_clear_isr(void)
{
uint32_t busy_clear = IPC_BUSY_CLEAR;
uint32_t pimr = IPC_PIMR;
if ((busy_clear & IPC_DB_CLR_STS_ISH2HOST_BIT) &&
(pimr & IPC_PIMR_ISH2HOST_CLR_BIT))
handle_busy_clear_interrupt(IPC_PEER_ID_HOST);
}
DECLARE_IRQ(ISH_IPC_ISH2HOST_CLR_IRQ, ipc_host2ish_busy_clear_isr);
int ipc_write_timestamp(const ipc_handle_t handle, const void *buf,
const size_t buf_size, uint32_t *timestamp)
{
int ret;
struct ipc_if_ctx *ctx;
uint32_t drbl = 0;
const uint8_t *payload = NULL;
int payload_size;
uint32_t protocol;
if (!IPC_IS_VALID_HANDLE(handle))
return -EC_ERROR_INVAL;
protocol = IPC_HANDLE_PROTOCOL(handle);
ctx = ipc_handle_to_if_ctx(handle);
if (ctx->initialized == 0) {
CPRINTS("open_ipc() for the peer is never called");
return -EC_ERROR_INVAL;
}
if (!ctx->msg_events[protocol].enabled) {
CPRINTS("call open_ipc() for the protocol first");
return -EC_ERROR_INVAL;
}
switch (protocol) {
case IPC_PROTOCOL_BOOT:
break;
case IPC_PROTOCOL_HECI:
drbl = IPC_BUILD_HECI_DB(buf_size);
payload = buf;
break;
case IPC_PROTOCOL_MCTP:
break;
case IPC_PROTOCOL_MNG:
drbl = ((struct ipc_msg *)buf)->drbl;
payload = ((struct ipc_msg *)buf)->payload;
break;
case IPC_PROTOCOL_ECP:
/* TODO : EC protocol */
break;
}
payload_size = IPC_DB_MSG_LENGTH(drbl);
if (payload_size > IPC_MSG_MAX_SIZE) {
/* too much input */
return -EC_ERROR_OVERFLOW;
}
ret = ipc_write_raw_timestamp(ctx, drbl, payload, payload_size,
timestamp);
if (ret)
return ret;
return buf_size;
}
ipc_handle_t ipc_open(const enum ipc_peer_id peer_id,
const enum ipc_protocol protocol,
const uint32_t event)
{
struct ipc_if_ctx *ctx;
if (protocol >= IPC_PROTOCOL_COUNT ||
peer_id >= IPC_PEERS_COUNT)
return IPC_INVALID_HANDLE;
ctx = ipc_get_if_ctx(peer_id);
mutex_lock(&ctx->lock);
if (ctx->msg_events[protocol].enabled) {
mutex_unlock(&ctx->lock);
return IPC_INVALID_HANDLE;
}
ctx->msg_events[protocol].task_id = task_get_current();
ctx->msg_events[protocol].enabled = 1;
ctx->msg_events[protocol].event = event;
/* For HECI protocol, set HECI UP status when IPC link is ready */
if (peer_id == IPC_PEER_ID_HOST &&
protocol == IPC_PROTOCOL_HECI && ish_fwst_is_ilup_set())
ish_fwst_set_hup();
if (ctx->initialized == 0) {
task_enable_irq(ctx->irq_in);
task_enable_irq(ctx->irq_clr);
ipc_enable_pimr_db_interrupt(ctx);
ipc_enable_pimr_clearing_interrupt(ctx);
ctx->initialized = 1;
}
mutex_unlock(&ctx->lock);
return IPC_BUILD_HANDLE(peer_id, protocol);
}
static void handle_mng_commands(const ipc_handle_t handle,
const struct ipc_msg *msg)
{
struct ipc_rst_payload *ipc_rst;
struct ipc_if_ctx *ctx;
uint32_t peer_id = IPC_HANDLE_PEER_ID(handle);
ctx = ipc_handle_to_if_ctx(handle);
switch (IPC_DB_CMD(msg->drbl)) {
case MNG_RX_CMPL_ENABLE:
case MNG_RX_CMPL_DISABLE:
case MNG_RX_CMPL_INDICATION:
case MNG_RESET_NOTIFY:
CPRINTS("msg not handled %d", IPC_DB_CMD(msg->drbl));
break;
case MNG_RESET_NOTIFY_ACK:
ipc_rst = (struct ipc_rst_payload *)msg->payload;
if (peer_id == IPC_PEER_ID_HOST &&
ipc_rst->reset_id == ctx->reset_id) {
ish_fwst_set_ilup();
if (ctx->msg_events[IPC_PROTOCOL_HECI].enabled)
ish_fwst_set_hup();
}
break;
case MNG_SYNC_FW_CLOCK:
/* Not supported currently, but kernel sends this about ~20s */
break;
}
}
static int do_ipc_read(struct ipc_if_ctx *ctx, const uint32_t protocol,
uint8_t *buf, const size_t buf_size)
{
int len;
len = ipc_get_protocol_data(ctx, protocol, buf, buf_size);
*(ctx->in_drbl_reg) = 0;
set_pimr_and_send_rx_complete(ctx);
return len;
}
static int ipc_check_read_validity(const struct ipc_if_ctx *ctx,
const uint32_t protocol)
{
if (ctx->initialized == 0)
return -EC_ERROR_INVAL;
if (!ctx->msg_events[protocol].enabled)
return -EC_ERROR_INVAL;
/* ipc_read() should be called by the same task called ipc_open() */
if (ctx->msg_events[protocol].task_id != task_get_current())
return -IPC_ERR_INVALID_TASK;
return 0;
}
/*
* ipc_read should be called by the same task context which called ipc_open()
*/
int ipc_read(const ipc_handle_t handle, void *buf, const size_t buf_size,
int timeout_us)
{
struct ipc_if_ctx *ctx;
uint32_t events, protocol, drbl_protocol, drbl_val;
int ret;
if (!IPC_IS_VALID_HANDLE(handle))
return -EC_ERROR_INVAL;
protocol = IPC_HANDLE_PROTOCOL(handle);
ctx = ipc_handle_to_if_ctx(handle);
ret = ipc_check_read_validity(ctx, protocol);
if (ret)
return ret;
if (timeout_us) {
events = task_wait_event_mask(ctx->msg_events[protocol].event,
timeout_us);
if (events & TASK_EVENT_TIMER)
return -EC_ERROR_TIMEOUT;
if (!(events & ctx->msg_events[protocol].event))
return -EC_ERROR_UNKNOWN;
} else {
/* check if msg for the protocol is available */
drbl_val = *(ctx->in_drbl_reg);
drbl_protocol = IPC_DB_PROTOCOL(drbl_val);
if (!(protocol == drbl_protocol) || !IPC_DB_BUSY(drbl_val))
return -IPC_ERR_MSG_NOT_AVAILABLE;
}
return do_ipc_read(ctx, protocol, buf, buf_size);
}
/* event flag for MNG msg */
#define EVENT_FLAG_BIT_MNG_MSG TASK_EVENT_CUSTOM_BIT(0)
/*
* This task handles MNG messages
*/
void ipc_mng_task(void)
{
int payload_size;
struct ipc_msg msg;
ipc_handle_t handle;
/*
* Ensure that power for host IPC writes is requested and ack'ed
*/
if (IS_ENABLED(CHIP_FAMILY_ISH5)) {
PMU_VNN_REQ = VNN_REQ_IPC_HOST_WRITE & ~PMU_VNN_REQ;
while (!(PMU_VNN_REQ_ACK & PMU_VNN_REQ_ACK_STATUS))
continue;
}
handle = ipc_open(IPC_PEER_ID_HOST, IPC_PROTOCOL_MNG,
EVENT_FLAG_BIT_MNG_MSG);
ASSERT(handle != IPC_INVALID_HANDLE);
ipc_send_reset_notify(handle);
while (1) {
payload_size = ipc_read(handle, &msg, sizeof(msg), -1);
/* allow doorbell with any payload */
if (payload_size < 0) {
CPRINTS("ipc_read error. discard msg");
continue; /* TODO: retry several and exit */
}
/* handle MNG commands */
handle_mng_commands(handle, &msg);
}
}
void ipc_init(void)
{
int i;
struct ipc_if_ctx *ctx;
for (i = 0; i < IPC_PEERS_COUNT; i++) {
ctx = ipc_get_if_ctx(i);
queue_init(&ctx->tx_queue);
}
/* inform host firmware is running */
ish_fwst_set_fw_status(FWSTS_FW_IS_RUNNING);
}
DECLARE_HOOK(HOOK_INIT, ipc_init, HOOK_PRIO_DEFAULT);