From 075267ef079ec00fb78125806c81a0deb3ad7890 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 3 Sep 2023 16:15:34 +0200 Subject: [PATCH] smc: Add minimal smc client Only supported operation is smc_write_u32() for turning the dp2hdmi converter on M2 desktop systems on. Signed-off-by: Janne Grunau --- Makefile | 1 + src/smc.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/smc.h | 17 +++++ 3 files changed, 199 insertions(+) create mode 100644 src/smc.c create mode 100644 src/smc.h diff --git a/Makefile b/Makefile index 6c0c25ac3..426d3c0be 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,7 @@ OBJECTS := \ sart.o \ sep.o \ sio.o \ + smc.o \ smp.o \ start.o \ startup.o \ diff --git a/src/smc.c b/src/smc.c new file mode 100644 index 000000000..ddcf8d995 --- /dev/null +++ b/src/smc.c @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: MIT */ + +#include "assert.h" +#include "malloc.h" +#include "smc.h" +#include "string.h" +#include "types.h" +#include "utils.h" + +#define SMC_READ_KEY 0x10 +#define SMC_WRITE_KEY 0x11 +#define SMC_GET_KEY_BY_INDEX 0x12 +#define SMC_GET_KEY_INFO 0x13 +#define SMC_INITIALIZE 0x17 +#define SMC_NOTIFICATION 0x18 +#define SMC_RW_KEY 0x20 + +#define SMC_MSG_TYPE GENMASK(7, 0) +#define SMC_MSG_ID GENMASK(15, 12) + +#define SMC_WRITE_KEY_SIZE GENMASK(23, 16) +#define SMC_WRITE_KEY_KEY GENMASK(63, 32) + +#define SMC_RESULT_RESULT GENMASK(7, 0) +#define SMC_RESULT_ID GENMASK(15, 12) +#define SMC_RESULT_SIZE GENMASK(31, 16) +#define SMC_RESULT_VALUE GENMASK(63, 32) + +#define SMC_NUM_IDS 16 + +#define SMC_ENDPOINT 0x20 + +struct smc_dev { + asc_dev_t *asc; + rtkit_dev_t *rtkit; + + void *shmem; + u32 msgid; + + bool outstanding[SMC_NUM_IDS]; + u64 ret[SMC_NUM_IDS]; +}; + +static void smc_handle_msg(smc_dev_t *smc, u64 msg) +{ + if (!smc->shmem) + smc->shmem = (void *)msg; + else { + u8 result = FIELD_GET(SMC_RESULT_RESULT, msg); + u8 id = FIELD_GET(SMC_RESULT_ID, msg); + if (result == SMC_NOTIFICATION) { + printf("SMC: Notification: 0x%08lx\n", FIELD_GET(SMC_RESULT_VALUE, msg)); + return; + } + smc->outstanding[id] = false; + smc->ret[id] = msg; + } +} + +static int smc_work(smc_dev_t *smc) +{ + int ret; + struct rtkit_message msg; + + while ((ret = rtkit_recv(smc->rtkit, &msg)) == 0) + ; + + if (ret < 0) { + printf("SMC: rtkit_recv failed!\n"); + return ret; + } + + if (msg.ep != SMC_ENDPOINT) { + printf("SMC: received message for unexpected endpoint 0x%02x\n", msg.ep); + return 0; + } + + smc_handle_msg(smc, msg.msg); + + return 0; +} + +static void smc_send(smc_dev_t *smc, u64 message) +{ + struct rtkit_message msg; + + msg.ep = SMC_ENDPOINT; + msg.msg = message; + + rtkit_send(smc->rtkit, &msg); +} + +static int smc_cmd(smc_dev_t *smc, u64 message) +{ + u8 id = smc->msgid++ & 0xF; + assert(!smc->outstanding[id]); + smc->outstanding[id] = true; + + message |= FIELD_PREP(SMC_MSG_ID, id); + + smc_send(smc, message); + while (smc->outstanding[id]) + smc_work(smc); + + u64 result = smc->ret[id]; + u32 ret = FIELD_GET(SMC_RESULT_RESULT, result); + if (ret) { + printf("SMC: smc_cmd[0x%x] failed: %u\n", id, ret); + return ret; + } + + return 0; +} + +void smc_shutdown(smc_dev_t *smc) +{ + rtkit_quiesce(smc->rtkit); + rtkit_free(smc->rtkit); + asc_free(smc->asc); + free(smc); +} + +smc_dev_t *smc_init() +{ + smc_dev_t *smc = calloc(1, sizeof(smc_dev_t)); + if (!smc) + return NULL; + + smc->asc = asc_init("/arm-io/smc"); + if (!smc->asc) { + printf("SMC: failed to initialize ASC\n"); + goto out_free; + } + + smc->rtkit = rtkit_init("smc", smc->asc, NULL, NULL, NULL, true); + if (!smc->rtkit) { + printf("SMC: failed to initialize RTKit\n"); + goto out_asc; + } + + if (!rtkit_boot(smc->rtkit)) { + printf("SMC: failed to boot RTKit\n"); + goto out_rtkit; + } + + if (!rtkit_start_ep(smc->rtkit, SMC_ENDPOINT)) { + printf("SMC: failed start SMC endpoint\n"); + goto out_rtkit; + } + + u64 initialize = + FIELD_PREP(SMC_MSG_TYPE, SMC_INITIALIZE) | FIELD_PREP(SMC_MSG_ID, smc->msgid++); + + smc_send(smc, initialize); + + while (!smc->shmem) { + int ret = smc_work(smc); + if (ret < 0) + goto out_rtkit; + } + + return smc; + +out_rtkit: + rtkit_free(smc->rtkit); +out_asc: + asc_free(smc->asc); +out_free: + free(smc); + return NULL; +} + +int smc_write_u32(smc_dev_t *smc, u32 key, u32 value) +{ + memcpy(smc->shmem, &value, sizeof(value)); + u64 msg = FIELD_PREP(SMC_MSG_TYPE, SMC_WRITE_KEY); + msg |= FIELD_PREP(SMC_WRITE_KEY_SIZE, sizeof(value)); + msg |= FIELD_PREP(SMC_WRITE_KEY_KEY, key); + + return smc_cmd(smc, msg); +} diff --git a/src/smc.h b/src/smc.h new file mode 100644 index 000000000..3cb470457 --- /dev/null +++ b/src/smc.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef SMC_H +#define SMC_H + +#include "asc.h" +#include "rtkit.h" +#include "types.h" + +typedef struct smc_dev smc_dev_t; + +int smc_write_u32(smc_dev_t *smc, u32 key, u32 value); + +smc_dev_t *smc_init(void); +void smc_shutdown(smc_dev_t *smc); + +#endif