Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/interrupt_controller/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ zephyr_library_sources_ifdef(CONFIG_PLIC intc_plic.c)
zephyr_library_sources_ifdef(CONFIG_RV32M1_INTMUX intc_rv32m1_intmux.c)
zephyr_library_sources_ifdef(CONFIG_SAM0_EIC intc_sam0_eic.c)
zephyr_library_sources_ifdef(CONFIG_SHARED_IRQ intc_shared_irq.c)
zephyr_library_sources_ifdef(CONFIG_SYSCFG_ITLINE_STM32 intc_syscfg_itline_stm32.c)
zephyr_library_sources_ifdef(CONFIG_INTC_ESP32 intc_esp32.c)
zephyr_library_sources_ifdef(CONFIG_INTC_ESP32C3 intc_esp32c3.c)
zephyr_library_sources_ifdef(CONFIG_SWERV_PIC intc_swerv_pic.c)
Expand Down
9 changes: 9 additions & 0 deletions drivers/interrupt_controller/Kconfig.stm32
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@ config EXTI_STM32
help
Enable EXTI driver for STM32 line of MCUs

config SYSCFG_ITLINE_STM32
bool "System Configuration Controller (SYSCFG) Interrupt Line Driver for STM32"
default y
depends on DT_HAS_ST_STM32_SYSCFG_ITLINE_ENABLED
select MULTI_LEVEL_INTERRUPTS
select 2ND_LEVEL_INTERRUPTS
help
Enable SYSCFG ITLINE driver for STM32 line of MCUs

endif # SOC_FAMILY_STM32
205 changes: 205 additions & 0 deletions drivers/interrupt_controller/intc_syscfg_itline_stm32.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* Copyright (c) 2023, Aurelien Jarno
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT st_stm32_syscfg_itline

/**
* @brief Driver for System Configuration Controller (SYSCFG) Interrupt Line in STM32 MCUs
*/

#include <zephyr/device.h>
#include <zephyr/irq_nextlevel.h>
#include <zephyr/irq.h>
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
#include <soc.h>

/* By design this can only support 32 second level interrupts, more than enough for STM32 MCUs. */
BUILD_ASSERT((CONFIG_MAX_IRQ_PER_AGGREGATOR > 0) && (CONFIG_MAX_IRQ_PER_AGGREGATOR <= 32),
"unsupported number of interrupts");

struct syscfg_itline_config {
SYSCFG_TypeDef *base;
int reg;
void (*irq_cfg_func)(void);
unsigned int parent_irq;
struct stm32_pclken pclken;
};

struct syscfg_itline_data {
uint32_t irq_enabled;
unsigned int isr_table_offset;
};

/*
* Mapping between the interrupt number and the offset in the _isr_table_entry
*/
struct irq_parent_offset {
unsigned int irq;
unsigned int offset;
};

#define INIT_IRQ_PARENT_OFFSET(i, o) { \
.irq = i, \
.offset = o, \
}

#define IRQ_INDEX_TO_OFFSET(i, base) (base + i * CONFIG_MAX_IRQ_PER_AGGREGATOR)

#define CAT_2ND_LVL_LIST(i, base) \
INIT_IRQ_PARENT_OFFSET(CONFIG_2ND_LVL_INTR_0##i##_OFFSET, \
IRQ_INDEX_TO_OFFSET(i, base))
static const struct irq_parent_offset lvl2_irq_list[CONFIG_NUM_2ND_LEVEL_AGGREGATORS]
= { LISTIFY(CONFIG_NUM_2ND_LEVEL_AGGREGATORS, CAT_2ND_LVL_LIST, (,),
CONFIG_2ND_LVL_ISR_TBL_OFFSET) };

/*
* <irq_nextlevel.h> API
*/

static void syscfg_itline_enable(const struct device *dev, uint32_t irq)
{
const struct syscfg_itline_config *config = dev->config;
struct syscfg_itline_data *data = dev->data;

if (irq >= CONFIG_MAX_IRQ_PER_AGGREGATOR) {
return;
}

data->irq_enabled |= BIT(irq);
irq_enable(config->parent_irq);
}

static void syscfg_itline_disable(const struct device *dev, uint32_t irq)
{
const struct syscfg_itline_config *config = dev->config;
struct syscfg_itline_data *data = dev->data;

if (irq >= CONFIG_MAX_IRQ_PER_AGGREGATOR) {
return;
}

data->irq_enabled &= ~BIT(irq);

/* Disable the 1st level interrupt if all the second ones are disabled. */
if (data->irq_enabled == 0) {
irq_disable(config->parent_irq);
}
}

