m1n1/src/asc.c
Hector Martin 83513c36a3 nvme: Add nvme_ensure_shutdown()
We're not using it in the end because we fixed this in Linux, but I went
through the trouble of writing the function so we might as well leave it
lying around.

Signed-off-by: Hector Martin <marcan@marcan.st>
2023-04-12 00:40:06 +09:00

131 lines
3 KiB
C

/* SPDX-License-Identifier: MIT */
#include "adt.h"
#include "asc.h"
#include "malloc.h"
#include "utils.h"
#define ASC_CPU_CONTROL 0x44
#define ASC_CPU_CONTROL_START 0x10
#define ASC_MBOX_CONTROL_FULL BIT(16)
#define ASC_MBOX_CONTROL_EMPTY BIT(17)
#define ASC_MBOX_A2I_CONTROL 0x110
#define ASC_MBOX_A2I_SEND0 0x800
#define ASC_MBOX_A2I_SEND1 0x808
#define ASC_MBOX_A2I_RECV0 0x810
#define ASC_MBOX_A2I_RECV1 0x818
#define ASC_MBOX_I2A_CONTROL 0x114
#define ASC_MBOX_I2A_SEND0 0x820
#define ASC_MBOX_I2A_SEND1 0x828
#define ASC_MBOX_I2A_RECV0 0x830
#define ASC_MBOX_I2A_RECV1 0x838
struct asc_dev {
uintptr_t cpu_base;
uintptr_t base;
int iop_node;
};
asc_dev_t *asc_init(const char *path)
{
int asc_path[8];
int node = adt_path_offset_trace(adt, path, asc_path);
if (node < 0) {
printf("asc: Error getting ASC node %s\n", path);
return NULL;
}
u64 base;
if (adt_get_reg(adt, asc_path, "reg", 0, &base, NULL) < 0) {
printf("asc: Error getting ASC %s base address.\n", path);
return NULL;
}
asc_dev_t *asc = malloc(sizeof(*asc));
if (!asc)
return NULL;
asc->iop_node = adt_first_child_offset(adt, node);
asc->cpu_base = base;
asc->base = base + 0x8000;
// clear32(base + ASC_CPU_CONTROL, ASC_CPU_CONTROL_START);
return asc;
}
void asc_free(asc_dev_t *asc)
{
free(asc);
}
int asc_get_iop_node(asc_dev_t *asc)
{
return asc->iop_node;
}
void asc_cpu_start(asc_dev_t *asc)
{
set32(asc->cpu_base + ASC_CPU_CONTROL, ASC_CPU_CONTROL_START);
}
void asc_cpu_stop(asc_dev_t *asc)
{
clear32(asc->cpu_base + ASC_CPU_CONTROL, ASC_CPU_CONTROL_START);
}
bool asc_cpu_running(asc_dev_t *asc)
{
return read32(asc->cpu_base + ASC_CPU_CONTROL) & ASC_CPU_CONTROL_START;
}
bool asc_can_recv(asc_dev_t *asc)
{
return !(read32(asc->base + ASC_MBOX_I2A_CONTROL) & ASC_MBOX_CONTROL_EMPTY);
}
bool asc_recv(asc_dev_t *asc, struct asc_message *msg)
{
if (!asc_can_recv(asc))
return false;
msg->msg0 = read64(asc->base + ASC_MBOX_I2A_RECV0);
msg->msg1 = (u32)read64(asc->base + ASC_MBOX_I2A_RECV1);
dma_rmb();
// printf("received msg: %lx %x\n", msg->msg0, msg->msg1);
return true;
}
bool asc_recv_timeout(asc_dev_t *asc, struct asc_message *msg, u32 delay_usec)
{
u64 timeout = timeout_calculate(delay_usec);
while (!timeout_expired(timeout)) {
if (asc_recv(asc, msg))
return true;
}
return false;
}
bool asc_can_send(asc_dev_t *asc)
{
return !(read32(asc->base + ASC_MBOX_A2I_CONTROL) & ASC_MBOX_CONTROL_FULL);
}
bool asc_send(asc_dev_t *asc, const struct asc_message *msg)
{
if (poll32(asc->base + ASC_MBOX_A2I_CONTROL, ASC_MBOX_CONTROL_FULL, 0, 200000)) {
printf("asc: A2I mailbox full for 200ms. Is the ASC stuck?");
return false;
}
dma_wmb();
write64(asc->base + ASC_MBOX_A2I_SEND0, msg->msg0);
write64(asc->base + ASC_MBOX_A2I_SEND1, msg->msg1);
// printf("sent msg: %lx %x\n", msg->msg0, msg->msg1);
return true;
}