From 9ad780e695ad9875f510d173e8b758bf3c89d755 Mon Sep 17 00:00:00 2001 From: Camille BAUD Date: Fri, 5 Dec 2025 18:34:37 +0100 Subject: [PATCH 1/5] dts: bflb: Add IR RX nodes Add binding and nodes for bflb,irx Signed-off-by: Camille BAUD --- dts/bindings/input/bflb,irx.yaml | 54 ++++++++++++++++++++++++++++++++ dts/riscv/bflb/bl60x.dtsi | 11 +++++++ dts/riscv/bflb/bl61x.dtsi | 11 +++++++ dts/riscv/bflb/bl70x.dtsi | 11 +++++++ 4 files changed, 87 insertions(+) create mode 100644 dts/bindings/input/bflb,irx.yaml diff --git a/dts/bindings/input/bflb,irx.yaml b/dts/bindings/input/bflb,irx.yaml new file mode 100644 index 0000000000000..acdd8408e4550 --- /dev/null +++ b/dts/bindings/input/bflb,irx.yaml @@ -0,0 +1,54 @@ +# Copyright (c) 2025 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +description: | + Bouffalolab Infrared Receiver Peripheral + Wire the output of a diode like VS1838B to the specified GPIO pin. + +compatible: "bflb,irx" + +include: + - name: base.yaml + +properties: + reg: + required: true + + ir-gpios: + required: true + type: phandle-array + description: | + Single GPIO used as input pin, must be first port (gpio0). + Flags do not apply as it is a special usage. + + protocol: + required: true + type: string + description: | + What IR protocol is used: + - NEC: NEC Protocol + - RC5: RC5 Protocol + - PW: Pulse width: count the length of each on/off pulse in us. + PW will easily overflow FIFO on BL60x and BL70x. + On BL61x, increase CONFIG_INPUT_QUEUE_MAX_MSGS to handle the large amount of data. + enum: + - "NEC" + - "RC5" + - "PW" + + pw-end-pulse-width: + type: int + description: | + Length of time for a pulse / lack of pulse in microseconds to trigger the end of transmission + interrupt in pulse width mode. + + invert: + type: boolean + description: | + Invert input. With VS1838B wired directly, you want this enabled. + + deglitch-cnt: + type: int + default: 0 + description: | + Deglitch count, 4-bits value. It is unnecessary most of the time, 0 disables it. diff --git a/dts/riscv/bflb/bl60x.dtsi b/dts/riscv/bflb/bl60x.dtsi index 521f34bf5fcad..69c172e47e272 100644 --- a/dts/riscv/bflb/bl60x.dtsi +++ b/dts/riscv/bflb/bl60x.dtsi @@ -195,6 +195,17 @@ interrupt-parent = <&clic>; }; + irx: irx@4000a600 { + compatible = "bflb,irx"; + reg = <0x4000a600 0x100>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + interrupts = <36 0>; + interrupt-parent = <&clic>; + }; + flashctrl: flash-controller@4000b000 { compatible = "bflb,flash-controller"; reg = <0x4000b000 0x1000>; diff --git a/dts/riscv/bflb/bl61x.dtsi b/dts/riscv/bflb/bl61x.dtsi index beacbfec2b005..dc4910a2a73b7 100644 --- a/dts/riscv/bflb/bl61x.dtsi +++ b/dts/riscv/bflb/bl61x.dtsi @@ -186,6 +186,17 @@ interrupt-parent = <&clic>; }; + irx: irx@2000a600 { + compatible = "bflb,irx"; + reg = <0x2000a600 0x100>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + interrupts = <36 1>; + interrupt-parent = <&clic>; + }; + dbi0: dbi@2000a800 { compatible = "bflb,dbi"; reg = <0x2000a800 0x100>; diff --git a/dts/riscv/bflb/bl70x.dtsi b/dts/riscv/bflb/bl70x.dtsi index 568ed3ff9c3d9..87224b47a28ad 100644 --- a/dts/riscv/bflb/bl70x.dtsi +++ b/dts/riscv/bflb/bl70x.dtsi @@ -200,6 +200,17 @@ interrupt-parent = <&clic>; }; + irx: irx@4000a600 { + compatible = "bflb,irx"; + reg = <0x4000a600 0x100>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + interrupts = <36 0>; + interrupt-parent = <&clic>; + }; + flashctrl: flash-controller@4000b000 { compatible = "bflb,flash-controller"; reg = <0x4000b000 0x1000>; From 231b8152b1823d0c0e2d14786dae643016800fd5 Mon Sep 17 00:00:00 2001 From: Camille BAUD Date: Fri, 5 Dec 2025 21:17:02 +0100 Subject: [PATCH 2/5] west.yml: Build commit Will be replaced when approved Signed-off-by: Camille BAUD --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index b30ee495cb1f2..2a8e566b555ba 100644 --- a/west.yml +++ b/west.yml @@ -165,7 +165,7 @@ manifest: - hal - name: hal_bouffalolab path: modules/hal/bouffalolab - revision: ebecd183d4f52225e465d056f457792e4ebe80c1 + revision: pull/9/head groups: - hal - name: hal_espressif From 91003870e30412c8c28320279432a387d67384de Mon Sep 17 00:00:00 2001 From: Camille BAUD Date: Fri, 5 Dec 2025 18:35:16 +0100 Subject: [PATCH 3/5] drivers: clock_control: Add IRX clock elements Enable the gates for IRX Signed-off-by: Camille BAUD --- drivers/clock_control/clock_control_bl60x.c | 2 ++ drivers/clock_control/clock_control_bl61x.c | 2 ++ drivers/clock_control/clock_control_bl70x.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/drivers/clock_control/clock_control_bl60x.c b/drivers/clock_control/clock_control_bl60x.c index 0da01091a127d..d6d53f725a877 100644 --- a/drivers/clock_control/clock_control_bl60x.c +++ b/drivers/clock_control/clock_control_bl60x.c @@ -660,6 +660,8 @@ static void clock_control_bl60x_peripheral_clock_init(void) regval |= (1 << 18); /* enable DMA clock routing */ regval |= (1 << 12); + /* enable IR clock routing */ + regval |= (1 << 22); sys_write32(regval, GLB_BASE + GLB_CGEN_CFG1_OFFSET); diff --git a/drivers/clock_control/clock_control_bl61x.c b/drivers/clock_control/clock_control_bl61x.c index 421d659e079dc..2efd7b5c03ad5 100644 --- a/drivers/clock_control/clock_control_bl61x.c +++ b/drivers/clock_control/clock_control_bl61x.c @@ -1045,6 +1045,8 @@ static void clock_control_bl61x_peripheral_clock_init(void) regval |= (1 << 13); /* enable DMA clock routing */ regval |= (1 << 12); + /* enable IR clock routing */ + regval |= (1 << 22); /* enable DBI clock routing */ regval |= (1 << 24); diff --git a/drivers/clock_control/clock_control_bl70x.c b/drivers/clock_control/clock_control_bl70x.c index 745eb42e58e8c..40214b422334c 100644 --- a/drivers/clock_control/clock_control_bl70x.c +++ b/drivers/clock_control/clock_control_bl70x.c @@ -540,6 +540,8 @@ static void clock_control_bl70x_peripheral_clock_init(void) regval |= (1 << 19); /* enable DMA clock routing */ regval |= (1 << 12); + /* enable IR clock routing */ + regval |= (1 << 22); sys_write32(regval, GLB_BASE + GLB_CGEN_CFG1_OFFSET); From 823fa667fee84e1a4ebd3709a556460ca20b3e0d Mon Sep 17 00:00:00 2001 From: Camille BAUD Date: Fri, 5 Dec 2025 18:39:54 +0100 Subject: [PATCH 4/5] drivers: input: Introduce bflb IR RX driver Introduces a driver for the IR receiver on BFLB socs Signed-off-by: Camille BAUD --- drivers/input/CMakeLists.txt | 1 + drivers/input/Kconfig | 1 + drivers/input/Kconfig.bflb | 10 + drivers/input/input_bflb_irx.c | 449 +++++++++++++++++++++++++++++++++ 4 files changed, 461 insertions(+) create mode 100644 drivers/input/Kconfig.bflb create mode 100644 drivers/input/input_bflb_irx.c diff --git a/drivers/input/CMakeLists.txt b/drivers/input/CMakeLists.txt index 2e205cc686896..bed126ff38dba 100644 --- a/drivers/input/CMakeLists.txt +++ b/drivers/input/CMakeLists.txt @@ -7,6 +7,7 @@ zephyr_library_property(ALLOW_EMPTY TRUE) zephyr_library_sources_ifdef(CONFIG_INPUT_ADC_KEYS input_adc_keys.c) zephyr_library_sources_ifdef(CONFIG_INPUT_ANALOG_AXIS input_analog_axis.c) zephyr_library_sources_ifdef(CONFIG_INPUT_ANALOG_AXIS_SETTINGS input_analog_axis_settings.c) +zephyr_library_sources_ifdef(CONFIG_INPUT_BFLB_IRX input_bflb_irx.c) zephyr_library_sources_ifdef(CONFIG_INPUT_CAP12XX input_cap12xx.c) zephyr_library_sources_ifdef(CONFIG_INPUT_CF1133 input_cf1133.c) zephyr_library_sources_ifdef(CONFIG_INPUT_CHSC5X input_chsc5x.c) diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index f14a5ab4073b5..d38892973617d 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -8,6 +8,7 @@ menu "Input drivers" # zephyr-keep-sorted-start source "drivers/input/Kconfig.adc_keys" source "drivers/input/Kconfig.analog_axis" +source "drivers/input/Kconfig.bflb" source "drivers/input/Kconfig.cap12xx" source "drivers/input/Kconfig.cf1133" source "drivers/input/Kconfig.chsc5x" diff --git a/drivers/input/Kconfig.bflb b/drivers/input/Kconfig.bflb new file mode 100644 index 0000000000000..0c600b0205c66 --- /dev/null +++ b/drivers/input/Kconfig.bflb @@ -0,0 +1,10 @@ +# Copyright (c) 2025 MASSDRIVER EI (massdriver.space) +# SPDX-License-Identifier: Apache-2.0 + +config INPUT_BFLB_IRX + bool "Bouffalolab Infrared Receiver" + default y + depends on DT_HAS_BFLB_IRX_ENABLED + select GPIO + help + This option enables the driver for the Bouffalolab Infrared Receiver Peripheral. diff --git a/drivers/input/input_bflb_irx.c b/drivers/input/input_bflb_irx.c new file mode 100644 index 0000000000000..8906a5558e5e6 --- /dev/null +++ b/drivers/input/input_bflb_irx.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2025 MASSDRIVER EI (massdriver.space) + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT bflb_irx + +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(input_bflb_irx, CONFIG_INPUT_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include + +/* The default uses 2MHz input clock, however it can go up to 32 MHz */ +#define BFLB_IRX_CLOCK MHZ(2) + +#if defined(CONFIG_SOC_SERIES_BL60X) +#define IRX_MIN_PIN 11 +#define IRX_MAX_PIN 13 +#define IRX_OFFSET_PIN 10 +#define IRX_PIN_OFFSET GLB_LED_DRIVER_OFFSET +#define IRX_FIFO_OFFSET IRRX_SWM_FIFO_CONFIG_0_OFFSET +#elif defined(CONFIG_SOC_SERIES_BL70X) +#define IRX_MIN_PIN 17 +#define IRX_MAX_PIN 31 +#define IRX_OFFSET_PIN 16 +#define IRX_PIN_OFFSET GLB_LED_DRIVER_OFFSET +#define IRX_FIFO_OFFSET IRRX_SWM_FIFO_CONFIG_0_OFFSET +#elif defined(CONFIG_SOC_SERIES_BL61X) +#define IRX_MIN_PIN 9 +#define IRX_MAX_PIN 23 +#define IRX_OFFSET_PIN 8 +#define IRX_PIN_OFFSET GLB_IR_CFG1_OFFSET +#define IRX_FIFO_OFFSET IR_FIFO_CONFIG_0_OFFSET +#define IRX_FIFO_THRES 1 +#else +#error Unsupported Platform +#endif + +#define IRX_US_TO_PW(rate, us) (((rate / USEC_PER_SEC) * us - 1) & UINT16_MAX) +#define IRX_PW_TO_US(rate, pw) ((pw * USEC_PER_SEC) / rate) + +#define IRX_WAIT_TIMEOUT_MS 1000 + +/* 1.7 ms (halfway between NEC 0 and NEC 1) */ +#define IRX_NEC_DATA_THRESHOLD_US 1700 +/* 4.5 ms, matches NEC spec*/ +#define IRX_NEC_END_THRESHOLD_US 4500 +/* 1.3 ms ? */ +#define IRX_RC5_DATA_THRESHOLD_US 1300 +/* 2.5 ms */ +#define IRX_RC5_END_THRESHOLD_US 2500 +/* Default to 4.5 ms end pulse for pulse width mode */ +#define IRX_DEFAULT_PW_END_US 4500 + +enum bflb_irx_protocol { + PROTOCOL_NEC = 0, + PROTOCOL_RC5 = 1, + PROTOCOL_PW = 2, +}; + +struct bflb_irx_data { + struct device const *dev; + uint32_t clock_rate; + struct k_work_delayable fetch_work; +}; + +struct bflb_irx_config { + struct gpio_dt_spec gpio; + uintptr_t reg; + void (*irq_config_func)(const struct device *dev); + enum bflb_irx_protocol protocol; + uint32_t pw_end_pulse_width; + bool invert; + uint16_t deglitch_cnt; +}; + +static uint32_t bflb_irx_get_set_clock(void) +{ + uint32_t ir_divider, set_divider; + uint32_t uclk; + const struct device *clock_ctrl = DEVICE_DT_GET_ANY(bflb_clock_controller); + uint32_t main_clock = clock_bflb_get_root_clock(); + + if (main_clock == BFLB_MAIN_CLOCK_RC32M || main_clock == BFLB_MAIN_CLOCK_PLL_RC32M) { + uclk = BFLB_RC32M_FREQUENCY; + } else { + clock_control_get_rate(clock_ctrl, (void *)BFLB_CLKID_CLK_CRYSTAL, &uclk); + } + + /* Set divider so the output clock is BFLB_IRX_CLOCK */ + set_divider = uclk / BFLB_IRX_CLOCK - 1; +#if defined(CONFIG_SOC_SERIES_BL60X) || defined(CONFIG_SOC_SERIES_BL70X) + ir_divider = sys_read32(GLB_BASE + GLB_CLK_CFG2_OFFSET); + ir_divider &= GLB_IR_CLK_DIV_UMSK; + ir_divider |= (set_divider << GLB_IR_CLK_DIV_POS) & GLB_IR_CLK_DIV_MSK; + sys_write32(ir_divider, GLB_BASE + GLB_CLK_CFG2_OFFSET); +#else + ir_divider = sys_read32(GLB_BASE + GLB_IR_CFG0_OFFSET); + ir_divider &= GLB_IR_CLK_DIV_UMSK; + ir_divider |= (set_divider << GLB_IR_CLK_DIV_POS) & GLB_IR_CLK_DIV_MSK; + sys_write32(ir_divider, GLB_BASE + GLB_IR_CFG0_OFFSET); +#endif + ir_divider = (ir_divider & GLB_IR_CLK_DIV_MSK) >> GLB_IR_CLK_DIV_POS; + + return uclk / (ir_divider + 1); +} + +static int bflb_irx_configure(struct device const *dev) +{ + struct bflb_irx_config const *cfg = dev->config; + struct bflb_irx_data *data = dev->data; + uint32_t tmp; + uint16_t data_threshold, end_threshold; + + data->clock_rate = bflb_irx_get_set_clock(); + + tmp = sys_read32(cfg->reg + IRRX_CONFIG_OFFSET); + tmp &= ~IR_CR_IRRX_MODE_MASK; + tmp |= cfg->protocol << IR_CR_IRRX_MODE_SHIFT; + if (cfg->invert) { + tmp |= IR_CR_IRRX_IN_INV; + } else { + tmp &= ~IR_CR_IRRX_IN_INV; + } + if (cfg->deglitch_cnt > 0) { + tmp |= IR_CR_IRRX_DEG_EN; + tmp &= ~IR_CR_IRRX_DEG_CNT_MASK; + tmp |= (cfg->deglitch_cnt << IR_CR_IRRX_DEG_CNT_SHIFT) & IR_CR_IRRX_DEG_CNT_MASK; + } else { + tmp &= ~IR_CR_IRRX_DEG_EN; + } + sys_write32(tmp, cfg->reg + IRRX_CONFIG_OFFSET); + + if (cfg->protocol == PROTOCOL_NEC) { + data_threshold = IRX_US_TO_PW(data->clock_rate, IRX_NEC_DATA_THRESHOLD_US); + end_threshold = IRX_US_TO_PW(data->clock_rate, IRX_NEC_END_THRESHOLD_US); + } else if (cfg->protocol == PROTOCOL_RC5) { + data_threshold = IRX_US_TO_PW(data->clock_rate, IRX_RC5_DATA_THRESHOLD_US); + end_threshold = IRX_US_TO_PW(data->clock_rate, IRX_RC5_END_THRESHOLD_US); + } else { + /* PW: doesn't care, have a value*/ + data_threshold = 0x1000; + end_threshold = IRX_US_TO_PW(data->clock_rate, cfg->pw_end_pulse_width); + } + + tmp = end_threshold << IR_CR_IRRX_END_TH_SHIFT | data_threshold; + sys_write32(tmp, cfg->reg + IRRX_PW_CONFIG_OFFSET); + +#if defined(CONFIG_SOC_SERIES_BL61X) + tmp = sys_read32(cfg->reg + IR_FIFO_CONFIG_1_OFFSET); + tmp &= ~IR_RX_FIFO_TH_MASK; + tmp |= IRX_FIFO_THRES << IR_RX_FIFO_TH_SHIFT; + sys_write32(tmp, cfg->reg + IR_FIFO_CONFIG_1_OFFSET); +#endif + + /* Setup Interrupts */ + tmp = sys_read32(cfg->reg + IRRX_INT_STS_OFFSET); + tmp |= IR_CR_IRRX_END_EN; + tmp |= IR_CR_IRRX_END_CLR; +#if defined(CONFIG_SOC_SERIES_BL61X) + tmp |= IR_CR_IRRX_FRDY_EN | IR_CR_IRRX_FER_EN; +#endif + tmp &= ~IR_CR_IRRX_END_MASK; +#if defined(CONFIG_SOC_SERIES_BL61X) + if (cfg->protocol == PROTOCOL_PW) { + tmp &= ~IR_CR_IRRX_FRDY_MASK; + } +#endif + sys_write32(tmp, cfg->reg + IRRX_INT_STS_OFFSET); + + return 0; +} + +static void bflb_irx_isr_handle_prot(const struct device *dev) +{ + const struct bflb_irx_config *cfg = dev->config; + uint32_t data_count, data; + int ret; + + data_count = sys_read32(cfg->reg + IRRX_DATA_COUNT_OFFSET) & IR_STS_IRRX_DATA_CNT_MASK; + + data = sys_read32(cfg->reg + IRRX_DATA_WORD0_OFFSET); + ret = input_report(dev, INPUT_EV_MSC, INPUT_MSC_SCAN, data, true, K_FOREVER); + if (ret < 0) { + LOG_ERR("Message failed to be enqueued: %d", ret); + } + if (data_count <= 32) { + return; + } + data = sys_read32(cfg->reg + IRRX_DATA_WORD1_OFFSET); + if (data == 0) { + return; + } + ret = input_report(dev, INPUT_EV_MSC, INPUT_MSC_SCAN, data, true, K_FOREVER); + if (ret < 0) { + LOG_ERR("Message failed to be enqueued: %d", ret); + } +} + +#if defined(CONFIG_SOC_SERIES_BL61X) + +static void bflb_irx_isr_handle_pw(const struct device *dev) +{ + const struct bflb_irx_config *cfg = dev->config; + struct bflb_irx_data *data = dev->data; + volatile uint32_t tmp; + volatile uint32_t x; + int ret; + k_timepoint_t end_timeout = sys_timepoint_calc(K_MSEC(IRX_WAIT_TIMEOUT_MS)); + + while ((sys_read32(cfg->reg + IR_FIFO_CONFIG_1_OFFSET) & IR_RX_FIFO_CNT_MASK + || !(sys_read32(cfg->reg + IRRX_INT_STS_OFFSET) & IRRX_END_INT)) + && !sys_timepoint_expired(end_timeout)) { + if ((sys_read32(cfg->reg + IR_FIFO_CONFIG_1_OFFSET) & IR_RX_FIFO_CNT_MASK) == 0) { + continue; + } + x = sys_read32(cfg->reg + IR_FIFO_RDATA_OFFSET); + ret = input_report(dev, INPUT_EV_MSC, INPUT_MSC_SCAN, + IRX_PW_TO_US(data->clock_rate, x), true, K_FOREVER); + if (ret < 0) { + LOG_ERR("Message failed to be enqueued: %d", ret); + break; + } + } + + if (sys_timepoint_expired(end_timeout)) { + LOG_ERR("Timed out"); + } + + tmp = sys_read32(cfg->reg + IRRX_CONFIG_OFFSET); + tmp &= ~IR_CR_IRRX_EN; + sys_write32(tmp, cfg->reg + IRRX_CONFIG_OFFSET); + + tmp = sys_read32(cfg->reg + IRX_FIFO_OFFSET); + if (tmp & IR_RX_FIFO_OVERFLOW) { + LOG_ERR("Too many pulses, FIFO overflow!"); + } + tmp |= IR_RX_FIFO_CLR; + sys_write32(tmp, cfg->reg + IRX_FIFO_OFFSET); + + tmp = sys_read32(cfg->reg + IRRX_INT_STS_OFFSET); + tmp |= IR_CR_IRRX_END_CLR; + tmp &= ~(IR_CR_IRRX_FRDY_MASK | IR_CR_IRRX_END_MASK); + sys_write32(tmp, cfg->reg + IRRX_INT_STS_OFFSET); +} + +#else + +static void bflb_irx_isr_handle_pw(const struct device *dev) +{ + const struct bflb_irx_config *cfg = dev->config; + struct bflb_irx_data *data = dev->data; + volatile uint32_t tmp; + volatile uint32_t x; + int ret; + + while (sys_read32(cfg->reg + IRRX_SWM_FIFO_CONFIG_0_OFFSET) & IR_RX_FIFO_CNT_MASK) { + x = sys_read32(cfg->reg + IRRX_SWM_FIFO_RDATA_OFFSET); + ret = input_report(dev, INPUT_EV_MSC, INPUT_MSC_SCAN, + IRX_PW_TO_US(data->clock_rate, x), true, K_FOREVER); + if (ret < 0) { + LOG_ERR("Message failed to be enqueued: %d", ret); + break; + } + } + + tmp = sys_read32(cfg->reg + IRX_FIFO_OFFSET); + if (tmp & IR_RX_FIFO_OVERFLOW) { + LOG_ERR("Too many pulses, FIFO overflow!"); + } + tmp |= IR_RX_FIFO_CLR; + sys_write32(tmp, cfg->reg + IRX_FIFO_OFFSET); +} + +#endif + +static void bflb_irx_fetch_work_handler(struct k_work *item) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(item); + struct bflb_irx_data *data = CONTAINER_OF(dwork, struct bflb_irx_data, fetch_work); + struct bflb_irx_config const *cfg = data->dev->config; + uint32_t tmp; + + if (cfg->protocol == PROTOCOL_PW) { + bflb_irx_isr_handle_pw(data->dev); + } else { + bflb_irx_isr_handle_prot(data->dev); + } + + tmp = sys_read32(cfg->reg + IRRX_CONFIG_OFFSET); + tmp |= IR_CR_IRRX_EN; + sys_write32(tmp, cfg->reg + IRRX_CONFIG_OFFSET); +} + +static int bflb_irx_init(struct device const *dev) +{ + struct bflb_irx_config const *config = dev->config; + struct gpio_dt_spec const *gpio = &config->gpio; + struct bflb_irx_data *data = dev->data; + int ret; + uint32_t tmp; + + data->dev = dev; + + if (!gpio_is_ready_dt(gpio)) { + LOG_ERR("GPIO input pin is not ready"); + return -ENODEV; + } + + /* IRX is a special case where the GPIO mode is SWGPIO input */ + gpio_pin_configure_dt(gpio, GPIO_INPUT); + + /* Select GPIO */ + tmp = sys_read32(GLB_BASE + IRX_PIN_OFFSET); + tmp &= GLB_IR_RX_GPIO_SEL_UMSK; + tmp |= ((gpio->pin - IRX_OFFSET_PIN) << GLB_IR_RX_GPIO_SEL_POS) & GLB_IR_RX_GPIO_SEL_MSK; + sys_write32(tmp, GLB_BASE + IRX_PIN_OFFSET); + + ret = bflb_irx_configure(dev); + if (ret < 0) { + return ret; + } + + config->irq_config_func(dev); + + k_work_init_delayable(&data->fetch_work, bflb_irx_fetch_work_handler); + + tmp = sys_read32(config->reg + IRX_FIFO_OFFSET); + tmp |= IR_RX_FIFO_CLR; + sys_write32(tmp, config->reg + IRX_FIFO_OFFSET); + + sys_write32(0, config->reg + IRRX_DATA_WORD0_OFFSET); + sys_write32(0, config->reg + IRRX_DATA_WORD1_OFFSET); + + tmp = sys_read32(config->reg + IRRX_CONFIG_OFFSET); + tmp |= IR_CR_IRRX_EN; + sys_write32(tmp, config->reg + IRRX_CONFIG_OFFSET); + + return 0; +} + +#if defined(CONFIG_SOC_SERIES_BL61X) +static void bflb_irx_isr(const struct device *dev) +{ + const struct bflb_irx_config *cfg = dev->config; + struct bflb_irx_data *data = dev->data; + volatile uint32_t tmp; + bool has_data = sys_read32(cfg->reg + IR_FIFO_CONFIG_1_OFFSET) & IR_RX_FIFO_CNT_MASK + || sys_read32(cfg->reg + IRRX_INT_STS_OFFSET) & IRRX_FRDY_INT; + + if (cfg->protocol != PROTOCOL_PW || !has_data) { + tmp = sys_read32(cfg->reg + IRRX_CONFIG_OFFSET); + tmp &= ~IR_CR_IRRX_EN; + sys_write32(tmp, cfg->reg + IRRX_CONFIG_OFFSET); + + tmp = sys_read32(cfg->reg + IRRX_INT_STS_OFFSET); + tmp |= IR_CR_IRRX_END_CLR; + sys_write32(tmp, cfg->reg + IRRX_INT_STS_OFFSET); + } + + if (cfg->protocol == PROTOCOL_PW) { + has_data = sys_read32(cfg->reg + IR_FIFO_CONFIG_1_OFFSET) & IR_RX_FIFO_CNT_MASK + || sys_read32(cfg->reg + IRRX_INT_STS_OFFSET) & IRRX_FRDY_INT; + if (has_data) { + tmp = sys_read32(cfg->reg + IRRX_INT_STS_OFFSET); + tmp |= IR_CR_IRRX_FRDY_MASK | IR_CR_IRRX_END_MASK; + sys_write32(tmp, cfg->reg + IRRX_INT_STS_OFFSET); + k_work_schedule(&data->fetch_work, K_NO_WAIT); + } else { + tmp = sys_read32(cfg->reg + IRRX_CONFIG_OFFSET); + tmp |= IR_CR_IRRX_EN; + sys_write32(tmp, cfg->reg + IRRX_CONFIG_OFFSET); + } + } else { + k_work_schedule(&data->fetch_work, K_NO_WAIT); + } +} +#else + +static void bflb_irx_isr(const struct device *dev) +{ + const struct bflb_irx_config *cfg = dev->config; + struct bflb_irx_data *data = dev->data; + volatile uint32_t tmp; + + tmp = sys_read32(cfg->reg + IRRX_CONFIG_OFFSET); + tmp &= ~IR_CR_IRRX_EN; + sys_write32(tmp, cfg->reg + IRRX_CONFIG_OFFSET); + + tmp = sys_read32(cfg->reg + IRRX_INT_STS_OFFSET); + tmp |= IR_CR_IRRX_END_CLR; + sys_write32(tmp, cfg->reg + IRRX_INT_STS_OFFSET); + + /* Don't do processing in ISR */ + k_work_schedule(&data->fetch_work, K_NO_WAIT); +} + +#endif + +#define IRX_BFLB_IRQ_HANDLER_DECL(n) \ + static void bflb_irx_config_func_##n(const struct device *dev); +#define IRX_BFLB_IRQ_HANDLER_FUNC(n) \ + .irq_config_func = bflb_irx_config_func_##n +#define IRX_BFLB_IRQ_HANDLER(n) \ + static void bflb_irx_config_func_##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + bflb_irx_isr, \ + DEVICE_DT_INST_GET(n), \ + 0); \ + irq_enable(DT_INST_IRQN(n)); \ + } + +#define BFLB_IRX_DEFINE(inst) \ + IRX_BFLB_IRQ_HANDLER_DECL(inst) \ + static struct bflb_irx_data bflb_irx_data_##inst; \ + static struct bflb_irx_config const bflb_irx_config_##inst = { \ + .gpio = GPIO_DT_SPEC_INST_GET(inst, ir_gpios), \ + .reg = DT_INST_REG_ADDR(inst), \ + .protocol = DT_INST_ENUM_IDX(inst, protocol), \ + .pw_end_pulse_width = \ + DT_INST_PROP_OR(inst, pw_end_pulse_width, IRX_DEFAULT_PW_END_US), \ + .invert = DT_INST_PROP(inst, invert), \ + .deglitch_cnt = DT_INST_PROP(inst, deglitch_cnt), \ + IRX_BFLB_IRQ_HANDLER_FUNC(inst) \ + }; \ + DEVICE_DT_INST_DEFINE(inst, bflb_irx_init, NULL, &bflb_irx_data_##inst, \ + &bflb_irx_config_##inst, POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, \ + NULL); \ + IRX_BFLB_IRQ_HANDLER(inst) \ + BUILD_ASSERT(DT_INST_GPIO_PIN(inst, ir_gpios) <= IRX_MAX_PIN, \ + "Pin is invalid for IRX, must be at most " STRINGIFY(IRX_MAX_PIN)); \ + BUILD_ASSERT(DT_INST_GPIO_PIN(inst, ir_gpios) >= IRX_MIN_PIN, \ + "Pin is invalid for IRX, must be at least " STRINGIFY(IRX_MIN_PIN)); + +DT_INST_FOREACH_STATUS_OKAY(BFLB_IRX_DEFINE) From a6e84b699dbe930b9c605fa4a4f5befb80efc00e Mon Sep 17 00:00:00 2001 From: Josuah Demangeon Date: Sat, 6 Dec 2025 22:30:30 +0000 Subject: [PATCH 5/5] tests: drivers: build_all: add tests for bflb,irx Add build test for Bouffalo Lab IR receiver input driver. Signed-off-by: Josuah Demangeon --- .../aithinker/ai_m62_12f_kit/ai_m62_12f_kit.yaml | 1 + .../aithinker/ai_wb2_12f_kit/ai_wb2_12f_kit.yaml | 1 + .../build_all/input/boards/ai_m62_12f_kit.overlay | 11 +++++++++++ .../build_all/input/boards/ai_wb2_12f_kit.overlay | 11 +++++++++++ tests/drivers/build_all/input/testcase.yaml | 15 +++++++++++++-- 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/drivers/build_all/input/boards/ai_m62_12f_kit.overlay create mode 100644 tests/drivers/build_all/input/boards/ai_wb2_12f_kit.overlay diff --git a/boards/aithinker/ai_m62_12f_kit/ai_m62_12f_kit.yaml b/boards/aithinker/ai_m62_12f_kit/ai_m62_12f_kit.yaml index 66afa3dafeb1c..5393f064ee887 100644 --- a/boards/aithinker/ai_m62_12f_kit/ai_m62_12f_kit.yaml +++ b/boards/aithinker/ai_m62_12f_kit/ai_m62_12f_kit.yaml @@ -22,4 +22,5 @@ supported: - display - spi - flash + - input vendor: bflb diff --git a/boards/aithinker/ai_wb2_12f_kit/ai_wb2_12f_kit.yaml b/boards/aithinker/ai_wb2_12f_kit/ai_wb2_12f_kit.yaml index 30bd05a43d7bf..337bffb02e021 100644 --- a/boards/aithinker/ai_wb2_12f_kit/ai_wb2_12f_kit.yaml +++ b/boards/aithinker/ai_wb2_12f_kit/ai_wb2_12f_kit.yaml @@ -21,4 +21,5 @@ supported: - i2c - spi - flash + - input vendor: bflb diff --git a/tests/drivers/build_all/input/boards/ai_m62_12f_kit.overlay b/tests/drivers/build_all/input/boards/ai_m62_12f_kit.overlay new file mode 100644 index 0000000000000..dc8de5ba8f427 --- /dev/null +++ b/tests/drivers/build_all/input/boards/ai_m62_12f_kit.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2025 MASSDRIVER EI (massdriver.space) + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&irx { + ir-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>; + protocol = "NEC"; + status = "okay"; +}; diff --git a/tests/drivers/build_all/input/boards/ai_wb2_12f_kit.overlay b/tests/drivers/build_all/input/boards/ai_wb2_12f_kit.overlay new file mode 100644 index 0000000000000..6fea3654c3829 --- /dev/null +++ b/tests/drivers/build_all/input/boards/ai_wb2_12f_kit.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2025 MASSDRIVER EI (massdriver.space) + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&irx { + ir-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>; + protocol = "NEC"; + status = "okay"; +}; diff --git a/tests/drivers/build_all/input/testcase.yaml b/tests/drivers/build_all/input/testcase.yaml index ea64f324fbb4e..9dee7ca7d33ae 100644 --- a/tests/drivers/build_all/input/testcase.yaml +++ b/tests/drivers/build_all/input/testcase.yaml @@ -3,27 +3,38 @@ common: - drivers - input build_only: true - platform_allow: - - native_sim tests: drivers.input.default: {} # Touchscreen drivers, non-default option drivers.input.touchscreen_interrupt: + platform_allow: + - native_sim extra_configs: - CONFIG_INPUT_CST816S_INTERRUPT=n - CONFIG_INPUT_FT5336_INTERRUPT=y - CONFIG_INPUT_GT911_INTERRUPT=y drivers.input.kbd_16_bit: + platform_allow: + - native_sim extra_configs: - CONFIG_INPUT_KBD_MATRIX_16_BIT_ROW=y drivers.input.analog_axis: + platform_allow: + - native_sim extra_configs: - CONFIG_ADC=y - CONFIG_SETTINGS=y drivers.input.adc_keys: + platform_allow: + - native_sim extra_configs: - CONFIG_ADC=y + + drivers.input.bflb_irx: + platform_allow: + - ai_m62_12f_kit + - ai_wb2_12f_kit