From cea0da3815232c437a0a695824184bd90f0b2c86 Mon Sep 17 00:00:00 2001 From: Yonatan Schachter Date: Sat, 26 Feb 2022 19:25:03 +0200 Subject: [PATCH 1/5] soc: rpi_pico: Added panic handler Some pico-sdk drivers call a panic function, originally implemented as part of the Pico's C runtime. This commit adds a Zephyr compatible implementation of panic, so that those drivers could be compiled with Zephyr. Signed-off-by: Yonatan Schachter --- soc/arm/rpi_pico/rp2/soc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/soc/arm/rpi_pico/rp2/soc.c b/soc/arm/rpi_pico/rp2/soc.c index afa50cf56ee53c..3e572ba57765da 100644 --- a/soc/arm/rpi_pico/rp2/soc.c +++ b/soc/arm/rpi_pico/rp2/soc.c @@ -13,10 +13,13 @@ * for the Raspberry Pi RP2040 family processor. */ +#include + #include #include #include #include +#include #include #include @@ -75,4 +78,18 @@ static int rp2040_init(const struct device *arg) return 0; } +/* + * Some pico-sdk drivers call panic on fatal error. + * This alternative implementation of panic handles the panic + * through Zephyr. + */ +void __attribute__((noreturn)) panic(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + k_fatal_halt(K_ERR_CPU_EXCEPTION); +} + SYS_INIT(rp2040_init, PRE_KERNEL_1, 0); From 72addcb6d95eee1f31fcd5d1384c06df27f5e8fa Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Wed, 18 Jan 2023 08:57:14 +0900 Subject: [PATCH 2/5] modules: hal_rpi_pico: Enable DMA driver Enable DMA driver. Add the path of the DMA driver header into include paths. `hardware_claim` is depends by DMA driver, also enable it. Signed-off-by: TOKITA Hiroshi --- modules/hal_rpi_pico/CMakeLists.txt | 10 ++++++++++ modules/hal_rpi_pico/Kconfig | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/modules/hal_rpi_pico/CMakeLists.txt b/modules/hal_rpi_pico/CMakeLists.txt index 891bd002283abb..6501c9e8ca9ff0 100644 --- a/modules/hal_rpi_pico/CMakeLists.txt +++ b/modules/hal_rpi_pico/CMakeLists.txt @@ -102,6 +102,16 @@ if(CONFIG_HAS_RPI_PICO) zephyr_include_directories_ifdef(CONFIG_PICOSDK_USE_ADC ${rp2_common_dir}/hardware_adc/include) + zephyr_library_sources_ifdef(CONFIG_PICOSDK_USE_DMA + ${rp2_common_dir}/hardware_dma/dma.c) + zephyr_include_directories_ifdef(CONFIG_PICOSDK_USE_DMA + ${rp2_common_dir}/hardware_dma/include) + + zephyr_library_sources_ifdef(CONFIG_PICOSDK_USE_CLAIM + ${rp2_common_dir}/hardware_claim/claim.c) + zephyr_include_directories_ifdef(CONFIG_PICOSDK_USE_CLAIM + ${rp2_common_dir}/hardware_claim/include) + # Some flash driver functions must be executed from the RAM. # Originally pico-sdk places them in the RW data section, so this # implementation does the same. diff --git a/modules/hal_rpi_pico/Kconfig b/modules/hal_rpi_pico/Kconfig index 9bb327b8735ba4..ecb4c4993c6afe 100644 --- a/modules/hal_rpi_pico/Kconfig +++ b/modules/hal_rpi_pico/Kconfig @@ -28,3 +28,13 @@ config PICOSDK_USE_ADC bool help Use the ADC driver from pico-sdk + +config PICOSDK_USE_DMA + bool + help + Use the DMA driver from pico-sdk + +config PICOSDK_USE_CLAIM + bool + help + Use the "claim" driver from pico-sdk From 367720c5e2c5c368208dbe37944a547d34548684 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Wed, 18 Jan 2023 08:13:54 +0900 Subject: [PATCH 3/5] drivers: dma: rpi_pico: add support for RaspberryPi Pico DMA Adding RaspberryPi Pico DMA driver. Signed-off-by: TOKITA Hiroshi --- boards/arm/rpi_pico/rpi_pico.yaml | 1 + drivers/dma/CMakeLists.txt | 1 + drivers/dma/Kconfig | 2 + drivers/dma/Kconfig.rpi_pico | 12 + drivers/dma/dma_rpi_pico.c | 384 ++++++++++++++++++ dts/arm/rpi_pico/rp2040.dtsi | 13 + dts/bindings/dma/raspberrypi,pico-dma.yaml | 42 ++ include/zephyr/dt-bindings/dma/rpi_pico_dma.h | 64 +++ 8 files changed, 519 insertions(+) create mode 100644 drivers/dma/Kconfig.rpi_pico create mode 100644 drivers/dma/dma_rpi_pico.c create mode 100644 dts/bindings/dma/raspberrypi,pico-dma.yaml create mode 100644 include/zephyr/dt-bindings/dma/rpi_pico_dma.h diff --git a/boards/arm/rpi_pico/rpi_pico.yaml b/boards/arm/rpi_pico/rpi_pico.yaml index 663e21417465b8..a76960bfb03e34 100644 --- a/boards/arm/rpi_pico/rpi_pico.yaml +++ b/boards/arm/rpi_pico/rpi_pico.yaml @@ -17,3 +17,4 @@ supported: - hwinfo - watchdog - pwm + - dma diff --git a/drivers/dma/CMakeLists.txt b/drivers/dma/CMakeLists.txt index de234095631b9d..78701c95c5f9fd 100644 --- a/drivers/dma/CMakeLists.txt +++ b/drivers/dma/CMakeLists.txt @@ -24,3 +24,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_INTEL_ADSP_HDA_LINK_IN dma_intel_adsp_hd zephyr_library_sources_ifdef(CONFIG_DMA_INTEL_ADSP_HDA_LINK_OUT dma_intel_adsp_hda_link_out.c) zephyr_library_sources_ifdef(CONFIG_DMA_INTEL_ADSP_GPDMA dma_intel_adsp_gpdma.c dma_dw_common.c) zephyr_library_sources_ifdef(CONFIG_DMA_GD32 dma_gd32.c) +zephyr_library_sources_ifdef(CONFIG_DMA_RPI_PICO dma_rpi_pico.c) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index cf3b60c24cb1b5..b5c10d9d6354d1 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -50,4 +50,6 @@ source "drivers/dma/Kconfig.intel_adsp_hda" source "drivers/dma/Kconfig.gd32" +source "drivers/dma/Kconfig.rpi_pico" + endif # DMA diff --git a/drivers/dma/Kconfig.rpi_pico b/drivers/dma/Kconfig.rpi_pico new file mode 100644 index 00000000000000..434ac9fdf14c57 --- /dev/null +++ b/drivers/dma/Kconfig.rpi_pico @@ -0,0 +1,12 @@ +# Copyright (c) 2023 Tokita, Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +config DMA_RPI_PICO + bool "Raspberry Pi Pico DMA driver" + default y + depends on DT_HAS_RASPBERRYPI_PICO_DMA_ENABLED + select PICOSDK_USE_DMA + select PICOSDK_USE_CLAIM + depends on RESET + help + DMA driver for RaspberryPi Pico. diff --git a/drivers/dma/dma_rpi_pico.c b/drivers/dma/dma_rpi_pico.c new file mode 100644 index 00000000000000..57c23ced657f1b --- /dev/null +++ b/drivers/dma/dma_rpi_pico.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2023 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DT_DRV_COMPAT raspberrypi_pico_dma + +#define DMA_INT_ERROR_FLAGS \ + (DMA_CH0_CTRL_TRIG_AHB_ERROR_BITS | DMA_CH0_CTRL_TRIG_READ_ERROR_BITS | \ + DMA_CH0_CTRL_TRIG_WRITE_ERROR_BITS) + +LOG_MODULE_REGISTER(dma_rpi_pico, CONFIG_DMA_LOG_LEVEL); + +struct dma_rpi_pico_config { + uint32_t reg; + uint32_t channels; + struct reset_dt_spec reset; + void (*irq_configure)(void); + uint32_t *irq0_channels; + size_t irq0_channels_size; +}; + +struct dma_rpi_pico_channel { + dma_callback_t callback; + void *user_data; + uint32_t direction; + dma_channel_config config; + void *source_address; + void *dest_address; + size_t block_size; +}; + +struct dma_rpi_pico_data { + struct dma_context ctx; + struct dma_rpi_pico_channel *channels; +}; + +/* + * Register access functions + */ + +static inline void rpi_pico_dma_channel_clear_error_flags(const struct device *dev, + uint32_t channel) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + + ((dma_hw_t *)cfg->reg)->ch[channel].al1_ctrl &= ~DMA_INT_ERROR_FLAGS; +} + +static inline uint32_t rpi_pico_dma_channel_get_error_flags(const struct device *dev, + uint32_t channel) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + + return ((dma_hw_t *)cfg->reg)->ch[channel].al1_ctrl & DMA_INT_ERROR_FLAGS; +} + +static inline void rpi_pico_dma_channel_abort(const struct device *dev, uint32_t channel) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + + ((dma_hw_t *)cfg->reg)->abort = BIT(channel); +} + +/* + * Utility functions + */ + +static inline uint32_t dma_rpi_pico_transfer_size(uint32_t width) +{ + switch (width) { + case 4: + return DMA_SIZE_32; + case 2: + return DMA_SIZE_16; + default: + return DMA_SIZE_8; + } +} + +static inline uint32_t dma_rpi_pico_channel_irq(const struct device *dev, uint32_t channel) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + + for (size_t i = 0; i < cfg->irq0_channels_size; i++) { + if (cfg->irq0_channels[i] == channel) { + return 0; + } + } + + return 1; +} + +/* + * API functions + */ + +static int dma_rpi_pico_config(const struct device *dev, uint32_t channel, + struct dma_config *dma_cfg) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + struct dma_rpi_pico_data *data = dev->data; + + if (channel >= cfg->channels) { + LOG_ERR("channel must be < %" PRIu32 " (%" PRIu32 ")", cfg->channels, channel); + return -EINVAL; + } + + if (dma_cfg->block_count != 1) { + LOG_ERR("chained block transfer not supported."); + return -ENOTSUP; + } + + if (dma_cfg->channel_priority > 3) { + LOG_ERR("channel_priority must be < 4 (%" PRIu32 ")", dma_cfg->channel_priority); + return -EINVAL; + } + + if (dma_cfg->head_block->source_addr_adj == DMA_ADDR_ADJ_DECREMENT) { + LOG_ERR("source_addr_adj not supported DMA_ADDR_ADJ_DECREMENT"); + return -ENOTSUP; + } + + if (dma_cfg->head_block->dest_addr_adj == DMA_ADDR_ADJ_DECREMENT) { + LOG_ERR("dest_addr_adj not supported DMA_ADDR_ADJ_DECREMENT"); + return -ENOTSUP; + } + + if (dma_cfg->head_block->source_addr_adj != DMA_ADDR_ADJ_INCREMENT && + dma_cfg->head_block->source_addr_adj != DMA_ADDR_ADJ_NO_CHANGE) { + LOG_ERR("invalid source_addr_adj %" PRIu16, dma_cfg->head_block->source_addr_adj); + return -ENOTSUP; + } + if (dma_cfg->head_block->dest_addr_adj != DMA_ADDR_ADJ_INCREMENT && + dma_cfg->head_block->dest_addr_adj != DMA_ADDR_ADJ_NO_CHANGE) { + LOG_ERR("invalid dest_addr_adj %" PRIu16, dma_cfg->head_block->dest_addr_adj); + return -ENOTSUP; + } + + if (dma_cfg->source_data_size != 1 && dma_cfg->source_data_size != 2 && + dma_cfg->source_data_size != 4) { + LOG_ERR("source_data_size must be 1, 2, or 4 (%" PRIu32 ")", + dma_cfg->source_data_size); + return -EINVAL; + } + + if (dma_cfg->source_data_size != dma_cfg->dest_data_size) { + return -EINVAL; + } + + if (dma_cfg->dest_data_size != 1 && dma_cfg->dest_data_size != 2 && + dma_cfg->dest_data_size != 4) { + LOG_ERR("dest_data_size must be 1, 2, or 4 (%" PRIu32 ")", dma_cfg->dest_data_size); + return -EINVAL; + } + + if (dma_cfg->channel_direction > PERIPHERAL_TO_MEMORY) { + LOG_ERR("channel_direction must be MEMORY_TO_MEMORY, " + "MEMORY_TO_PERIPHERAL or PERIPHERAL_TO_MEMORY (%" PRIu32 ")", + dma_cfg->channel_direction); + return -ENOTSUP; + } + + data->channels[channel].config = dma_channel_get_default_config(channel); + + data->channels[channel].source_address = (void *)dma_cfg->head_block->source_address; + data->channels[channel].dest_address = (void *)dma_cfg->head_block->dest_address; + data->channels[channel].block_size = dma_cfg->head_block->block_size; + channel_config_set_read_increment(&data->channels[channel].config, + dma_cfg->head_block->source_addr_adj == + DMA_ADDR_ADJ_INCREMENT); + channel_config_set_write_increment(&data->channels[channel].config, + dma_cfg->head_block->dest_addr_adj == + DMA_ADDR_ADJ_INCREMENT); + channel_config_set_transfer_data_size( + &data->channels[channel].config, + dma_rpi_pico_transfer_size(dma_cfg->source_data_size)); + channel_config_set_dreq(&data->channels[channel].config, + RPI_PICO_DMA_SLOT_TO_DREQ(dma_cfg->dma_slot)); + channel_config_set_high_priority(&data->channels[channel].config, + !!(dma_cfg->channel_priority)); + + data->channels[channel].callback = dma_cfg->dma_callback; + data->channels[channel].user_data = dma_cfg->user_data; + data->channels[channel].direction = dma_cfg->channel_direction; + + return 0; +} + +static int dma_rpi_pico_reload(const struct device *dev, uint32_t ch, uint32_t src, uint32_t dst, + size_t size) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + struct dma_rpi_pico_data *data = dev->data; + + if (ch >= cfg->channels) { + LOG_ERR("reload channel must be < %" PRIu32 " (%" PRIu32 ")", cfg->channels, ch); + return -EINVAL; + } + + if (dma_channel_is_busy(ch)) { + return -EBUSY; + } + + data->channels[ch].source_address = (void *)src; + data->channels[ch].dest_address = (void *)dst; + data->channels[ch].block_size = size; + dma_channel_configure(ch, &data->channels[ch].config, data->channels[ch].dest_address, + data->channels[ch].source_address, data->channels[ch].block_size, + true); + + return 0; +} + +static int dma_rpi_pico_start(const struct device *dev, uint32_t ch) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + struct dma_rpi_pico_data *data = dev->data; + + if (ch >= cfg->channels) { + LOG_ERR("start channel must be < %" PRIu32 " (%" PRIu32 ")", cfg->channels, ch); + return -EINVAL; + } + + dma_irqn_acknowledge_channel(dma_rpi_pico_channel_irq(dev, ch), ch); + dma_irqn_set_channel_enabled(dma_rpi_pico_channel_irq(dev, ch), ch, true); + + dma_channel_configure(ch, &data->channels[ch].config, data->channels[ch].dest_address, + data->channels[ch].source_address, data->channels[ch].block_size, + true); + + return 0; +} + +static int dma_rpi_pico_stop(const struct device *dev, uint32_t ch) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + + if (ch >= cfg->channels) { + LOG_ERR("stop channel must be < %" PRIu32 " (%" PRIu32 ")", cfg->channels, ch); + return -EINVAL; + } + + dma_irqn_set_channel_enabled(dma_rpi_pico_channel_irq(dev, ch), ch, false); + rpi_pico_dma_channel_clear_error_flags(dev, ch); + + /* + * Considering the possibility of being called in an interrupt context, + * it does not wait until the abort bit becomes clear. + * Ensure the busy status is canceled with dma_get_status + * before the next transfer starts. + */ + rpi_pico_dma_channel_abort(dev, ch); + + return 0; +} + +static int dma_rpi_pico_get_status(const struct device *dev, uint32_t ch, struct dma_status *stat) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + struct dma_rpi_pico_data *data = dev->data; + + if (ch >= cfg->channels) { + LOG_ERR("channel must be < %" PRIu32 " (%" PRIu32 ")", cfg->channels, ch); + return -EINVAL; + } + + stat->pending_length = 0; + stat->dir = data->channels[ch].direction; + stat->busy = dma_channel_is_busy(ch); + + return 0; +} + +static bool dma_rpi_pico_api_chan_filter(const struct device *dev, int ch, void *filter_param) +{ + uint32_t filter; + + if (!filter_param) { + LOG_ERR("filter_param must not be NULL"); + return false; + } + + filter = *((uint32_t *)filter_param); + + return (filter & BIT(ch)); +} + +static int dma_rpi_pico_init(const struct device *dev) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + + (void)reset_line_toggle_dt(&cfg->reset); + + cfg->irq_configure(); + + return 0; +} + +static void dma_rpi_pico_isr(const struct device *dev) +{ + const struct dma_rpi_pico_config *cfg = dev->config; + struct dma_rpi_pico_data *data = dev->data; + int err = 0; + + for (uint32_t i = 0; i < cfg->channels; i++) { + if (!dma_irqn_get_channel_status(dma_rpi_pico_channel_irq(dev, i), i)) { + continue; + } + + if (rpi_pico_dma_channel_get_error_flags(dev, i)) { + err = -EIO; + } + + dma_irqn_acknowledge_channel(dma_rpi_pico_channel_irq(dev, i), i); + dma_irqn_set_channel_enabled(dma_rpi_pico_channel_irq(dev, i), i, false); + rpi_pico_dma_channel_clear_error_flags(dev, i); + + if (data->channels[i].callback) { + data->channels[i].callback(dev, data->channels[i].user_data, i, err); + } + } +} + +static const struct dma_driver_api dma_rpi_pico_driver_api = { + .config = dma_rpi_pico_config, + .reload = dma_rpi_pico_reload, + .start = dma_rpi_pico_start, + .stop = dma_rpi_pico_stop, + .get_status = dma_rpi_pico_get_status, + .chan_filter = dma_rpi_pico_api_chan_filter, +}; + +#define IRQ_CONFIGURE(n, inst) \ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(inst, n, irq), DT_INST_IRQ_BY_IDX(inst, n, priority), \ + dma_rpi_pico_isr, DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(DT_INST_IRQ_BY_IDX(inst, n, irq)); + +#define CONFIGURE_ALL_IRQS(inst, n) LISTIFY(n, IRQ_CONFIGURE, (), inst) + +#define RPI_PICO_DMA_INIT(inst) \ + static void dma_rpi_pico##inst##_irq_configure(void) \ + { \ + CONFIGURE_ALL_IRQS(inst, DT_NUM_IRQS(DT_DRV_INST(inst))); \ + } \ + static uint32_t dma_rpi_pico##inst##_irq0_channels[] = \ + DT_INST_PROP_OR(inst, irq0_channels, {0}); \ + static const struct dma_rpi_pico_config dma_rpi_pico##inst##_config = { \ + .reg = DT_INST_REG_ADDR(inst), \ + .channels = DT_INST_PROP(inst, dma_channels), \ + .reset = RESET_DT_SPEC_INST_GET(inst), \ + .irq_configure = dma_rpi_pico##inst##_irq_configure, \ + .irq0_channels = dma_rpi_pico##inst##_irq0_channels, \ + .irq0_channels_size = ARRAY_SIZE(dma_rpi_pico##inst##_irq0_channels), \ + }; \ + static struct dma_rpi_pico_channel \ + dma_rpi_pico##inst##_channels[DT_INST_PROP(inst, dma_channels)]; \ + ATOMIC_DEFINE(dma_rpi_pico_atomic##inst, DT_INST_PROP(inst, dma_channels)); \ + static struct dma_rpi_pico_data dma_rpi_pico##inst##_data = { \ + .ctx = \ + { \ + .magic = DMA_MAGIC, \ + .atomic = dma_rpi_pico_atomic##inst, \ + .dma_channels = DT_INST_PROP(inst, dma_channels), \ + }, \ + .channels = dma_rpi_pico##inst##_channels, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, &dma_rpi_pico_init, NULL, &dma_rpi_pico##inst##_data, \ + &dma_rpi_pico##inst##_config, POST_KERNEL, CONFIG_DMA_INIT_PRIORITY, \ + &dma_rpi_pico_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(RPI_PICO_DMA_INIT) diff --git a/dts/arm/rpi_pico/rp2040.dtsi b/dts/arm/rpi_pico/rp2040.dtsi index 2bd69486ab1919..c22408617ddf49 100644 --- a/dts/arm/rpi_pico/rp2040.dtsi +++ b/dts/arm/rpi_pico/rp2040.dtsi @@ -178,6 +178,19 @@ status = "disabled"; #pwm-cells = <3>; }; + dma: dma@50000000 { + compatible = "raspberrypi,pico-dma"; + reg = <0x50000000 DT_SIZE_K(64)>; + resets = <&reset RPI_PICO_RESETS_RESET_DMA>; + clocks = <&system_clk>; + interrupts = <11 RPI_PICO_DEFAULT_IRQ_PRIORITY>, + <12 RPI_PICO_DEFAULT_IRQ_PRIORITY>; + interrupt-names = "dma0", "dma1"; + dma-channels = <12>; + status = "disabled"; + #dma-cells = <3>; + }; + }; }; diff --git a/dts/bindings/dma/raspberrypi,pico-dma.yaml b/dts/bindings/dma/raspberrypi,pico-dma.yaml new file mode 100644 index 00000000000000..35960fbca7fe9f --- /dev/null +++ b/dts/bindings/dma/raspberrypi,pico-dma.yaml @@ -0,0 +1,42 @@ +# Copyright (c) 2023 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +description: | + Raspberry Pi Pico GPIO + + channel: Select channel for data transmitting + + slot: Select peripheral data request + Use the definitions defined in `zephyr/dt-bindings/dma/rpi_pico_dma.h`. + + channel-config: A 32bit mask specifying the DMA channel configuration + - bit 3: Enable Quiet IRQ + - bit 1: Enable Byte Swap + - bit 0: Enable High Priority + +compatible: "raspberrypi,pico-dma" + +include: [dma-controller.yaml, reset-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + dma-channels: + required: true + + irq0-channels: + type: uint8-array + default: [0, 2, 4, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30] + description: Channels list that uses the irq0 + + "#dma-cells": + const: 3 + +dma-cells: + - channel + - slot + - channel-config diff --git a/include/zephyr/dt-bindings/dma/rpi_pico_dma.h b/include/zephyr/dt-bindings/dma/rpi_pico_dma.h new file mode 100644 index 00000000000000..f181a31beba90b --- /dev/null +++ b/include/zephyr/dt-bindings/dma/rpi_pico_dma.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_RPI_PICO_DMA_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_RPI_PICO_DMA_H_ + +/* + * Use lower 6-bit of inverted DREQ value for `slot` cell. + * Need to be able to work for memory-to-memory transfer + * with zero, which is the default value. + */ +#define RPI_PICO_DMA_SLOT_TO_DREQ(s) (~(s)&0x3F) +#define RPI_PICO_DMA_DREQ_TO_SLOT RPI_PICO_DMA_SLOT_TO_DREQ + +#define RPI_PICO_DMA_SLOT_PIO0_TX0 RPI_PICO_DMA_DREQ_TO_SLOT(0x00) +#define RPI_PICO_DMA_SLOT_PIO0_TX1 RPI_PICO_DMA_DREQ_TO_SLOT(0x01) +#define RPI_PICO_DMA_SLOT_PIO0_TX2 RPI_PICO_DMA_DREQ_TO_SLOT(0x02) +#define RPI_PICO_DMA_SLOT_PIO0_TX3 RPI_PICO_DMA_DREQ_TO_SLOT(0x03) +#define RPI_PICO_DMA_SLOT_PIO0_RX0 RPI_PICO_DMA_DREQ_TO_SLOT(0x04) +#define RPI_PICO_DMA_SLOT_PIO0_RX1 RPI_PICO_DMA_DREQ_TO_SLOT(0x05) +#define RPI_PICO_DMA_SLOT_PIO0_RX2 RPI_PICO_DMA_DREQ_TO_SLOT(0x06) +#define RPI_PICO_DMA_SLOT_PIO0_RX3 RPI_PICO_DMA_DREQ_TO_SLOT(0x07) +#define RPI_PICO_DMA_SLOT_PIO1_TX0 RPI_PICO_DMA_DREQ_TO_SLOT(0x08) +#define RPI_PICO_DMA_SLOT_PIO1_TX1 RPI_PICO_DMA_DREQ_TO_SLOT(0x09) +#define RPI_PICO_DMA_SLOT_PIO1_TX2 RPI_PICO_DMA_DREQ_TO_SLOT(0x0A) +#define RPI_PICO_DMA_SLOT_PIO1_TX3 RPI_PICO_DMA_DREQ_TO_SLOT(0x0B) +#define RPI_PICO_DMA_SLOT_PIO1_RX0 RPI_PICO_DMA_DREQ_TO_SLOT(0x0C) +#define RPI_PICO_DMA_SLOT_PIO1_RX1 RPI_PICO_DMA_DREQ_TO_SLOT(0x0D) +#define RPI_PICO_DMA_SLOT_PIO1_RX2 RPI_PICO_DMA_DREQ_TO_SLOT(0x0E) +#define RPI_PICO_DMA_SLOT_PIO1_RX3 RPI_PICO_DMA_DREQ_TO_SLOT(0x0F) +#define RPI_PICO_DMA_SLOT_SPI0_TX RPI_PICO_DMA_DREQ_TO_SLOT(0x10) +#define RPI_PICO_DMA_SLOT_SPI0_RX RPI_PICO_DMA_DREQ_TO_SLOT(0x11) +#define RPI_PICO_DMA_SLOT_SPI1_TX RPI_PICO_DMA_DREQ_TO_SLOT(0x12) +#define RPI_PICO_DMA_SLOT_SPI1_RX RPI_PICO_DMA_DREQ_TO_SLOT(0x13) +#define RPI_PICO_DMA_SLOT_UART0_TX RPI_PICO_DMA_DREQ_TO_SLOT(0x14) +#define RPI_PICO_DMA_SLOT_UART0_RX RPI_PICO_DMA_DREQ_TO_SLOT(0x15) +#define RPI_PICO_DMA_SLOT_UART1_TX RPI_PICO_DMA_DREQ_TO_SLOT(0x16) +#define RPI_PICO_DMA_SLOT_UART1_RX RPI_PICO_DMA_DREQ_TO_SLOT(0x17) +#define RPI_PICO_DMA_SLOT_PWM_WRAP0 RPI_PICO_DMA_DREQ_TO_SLOT(0x18) +#define RPI_PICO_DMA_SLOT_PWM_WRAP1 RPI_PICO_DMA_DREQ_TO_SLOT(0x19) +#define RPI_PICO_DMA_SLOT_PWM_WRAP2 RPI_PICO_DMA_DREQ_TO_SLOT(0x1A) +#define RPI_PICO_DMA_SLOT_PWM_WRAP3 RPI_PICO_DMA_DREQ_TO_SLOT(0x1B) +#define RPI_PICO_DMA_SLOT_PWM_WRAP4 RPI_PICO_DMA_DREQ_TO_SLOT(0x1C) +#define RPI_PICO_DMA_SLOT_PWM_WRAP5 RPI_PICO_DMA_DREQ_TO_SLOT(0x1D) +#define RPI_PICO_DMA_SLOT_PWM_WRAP6 RPI_PICO_DMA_DREQ_TO_SLOT(0x1E) +#define RPI_PICO_DMA_SLOT_PWM_WRAP7 RPI_PICO_DMA_DREQ_TO_SLOT(0x1F) +#define RPI_PICO_DMA_SLOT_I2C0_TX RPI_PICO_DMA_DREQ_TO_SLOT(0x30) +#define RPI_PICO_DMA_SLOT_I2C0_RX RPI_PICO_DMA_DREQ_TO_SLOT(0x31) +#define RPI_PICO_DMA_SLOT_I2C1_TX RPI_PICO_DMA_DREQ_TO_SLOT(0x32) +#define RPI_PICO_DMA_SLOT_I2C1_RX RPI_PICO_DMA_DREQ_TO_SLOT(0x33) +#define RPI_PICO_DMA_SLOT_ADC RPI_PICO_DMA_DREQ_TO_SLOT(0x34) +#define RPI_PICO_DMA_SLOT_XIP_STREAM RPI_PICO_DMA_DREQ_TO_SLOT(0x35) +#define RPI_PICO_DMA_SLOT_XIP_SSITX RPI_PICO_DMA_DREQ_TO_SLOT(0x36) +#define RPI_PICO_DMA_SLOT_XIP_SSIRX RPI_PICO_DMA_DREQ_TO_SLOT(0x37) +#define RPI_PICO_DMA_SLOT_DMA_TIMER0 RPI_PICO_DMA_DREQ_TO_SLOT(0x3B) +#define RPI_PICO_DMA_SLOT_DMA_TIMER1 RPI_PICO_DMA_DREQ_TO_SLOT(0x3C) +#define RPI_PICO_DMA_SLOT_DMA_TIMER2 RPI_PICO_DMA_DREQ_TO_SLOT(0x3D) +#define RPI_PICO_DMA_SLOT_DMA_TIMER3 RPI_PICO_DMA_DREQ_TO_SLOT(0x3E) +#define RPI_PICO_DMA_SLOT_FORCE RPI_PICO_DMA_DREQ_TO_SLOT(0x3F) + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_RPI_PICO_DMA_H_ */ From a7dbf33fdfa955a8614341821f35bd801f94be33 Mon Sep 17 00:00:00 2001 From: Yonatan Schachter Date: Sun, 5 Mar 2023 23:06:16 +0200 Subject: [PATCH 4/5] drivers: misc: Add driver for RaspberryPi Pico PIO Added a generic driver for RaspberryPi Pico PIO. This driver is an intermediate driver for abstracting the PIO device driver from physical pin configuration. Signed-off-by: TOKITA Hiroshi Signed-off-by: Yonatan Schachter Signed-off-by: Ionut Catalin Pavel --- drivers/misc/CMakeLists.txt | 1 + drivers/misc/Kconfig | 1 + drivers/misc/pio_rpi_pico/CMakeLists.txt | 6 + drivers/misc/pio_rpi_pico/Kconfig | 9 ++ drivers/misc/pio_rpi_pico/pio_rpi_pico.c | 53 +++++++ dts/arm/rpi_pico/rp2040.dtsi | 16 ++ .../misc/raspberrypi,pico-pio-device.yaml | 9 ++ dts/bindings/misc/raspberrypi,pico-pio.yaml | 8 + .../drivers/misc/pio_rpi_pico/pio_rpi_pico.h | 148 ++++++++++++++++++ .../pinctrl/rpi-pico-rp2040-pinctrl.h | 62 ++++++++ modules/hal_rpi_pico/CMakeLists.txt | 10 ++ modules/hal_rpi_pico/Kconfig | 6 + 12 files changed, 329 insertions(+) create mode 100644 drivers/misc/pio_rpi_pico/CMakeLists.txt create mode 100644 drivers/misc/pio_rpi_pico/Kconfig create mode 100644 drivers/misc/pio_rpi_pico/pio_rpi_pico.c create mode 100644 dts/bindings/misc/raspberrypi,pico-pio-device.yaml create mode 100644 dts/bindings/misc/raspberrypi,pico-pio.yaml create mode 100644 include/zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico.h diff --git a/drivers/misc/CMakeLists.txt b/drivers/misc/CMakeLists.txt index cd0be16d423b01..6db1f7c92426e7 100644 --- a/drivers/misc/CMakeLists.txt +++ b/drivers/misc/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory_ifdef(CONFIG_FT800 ft8xx) add_subdirectory_ifdef(CONFIG_GROVE_LCD_RGB grove_lcd_rgb) +add_subdirectory_ifdef(CONFIG_PIO_RPI_PICO pio_rpi_pico) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e7301ee85d6abe..e8137dcc131fcf 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -7,5 +7,6 @@ menu "Miscellaneous Drivers" source "drivers/misc/ft8xx/Kconfig" source "drivers/misc/grove_lcd_rgb/Kconfig" +source "drivers/misc/pio_rpi_pico/Kconfig" endmenu diff --git a/drivers/misc/pio_rpi_pico/CMakeLists.txt b/drivers/misc/pio_rpi_pico/CMakeLists.txt new file mode 100644 index 00000000000000..ba762d907a81cd --- /dev/null +++ b/drivers/misc/pio_rpi_pico/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2023 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(pio_rpi_pico.c) diff --git a/drivers/misc/pio_rpi_pico/Kconfig b/drivers/misc/pio_rpi_pico/Kconfig new file mode 100644 index 00000000000000..ff312cb81ebef8 --- /dev/null +++ b/drivers/misc/pio_rpi_pico/Kconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2023 TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +config PIO_RPI_PICO + bool "RaspberryPi Pico PIO" + default y + depends on DT_HAS_RASPBERRYPI_PICO_PIO_ENABLED + depends on RESET + select PICOSDK_USE_PIO diff --git a/drivers/misc/pio_rpi_pico/pio_rpi_pico.c b/drivers/misc/pio_rpi_pico/pio_rpi_pico.c new file mode 100644 index 00000000000000..03a03e824c20be --- /dev/null +++ b/drivers/misc/pio_rpi_pico/pio_rpi_pico.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Tokita, Hiroshi + * Copyright (c) 2023 Yonatan Schachter + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DT_DRV_COMPAT raspberrypi_pico_pio + +struct pio_rpi_pico_config { + PIO pio; +}; + +int pio_rpi_pico_allocate_sm(const struct device *dev, size_t *sm) +{ + const struct pio_rpi_pico_config *config = dev->config; + int retval; + + retval = pio_claim_unused_sm(config->pio, false); + if (retval < 0) { + return -EBUSY; + } + + *sm = (size_t)retval; + return 0; +} + +PIO pio_rpi_pico_get_pio(const struct device *dev) +{ + const struct pio_rpi_pico_config *config = dev->config; + + return config->pio; +} + +static int pio_rpi_pico_init(const struct device *dev) +{ + return 0; +} + +#define RPI_PICO_PIO_INIT(idx) \ + static const struct pio_rpi_pico_config pio_rpi_pico_config_##idx = { \ + .pio = (PIO)DT_INST_REG_ADDR(idx), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(idx, &pio_rpi_pico_init, NULL, NULL, \ + &pio_rpi_pico_config_##idx, PRE_KERNEL_2, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL); + +DT_INST_FOREACH_STATUS_OKAY(RPI_PICO_PIO_INIT) diff --git a/dts/arm/rpi_pico/rp2040.dtsi b/dts/arm/rpi_pico/rp2040.dtsi index c22408617ddf49..bbc304c53ec84e 100644 --- a/dts/arm/rpi_pico/rp2040.dtsi +++ b/dts/arm/rpi_pico/rp2040.dtsi @@ -191,6 +191,22 @@ #dma-cells = <3>; }; + + pio0: pio@50200000 { + compatible = "raspberrypi,pico-pio"; + reg = <0x50200000 DT_SIZE_K(4)>; + clocks = <&system_clk>; + resets = <&reset RPI_PICO_RESETS_RESET_PIO0>; + status = "disabled"; + }; + + pio1: pio@50300000 { + compatible = "raspberrypi,pico-pio"; + reg = <0x50300000 DT_SIZE_K(4)>; + clocks = <&system_clk>; + resets = <&reset RPI_PICO_RESETS_RESET_PIO1>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/misc/raspberrypi,pico-pio-device.yaml b/dts/bindings/misc/raspberrypi,pico-pio-device.yaml new file mode 100644 index 00000000000000..a75a59107c73d7 --- /dev/null +++ b/dts/bindings/misc/raspberrypi,pico-pio-device.yaml @@ -0,0 +1,9 @@ +# Copyright (c) 2023, TOKITA Hiroshi +# Copyright (c) 2023, Yonatan Schachter +# SPDX-License-Identifier: Apache-2.0 + +description: Raspberry Pi Pico PIO device + +compatible: "raspberrypi,pico-pio-device" + +include: pinctrl-device.yaml diff --git a/dts/bindings/misc/raspberrypi,pico-pio.yaml b/dts/bindings/misc/raspberrypi,pico-pio.yaml new file mode 100644 index 00000000000000..0bdbd8ab658981 --- /dev/null +++ b/dts/bindings/misc/raspberrypi,pico-pio.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2023, TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +description: Raspberry Pi Pico PIO + +compatible: "raspberrypi,pico-pio" + +include: [base.yaml, reset-device.yaml] diff --git a/include/zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico.h b/include/zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico.h new file mode 100644 index 00000000000000..bd24744426f10e --- /dev/null +++ b/include/zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2023 Tokita, Hiroshi + * Copyright (c) 2023 Yonatan Schachter + * Copyright (c) 2023 Ionut Pavel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_MISC_PIO_PICO_RPI_PIO_PICO_RPI_H_ +#define ZEPHYR_DRIVERS_MISC_PIO_PICO_RPI_PIO_PICO_RPI_H_ + +#include + +#include + +/** + * @brief Utility macro to define a PIO program. The program is a list + * of 16 bit instructions, generated by the pioasm tool. + * + * @param name Name of the program. + * @param wrap_target Wrap target as specified by the PIO program. + * @param wrap Wrap source as specified by the PIO program. + * @param ... Comma separated list of PIO instructions. + */ +#define RPI_PICO_PIO_DEFINE_PROGRAM(name, wrap_target, wrap, ...) \ + static const uint32_t name ## _wrap_target = wrap_target; \ + static const uint32_t name ## _wrap = wrap; \ + static const uint16_t name ## _program_instructions[] = { \ + __VA_ARGS__ \ + }; \ + static const struct pio_program name ## _program = { \ + .instructions = name ## _program_instructions, \ + .length = ARRAY_SIZE(name ## _program_instructions), \ + .origin = -1, \ + } + +/** + * @brief Utility macro to get the wrap target of a program. + * + * @param name Name of the program. + */ +#define RPI_PICO_PIO_GET_WRAP_TARGET(name) name ## _wrap_target + +/** + * @brief Utility macro to get the wrap source of a program. + * + * @param name Name of the program. + */ +#define RPI_PICO_PIO_GET_WRAP(name) name ## _wrap + +/** + * @brief Utility macro to get a pointer to a PIO program. + * + * @param name Name of the program. + */ +#define RPI_PICO_PIO_GET_PROGRAM(name) &name ## _program + +/** + * @brief Get a pin number from a pinctrl / group name and index + * + * Example devicetree fragment(s): + * + * @code{.dts} + * pinctrl { + * pio_child_default: pio_child_default { + * tx_gpio { + * pinmux = , ; + * }; + * + * rx_gpio { + * pinmux = ; + * input-enable; + * }; + * }; + * }; + * @endcode + * + * @code{.dts} + * pio { + * status = "okay"; + * c: child { + * pinctrl-0 = <&pio_child_default>; + * pinctrl-names = "default"; + * }; + * }; + * @endcode + * + * Example usage: + * + * @code{.c} + * DT_RPI_PICO_PIO_PIN_BY_NAME(node, default, 0, tx_gpio, 0) // 0 + * DT_RPI_PICO_PIO_PIN_BY_NAME(node, default, 0, tx_gpio, 1) // 2 + * DT_RPI_PICO_PIO_PIN_BY_NAME(node, default, 0, rx_gpio, 0) // 1 + * @endcode + * + * @param node_id node identifier + * @param p_name pinctrl name + * @param p_idx pinctrl index + * @param g_name group name + * @param g_idx group index + * @return pin number + */ +#define DT_RPI_PICO_PIO_PIN_BY_NAME(node_id, p_name, p_idx, g_name, g_idx) \ + RP2_GET_PIN_NUM(DT_PROP_BY_IDX( \ + DT_CHILD(DT_PINCTRL_BY_NAME(node_id, p_name, p_idx), g_name), pinmux, g_idx)) + +/** + * @brief Get a pin number from a pinctrl / group name and index + * + * @param inst instance number + * @param p_name pinctrl name + * @param p_idx pinctrl index + * @param g_name group name + * @param g_idx group index + * @return pin number + * + * @see DT_RPI_PICO_PIO_PIN_BY_NAME + */ +#define DT_INST_RPI_PICO_PIO_PIN_BY_NAME(inst, p_name, p_idx, g_name, g_idx) \ + DT_RPI_PICO_PIO_PIN_BY_NAME(DT_DRV_INST(inst), p_name, p_idx, g_name, g_idx) + +/** + * @brief Get the pin number of a pin by its name. + * + * @param name Name of the pin (e.g. tx, rx, sck). + */ +#define DT_INST_PIO_PIN_BY_NAME(inst, name) \ + DT_PIO_PIN_BY_NAME(DT_DRV_INST(inst), name) + +/** + * Get PIO object + * + * @param dev Pointer to device structure for rpi_pio device instance + * @return PIO object + */ +PIO pio_rpi_pico_get_pio(const struct device *dev); + +/** + * Allocate a state machine. + * + * @param dev Pointer to device structure for rpi_pio device instance + * @param sm Pointer to store allocated state machine + * @retval 0 on success + * @retval -EBUSY if no state machines were available + */ +int pio_rpi_pico_allocate_sm(const struct device *dev, size_t *sm); + +#endif /* ZEPHYR_DRIVERS_MISC_PIO_PICO_RPI_PIO_PICO_RPI_H_ */ diff --git a/include/zephyr/dt-bindings/pinctrl/rpi-pico-rp2040-pinctrl.h b/include/zephyr/dt-bindings/pinctrl/rpi-pico-rp2040-pinctrl.h index 46d7118d121ec6..4249fd24a3a4e2 100644 --- a/include/zephyr/dt-bindings/pinctrl/rpi-pico-rp2040-pinctrl.h +++ b/include/zephyr/dt-bindings/pinctrl/rpi-pico-rp2040-pinctrl.h @@ -157,4 +157,66 @@ #define ADC_CH2_P28 RP2040_PINMUX(28, RP2_PINCTRL_GPIO_FUNC_NULL) #define ADC_CH3_P29 RP2040_PINMUX(29, RP2_PINCTRL_GPIO_FUNC_NULL) +#define PIO0_P0 RP2040_PINMUX(0, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P1 RP2040_PINMUX(1, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P2 RP2040_PINMUX(2, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P3 RP2040_PINMUX(3, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P4 RP2040_PINMUX(4, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P5 RP2040_PINMUX(5, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P6 RP2040_PINMUX(6, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P7 RP2040_PINMUX(7, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P8 RP2040_PINMUX(8, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P9 RP2040_PINMUX(9, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P10 RP2040_PINMUX(10, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P11 RP2040_PINMUX(11, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P12 RP2040_PINMUX(12, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P13 RP2040_PINMUX(13, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P14 RP2040_PINMUX(14, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P15 RP2040_PINMUX(15, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P16 RP2040_PINMUX(16, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P17 RP2040_PINMUX(17, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P18 RP2040_PINMUX(18, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P19 RP2040_PINMUX(19, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P20 RP2040_PINMUX(20, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P21 RP2040_PINMUX(21, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P22 RP2040_PINMUX(22, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P23 RP2040_PINMUX(23, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P24 RP2040_PINMUX(24, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P25 RP2040_PINMUX(25, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P26 RP2040_PINMUX(26, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P27 RP2040_PINMUX(27, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P28 RP2040_PINMUX(28, RP2_PINCTRL_GPIO_FUNC_PIO0) +#define PIO0_P29 RP2040_PINMUX(29, RP2_PINCTRL_GPIO_FUNC_PIO0) + +#define PIO1_P0 RP2040_PINMUX(0, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P1 RP2040_PINMUX(1, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P2 RP2040_PINMUX(2, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P3 RP2040_PINMUX(3, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P4 RP2040_PINMUX(4, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P5 RP2040_PINMUX(5, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P6 RP2040_PINMUX(6, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P7 RP2040_PINMUX(7, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P8 RP2040_PINMUX(8, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P9 RP2040_PINMUX(9, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P10 RP2040_PINMUX(10, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P11 RP2040_PINMUX(11, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P12 RP2040_PINMUX(12, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P13 RP2040_PINMUX(13, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P14 RP2040_PINMUX(14, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P15 RP2040_PINMUX(15, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P16 RP2040_PINMUX(16, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P17 RP2040_PINMUX(17, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P18 RP2040_PINMUX(18, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P19 RP2040_PINMUX(19, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P20 RP2040_PINMUX(20, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P21 RP2040_PINMUX(21, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P22 RP2040_PINMUX(22, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P23 RP2040_PINMUX(23, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P24 RP2040_PINMUX(24, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P25 RP2040_PINMUX(25, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P26 RP2040_PINMUX(26, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P27 RP2040_PINMUX(27, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P28 RP2040_PINMUX(28, RP2_PINCTRL_GPIO_FUNC_PIO1) +#define PIO1_P29 RP2040_PINMUX(29, RP2_PINCTRL_GPIO_FUNC_PIO1) + #endif /* __RP2040_PINCTRL_H__ */ diff --git a/modules/hal_rpi_pico/CMakeLists.txt b/modules/hal_rpi_pico/CMakeLists.txt index 6501c9e8ca9ff0..7710b1da240efc 100644 --- a/modules/hal_rpi_pico/CMakeLists.txt +++ b/modules/hal_rpi_pico/CMakeLists.txt @@ -124,4 +124,14 @@ if(CONFIG_HAS_RPI_PICO) COMPILE_FLAGS $ ) + zephyr_library_sources_ifdef(CONFIG_PICOSDK_USE_PIO + ${rp2_common_dir}/hardware_pio/pio.c) + zephyr_include_directories_ifdef(CONFIG_PICOSDK_USE_PIO + ${rp2_common_dir}/hardware_pio/include) + + zephyr_library_sources_ifdef(CONFIG_PICOSDK_USE_CLAIM + ${rp2_common_dir}/hardware_claim/claim.c) + zephyr_include_directories_ifdef(CONFIG_PICOSDK_USE_CLAIM + ${rp2_common_dir}/hardware_claim/include) + endif() diff --git a/modules/hal_rpi_pico/Kconfig b/modules/hal_rpi_pico/Kconfig index ecb4c4993c6afe..4baf11dd406d2f 100644 --- a/modules/hal_rpi_pico/Kconfig +++ b/modules/hal_rpi_pico/Kconfig @@ -34,6 +34,12 @@ config PICOSDK_USE_DMA help Use the DMA driver from pico-sdk +config PICOSDK_USE_PIO + bool + select PICOSDK_USE_CLAIM + help + Use the PIO driver from pico-sdk + config PICOSDK_USE_CLAIM bool help From a4c8b97db0f3fb5a807ed4d10cd4085070e5ebca Mon Sep 17 00:00:00 2001 From: Yonatan Schachter Date: Sun, 5 Mar 2023 23:14:13 +0200 Subject: [PATCH 5/5] drivers: serial: Added rpi_pico driver over PIO Implements a UART driver using PIO. Both PIOs are supported. Only polling API is supported. Only 8N1 mode is supported. Signed-off-by: Yonatan Schachter --- boards/arm/rpi_pico/doc/index.rst | 3 + drivers/serial/CMakeLists.txt | 1 + drivers/serial/Kconfig.rpi_pico | 9 + drivers/serial/uart_rpi_pico_pio.c | 200 ++++++++++++++++++ .../serial/raspberrypi,pico-uart-pio.yaml | 5 + 5 files changed, 218 insertions(+) create mode 100644 drivers/serial/uart_rpi_pico_pio.c create mode 100644 dts/bindings/serial/raspberrypi,pico-uart-pio.yaml diff --git a/boards/arm/rpi_pico/doc/index.rst b/boards/arm/rpi_pico/doc/index.rst index 832ecc1a2a56cd..466a6ad8c59ca2 100644 --- a/boards/arm/rpi_pico/doc/index.rst +++ b/boards/arm/rpi_pico/doc/index.rst @@ -78,6 +78,9 @@ hardware features: * - PWM - :kconfig:option:`CONFIG_PWM` - :dtcompatible:`raspberrypi,pico-pwm` + * - UART (PIO) + - :kconfig:option:`CONFIG_SERIAL` + - :dtcompatible:`raspberrypi,pico-uart-pio` Pin Mapping =========== diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 8fac82f7a896d5..ae6d8cb8688064 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -34,6 +34,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_PSOC6 uart_psoc6.c) zephyr_library_sources_ifdef(CONFIG_UART_PL011 uart_pl011.c) zephyr_library_sources_ifdef(CONFIG_UART_RV32M1_LPUART uart_rv32m1_lpuart.c) zephyr_library_sources_ifdef(CONFIG_UART_RPI_PICO uart_rpi_pico.c) +zephyr_library_sources_ifdef(CONFIG_UART_RPI_PICO_PIO uart_rpi_pico_pio.c) zephyr_library_sources_ifdef(CONFIG_UART_LITEUART uart_liteuart.c) zephyr_library_sources_ifdef(CONFIG_UART_RTT_DRIVER uart_rtt.c) zephyr_library_sources_ifdef(CONFIG_UART_XLNX_PS uart_xlnx_ps.c) diff --git a/drivers/serial/Kconfig.rpi_pico b/drivers/serial/Kconfig.rpi_pico index 913ec873863c73..0465570f704d08 100644 --- a/drivers/serial/Kconfig.rpi_pico +++ b/drivers/serial/Kconfig.rpi_pico @@ -9,3 +9,12 @@ config UART_RPI_PICO select PICOSDK_USE_UART select SERIAL_SUPPORT_INTERRUPT depends on RESET + +config UART_RPI_PICO_PIO + bool "Raspberry Pi PIO UART driver" + default y + depends on DT_HAS_RASPBERRYPI_PICO_UART_PIO_ENABLED + select SERIAL_HAS_DRIVER + select PICOSDK_USE_PIO + select PICOSDK_USE_CLAIM + depends on RESET diff --git a/drivers/serial/uart_rpi_pico_pio.c b/drivers/serial/uart_rpi_pico_pio.c new file mode 100644 index 00000000000000..8efb725b44ab39 --- /dev/null +++ b/drivers/serial/uart_rpi_pico_pio.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2022, Yonatan Schachter + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include +#include + +#define DT_DRV_COMPAT raspberrypi_pico_uart_pio + +#define CYCLES_PER_BIT 8 +#define SIDESET_BIT_COUNT 2 + +struct pio_uart_config { + const struct device *piodev; + const struct pinctrl_dev_config *pcfg; + const uint32_t tx_pin; + const uint32_t rx_pin; + uint32_t baudrate; +}; + +struct pio_uart_data { + size_t tx_sm; + size_t rx_sm; +}; + +RPI_PICO_PIO_DEFINE_PROGRAM(uart_tx, 0, 3, + /* .wrap_target */ + 0x9fa0, /* 0: pull block side 1 [7] */ + 0xf727, /* 1: set x, 7 side 0 [7] */ + 0x6001, /* 2: out pins, 1 */ + 0x0642, /* 3: jmp x--, 2 [6] */ + /* .wrap */ +); + +RPI_PICO_PIO_DEFINE_PROGRAM(uart_rx, 0, 8, + /* .wrap_target */ + 0x2020, /* 0: wait 0 pin, 0 */ + 0xea27, /* 1: set x, 7 [10] */ + 0x4001, /* 2: in pins, 1 */ + 0x0642, /* 3: jmp x--, 2 [6] */ + 0x00c8, /* 4: jmp pin, 8 */ + 0xc014, /* 5: irq nowait 4 rel */ + 0x20a0, /* 6: wait 1 pin, 0 */ + 0x0000, /* 7: jmp 0 */ + 0x8020, /* 8: push block */ + /* .wrap */ +); + +static int pio_uart_tx_init(PIO pio, uint32_t sm, uint32_t tx_pin, float div) +{ + uint32_t offset; + pio_sm_config sm_config; + + if (!pio_can_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_tx))) { + return -EBUSY; + } + + offset = pio_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_tx)); + sm_config = pio_get_default_sm_config(); + + sm_config_set_sideset(&sm_config, SIDESET_BIT_COUNT, true, false); + sm_config_set_out_shift(&sm_config, true, false, 0); + sm_config_set_out_pins(&sm_config, tx_pin, 1); + sm_config_set_sideset_pins(&sm_config, tx_pin); + sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX); + sm_config_set_clkdiv(&sm_config, div); + sm_config_set_wrap(&sm_config, + offset + RPI_PICO_PIO_GET_WRAP_TARGET(uart_tx), + offset + RPI_PICO_PIO_GET_WRAP(uart_tx)); + + pio_sm_set_pins_with_mask(pio, sm, BIT(tx_pin), BIT(tx_pin)); + pio_sm_set_pindirs_with_mask(pio, sm, BIT(tx_pin), BIT(tx_pin)); + pio_sm_init(pio, sm, offset, &sm_config); + pio_sm_set_enabled(pio, sm, true); + + return 0; +} + +static int pio_uart_rx_init(PIO pio, uint32_t sm, uint32_t rx_pin, float div) +{ + pio_sm_config sm_config; + uint32_t offset; + + if (!pio_can_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_rx))) { + return -EBUSY; + } + + offset = pio_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_rx)); + sm_config = pio_get_default_sm_config(); + + pio_sm_set_consecutive_pindirs(pio, sm, rx_pin, 1, false); + sm_config_set_in_pins(&sm_config, rx_pin); + sm_config_set_jmp_pin(&sm_config, rx_pin); + sm_config_set_in_shift(&sm_config, true, false, 0); + sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_RX); + sm_config_set_clkdiv(&sm_config, div); + sm_config_set_wrap(&sm_config, + offset + RPI_PICO_PIO_GET_WRAP_TARGET(uart_rx), + offset + RPI_PICO_PIO_GET_WRAP(uart_rx)); + + pio_sm_init(pio, sm, offset, &sm_config); + pio_sm_set_enabled(pio, sm, true); + + return 0; +} + +static int pio_uart_poll_in(const struct device *dev, unsigned char *c) +{ + const struct pio_uart_config *config = dev->config; + PIO pio = pio_rpi_pico_get_pio(config->piodev); + struct pio_uart_data *data = dev->data; + io_rw_8 *uart_rx_fifo_msb; + + /* + * The rx FIFO is 4 bytes wide, add 3 to get the most significant + * byte. + */ + uart_rx_fifo_msb = (io_rw_8 *)&pio->rxf[data->rx_sm] + 3; + if (pio_sm_is_rx_fifo_empty(pio, data->rx_sm)) { + return -1; + } + + /* Accessing the FIFO pops the read word from it */ + *c = (char)*uart_rx_fifo_msb; + return 0; +} + +static void pio_uart_poll_out(const struct device *dev, unsigned char c) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + + pio_sm_put_blocking(pio_rpi_pico_get_pio(config->piodev), data->tx_sm, (uint32_t)c); +} + +static int pio_uart_init(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + float sm_clock_div; + size_t tx_sm; + size_t rx_sm; + int retval; + PIO pio; + + pio = pio_rpi_pico_get_pio(config->piodev); + sm_clock_div = (float)clock_get_hz(clk_sys) / (CYCLES_PER_BIT * config->baudrate); + + retval = pio_rpi_pico_allocate_sm(config->piodev, &tx_sm); + retval |= pio_rpi_pico_allocate_sm(config->piodev, &rx_sm); + + if (retval < 0) { + return retval; + } + + data->tx_sm = tx_sm; + data->rx_sm = rx_sm; + + retval = pio_uart_tx_init(pio, tx_sm, config->tx_pin, sm_clock_div); + if (retval < 0) { + return retval; + } + + retval = pio_uart_rx_init(pio, rx_sm, config->rx_pin, sm_clock_div); + if (retval < 0) { + return retval; + } + + return pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); +} + +static const struct uart_driver_api pio_uart_driver_api = { + .poll_in = pio_uart_poll_in, + .poll_out = pio_uart_poll_out, +}; + +#define PIO_UART_INIT(idx) \ + PINCTRL_DT_INST_DEFINE(idx); \ + static const struct pio_uart_config pio_uart##idx##_config = { \ + .piodev = DEVICE_DT_GET(DT_INST_PARENT(idx)), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \ + .tx_pin = DT_INST_RPI_PICO_PIO_PIN_BY_NAME(idx, default, 0, tx_pins, 0), \ + .rx_pin = DT_INST_RPI_PICO_PIO_PIN_BY_NAME(idx, default, 0, rx_pins, 0), \ + .baudrate = DT_INST_PROP(idx, current_speed), \ + }; \ + static struct pio_uart_data pio_uart##idx##_data; \ + \ + DEVICE_DT_INST_DEFINE(idx, &pio_uart_init, NULL, &pio_uart##idx##_data, \ + &pio_uart##idx##_config, POST_KERNEL, \ + CONFIG_SERIAL_INIT_PRIORITY, \ + &pio_uart_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(PIO_UART_INIT) diff --git a/dts/bindings/serial/raspberrypi,pico-uart-pio.yaml b/dts/bindings/serial/raspberrypi,pico-uart-pio.yaml new file mode 100644 index 00000000000000..bedbf4f5901245 --- /dev/null +++ b/dts/bindings/serial/raspberrypi,pico-uart-pio.yaml @@ -0,0 +1,5 @@ +description: Raspberry Pi Pico PIO UART + +compatible: "raspberrypi,pico-uart-pio" + +include: [uart-controller.yaml, "raspberrypi,pico-pio-device.yaml"]