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:
Felipe Neves 2022-07-14 18:34:05 -03:00 committed by Carles Cufí
parent bb6e656ec0
commit 4bff7ecab3
9 changed files with 274 additions and 13 deletions

View file

@ -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

View file

@ -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,

View file

@ -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.

View file

@ -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]) {

View file

@ -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)

View file

@ -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
View 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);

View 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

View file

@ -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>;