Skip to content

Commit

Permalink
drivers: ipm: esp32: added IPM driver
Browse files Browse the repository at this point in the history
implemented by software for esp32 dual core
variants.

Signed-off-by: Felipe Neves <[email protected]>
  • Loading branch information
Felipe Neves authored and carlescufi committed Aug 16, 2022
1 parent bb6e656 commit 4bff7ec
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 13 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions drivers/clock_control/clock_control_esp32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion drivers/interrupt_controller/Kconfig.esp32
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 8 additions & 8 deletions drivers/interrupt_controller/intc_esp32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 */
}
Expand All @@ -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]) {
Expand All @@ -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]) {
Expand Down
2 changes: 1 addition & 1 deletion drivers/ipm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
6 changes: 6 additions & 0 deletions drivers/ipm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
229 changes: 229 additions & 0 deletions drivers/ipm/ipm_esp32.c
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions dts/bindings/ipm/espressif,esp32-ipm.yaml
Original file line number Diff line number Diff line change
@@ -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
12 changes: 11 additions & 1 deletion dts/xtensa/espressif/esp32.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -93,6 +98,11 @@
};
};

ipm0: ipm@3ffe5a30 {
compatible = "espressif,esp32-ipm";
reg = <0x3FFE5A30 0x8>;
};

ipi0: ipi@3f4c0058 {
compatible = "espressif,crosscore-interrupt";
reg = <0x3f4c0058 0x4>;
Expand Down

0 comments on commit 4bff7ec

Please sign in to comment.