static uint32_t syscfg_itline_get_state(const struct device *dev)
{
const struct syscfg_itline_config *config = dev->config;
struct syscfg_itline_data *data = dev->data;

return (SYSCFG->IT_LINE_SR[config->reg] & data->irq_enabled) != 0;
}

static int syscfg_itline_get_line_state(const struct device *dev, unsigned int irq)
{
const struct syscfg_itline_config *config = dev->config;
struct syscfg_itline_data *data = dev->data;

if (irq >= CONFIG_MAX_IRQ_PER_AGGREGATOR) {
return 0;
}

return (SYSCFG->IT_LINE_SR[config->reg] & data->irq_enabled & BIT(irq)) != 0;
}

/*
* IRQ handling.
*/

static void syscfg_itline_isr(const struct device *dev)
{
const struct syscfg_itline_config *config = dev->config;
struct syscfg_itline_data *data = dev->data;

uint32_t sr = SYSCFG->IT_LINE_SR[config->reg] & data->irq_enabled;

/* Dispatch lower level ISRs depending upon the bit set */
while (sr) {
int bit = find_lsb_set(sr) - 1;
struct _isr_table_entry *ent = &_sw_isr_table[data->isr_table_offset + bit];

sr &= ~BIT(bit);
ent->isr(ent->arg);
}
}

/*
* Instance and initialization
*/

static const struct irq_next_level_api syscfg_itline_apis = {
.intr_enable = syscfg_itline_enable,
.intr_disable = syscfg_itline_disable,
.intr_get_state = syscfg_itline_get_state,
.intr_get_line_state = syscfg_itline_get_line_state,
};

static int syscfg_itline_init(const struct device *dev)
{
const struct syscfg_itline_config *config = dev->config;
struct syscfg_itline_data *data = dev->data;
unsigned int i;

/* enable clock for SYSCFG device */
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);

if (!device_is_ready(clk)) {
return -ENODEV;
}

if (clock_control_on(clk, (clock_control_subsys_t) &config->pclken) != 0) {
return -EIO;
}

/* Find the offset in the _isr_table_entry for that parent interrupt */
for (i = 0U; i < CONFIG_NUM_2ND_LEVEL_AGGREGATORS; i++) {
if (lvl2_irq_list[i].irq == config->parent_irq) {
data->isr_table_offset = lvl2_irq_list[i].offset;
break;
}
}
if (i == CONFIG_NUM_2ND_LEVEL_AGGREGATORS) {
return -EINVAL;
}

config->irq_cfg_func();

return 0;
}

#define SYSCFG_ITLINE_INIT(index) \
\
static void syscfg_itline_irq_config_func_##index(void) \
{ \
IRQ_CONNECT(DT_INST_IRQN(index), \
DT_INST_IRQ(index, priority), \
syscfg_itline_isr, DEVICE_DT_INST_GET(index), 0); \
} \
\
const struct syscfg_itline_config syscfg_itline_config_##index = { \
.base = (SYSCFG_TypeDef *)DT_REG_ADDR(DT_INST_PARENT(index)), \
.reg = DT_INST_REG_ADDR(index), \
.irq_cfg_func = syscfg_itline_irq_config_func_##index, \
.parent_irq = DT_INST_IRQN(index), \
.pclken = STM32_CLOCK_INFO(0, DT_INST_PARENT(index)), \
}; \
\
static struct syscfg_itline_data syscfg_itline_data_##index = { \
.irq_enabled = 0, \
.isr_table_offset = 0, \
}; \
\
DEVICE_DT_INST_DEFINE(index, syscfg_itline_init, NULL, \
&syscfg_itline_data_##index, &syscfg_itline_config_##index, \
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, \
&syscfg_itline_apis);


DT_INST_FOREACH_STATUS_OKAY(SYSCFG_ITLINE_INIT)
81 changes: 15 additions & 66 deletions drivers/usb_c/tcpc/ucpd_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ LOG_MODULE_REGISTER(ucpd_stm32, CONFIG_USBC_LOG_LEVEL);

#include "ucpd_stm32_priv.h"

static void config_tcpc_irq(void);

