From cf242016b413cc0956d67712c8ce9b8edd98f432 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Fri, 21 Apr 2023 12:27:36 +0900 Subject: [PATCH] drivers: counter: Add support for rpi_pico timer Adds support for rpi_pico timer Signed-off-by: TOKITA Hiroshi --- drivers/counter/CMakeLists.txt | 1 + drivers/counter/Kconfig | 2 + drivers/counter/Kconfig.rpi_pico | 8 + drivers/counter/counter_rpi_pico_timer.c | 217 ++++++++++++++++++ dts/arm/rpi_pico/rp2040.dtsi | 16 ++ .../timer/raspberrypi,pico-timer.yaml | 8 + 6 files changed, 252 insertions(+) create mode 100644 drivers/counter/Kconfig.rpi_pico create mode 100644 drivers/counter/counter_rpi_pico_timer.c create mode 100644 dts/bindings/timer/raspberrypi,pico-timer.yaml diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index e250a2851b2..3a59312da24 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -47,3 +47,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_NXP_S32_SYS_TIMER counter_nxp_s32_ zephyr_library_sources_ifdef(CONFIG_COUNTER_TIMER_GD32 counter_gd32_timer.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_SNPS_DW counter_dw_timer.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_SHELL counter_timer_shell.c) +zephyr_library_sources_ifdef(CONFIG_COUNTER_TIMER_RPI_PICO counter_rpi_pico_timer.c) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 793c58dd063..956c5d59f64 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -92,4 +92,6 @@ source "drivers/counter/Kconfig.gd32" source "drivers/counter/Kconfig.dw" +source "drivers/counter/Kconfig.rpi_pico" + endif # COUNTER diff --git a/drivers/counter/Kconfig.rpi_pico b/drivers/counter/Kconfig.rpi_pico new file mode 100644 index 00000000000..53fc748e252 --- /dev/null +++ b/drivers/counter/Kconfig.rpi_pico @@ -0,0 +1,8 @@ +# Copyright (c) 2023 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +config COUNTER_TIMER_RPI_PICO + def_bool y + select PICOSDK_USE_TIMER + select PICOSDK_USE_CLAIM + depends on DT_HAS_RASPBERRYPI_PICO_TIMER_ENABLED diff --git a/drivers/counter/counter_rpi_pico_timer.c b/drivers/counter/counter_rpi_pico_timer.c new file mode 100644 index 00000000000..3e1a1dcbb0b --- /dev/null +++ b/drivers/counter/counter_rpi_pico_timer.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2023 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_COUNTER_LOG_LEVEL +#include + +LOG_MODULE_REGISTER(counter_rpi_pico_timer, LOG_LEVEL); + +#define DT_DRV_COMPAT raspberrypi_pico_timer + +struct counter_rpi_pico_timer_ch_data { + counter_alarm_callback_t callback; + void *user_data; +}; + +struct counter_rpi_pico_timer_data { + struct counter_rpi_pico_timer_ch_data *ch_data; + uint32_t guard_period; +}; + +struct counter_rpi_pico_timer_config { + struct counter_config_info info; + timer_hw_t *timer; + void (*irq_config)(); +}; + +static int counter_rpi_pico_timer_start(const struct device *dev) +{ + const struct counter_rpi_pico_timer_config *config = dev->config; + + config->timer->pause = 0; + + return 0; +} + +static int counter_rpi_pico_timer_stop(const struct device *dev) +{ + const struct counter_rpi_pico_timer_config *config = dev->config; + + config->timer->pause = 1u; + config->timer->timelw = 0; + config->timer->timehw = 0; + + return 0; +} + +static uint32_t counter_rpi_pico_timer_get_top_value(const struct device *dev) +{ + const struct counter_rpi_pico_timer_config *config = dev->config; + + return config->info.max_top_value; +} + +static int counter_rpi_pico_timer_get_value(const struct device *dev, uint32_t *ticks) +{ + *ticks = time_us_32(); + return 0; +} + +static int counter_rpi_pico_timer_set_alarm(const struct device *dev, uint8_t id, + const struct counter_alarm_cfg *alarm_cfg) +{ + const struct counter_rpi_pico_timer_config *config = dev->config; + struct counter_rpi_pico_timer_data *data = dev->data; + struct counter_rpi_pico_timer_ch_data *chdata = &data->ch_data[id]; + uint64_t target = (alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) ? 0 : alarm_cfg->ticks; + absolute_time_t alarm_at; + bool missed; + + update_us_since_boot(&alarm_at, config->timer->timerawl + target); + + if (alarm_cfg->ticks > counter_rpi_pico_timer_get_top_value(dev)) { + return -EINVAL; + } + + if (chdata->callback) { + return -EBUSY; + } + + chdata->callback = alarm_cfg->callback; + chdata->user_data = alarm_cfg->user_data; + + missed = hardware_alarm_set_target(id, alarm_at); + + if (missed) { + if (alarm_cfg->flags & COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE) { + hardware_alarm_force_irq(id); + } + chdata->callback = NULL; + chdata->user_data = NULL; + return -ETIME; + } + + return 0; +} + +static int counter_rpi_pico_timer_cancel_alarm(const struct device *dev, uint8_t id) +{ + hardware_alarm_cancel(id); + + return 0; +} + +static int counter_rpi_pico_timer_set_top_value(const struct device *dev, + const struct counter_top_cfg *cfg) +{ + ARG_UNUSED(dev); + ARG_UNUSED(cfg); + + return -ENOTSUP; +} + +static uint32_t counter_rpi_pico_timer_get_pending_int(const struct device *dev) +{ + return 0; +} + +static uint32_t counter_rpi_pico_timer_get_guard_period(const struct device *dev, uint32_t flags) +{ + struct counter_rpi_pico_timer_data *data = dev->data; + + return data->guard_period; +} + +static int counter_rpi_pico_timer_set_guard_period(const struct device *dev, uint32_t guard, + uint32_t flags) +{ + struct counter_rpi_pico_timer_data *data = dev->data; + + __ASSERT_NO_MSG(guard < counter_rpi_pico_timer_get_top_value(dev)); + + data->guard_period = guard; + + return 0; +} + +static void counter_rpi_pico_irq_handle(uint32_t ch, void *arg) +{ + struct device *dev = arg; + struct counter_rpi_pico_timer_data *data = dev->data; + counter_alarm_callback_t cb = data->ch_data[ch].callback; + void *user_data = data->ch_data[ch].user_data; + + if (cb) { + data->ch_data[ch].callback = NULL; + data->ch_data[ch].user_data = NULL; + cb(dev, ch, time_us_32(), user_data); + } +} + +static int counter_rpi_pico_timer_init(const struct device *dev) +{ + const struct counter_rpi_pico_timer_config *config = dev->config; + + config->irq_config(); + + return 0; +} + +static const struct counter_driver_api counter_rpi_pico_driver_api = { + .start = counter_rpi_pico_timer_start, + .stop = counter_rpi_pico_timer_stop, + .get_value = counter_rpi_pico_timer_get_value, + .set_alarm = counter_rpi_pico_timer_set_alarm, + .cancel_alarm = counter_rpi_pico_timer_cancel_alarm, + .set_top_value = counter_rpi_pico_timer_set_top_value, + .get_pending_int = counter_rpi_pico_timer_get_pending_int, + .get_top_value = counter_rpi_pico_timer_get_top_value, + .get_guard_period = counter_rpi_pico_timer_get_guard_period, + .set_guard_period = counter_rpi_pico_timer_set_guard_period, +}; + +#define RPI_PICO_TIMER_IRQ_ENABLE(node_id, name, idx) \ + do { \ + hardware_alarm_set_callback(idx, counter_rpi_pico_irq_handle); \ + IRQ_CONNECT((DT_IRQ_BY_IDX(node_id, idx, irq)), \ + (DT_IRQ_BY_IDX(node_id, idx, priority)), hardware_alarm_irq_handler, \ + (DEVICE_DT_GET(node_id)), 0); \ + irq_enable((DT_IRQ_BY_IDX(node_id, idx, irq))); \ + } while (false); + +#define COUNTER_RPI_PICO_TIMER(inst) \ + static void counter_irq_config##inst(void) \ + { \ + DT_INST_FOREACH_PROP_ELEM(inst, interrupt_names, RPI_PICO_TIMER_IRQ_ENABLE); \ + } \ + static struct counter_rpi_pico_timer_ch_data \ + ch_data##inst[DT_NUM_IRQS(DT_DRV_INST(inst))]; \ + static struct counter_rpi_pico_timer_data counter_##inst##_data = { \ + .ch_data = ch_data##inst, \ + }; \ + static const struct counter_rpi_pico_timer_config counter_##inst##_config = { \ + .timer = (timer_hw_t *)DT_INST_REG_ADDR(inst), \ + .irq_config = counter_irq_config##inst, \ + .info = \ + { \ + .max_top_value = UINT32_MAX, \ + .freq = 1000000, \ + .flags = COUNTER_CONFIG_INFO_COUNT_UP, \ + .channels = ARRAY_SIZE(ch_data##inst), \ + }, \ + }; \ + DEVICE_DT_INST_DEFINE(inst, counter_rpi_pico_timer_init, NULL, &counter_##inst##_data, \ + &counter_##inst##_config, PRE_KERNEL_1, \ + CONFIG_COUNTER_INIT_PRIORITY, &counter_rpi_pico_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(COUNTER_RPI_PICO_TIMER) diff --git a/dts/arm/rpi_pico/rp2040.dtsi b/dts/arm/rpi_pico/rp2040.dtsi index 131c59a1b1b..fd83297fa5a 100644 --- a/dts/arm/rpi_pico/rp2040.dtsi +++ b/dts/arm/rpi_pico/rp2040.dtsi @@ -189,6 +189,22 @@ #pwm-cells = <3>; }; + timer: timer@40054000 { + compatible = "raspberrypi,pico-timer"; + reg = <0x40054000 DT_SIZE_K(4)>; + resets = <&reset RPI_PICO_RESETS_RESET_TIMER>; + clocks = <&xtal_clk>; + interrupts = <0 RPI_PICO_DEFAULT_IRQ_PRIORITY>, + <1 RPI_PICO_DEFAULT_IRQ_PRIORITY>, + <2 RPI_PICO_DEFAULT_IRQ_PRIORITY>, + <3 RPI_PICO_DEFAULT_IRQ_PRIORITY>; + interrupt-names = "TIMER_IRQ_0", + "TIMER_IRQ_1", + "TIMER_IRQ_2", + "TIMER_IRQ_3"; + status = "disabled"; + }; + dma: dma@50000000 { compatible = "raspberrypi,pico-dma"; reg = <0x50000000 DT_SIZE_K(64)>; diff --git a/dts/bindings/timer/raspberrypi,pico-timer.yaml b/dts/bindings/timer/raspberrypi,pico-timer.yaml new file mode 100644 index 00000000000..98afd1b9659 --- /dev/null +++ b/dts/bindings/timer/raspberrypi,pico-timer.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2023 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +description: RaspberryPi Pico timer + +compatible: "raspberrypi,pico-timer" + +include: [base.yaml, reset-device.yaml]