drivers: ipm: esp32: added IPM driver
implemented by software for esp32 dual core variants. Signed-off-by: Felipe Neves <felipe.neves@linaro.org>
This commit is contained in:
parent
bb6e656ec0
commit
4bff7ecab3
9 changed files with 274 additions and 13 deletions
|
|
@ -319,6 +319,7 @@
|
|||
/drivers/ipm/ipm_nrfx_ipc.h @masz-nordic
|
||||
/drivers/ipm/ipm_stm32_ipcc.c @arnopo
|
||||
/drivers/ipm/ipm_stm32_hsem.c @cameled
|
||||
/drivers/ipm/ipm_esp32.c @uLipe
|
||||
/drivers/kscan/ @VenkatKotakonda @franciscomunoz @sjvasanth1
|
||||
/drivers/kscan/*xec* @franciscomunoz @sjvasanth1
|
||||
/drivers/kscan/*ft5336* @MaureenHelm
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#define CPU_RESET_REASON RTC_SW_CPU_RESET
|
||||
|
||||
#ifdef CONFIG_SOC_ESP32
|
||||
#if defined(CONFIG_SOC_ESP32) || defined(CONFIG_SOC_ESP32_NET)
|
||||
#define DT_CPU_COMPAT cdns_tensilica_xtensa_lx6
|
||||
#undef CPU_RESET_REASON
|
||||
#define CPU_RESET_REASON SW_CPU_RESET
|
||||
|
|
@ -46,7 +46,7 @@ struct esp32_clock_config {
|
|||
};
|
||||
|
||||
static uint8_t const xtal_freq[] = {
|
||||
#ifdef CONFIG_SOC_ESP32
|
||||
#if defined(CONFIG_SOC_ESP32) || defined(CONFIG_SOC_ESP32_NET)
|
||||
[ESP32_CLK_XTAL_24M] = 24,
|
||||
[ESP32_CLK_XTAL_26M] = 26,
|
||||
[ESP32_CLK_XTAL_40M] = 40,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
config INTC_ESP32
|
||||
bool "Interrupt allocator for Xtensa-based Espressif SoCs"
|
||||
default y if SOC_ESP32 || SOC_ESP32S2
|
||||
default y if SOC_ESP32 || SOC_ESP32S2 || SOC_ESP32_NET
|
||||
help
|
||||
Enable custom interrupt allocator for Espressif SoCs based on Xtensa
|
||||
architecture.
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ struct intr_alloc_table_entry {
|
|||
/* Default handler for unhandled interrupts. */
|
||||
void default_intr_handler(void *arg)
|
||||
{
|
||||
printk("Unhandled interrupt %d on cpu %d!\n", (int)arg, arch_curr_cpu()->id);
|
||||
printk("Unhandled interrupt %d on cpu %d!\n", (int)arg, esp_core_id());
|
||||
}
|
||||
|
||||
static struct intr_alloc_table_entry intr_alloc_table[ESP_INTC_INTS_NUM * CONFIG_MP_NUM_CPUS];
|
||||
|
|
@ -507,7 +507,7 @@ int esp_intr_alloc_intrstatus(int source,
|
|||
struct intr_handle_data_t *ret = NULL;
|
||||
int force = -1;
|
||||
|
||||
INTC_LOG("%s (cpu %d): checking args", __func__, arch_curr_cpu()->id);
|
||||
INTC_LOG("%s (cpu %d): checking args", __func__, esp_core_id());
|
||||
/* Shared interrupts should be level-triggered. */
|
||||
if ((flags & ESP_INTR_FLAG_SHARED) && (flags & ESP_INTR_FLAG_EDGE)) {
|
||||
return -EINVAL;
|
||||
|
|
@ -548,7 +548,7 @@ int esp_intr_alloc_intrstatus(int source,
|
|||
}
|
||||
}
|
||||
INTC_LOG("%s (cpu %d): Args okay."
|
||||
"Resulting flags 0x%X", __func__, arch_curr_cpu()->id, flags);
|
||||
"Resulting flags 0x%X", __func__, esp_core_id(), flags);
|
||||
|
||||
/*
|
||||
* Check 'special' interrupt sources. These are tied to one specific
|
||||
|
|
@ -584,7 +584,7 @@ int esp_intr_alloc_intrstatus(int source,
|
|||
}
|
||||
|
||||
esp_intr_lock();
|
||||
int cpu = arch_curr_cpu()->id;
|
||||
int cpu = esp_core_id();
|
||||
/* See if we can find an interrupt that matches the flags. */
|
||||
int intr = get_available_int(flags, cpu, force, source);
|
||||
|
||||
|
|
@ -815,7 +815,7 @@ int IRAM_ATTR esp_intr_enable(struct intr_handle_data_t *handle)
|
|||
intr_matrix_set(handle->vector_desc->cpu, source, handle->vector_desc->intno);
|
||||
} else {
|
||||
/* Re-enable using cpu int ena reg */
|
||||
if (handle->vector_desc->cpu != arch_curr_cpu()->id) {
|
||||
if (handle->vector_desc->cpu != esp_core_id()) {
|
||||
return -EINVAL; /* Can only enable these ints on this cpu */
|
||||
}
|
||||
irq_enable(handle->vector_desc->intno);
|
||||
|
|
@ -858,7 +858,7 @@ int IRAM_ATTR esp_intr_disable(struct intr_handle_data_t *handle)
|
|||
}
|
||||
} else {
|
||||
/* Disable using per-cpu regs */
|
||||
if (handle->vector_desc->cpu != arch_curr_cpu()->id) {
|
||||
if (handle->vector_desc->cpu != esp_core_id()) {
|
||||
esp_intr_unlock();
|
||||
return -EINVAL; /* Can only enable these ints on this cpu */
|
||||
}
|
||||
|
|
@ -872,7 +872,7 @@ int IRAM_ATTR esp_intr_disable(struct intr_handle_data_t *handle)
|
|||
void IRAM_ATTR esp_intr_noniram_disable(void)
|
||||
{
|
||||
int oldint;
|
||||
int cpu = arch_curr_cpu()->id;
|
||||
int cpu = esp_core_id();
|
||||
int intmask = ~non_iram_int_mask[cpu];
|
||||
|
||||
if (non_iram_int_disabled_flag[cpu]) {
|
||||
|
|
@ -886,7 +886,7 @@ void IRAM_ATTR esp_intr_noniram_disable(void)
|
|||
|
||||
void IRAM_ATTR esp_intr_noniram_enable(void)
|
||||
{
|
||||
int cpu = arch_curr_cpu()->id;
|
||||
int cpu = esp_core_id();
|
||||
int intmask = non_iram_int_disabled[cpu];
|
||||
|
||||
if (!non_iram_int_disabled_flag[cpu]) {
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@ 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_ESP32_SOFT_IPM ipm_esp32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE ipm_handlers.c)
|
||||
|
|
|
|||
|
|
@ -179,6 +179,12 @@ config IPM_CAVS_HOST_REGWORD
|
|||
|
||||
endif # IPM_CAVS_HOST
|
||||
|
||||
config ESP32_SOFT_IPM
|
||||
bool "ESP32 Software IPM driver"
|
||||
depends on ESP32_NETWORK_CORE || SOC_ESP32_NET
|
||||
help
|
||||
Interprocessor driver for ESP32 when using AMP.
|
||||
|
||||
module = IPM
|
||||
module-str = ipm
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
|
|
|||
229
drivers/ipm/ipm_esp32.c
Normal file
229
drivers/ipm/ipm_esp32.c
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT espressif_esp32_ipm
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <device.h>
|
||||
#include <init.h>
|
||||
#include <drivers/ipm.h>
|
||||
#include <drivers/interrupt_controller/intc_esp32.h>
|
||||
#include <soc.h>
|
||||
#include <sys/atomic.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(ipm_esp32, CONFIG_IPM_LOG_LEVEL);
|
||||
|
||||
#define ESP32_IPM_LOCK_FREE_VAL 0xB33FFFFF
|
||||
#define ESP32_IPM_NOOP_VAL 0xFF
|
||||
|
||||
__packed struct esp32_ipm_control {
|
||||
uint16_t dest_cpu_msg_id[2];
|
||||
atomic_val_t lock;
|
||||
};
|
||||
|
||||
__packed struct esp32_ipm_memory {
|
||||
uint8_t pro_cpu_shm[DT_REG_SIZE(DT_NODELABEL(shm0))/2];
|
||||
uint8_t app_cpu_shm[DT_REG_SIZE(DT_NODELABEL(shm0))/2];
|
||||
};
|
||||
|
||||
struct esp32_ipm_data {
|
||||
ipm_callback_t cb;
|
||||
void *user_data;
|
||||
uint32_t this_core_id;
|
||||
uint32_t other_core_id;
|
||||
uint32_t shm_size;
|
||||
struct esp32_ipm_memory *shm;
|
||||
struct esp32_ipm_control *control;
|
||||
};
|
||||
|
||||
static struct esp32_ipm_data esp32_ipm_device_data;
|
||||
|
||||
IRAM_ATTR static void esp32_ipm_isr(const struct device *dev)
|
||||
{
|
||||
struct esp32_ipm_data *dev_data = (struct esp32_ipm_data *)dev->data;
|
||||
uint32_t core_id = dev_data->this_core_id;
|
||||
|
||||
/* clear interrupt flag */
|
||||
if (core_id == 0) {
|
||||
DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0);
|
||||
} else {
|
||||
DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0);
|
||||
}
|
||||
|
||||
/* first of all take the own of the shared memory */
|
||||
while (!atomic_cas(&dev_data->control->lock,
|
||||
ESP32_IPM_LOCK_FREE_VAL, dev_data->this_core_id))
|
||||
;
|
||||
|
||||
if (dev_data->cb) {
|
||||
|
||||
volatile void *shm = &dev_data->shm->pro_cpu_shm;
|
||||
|
||||
if (core_id != 0) {
|
||||
shm = &dev_data->shm->app_cpu_shm;
|
||||
}
|
||||
|
||||
dev_data->cb(dev,
|
||||
dev_data->user_data,
|
||||
dev_data->control->dest_cpu_msg_id[core_id],
|
||||
shm);
|
||||
}
|
||||
|
||||
/* unlock the shared memory */
|
||||
atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
|
||||
}
|
||||
|
||||
static int esp32_ipm_send(const struct device *dev, int wait, uint32_t id,
|
||||
const void *data, int size)
|
||||
{
|
||||
struct esp32_ipm_data *dev_data = (struct esp32_ipm_data *)dev->data;
|
||||
|
||||
if (data == NULL) {
|
||||
LOG_ERR("Invalid data source");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (id > 0xFFFF) {
|
||||
LOG_ERR("Invalid message ID format");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dev_data->shm_size < size) {
|
||||
LOG_ERR("Not enough memory in IPM channel");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
uint32_t key = irq_lock();
|
||||
|
||||
/* try to lock the shared memory */
|
||||
while (!atomic_cas(&dev_data->control->lock,
|
||||
ESP32_IPM_LOCK_FREE_VAL,
|
||||
dev_data->this_core_id)) {
|
||||
|
||||
k_busy_wait(1);
|
||||
|
||||
if ((wait != -1) && (wait > 0)) {
|
||||
/* lock could not be held this time, return */
|
||||
wait--;
|
||||
if (wait == 0) {
|
||||
irq_unlock(key);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Only the lower 16bits of id are used */
|
||||
dev_data->control->dest_cpu_msg_id[dev_data->other_core_id] = (uint16_t)(id & 0xFFFF);
|
||||
|
||||
/* data copied, set the id and, generate interrupt in the remote core */
|
||||
if (dev_data->this_core_id == 0) {
|
||||
memcpy(&dev_data->shm->app_cpu_shm[0], data, size);
|
||||
atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
|
||||
LOG_DBG("Generating interrupt on remote CPU 1 from CPU 0");
|
||||
DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, DPORT_CPU_INTR_FROM_CPU_1);
|
||||
} else {
|
||||
memcpy(&dev_data->shm->pro_cpu_shm[0], data, size);
|
||||
atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
|
||||
LOG_DBG("Generating interrupt on remote CPU 0 from CPU 1");
|
||||
DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, DPORT_CPU_INTR_FROM_CPU_0);
|
||||
}
|
||||
|
||||
irq_unlock(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void esp32_ipm_register_callback(const struct device *dev,
|
||||
ipm_callback_t cb,
|
||||
void *user_data)
|
||||
{
|
||||
struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
|
||||
|
||||
uint32_t key = irq_lock();
|
||||
|
||||
data->cb = cb;
|
||||
data->user_data = user_data;
|
||||
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
static int esp32_ipm_max_data_size_get(const struct device *dev)
|
||||
{
|
||||
struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
|
||||
|
||||
return data->shm_size;
|
||||
}
|
||||
|
||||
static uint32_t esp_32_ipm_max_id_val_get(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
static int esp32_ipm_init(const struct device *dev)
|
||||
{
|
||||
struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
|
||||
|
||||
data->this_core_id = esp_core_id();
|
||||
data->other_core_id = (data->this_core_id == 0) ? 1 : 0;
|
||||
data->shm_size = (DT_REG_SIZE(DT_NODELABEL(shm0))/2);
|
||||
data->shm = (struct esp32_ipm_memory *)DT_REG_ADDR(DT_NODELABEL(shm0));
|
||||
data->control = (struct esp32_ipm_control *)DT_REG_ADDR(DT_NODELABEL(ipm0));
|
||||
|
||||
LOG_DBG("Size of IPM shared memory: %d", data->shm_size);
|
||||
LOG_DBG("Address of IPM shared memory: %p", data->shm);
|
||||
LOG_DBG("Address of IPM control structure: %p", data->control);
|
||||
|
||||
/* pro_cpu is responsible to initialize the lock of shared memory */
|
||||
if (data->this_core_id == 0) {
|
||||
esp_intr_alloc(DT_IRQN(DT_NODELABEL(ipi0)),
|
||||
ESP_INTR_FLAG_IRAM,
|
||||
(intr_handler_t)esp32_ipm_isr,
|
||||
(void *)dev,
|
||||
NULL);
|
||||
|
||||
atomic_set(&data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
|
||||
} else {
|
||||
/* app_cpu wait for initialization from pro_cpu, then takes it,
|
||||
* after that releases
|
||||
*/
|
||||
esp_intr_alloc(DT_IRQN(DT_NODELABEL(ipi1)),
|
||||
ESP_INTR_FLAG_IRAM,
|
||||
(intr_handler_t)esp32_ipm_isr,
|
||||
(void *)dev,
|
||||
NULL);
|
||||
|
||||
LOG_DBG("Waiting CPU0 to sync");
|
||||
|
||||
while (!atomic_cas(&data->control->lock,
|
||||
ESP32_IPM_LOCK_FREE_VAL, data->this_core_id))
|
||||
;
|
||||
|
||||
atomic_set(&data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
|
||||
|
||||
LOG_DBG("Synchronization done");
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ipm_driver_api esp32_ipm_driver_api = {
|
||||
.send = esp32_ipm_send,
|
||||
.register_callback = esp32_ipm_register_callback,
|
||||
.max_data_size_get = esp32_ipm_max_data_size_get,
|
||||
.max_id_val_get = esp_32_ipm_max_id_val_get
|
||||
};
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, &esp32_ipm_init, NULL,
|
||||
&esp32_ipm_device_data, NULL,
|
||||
PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||
&esp32_ipm_driver_api);
|
||||
15
dts/bindings/ipm/espressif,esp32-ipm.yaml
Normal file
15
dts/bindings/ipm/espressif,esp32-ipm.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Copyright (c) 2022 Espressif Systems
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: ESP32 soft inter processor message
|
||||
|
||||
compatible: "espressif,esp32-ipm"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
interrupts:
|
||||
required: false
|
||||
|
|
@ -50,7 +50,12 @@
|
|||
soc {
|
||||
sram0: memory@3ffb0000 {
|
||||
compatible = "mmio-sram";
|
||||
reg = <0x3FFB0000 0x50000>;
|
||||
reg = <0x3FFB0000 0x2c200>;
|
||||
};
|
||||
|
||||
shm0: memory@3ffe5230 {
|
||||
compatible = "mmio-sram";
|
||||
reg = <0x3FFE5230 0x800>;
|
||||
};
|
||||
|
||||
intc: interrupt-controller@3ff00104 {
|
||||
|
|
@ -93,6 +98,11 @@
|
|||
};
|
||||
};
|
||||
|
||||
ipm0: ipm@3ffe5a30 {
|
||||
compatible = "espressif,esp32-ipm";
|
||||
reg = <0x3FFE5A30 0x8>;
|
||||
};
|
||||
|
||||
ipi0: ipi@3f4c0058 {
|
||||
compatible = "espressif,crosscore-interrupt";
|
||||
reg = <0x3f4c0058 0x4>;
|
||||
|
|
|
|||
Loading…
Reference in a new issue