/**
* @brief UCPD TX ORDSET values
*/
Expand Down Expand Up @@ -1099,59 +1097,19 @@ static int ucpd_set_bist_test_mode(const struct device *dev,
/**
* @brief UCPD interrupt handler
*/
static void ucpd_isr(const struct device *dev_inst[])
static void ucpd_isr(const struct device *dev)
{
const struct device *dev;
const struct tcpc_config *config;
struct tcpc_data *data;
const struct tcpc_config *config = dev->config;
struct tcpc_data *data = dev->data;
uint32_t sr;
struct alert_info *info;
struct alert_info *info = &data->alert_info;
uint32_t tx_done_mask = UCPD_SR_TXUND |
UCPD_SR_TXMSGSENT |
UCPD_SR_TXMSGABT |
UCPD_SR_TXMSGDISC |
UCPD_SR_HRSTSENT |
UCPD_SR_HRSTDISC;

#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 1
/*
* Multiple UCPD ports are available
*/

uint32_t sr0;
uint32_t sr1;

/*
* Since the UCPD peripherals share the same interrupt line, determine
* which one generated the interrupt.
*/

/* Read UCPD1 and UCPD2 Status Registers */

sr0 =
LL_UCPD_ReadReg(((const struct tcpc_config *)dev_inst[0]->config)->ucpd_port, SR);
sr1 =
LL_UCPD_ReadReg(((const struct tcpc_config *)dev_inst[1]->config)->ucpd_port, SR);

if (sr0) {
dev = dev_inst[0];
} else if (sr1) {
dev = dev_inst[1];
} else {
/*
* The interrupt was triggered by some other device sharing this
* interrupt line.
*/
return;
}
#else
/*
* Only one UCPD port available
*/

dev = dev_inst[0];
#endif /* Get the UCPD port that initiated that interrupt */

config = dev->config;
data = dev->data;
info = &data->alert_info;
Expand Down Expand Up @@ -1368,7 +1326,7 @@ static void ucpd_isr_init(const struct device *dev)
stm32_ucpd_state_init(dev);

/* Configure and enable the IRQ */
config_tcpc_irq();
config->irq_config_func(dev);
}

/**
Expand Down Expand Up @@ -1460,29 +1418,19 @@ static const struct tcpc_driver_api driver_api = {
.sop_prime_enable = ucpd_sop_prime_enable,
};

#define DEV_INST_INIT(n) dev_inst[n] = DEVICE_DT_INST_GET(n);
static void config_tcpc_irq(void)
{
static int inst_num;
static const struct device
*dev_inst[DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT)];

/* Initialize and enable shared irq on last instance */
if (++inst_num == DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT)) {
DT_INST_FOREACH_STATUS_OKAY(DEV_INST_INIT)

IRQ_CONNECT(DT_INST_IRQN(0),
DT_INST_IRQ(0, priority),
ucpd_isr, dev_inst, 0);

irq_enable(DT_INST_IRQN(0));
}
}

BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 0,
"No compatible STM32 TCPC instance found");

#define TCPC_DRIVER_INIT(inst) \
static void config_tcpc_irq_##inst(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(inst), \
DT_INST_IRQ(inst, priority), \
ucpd_isr, \
DEVICE_DT_INST_GET(inst), \
0); \
irq_enable(DT_INST_IRQN(inst)); \
} \
PINCTRL_DT_INST_DEFINE(inst); \
static struct tcpc_data drv_data_##inst; \
static const struct tcpc_config drv_config_##inst = { \
Expand All @@ -1493,6 +1441,7 @@ BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 0,
.ucpd_params.IfrGap = DT_INST_PROP(inst, ifrgap) - 1, \
.ucpd_params.HbitClockDiv = DT_INST_PROP(inst, hbitclkdiv) - 1, \
.ucpd_dead_battery = DT_INST_PROP(inst, dead_battery), \
.irq_config_func = config_tcpc_irq_##inst, \
}; \
DEVICE_DT_INST_DEFINE(inst, \
&ucpd_init, \
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb_c/tcpc/ucpd_stm32_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ struct tcpc_config {
LL_UCPD_InitTypeDef ucpd_params;
/* STM32 UCPD dead battery support */
bool ucpd_dead_battery;
/* STM32 UCPD IRQ config function */
void (*irq_config_func)(const struct device *dev);
};

/**
Expand Down
Loading