From a5f4beccd2f6a6bd3df5e7f23e2d4770c2b3a063 Mon Sep 17 00:00:00 2001 From: Jiang Wei W Date: Fri, 4 Aug 2023 11:42:10 +0800 Subject: [PATCH] drivers: ipm: add init version of sedi ipm driver add init version of sedi ipm driver Signed-off-by: Jiang Wei W --- drivers/ipm/CMakeLists.txt | 4 +- drivers/ipm/Kconfig | 1 + drivers/ipm/Kconfig.sedi | 11 + drivers/ipm/ipm_sedi.c | 297 +++++++++++++++++++++++++++ drivers/ipm/ipm_sedi.h | 49 +++++ dts/bindings/ipm/intel,sedi-ipm.yaml | 21 ++ dts/x86/intel/intel_ish5.dtsi | 10 + 7 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 drivers/ipm/Kconfig.sedi create mode 100644 drivers/ipm/ipm_sedi.c create mode 100644 drivers/ipm/ipm_sedi.h create mode 100644 dts/bindings/ipm/intel,sedi-ipm.yaml diff --git a/drivers/ipm/CMakeLists.txt b/drivers/ipm/CMakeLists.txt index f2d37bb67c9..dbc8affc7c2 100644 --- a/drivers/ipm/CMakeLists.txt +++ b/drivers/ipm/CMakeLists.txt @@ -13,7 +13,9 @@ zephyr_library_sources_ifdef(CONFIG_IPM_NRFX ipm_nrfx_ipc.c) zephyr_library_sources_ifdef(CONFIG_IPM_CAVS_IDC ipm_cavs_idc.c) zephyr_library_sources_ifdef(CONFIG_IPM_STM32_HSEM ipm_stm32_hsem.c) zephyr_library_sources_ifdef(CONFIG_IPM_CAVS_HOST ipm_cavs_host.c) +zephyr_library_sources_ifdef(CONFIG_IPM_SEDI ipm_sedi.c) +zephyr_library_sources_ifdef(CONFIG_IPM_IVSHMEM ipm_ivshmem.c) zephyr_library_sources_ifdef(CONFIG_ESP32_SOFT_IPM ipm_esp32.c) zephyr_library_sources_ifdef(CONFIG_XLNX_IPI ipm_xlnx_ipi.c) + zephyr_library_sources_ifdef(CONFIG_USERSPACE ipm_handlers.c) -zephyr_library_sources_ifdef(CONFIG_IPM_IVSHMEM ipm_ivshmem.c) diff --git a/drivers/ipm/Kconfig b/drivers/ipm/Kconfig index 17c92b7e241..81c64202427 100644 --- a/drivers/ipm/Kconfig +++ b/drivers/ipm/Kconfig @@ -60,6 +60,7 @@ source "drivers/ipm/Kconfig.imx" source "drivers/ipm/Kconfig.stm32" source "drivers/ipm/Kconfig.intel_adsp" source "drivers/ipm/Kconfig.ivshmem" +source "drivers/ipm/Kconfig.sedi" module = IPM diff --git a/drivers/ipm/Kconfig.sedi b/drivers/ipm/Kconfig.sedi new file mode 100644 index 00000000000..bbb347dd5f4 --- /dev/null +++ b/drivers/ipm/Kconfig.sedi @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2023 Intel Corporation + +config IPM_SEDI + bool "Intel SEDI IPM Driver" + default y if DT_HAS_INTEL_SEDI_IPM_ENABLED + select IPM_CALLBACK_ASYNC + help + This option enables the Intel SEDI IPM(IPC) driver. + This driver is simply a shim driver built upon the SEDI + bare metal IPC driver in the hal-intel module diff --git a/drivers/ipm/ipm_sedi.c b/drivers/ipm/ipm_sedi.c new file mode 100644 index 00000000000..04a48029c6d --- /dev/null +++ b/drivers/ipm/ipm_sedi.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2020-2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT intel_sedi_ipm + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(ipm_sedi, CONFIG_IPM_LOG_LEVEL); + +#include "ipm_sedi.h" + +extern void sedi_ipc_isr(IN sedi_ipc_t ipc_device); + +static void set_ipm_dev_busy(const struct device *dev, bool is_write) +{ + struct ipm_sedi_context *ipm = dev->data; + unsigned int key = irq_lock(); + + atomic_set_bit(&ipm->status, is_write ? IPM_WRITE_BUSY_BIT : IPM_READ_BUSY_BIT); + pm_device_busy_set(dev); + irq_unlock(key); +} + +static void clear_ipm_dev_busy(const struct device *dev, bool is_write) +{ + struct ipm_sedi_context *ipm = dev->data; + unsigned int key = irq_lock(); + + atomic_clear_bit(&ipm->status, is_write ? IPM_WRITE_BUSY_BIT : IPM_READ_BUSY_BIT); + if ((!atomic_test_bit(&ipm->status, IPM_WRITE_BUSY_BIT)) + && (!atomic_test_bit(&ipm->status, IPM_READ_BUSY_BIT))) { + pm_device_busy_clear(dev); + } + irq_unlock(key); +} + +static void ipm_event_dispose(IN sedi_ipc_t device, IN uint32_t event, INOUT void *params) +{ + const struct device *dev = (const struct device *)params; + struct ipm_sedi_context *ipm = dev->data; + uint32_t drbl_in = 0, len; + + LOG_DBG("dev: %u, event: %u", device, event); + switch (event) { + case SEDI_IPC_EVENT_MSG_IN: + if (ipm->rx_msg_notify_cb != NULL) { + set_ipm_dev_busy(dev, false); + sedi_ipc_read_dbl(device, &drbl_in); + len = IPC_HEADER_GET_LENGTH(drbl_in); + sedi_ipc_read_msg(device, ipm->incoming_data_buf, len); + ipm->rx_msg_notify_cb(dev, + ipm->rx_msg_notify_cb_data, + drbl_in, ipm->incoming_data_buf); + } else { + LOG_WRN("no handler for ipm new msg"); + } + break; + case SEDI_IPC_EVENT_MSG_PEER_ACKED: + if (atomic_test_bit(&ipm->status, IPM_WRITE_IN_PROC_BIT)) { + k_sem_give(&ipm->device_write_msg_sem); + } else { + LOG_WRN("no sending in progress, got an ack"); + } + break; + default: + return; + } +} + +static int ipm_init(const struct device *dev) +{ + /* allocate resource and context*/ + const struct ipm_sedi_config_t *info = dev->config; + sedi_ipc_t device = info->ipc_device; + struct ipm_sedi_context *ipm = dev->data; + + info->irq_config(); + k_sem_init(&ipm->device_write_msg_sem, 0, 1); + k_mutex_init(&ipm->device_write_lock); + ipm->status = 0; + + sedi_ipc_init(device, ipm_event_dispose, (void *)dev); + atomic_set_bit(&ipm->status, IPM_PEER_READY_BIT); + LOG_DBG("ipm driver initialized on device: %p", dev); + return 0; +} + +static int ipm_send_isr(const struct device *dev, + uint32_t drbl, + const void *msg, + int msg_size) +{ + const struct ipm_sedi_config_t *info = dev->config; + sedi_ipc_t device = info->ipc_device; + uint32_t drbl_acked = 0; + + sedi_ipc_write_msg(device, (uint8_t *)msg, + (uint32_t)msg_size); + sedi_ipc_write_dbl(device, drbl); + do { + sedi_ipc_read_ack_drbl(device, &drbl_acked); + } while ((drbl_acked & BIT(IPC_BUSY_BIT)) == 0); + + return 0; +} + +static int ipm_sedi_send(const struct device *dev, + int wait, + uint32_t drbl, + const void *msg, + int msg_size) +{ + __ASSERT((dev != NULL), "bad params\n"); + const struct ipm_sedi_config_t *info = dev->config; + struct ipm_sedi_context *ipm = dev->data; + sedi_ipc_t device = info->ipc_device; + int ret, sedi_ret; + + /* check params, check status */ + if ((msg_size > IPC_DATA_LEN_MAX) || ((msg_size > 0) && (msg == NULL)) || + ((drbl & BIT(IPC_BUSY_BIT)) == 0)) { + LOG_ERR("bad params when sending ipm msg on device: %p", dev); + return -EINVAL; + } + + if (wait == 0) { + LOG_ERR("not support no wait mode when sending ipm msg"); + return -ENOTSUP; + } + + if (k_is_in_isr()) { + return ipm_send_isr(dev, drbl, msg, msg_size); + } + + k_mutex_lock(&ipm->device_write_lock, K_FOREVER); + set_ipm_dev_busy(dev, true); + + if (!atomic_test_bit(&ipm->status, IPM_PEER_READY_BIT)) { + LOG_WRN("peer is not ready"); + ret = -EBUSY; + goto write_err; + } + + /* write data regs */ + if (msg_size > 0) { + sedi_ret = sedi_ipc_write_msg(device, (uint8_t *)msg, + (uint32_t)msg_size); + if (sedi_ret != SEDI_DRIVER_OK) { + LOG_ERR("ipm write data fail on device: %p", dev); + ret = -EBUSY; + goto write_err; + } + } + + atomic_set_bit(&ipm->status, IPM_WRITE_IN_PROC_BIT); + /* write drbl regs to interrupt peer*/ + sedi_ret = sedi_ipc_write_dbl(device, drbl); + + if (sedi_ret != SEDI_DRIVER_OK) { + LOG_ERR("ipm write doorbell fail on device: %p", dev); + ret = -EBUSY; + goto func_out; + } + + /* wait for busy-bit-consumed interrupt */ + ret = k_sem_take(&ipm->device_write_msg_sem, K_MSEC(IPM_TIMEOUT_MS)); + if (ret) { + LOG_WRN("ipm write timeout on device: %p", dev); + sedi_ipc_write_dbl(device, 0); + } + +func_out: + atomic_clear_bit(&ipm->status, IPM_WRITE_IN_PROC_BIT); + +write_err: + clear_ipm_dev_busy(dev, true); + k_mutex_unlock(&ipm->device_write_lock); + if (ret == 0) { + LOG_DBG("ipm wrote a new message on device: %p, drbl=%08x", + dev, drbl); + } + return ret; +} + +static void ipm_sedi_register_callback(const struct device *dev, ipm_callback_t cb, + void *user_data) +{ + __ASSERT((dev != NULL), "bad params\n"); + + struct ipm_sedi_context *ipm = dev->data; + + if (cb == NULL) { + LOG_ERR("bad params when add ipm callback on device: %p", dev); + return; + } + + if (ipm->rx_msg_notify_cb == NULL) { + ipm->rx_msg_notify_cb = cb; + ipm->rx_msg_notify_cb_data = user_data; + } else { + LOG_ERR("ipm rx callback already exists on device: %p", dev); + } +} + +static void ipm_sedi_complete(const struct device *dev) +{ + int ret; + + __ASSERT((dev != NULL), "bad params\n"); + + const struct ipm_sedi_config_t *info = dev->config; + sedi_ipc_t device = info->ipc_device; + + ret = sedi_ipc_send_ack_drbl(device, 0); + if (ret != SEDI_DRIVER_OK) { + LOG_ERR("ipm send ack drl fail on device: %p", dev); + } + + clear_ipm_dev_busy(dev, false); +} + +static int ipm_sedi_get_max_data_size(const struct device *ipmdev) +{ + ARG_UNUSED(ipmdev); + return IPC_DATA_LEN_MAX; +} + +static uint32_t ipm_sedi_get_max_id(const struct device *ipmdev) +{ + ARG_UNUSED(ipmdev); + return UINT32_MAX; +} + +static int ipm_sedi_set_enable(const struct device *dev, int enable) +{ + __ASSERT((dev != NULL), "bad params\n"); + + const struct ipm_sedi_config_t *info = dev->config; + + if (enable) { + irq_enable(info->irq_num); + } else { + irq_disable(info->irq_num); + } + return 0; +} + +#if defined(CONFIG_PM_DEVICE) +static int ipm_power_ctrl(const struct device *dev, + enum pm_device_action action) +{ + return 0; +} +#endif + +static const struct ipm_driver_api ipm_funcs = { + .send = ipm_sedi_send, + .register_callback = ipm_sedi_register_callback, + .max_data_size_get = ipm_sedi_get_max_data_size, + .max_id_val_get = ipm_sedi_get_max_id, + .complete = ipm_sedi_complete, + .set_enabled = ipm_sedi_set_enable +}; + +#define IPM_SEDI_DEV_DEFINE(n) \ + static struct ipm_sedi_context ipm_data_##n; \ + static void ipm_##n##_irq_config(void); \ + static const struct ipm_sedi_config_t ipm_config_##n = { \ + .ipc_device = DT_INST_PROP(n, peripheral_id), \ + .irq_num = DT_INST_IRQN(n), \ + .irq_config = ipm_##n##_irq_config, \ + }; \ + static void ipm_##n##_irq_config(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), sedi_ipc_isr, \ + DT_INST_PROP(n, peripheral_id), \ + DT_INST_IRQ(n, sense)); \ + } \ + PM_DEVICE_DT_DEFINE(DT_NODELABEL(ipm##n), ipm_power_ctrl); \ + DEVICE_DT_INST_DEFINE(n, \ + &ipm_init, \ + PM_DEVICE_DT_GET(DT_NODELABEL(ipm##n)), \ + &ipm_data_##n, \ + &ipm_config_##n, \ + POST_KERNEL, \ + 0, \ + &ipm_funcs); + +DT_INST_FOREACH_STATUS_OKAY(IPM_SEDI_DEV_DEFINE) diff --git a/drivers/ipm/ipm_sedi.h b/drivers/ipm/ipm_sedi.h new file mode 100644 index 00000000000..bb1e945c2c2 --- /dev/null +++ b/drivers/ipm/ipm_sedi.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020-2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __DRIVERS_IPM_SEDI_H +#define __DRIVERS_IPM_SEDI_H + +#ifdef __cplusplus +extern "C" { +#endif +#include "sedi_driver_common.h" +#include "sedi_driver_ipc.h" +#include "zephyr/sys/atomic.h" + +/* + * bit 31 indicates whether message is valid, and could generate interrupt + * while set/clear + */ +#define IPC_BUSY_BIT 31 + +#define IPM_WRITE_IN_PROC_BIT 0 +#define IPM_WRITE_BUSY_BIT 1 +#define IPM_READ_BUSY_BIT 2 +#define IPM_PEER_READY_BIT 3 + +#define IPM_TIMEOUT_MS 1000 + +struct ipm_sedi_config_t { + sedi_ipc_t ipc_device; + int32_t irq_num; + void (*irq_config)(void); +}; + +struct ipm_sedi_context { + ipm_callback_t rx_msg_notify_cb; + void *rx_msg_notify_cb_data; + uint8_t incoming_data_buf[IPC_DATA_LEN_MAX]; + struct k_sem device_write_msg_sem; + struct k_mutex device_write_lock; + atomic_t status; + uint32_t power_status; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __DRIVERS_IPM_SEDI_H */ diff --git a/dts/bindings/ipm/intel,sedi-ipm.yaml b/dts/bindings/ipm/intel,sedi-ipm.yaml new file mode 100644 index 00000000000..48acb7d9b70 --- /dev/null +++ b/dts/bindings/ipm/intel,sedi-ipm.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2020-2023 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +description: INTEL SEDI IPM controller. + +compatible: "intel,sedi-ipm" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + peripheral-id: + type: int + required: true + description: sedi instance id of ipm diff --git a/dts/x86/intel/intel_ish5.dtsi b/dts/x86/intel/intel_ish5.dtsi index fece4af299f..51f15907bef 100644 --- a/dts/x86/intel/intel_ish5.dtsi +++ b/dts/x86/intel/intel_ish5.dtsi @@ -95,6 +95,16 @@ status = "okay"; }; + ipmhost: ipm@4100000 { + compatible = "intel,sedi-ipm"; + reg = <0x4100000 0x1000>; + peripheral-id = <0>; + interrupt-parent = <&intc>; + interrupts = <0 IRQ_TYPE_LOWEST_LEVEL_HIGH 2>; + + status = "okay"; + }; + uart0: uart@8100000 { compatible = "intel,sedi-uart"; reg = <0x08100000 0x1000>;