From c84f785041536ce012b9046754abb7ce0aba3fc9 Mon Sep 17 00:00:00 2001 From: Ionut Catalin Pavel Date: Sun, 9 Apr 2023 14:07:23 +0300 Subject: [PATCH 1/3] drivers: misc: Added raspberrypi pico PIO support Follows a 'bus' / 'on-bus' approach. The PIO device grants access to program instructions, state machines and shared interrupt lines. Each child device will use these to implement a specific device function. Pin numbers are derrived from the pinctrl properties, but the specifics are left to the device implementing the function. Implementation does not involve any external tool (such as pioasm). The HW usage is up to the child device, (some helper functions are provided). This makes more sense than using the sdk provided by raspberrypi. In reality, the PIO (aside from program design), is not that complicated at the register level. This flexibility allows for different design approaches (such as runtime program morphing and patching). Signed-off-by: TOKITA Hiroshi Signed-off-by: Yonatan Schachter Signed-off-by: Ionut Catalin Pavel --- boards/arm/rpi_pico/doc/index.rst | 2 +- drivers/misc/pio_rpi_pico/CMakeLists.txt | 1 + drivers/misc/pio_rpi_pico/Kconfig | 19 +- drivers/misc/pio_rpi_pico/pio_rpi_pico.c | 221 +++++++++++++++-- dts/arm/rpi_pico/rp2040.dtsi | 6 + .../misc/raspberrypi,pico-pio-device.yaml | 12 +- dts/bindings/misc/raspberrypi,pico-pio.yaml | 13 + .../drivers/misc/pio_rpi_pico/pio_rpi_pico.h | 193 ++++++++++----- .../misc/pio_rpi_pico/pio_rpi_pico_util.h | 232 ++++++++++++++++++ modules/hal_rpi_pico/CMakeLists.txt | 10 - modules/hal_rpi_pico/Kconfig | 6 - 11 files changed, 613 insertions(+), 102 deletions(-) create mode 100644 include/zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico_util.h diff --git a/boards/arm/rpi_pico/doc/index.rst b/boards/arm/rpi_pico/doc/index.rst index 69cb6acd2ce9e..d242196e3f4c0 100644 --- a/boards/arm/rpi_pico/doc/index.rst +++ b/boards/arm/rpi_pico/doc/index.rst @@ -136,7 +136,7 @@ co-processors that are designed for I/O operations. The PIOs run a custom instruction set, generated from a custom assembly language. PIO programs are assembled using `pioasm`, a tool provided by Raspberry Pi. -Zephyr does not (currently) assemble PIO programs. Rather, they should be +Zephyr does not assemble PIO programs. Rather, they should be manually assembled and embedded in source code. An example of how this is done can be found at `drivers/serial/uart_rpi_pico_pio.c`. diff --git a/drivers/misc/pio_rpi_pico/CMakeLists.txt b/drivers/misc/pio_rpi_pico/CMakeLists.txt index ba762d907a81c..af4e8edc7b1a5 100644 --- a/drivers/misc/pio_rpi_pico/CMakeLists.txt +++ b/drivers/misc/pio_rpi_pico/CMakeLists.txt @@ -1,3 +1,4 @@ +# Copyright (c) 2023 Ionut Pavel # Copyright (c) 2023 TOKITA Hiroshi # SPDX-License-Identifier: Apache-2.0 diff --git a/drivers/misc/pio_rpi_pico/Kconfig b/drivers/misc/pio_rpi_pico/Kconfig index ff312cb81ebef..efe19daad34a7 100644 --- a/drivers/misc/pio_rpi_pico/Kconfig +++ b/drivers/misc/pio_rpi_pico/Kconfig @@ -1,3 +1,4 @@ +# Copyright (c) 2023 Ionut Pavel # Copyright (c) 2023 TOKITA Hiroshi # SPDX-License-Identifier: Apache-2.0 @@ -5,5 +6,19 @@ 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 + +if PIO_RPI_PICO + +module = PIO_RPI_PICO +module-str = pio_rpi_pico +source "subsys/logging/Kconfig.template.log_config" + +config PIO_RPI_PICO_INSTR_COUNT + int "Number of Program Instructions for each PIO" + default 32 if SOC_RP2040 + +config PIO_RPI_PICO_SM_COUNT + int "Number of State Machine instances for each PIO" + default 4 if SOC_RP2040 + +endif diff --git a/drivers/misc/pio_rpi_pico/pio_rpi_pico.c b/drivers/misc/pio_rpi_pico/pio_rpi_pico.c index 03a03e824c20b..cdae9604dbd52 100644 --- a/drivers/misc/pio_rpi_pico/pio_rpi_pico.c +++ b/drivers/misc/pio_rpi_pico/pio_rpi_pico.c @@ -1,53 +1,230 @@ -/* +/** + * Copyright (c) 2023, Ionut Pavel * Copyright (c) 2023 Tokita, Hiroshi * Copyright (c) 2023 Yonatan Schachter * * SPDX-License-Identifier: Apache-2.0 */ +#include +#include #include +#include #include -#include -#define DT_DRV_COMPAT raspberrypi_pico_pio +#include +LOG_MODULE_REGISTER(pio_rpi_pico, CONFIG_PIO_RPI_PICO_LOG_LEVEL); + +#define DT_DRV_COMPAT raspberrypi_pico_pio + +typedef void (*pio_rpi_pico_irq_config_func_t)(void); + +struct pio_rpi_pico_irq_config { + pio_rpi_pico_irq_config_func_t irq_config; + uint irq_map; +}; struct pio_rpi_pico_config { - PIO pio; + const struct pio_rpi_pico_irq_config *irq_configs; + sys_slist_t *irq_lists; + uint8_t irq_cnt; }; -int pio_rpi_pico_allocate_sm(const struct device *dev, size_t *sm) +struct pio_rpi_pico_data { + /** + * The maximum shared instruction scheme is SM count dependent. + */ + const char *shared_key[CONFIG_PIO_RPI_PICO_SM_COUNT / 2]; + uint8_t shared_instr[CONFIG_PIO_RPI_PICO_SM_COUNT / 2]; + + uint8_t alloc_sm; + uint8_t alloc_instr; +}; + +int pio_rpi_pico_alloc_sm(const struct device *dev, size_t count, uint8_t *sm) { - const struct pio_rpi_pico_config *config = dev->config; - int retval; + struct pio_rpi_pico_data *data = dev->data; + size_t available; - retval = pio_claim_unused_sm(config->pio, false); - if (retval < 0) { - return -EBUSY; + __ASSERT_NO_MSG(sm); + __ASSERT_NO_MSG(count); + + available = CONFIG_PIO_RPI_PICO_SM_COUNT - data->alloc_sm; + + if (available < count) { + return -EIO; } - *sm = (size_t)retval; + *sm = data->alloc_sm; + data->alloc_sm += (uint8_t)count; + return 0; } -PIO pio_rpi_pico_get_pio(const struct device *dev) +int pio_rpi_pico_alloc_instr(const struct device *dev, size_t count, uint8_t *instr) +{ + struct pio_rpi_pico_data *data = dev->data; + size_t available; + + __ASSERT_NO_MSG(instr); + __ASSERT_NO_MSG(count); + + available = CONFIG_PIO_RPI_PICO_INSTR_COUNT - data->alloc_instr; + + if (available < count) { + return -ENOMEM; + } + + *instr = data->alloc_instr; + data->alloc_instr += (uint8_t)count; + + return 0; +} + +int pio_rpi_pico_alloc_shared_instr(const struct device *dev, const char *key, + size_t count, uint8_t *instr) +{ + struct pio_rpi_pico_data *data = dev->data; + uint8_t i; + int rc; + + __ASSERT_NO_MSG(dev); + __ASSERT_NO_MSG(key); + __ASSERT_NO_MSG(instr); + __ASSERT_NO_MSG(count); + + for (i = 0u; i < (CONFIG_PIO_RPI_PICO_SM_COUNT / 2); i++) { + + if (!data->shared_key[i]) { + rc = pio_rpi_pico_alloc_instr(dev, count, instr); + if (rc < 0) { + return rc; + } + + data->shared_instr[i] = *instr; + data->shared_key[i] = key; + return 0; + + } else if (strcmp(data->shared_key[i], key) == 0) { + *instr = data->shared_instr[i]; + return -EALREADY; + } + } + + return -ENOMEM; +} + +void pio_rpi_pico_irq_register(const struct device *dev, struct pio_rpi_pico_irq_cfg *cfg) { const struct pio_rpi_pico_config *config = dev->config; - return config->pio; + __ASSERT_NO_MSG(dev); + __ASSERT_NO_MSG(cfg); + __ASSERT_NO_MSG(cfg->irq_idx < config->irq_cnt); + + sys_slist_append(&config->irq_lists[cfg->irq_idx], (sys_snode_t *)cfg); +} + +void pio_rpi_pico_irq_enable(const struct device *dev, struct pio_rpi_pico_irq_cfg *cfg) +{ + const struct pio_rpi_pico_config *config = dev->config; + + __ASSERT_NO_MSG(dev); + __ASSERT_NO_MSG(cfg); + __ASSERT_NO_MSG(cfg->irq_idx < config->irq_cnt); + + cfg->enabled = true; + + /* Just enable the line. Doesn't really matter if already enabled */ + irq_enable(config->irq_configs[cfg->irq_idx].irq_map); +} + +void pio_rpi_pico_irq_disable(const struct device *dev, struct pio_rpi_pico_irq_cfg *cfg) +{ + const struct pio_rpi_pico_config *config = dev->config; + struct pio_rpi_pico_irq_cfg *irq_cfg; + sys_snode_t *pnode; + + __ASSERT_NO_MSG(dev); + __ASSERT_NO_MSG(cfg); + __ASSERT_NO_MSG(cfg->irq_idx < config->irq_cnt); + + cfg->enabled = false; + + /* Return if not last one */ + SYS_SLIST_FOR_EACH_NODE(&config->irq_lists[cfg->irq_idx], pnode) { + irq_cfg = CONTAINER_OF(pnode, struct pio_rpi_pico_irq_cfg, node); + if ((irq_cfg != cfg) && irq_cfg->enabled) { + return; + } + } + + irq_disable(config->irq_configs[cfg->irq_idx].irq_map); } static int pio_rpi_pico_init(const struct device *dev) { + const struct pio_rpi_pico_config *config = dev->config; + uint8_t i; + + for (i = 0u; i < config->irq_cnt; i++) { + sys_slist_init(&config->irq_lists[i]); + config->irq_configs[i].irq_config(); + } + 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); +static void pio_rpi_pico_irq(sys_slist_t *irq_list) +{ + struct pio_rpi_pico_irq_cfg *irq_cfg; + sys_snode_t *pnode; + + SYS_SLIST_FOR_EACH_NODE(irq_list, pnode) { + irq_cfg = CONTAINER_OF(pnode, struct pio_rpi_pico_irq_cfg, node); + if (irq_cfg->enabled) { + irq_cfg->irq_func(irq_cfg->irq_param); + } + } +} + +#define PIO_RPI_PICO_IRQ_CONFIG_FUNC(idx, inst) \ +static void pio_rpi_pico_irq_config##inst##_##idx(void) \ +{ \ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(inst, idx, irq), \ + DT_INST_IRQ_BY_IDX(inst, idx, priority), \ + pio_rpi_pico_irq, \ + &pio_rpi_pico_irq_data##inst[idx], (0)); \ +} + +#define PIO_RPI_PICO_IRQ_CONFIG_DATA(idx, inst) \ + { \ + .irq_config = pio_rpi_pico_irq_config##inst##_##idx, \ + .irq_map = DT_INST_IRQ_BY_IDX(inst, idx, irq), \ + } + +#define PIO_RPI_PICO_INIT(inst) \ + static sys_slist_t pio_rpi_pico_irq_data##inst[DT_NUM_IRQS(DT_DRV_INST(inst))]; \ + LISTIFY(DT_NUM_IRQS(DT_DRV_INST(inst)), PIO_RPI_PICO_IRQ_CONFIG_FUNC, (), inst) \ + \ + static struct pio_rpi_pico_data pio_rpi_pico_data##inst; \ + \ + static const struct pio_rpi_pico_irq_config \ + pio_rpi_pico_irq_config##inst[DT_NUM_IRQS(DT_DRV_INST(inst))] = \ + {LISTIFY(DT_NUM_IRQS(DT_DRV_INST(inst)), PIO_RPI_PICO_IRQ_CONFIG_DATA, (,), inst)}; \ + \ + static const struct pio_rpi_pico_config pio_rpi_pico_config##inst = { \ + .irq_configs = pio_rpi_pico_irq_config##inst, \ + .irq_lists = pio_rpi_pico_irq_data##inst, \ + .irq_cnt = DT_NUM_IRQS(DT_DRV_INST(inst)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, &pio_rpi_pico_init, \ + NULL, \ + &pio_rpi_pico_data##inst, \ + &pio_rpi_pico_config##inst, \ + PRE_KERNEL_1, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + NULL); -DT_INST_FOREACH_STATUS_OKAY(RPI_PICO_PIO_INIT) +DT_INST_FOREACH_STATUS_OKAY(PIO_RPI_PICO_INIT); diff --git a/dts/arm/rpi_pico/rp2040.dtsi b/dts/arm/rpi_pico/rp2040.dtsi index 24c0a3e7bae01..644106d1f44f6 100644 --- a/dts/arm/rpi_pico/rp2040.dtsi +++ b/dts/arm/rpi_pico/rp2040.dtsi @@ -213,6 +213,9 @@ reg = <0x50200000 DT_SIZE_K(4)>; clocks = <&system_clk>; resets = <&reset RPI_PICO_RESETS_RESET_PIO0>; + interrupts = <7 RPI_PICO_DEFAULT_IRQ_PRIORITY>, \ + <8 RPI_PICO_DEFAULT_IRQ_PRIORITY>; + interrupt-names = "pio00", "pio01"; status = "disabled"; }; @@ -221,6 +224,9 @@ reg = <0x50300000 DT_SIZE_K(4)>; clocks = <&system_clk>; resets = <&reset RPI_PICO_RESETS_RESET_PIO1>; + interrupts = <9 RPI_PICO_DEFAULT_IRQ_PRIORITY>, \ + <10 RPI_PICO_DEFAULT_IRQ_PRIORITY>; + interrupt-names = "pio10", "pio11"; status = "disabled"; }; }; diff --git a/dts/bindings/misc/raspberrypi,pico-pio-device.yaml b/dts/bindings/misc/raspberrypi,pico-pio-device.yaml index a75a59107c73d..8cc3e1300dfef 100644 --- a/dts/bindings/misc/raspberrypi,pico-pio-device.yaml +++ b/dts/bindings/misc/raspberrypi,pico-pio-device.yaml @@ -1,9 +1,17 @@ +# Copyright (c) 2023 Ionut Pavel # 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" +on-bus: rpi-pico-pio -include: pinctrl-device.yaml +properties: + pio,interrupts: + type: array + description: Shared PIO interrupts for device + + pio,interrupt-names: + type: string-array + description: Name of each shared PIO interrupt diff --git a/dts/bindings/misc/raspberrypi,pico-pio.yaml b/dts/bindings/misc/raspberrypi,pico-pio.yaml index 0bdbd8ab65898..73f3000cd5301 100644 --- a/dts/bindings/misc/raspberrypi,pico-pio.yaml +++ b/dts/bindings/misc/raspberrypi,pico-pio.yaml @@ -1,3 +1,4 @@ +# Copyright (c) 2023 Ionut Pavel # Copyright (c) 2023, TOKITA Hiroshi # SPDX-License-Identifier: Apache-2.0 @@ -6,3 +7,15 @@ description: Raspberry Pi Pico PIO compatible: "raspberrypi,pico-pio" include: [base.yaml, reset-device.yaml] + +bus: pio-rpi-pico + +properties: + reg: + required: true + + interrupts: + required: true + + clocks: + required: true 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 index 9c6cefb81ac61..4fb039455c8f5 100644 --- a/include/zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico.h +++ b/include/zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico.h @@ -1,59 +1,55 @@ /* + * Copyright (c) 2023 Ionut Pavel * 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_ +#ifndef ZEPHYR_DRIVERS_MISC_PIO_RPI_PICO_H_ +#define ZEPHYR_DRIVERS_MISC_PIO_RPI_PICO_H_ +#include +#include #include - -#include +#include /** - * @brief Utility macro to define a PIO program. The program is a list - * of 16 bit instructions, generated by the pioasm tool. + * @brief Get the clock frequency of the associated PIO instance * - * @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. + * Example devicetree fragment: * - * @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. + * @code{.dts} + * pio { + * status = "okay"; + * clocks = <&system_clk>; // 125000000 + * c: child {}; + * }; + * @endcode * - * @param name Name of the program. + * Example usage: + * + * @code{.c} + * // child.c + * DT_PIO_RPI_PICO_CLOCK_FREQ_HZ(node, 0) // 125000000 + * @endcode + * + * @param node_id node identifier + * @return the parent's clock frequency */ -#define RPI_PICO_PIO_GET_WRAP(name) name ## _wrap +#define DT_PIO_RPI_PICO_CLOCK_FREQ_HZ(node_id) \ + DT_PROP(DT_CLOCKS_CTLR(DT_PARENT(node_id)), clock_frequency) /** - * @brief Utility macro to get a pointer to a PIO program. + * @brief Get the clock frequency of the associated PIO instance + * + * @param inst instance number + * @return the parent's clock frequency * - * @param name Name of the program. + * @see DT_PIO_RPI_PICO_CLOCK_FREQ_HZ */ -#define RPI_PICO_PIO_GET_PROGRAM(name) &name ## _program +#define DT_INST_PIO_RPI_PICO_CLOCK_FREQ_HZ(inst) \ + DT_PIO_RPI_PICO_CLOCK_FREQ_HZ(DT_DRV_INST(inst)) /** * @brief Get a pin number from a pinctrl / group name and index @@ -88,9 +84,9 @@ * 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 + * DT_PIO_RPI_PICO_PIN_BY_NAME(node, default, 0, tx_gpio, 0) // 0 + * DT_PIO_RPI_PICO_PIN_BY_NAME(node, default, 0, tx_gpio, 1) // 2 + * DT_PIO_RPI_PICO_PIN_BY_NAME(node, default, 0, rx_gpio, 0) // 1 * @endcode * * @param node_id node identifier @@ -100,7 +96,7 @@ * @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) \ +#define DT_PIO_RPI_PICO_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)) @@ -114,36 +110,115 @@ * @param g_idx group index * @return pin number * - * @see DT_RPI_PICO_PIO_PIN_BY_NAME + * @see DT_PIO_RPI_PICO_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) +#define DT_INST_PIO_RPI_PICO_PIN_BY_NAME(inst, p_name, p_idx, g_name, g_idx) \ + DT_PIO_RPI_PICO_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. + * @brief Get assigned PIO base address + * + * @param node_id node identifier + * @return base address + */ +#define DT_PIO_RPI_PICO_REG_ADDR(node_id) \ + DT_REG_ADDR(DT_PARENT(node_id)) + +/** + * @brief Get assigned PIO base address * * @param inst instance number - * @param name name of the pin (e.g. tx, rx, sck). + * @return base address + * + * @see DT_PIO_RPI_PICO_REG_ADDR */ -#define DT_INST_PIO_PIN_BY_NAME(inst, name) \ - DT_PIO_PIN_BY_NAME(DT_DRV_INST(inst), name) +#define DT_INST_PIO_RPI_PICO_REG_ADDR(inst) \ + DT_PIO_RPI_PICO_REG_ADDR(DT_DRV_INST(inst)) /** - * Get PIO object + * @brief PIO shared IRQ handler type * - * @param dev Pointer to device structure for rpi_pio device instance - * @return PIO object + * @param dev Pointer to registered device */ -PIO pio_rpi_pico_get_pio(const struct device *dev); +typedef void (*pio_rpi_pico_irq_t)(const struct device *dev); /** - * Allocate a state machine. + * @brief PIO shared IRQ configuration data + */ +struct pio_rpi_pico_irq_cfg { + /* Device node */ + sys_snode_t node; + + /* IRQ callback */ + pio_rpi_pico_irq_t irq_func; + + /* Callback parameter */ + const void *irq_param; + + /* Shared IRQ index */ + uint8_t irq_idx; + + /* IRQ status */ + bool enabled; +}; + +/** + * @brief Register a shared IRQ + * + * @param dev Pointer to the parent device + * @param cfg Pointer to the IRQ configuration + */ +void pio_rpi_pico_irq_register(const struct device *dev, struct pio_rpi_pico_irq_cfg *cfg); + +/** + * @brief Enable a shared IRQ + * + * @param dev Pointer to the parent device + * @param cfg Pointer to the IRQ configuration + */ +void pio_rpi_pico_irq_enable(const struct device *dev, struct pio_rpi_pico_irq_cfg *cfg); + +/** + * @brief Disable a shared IRQ + * + * @param dev Pointer to the parent device + * @param cfg Pointer to the IRQ configuration + */ +void pio_rpi_pico_irq_disable(const struct device *dev, struct pio_rpi_pico_irq_cfg *cfg); + +/** + * @brief Dynamic state machine allocator + * + * @param dev Pointer to the PIO device structure + * @param count State machines to allocate + * @param sm Output pointer for the first allocated state machine + * @return -EIO on error + */ +int pio_rpi_pico_alloc_sm(const struct device *dev, size_t count, uint8_t *sm); + +/** + * @brief Dynamic instruction allocator + * + * @param dev Pointer to the PIO device structure + * @param count Instructions to allocate + * @param instr Output pointer for the first allocated instruction + * @return -ENOMEM on error + */ +int pio_rpi_pico_alloc_instr(const struct device *dev, size_t count, uint8_t *instr); + +/** + * @brief Dynamic shared instruction allocator + * + * @param dev Pointer to the PIO device structure + * @param key String key to identify the shared section + * @param count Instructions to allocate + * @param instr Output pointer for the first allocated instruction + * @return -ENOMEM on error + * @return -EALREADY if the section was already allocated * - * @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 + * @note in case of -EALREADY, instr will contain the previous allocated instruction */ -int pio_rpi_pico_allocate_sm(const struct device *dev, size_t *sm); +int pio_rpi_pico_alloc_shared_instr(const struct device *dev, const char *key, + size_t count, uint8_t *instr); -#endif /* ZEPHYR_DRIVERS_MISC_PIO_PICO_RPI_PIO_PICO_RPI_H_ */ +#endif /* ZEPHYR_DRIVERS_MISC_PIO_RPI_PICO_H_ */ diff --git a/include/zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico_util.h b/include/zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico_util.h new file mode 100644 index 0000000000000..ae5f05df4973a --- /dev/null +++ b/include/zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico_util.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2023 Ionut Pavel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_MISC_PIO_RPI_PICO_UTIL_H_ +#define ZEPHYR_DRIVERS_MISC_PIO_RPI_PICO_UTIL_H_ + +#include +#include + +/* PIO registers */ +#include + +/** Indexed interrupt access macros TODO: should be indexed in HW structs */ + +struct pio_irq_hw { + io_rw_32 inte; + io_rw_32 intf; + io_ro_32 ints; +}; + +#define PIO_IRQ_HW_INDEX(hw, index) \ + (&(((struct pio_irq_hw *)&((hw)->inte0))[(index)])) + +/** Shared registers atomic bit ops */ + +#define PIO_ATOMIC_SET(reg, mask) \ + (*((io_wo_32 *)(((io_wo_8 *)(&(reg))) + REG_ALIAS_SET_BITS)) = (mask)) + +#define PIO_ATOMIC_CLR(reg, mask) \ + (*((io_wo_32 *)(((io_wo_8 *)(&(reg))) + REG_ALIAS_CLR_BITS)) = (mask)) + +#define PIO_ATOMIC_XOR(reg, mask) \ + (*((io_wo_32 *)(((io_wo_8 *)(&(reg))) + REG_ALIAS_XOR_BITS)) = (mask)) + +/** Instruction macros */ + +#define PIO_ISTR_IS_JMP(instr) \ + (((instr) >> 13) == 0x00u) + +/** Prescaler macros */ + +#define PIO_SM_CLKDIV(fin, fout) \ + (uint32_t)(((((uint64_t)(fin)) * 65536u) / (fout)) & 0xFFFFFF00u) + +/** Index macros */ + +#define PIO_ASM_INDEX(rel, idx) \ + ((!!(rel) ? BIT(4) : 0) | ((idx) & BIT_MASK(4))) + +/** Bit count macros */ + +#define PIO_ASM_BITCOUNT(count) \ + ((count) & BIT_MASK(5)) + +/** Address macros */ + +#define PIO_ASM_ADDR(base, offs) \ + (((base) + (offs)) & BIT_MASK(5)) + +/** Side set macros */ + +#define PIO_ASM_SIDE(opt, opt_val, ss_cnt, ss_val, delay) \ + (((!!(opt) ? ( \ + ((opt_val) << ((ss_cnt) - 1)) | ((ss_val) & BIT_MASK((ss_cnt) - 1)) \ + ) : ( \ + (ss_val) & BIT_MASK((ss_cnt)) \ + )) << (5 - (ss_cnt))) | ((delay) & BIT_MASK(5 - (ss_cnt)))) + +/** JMP Instruction macros */ + +#define PIO_ASM_JMP_COND_ALWAYS 0u +#define PIO_ASM_JMP_COND_NOTX 1u +#define PIO_ASM_JMP_COND_DECX 2u +#define PIO_ASM_JMP_COND_NOTY 3u +#define PIO_ASM_JMP_COND_DECY 4u +#define PIO_ASM_JMP_COND_XNOTEQY 5u +#define PIO_ASM_JMP_COND_PIN 6u +#define PIO_ASM_JMP_COND_NOTOSRE 7u + +#define PIO_ASM_JMP(cond, addr, dss) \ + (uint16_t)((0x0u << 13) \ + | (((dss) & BIT_MASK(5)) << 8) \ + | (((cond) & BIT_MASK(3)) << 5) \ + | (((addr) & BIT_MASK(5)) << 0)) + +/** WAIT Instruction macros */ + +#define PIO_ASM_WAIT_SRC_GPIO 0u +#define PIO_ASM_WAIT_SRC_PIN 1u +#define PIO_ASM_WAIT_SRC_IRQ 2u + +#define PIO_ASM_WAIT(pol, src, ind, dss) \ + (uint16_t)((0x1u << 13) \ + | (((dss) & BIT_MASK(5)) << 8) \ + | (((pol) & BIT_MASK(1)) << 7) \ + | (((src) & BIT_MASK(2)) << 5) \ + | (((ind) & BIT_MASK(5)) << 0)) + +/** IN Instruction macros */ + +#define PIO_ASM_IN_SRC_PINS 0u +#define PIO_ASM_IN_SRC_X 1u +#define PIO_ASM_IN_SRC_Y 2u +#define PIO_ASM_IN_SRC_NULL 3u +#define PIO_ASM_IN_SRC_ISR 6u +#define PIO_ASM_IN_SRC_OSR 7u + +#define PIO_ASM_IN(src, bcnt, dss) \ + (uint16_t)((0x2u << 13) \ + | (((dss) & BIT_MASK(5)) << 8) \ + | (((src) & BIT_MASK(3)) << 5) \ + | (((bcnt) & BIT_MASK(5)) << 0)) + +/** OUT Instruction macros */ + +#define PIO_ASM_OUT_DST_PINS 0u +#define PIO_ASM_OUT_DST_X 1u +#define PIO_ASM_OUT_DST_Y 2u +#define PIO_ASM_OUT_DST_NULL 3u +#define PIO_ASM_OUT_DST_PINDIRS 4u +#define PIO_ASM_OUT_DST_PC 5u +#define PIO_ASM_OUT_DST_ISR 6u +#define PIO_ASM_OUT_DST_EXEC 7u + +#define PIO_ASM_OUT(dst, bcnt, dss) \ + (uint16_t)((0x3u << 13) \ + | (((dss) & BIT_MASK(5)) << 8) \ + | (((dst) & BIT_MASK(3)) << 5) \ + | (((bcnt) & BIT_MASK(5)) << 0)) + +/** PUSH Instruction macros */ + +#define PIO_ASM_PUSH(iff, blk, dss) \ + (uint16_t)((0x4u << 13) \ + | (((dss) & BIT_MASK(5)) << 8) \ + | (((0u) & BIT_MASK(1)) << 7) \ + | (((iff) & BIT_MASK(1)) << 6) \ + | (((blk) & BIT_MASK(1)) << 5) \ + | (((0u) & BIT_MASK(5)) << 0)) + +/** PULL Instruction macros */ + +#define PIO_ASM_PULL(ife, blk, dss) \ + (uint16_t)((0x4u << 13) \ + | (((dss) & BIT_MASK(5)) << 8) \ + | (((1u) & BIT_MASK(1)) << 7) \ + | (((ife) & BIT_MASK(1)) << 6) \ + | (((blk) & BIT_MASK(1)) << 5) \ + | (((0u) & BIT_MASK(5)) << 0)) + +/** MOV Instruction macros */ + +#define PIO_ASM_MOV_DST_PINS 0u +#define PIO_ASM_MOV_DST_X 1u +#define PIO_ASM_MOV_DST_Y 2u +#define PIO_ASM_MOV_DST_EXEC 4u +#define PIO_ASM_MOV_DST_PC 5u +#define PIO_ASM_MOV_DST_ISR 6u +#define PIO_ASM_MOV_DST_OSR 7u + +#define PIO_ASM_MOV_OP_NONE 0u +#define PIO_ASM_MOV_OP_INVERT 1u +#define PIO_ASM_MOV_OP_BITREVERSE 2u + +#define PIO_ASM_MOV_SRC_PINS 0u +#define PIO_ASM_MOV_SRC_X 1u +#define PIO_ASM_MOV_SRC_Y 2u +#define PIO_ASM_MOV_SRC_NULL 3u +#define PIO_ASM_MOV_SRC_STATUS 5u +#define PIO_ASM_MOV_SRC_ISR 6u +#define PIO_ASM_MOV_SRC_OSR 7u + +#define PIO_ASM_MOV(dst, op, src, dss) \ + (uint16_t)((0x5u << 13) \ + | (((dss) & BIT_MASK(5)) << 8) \ + | (((dst) & BIT_MASK(3)) << 5) \ + | (((op) & BIT_MASK(2)) << 3) \ + | (((src) & BIT_MASK(3)) << 0)) + +/** IRQ Instruction macros */ + +#define PIO_ASM_IRQ(clr, wait, ind, dss) \ + (uint16_t)((0x6u << 13) \ + | (((dss) & BIT_MASK(5)) << 8) \ + | (((0u) & BIT_MASK(1)) << 7) \ + | (((clr) & BIT_MASK(1)) << 6) \ + | (((wait) & BIT_MASK(1)) << 5) \ + | (((ind) & BIT_MASK(5)) << 0)) + +/** SET Instruction macros */ + +#define PIO_ASM_SET_DST_PINS 0u +#define PIO_ASM_SET_DST_X 1u +#define PIO_ASM_SET_DST_Y 2u +#define PIO_ASM_SET_DST_PINDIRS 4u + +#define PIO_ASM_SET(dst, data, dss) \ + (uint16_t)((0x7u << 13) \ + | (((dss) & BIT_MASK(5)) << 8) \ + | (((dst) & BIT_MASK(3)) << 5) \ + | (((data) & BIT_MASK(5)) << 0)) + +/** + * @brief Load and patch program + * + * @param imem Pointer to the instruction memory + * @param base Base address + * @param src Pointer to the source data + * @param size Source data size + * + * @note User should assure no overflow will happen + */ +static inline void pio_rpi_pico_util_load_prg(io_wo_32 *imem, uint8_t base, + const uint16_t *src, size_t size) +{ + io_wo_32 *dst = &imem[base]; + uint16_t instr; + size_t i; + + for (i = 0u; i < size; i++) { + instr = src[i]; + if (PIO_ISTR_IS_JMP(instr)) { + instr += base; + } + dst[i] = instr; + } +} + +#endif /* ZEPHYR_DRIVERS_MISC_PIO_RPI_PICO_UTIL_H_ */ diff --git a/modules/hal_rpi_pico/CMakeLists.txt b/modules/hal_rpi_pico/CMakeLists.txt index c085785a09366..9db2b66e4d94f 100644 --- a/modules/hal_rpi_pico/CMakeLists.txt +++ b/modules/hal_rpi_pico/CMakeLists.txt @@ -122,14 +122,4 @@ 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 4baf11dd406d2..ecb4c4993c6af 100644 --- a/modules/hal_rpi_pico/Kconfig +++ b/modules/hal_rpi_pico/Kconfig @@ -34,12 +34,6 @@ 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 56f48dfc5c8779ff236e754930c0dbe2b53503b6 Mon Sep 17 00:00:00 2001 From: Ionut Catalin Pavel Date: Sun, 9 Apr 2023 14:51:09 +0300 Subject: [PATCH 2/3] drivers: serial: Added support for PIO based UART Added a full(ish) PIO based uart driver. Supports [5, 6, 7, 8]N[0.5, 1, 1.5, 2] modes. Experiments were made with adding parity support on the TX side, and wide (9 bit) data support. However I don't think these are really usefull ATM. Supports interrupt driven mode and polled mode. Can be used as a shell uart. Uses 2 state machines and < 16 instruction, therefore, 4 extra uarts could be added to the pico. Signed-off-by: TOKITA Hiroshi Signed-off-by: Yonatan Schachter Signed-off-by: Ionut Catalin Pavel --- drivers/serial/Kconfig.rpi_pico | 3 +- drivers/serial/uart_rpi_pico_pio.c | 822 +++++++++++++++--- .../misc/raspberrypi,pico-pio-device.yaml | 2 +- .../serial/raspberrypi,pico-uart-pio.yaml | 2 +- 4 files changed, 697 insertions(+), 132 deletions(-) diff --git a/drivers/serial/Kconfig.rpi_pico b/drivers/serial/Kconfig.rpi_pico index 0465570f704d0..aefc217014753 100644 --- a/drivers/serial/Kconfig.rpi_pico +++ b/drivers/serial/Kconfig.rpi_pico @@ -15,6 +15,5 @@ config UART_RPI_PICO_PIO default y depends on DT_HAS_RASPBERRYPI_PICO_UART_PIO_ENABLED select SERIAL_HAS_DRIVER - select PICOSDK_USE_PIO - select PICOSDK_USE_CLAIM + select SERIAL_SUPPORT_INTERRUPT depends on RESET diff --git a/drivers/serial/uart_rpi_pico_pio.c b/drivers/serial/uart_rpi_pico_pio.c index 8efb725b44ab3..5ed37d401714e 100644 --- a/drivers/serial/uart_rpi_pico_pio.c +++ b/drivers/serial/uart_rpi_pico_pio.c @@ -1,200 +1,766 @@ /* + * Copyright (c) 2023, Ionut Pavel * Copyright (c) 2022, Yonatan Schachter * * SPDX-License-Identifier: Apache-2.0 */ -#include +#include +#include +#include +#include #include - +#include #include +#include -#include -#include - -#define DT_DRV_COMPAT raspberrypi_pico_uart_pio +/* PIO registers */ +#include -#define CYCLES_PER_BIT 8 -#define SIDESET_BIT_COUNT 2 +#define DT_DRV_COMPAT raspberrypi_pico_uart_pio struct pio_uart_config { - const struct device *piodev; + const struct device *parent; const struct pinctrl_dev_config *pcfg; - const uint32_t tx_pin; - const uint32_t rx_pin; - uint32_t baudrate; + pio_hw_t *pio_regs; + uint32_t clock_frequency; +#if CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_config_func_t irq_config; + uint8_t irq_idx; +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + uint8_t tx_gpio; + uint8_t rx_gpio; }; struct pio_uart_data { - size_t tx_sm; - size_t rx_sm; +#if CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t irq_cb; + void *irq_cb_data; +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + struct uart_config uart_config; + uint8_t tx_sm; + uint8_t tx_sm_mask; + uint8_t tx_prg; + uint8_t rx_sm; + uint8_t rx_sm_mask; + uint8_t rx_shift; + uint8_t rx_prg; +}; + +/* Number of bits to shift */ +#define PIO_UART_TXRX_BITS_CNT(bits) ((bits) - 1) + +/** + * 8N1 initial configuration + * OUT pin 0 and side-set pin 0 mapped to the TX pin + * Scratch Y for number of bits to shift (-1) + * SMx TXNFULL IRQ for signalling + * SMx clk_div = baud * 4 + */ + +/* .side_set 1 opt */ +#define PIO_UART_TX_DSS(opt, ss, delay) PIO_ASM_SIDE(1, opt, 2, ss, delay) + +/* Patching parameters */ +#define PIO_UART_TX_STOP_OFFSET 0 +#define PIO_UART_TX_STOP_0_5 1 +#define PIO_UART_TX_STOP_1 3 +#define PIO_UART_TX_STOP_1_5 5 +#define PIO_UART_TX_STOP_2 7 + +/* TX instructions */ +static const uint16_t pio_uart_tx_prg[] = { + /* wrap_bot */ + /* [stop bits : delay] => [0.5 : 1], [1 : 3], [1.5 : 5], [2 : 7]*/ + /* pull side 1 [2] */ + PIO_ASM_PULL(0, 1, PIO_UART_TX_DSS(1, 1, 3)), + /* mov x, y side 0 [3] */ + PIO_ASM_MOV(PIO_ASM_MOV_DST_X, PIO_ASM_MOV_OP_NONE, PIO_ASM_MOV_SRC_Y, + PIO_UART_TX_DSS(1, 0, 3)), + /* loop: */ + /* out pins, 1 */ + PIO_ASM_OUT(PIO_ASM_OUT_DST_PINS, 1, PIO_UART_TX_DSS(0, 0, 0)), + /* jmp x-- loop [2] */ + PIO_ASM_JMP(PIO_ASM_JMP_COND_DECX, PIO_ASM_ADDR(0, 2), PIO_UART_TX_DSS(0, 0, 2)), + /* wrap_top */ }; -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); +/** + * 8N1 initial configuration + * IN pin 0 and JMP pin mapped to the RX pin + * Scratch Y for number of bits to shift (-1) + * SMx IRQ for framing error + * SMx RXNEMPTY IRQ for signalling + * SMx clk_div = baud * 8 + */ + +/* Shift count due to right alignment */ +#define PIO_UART_RX_SHIFT_CNT(bits) (16 - (bits)) + +/* Default */ +#define PIO_UART_RX_DSS(delay) PIO_ASM_SIDE(0, 0, 0, 0, delay) + +/* RX program */ +static const uint16_t pio_uart_rx_prg[] = { + /* wrap bot */ + /* start: */ + /* wait 0 pin 0 */ + PIO_ASM_WAIT(0, PIO_ASM_WAIT_SRC_PIN, 0, PIO_UART_RX_DSS(0)), + /* mov x, y [10] */ + PIO_ASM_MOV(PIO_ASM_MOV_DST_X, PIO_ASM_MOV_OP_NONE, PIO_ASM_MOV_SRC_Y, PIO_UART_RX_DSS(10)), + /* bitloop: */ + /* in pins, 1 */ + PIO_ASM_IN(PIO_ASM_IN_SRC_PINS, 1, PIO_UART_RX_DSS(0)), + /* jmp x--, bitloop [6] */ + PIO_ASM_JMP(PIO_ASM_JMP_COND_DECX, PIO_ASM_ADDR(0, 2), PIO_UART_RX_DSS(6)), + /* jmp pin, stop */ + PIO_ASM_JMP(PIO_ASM_JMP_COND_PIN, PIO_ASM_ADDR(0, 8), PIO_UART_RX_DSS(0)), + /* irq 0 rel */ + PIO_ASM_IRQ(0, 0, PIO_ASM_INDEX(true, 0), PIO_UART_RX_DSS(0)), + /* wait 1 pin 0 */ + PIO_ASM_WAIT(1, PIO_ASM_WAIT_SRC_PIN, 0, PIO_UART_RX_DSS(0)), + /* jmp start */ + PIO_ASM_JMP(PIO_ASM_JMP_COND_ALWAYS, PIO_ASM_ADDR(0, 0), PIO_UART_RX_DSS(0)), + /* stop: */ + /* push */ + PIO_ASM_PUSH(0, 0, PIO_UART_RX_DSS(0)), + /* wrap top */ +}; + +static int pio_uart_init_tx(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + pio_sm_hw_t *pio_sm; + int rc; + + /* Allocate state machine */ + rc = pio_rpi_pico_alloc_sm(config->parent, 1, &data->tx_sm); + if (rc < 0) { + return rc; + } + + pio_sm = &pio_hw->sm[data->tx_sm]; + data->tx_sm_mask = BIT(data->tx_sm); + + /* Allocate instructions */ + rc = pio_rpi_pico_alloc_instr(config->parent, ARRAY_SIZE(pio_uart_tx_prg), &data->tx_prg); + if (rc < 0) { + return rc; + } + + /* Load initial program */ + pio_rpi_pico_util_load_prg(pio_hw->instr_mem, data->tx_prg, + pio_uart_tx_prg, ARRAY_SIZE(pio_uart_tx_prg)); + + /** + * 2 side set count (1 extra for enable) + * 1 set count + * 1 out count + * tx_gpio sideset base + * tx_gpio set base + * tx_gpio out base + */ + pio_sm->pinctrl = (2u << PIO_SM0_PINCTRL_SIDESET_COUNT_LSB) + | (1u << PIO_SM0_PINCTRL_SET_COUNT_LSB) + | (1u << PIO_SM0_PINCTRL_OUT_COUNT_LSB) + | (config->tx_gpio << PIO_SM0_PINCTRL_SIDESET_BASE_LSB) + | (config->tx_gpio << PIO_SM0_PINCTRL_SET_BASE_LSB) + | (config->tx_gpio << PIO_SM0_PINCTRL_OUT_BASE_LSB); + + /* Force pin to 1 */ + pio_sm->instr = PIO_ASM_SET(PIO_ASM_SET_DST_PINS, 1, PIO_UART_TX_DSS(0, 0, 0)); + + /* Force direction to output */ + pio_sm->instr = PIO_ASM_SET(PIO_ASM_SET_DST_PINDIRS, 1, PIO_UART_TX_DSS(0, 0, 0)); + + /** + * Enable side bit + * Set wraps + */ + pio_sm->execctrl = PIO_SM0_EXECCTRL_SIDE_EN_BITS + | (data->tx_prg << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) + | ((data->tx_prg + ARRAY_SIZE(pio_uart_tx_prg) - 1) + << PIO_SM0_EXECCTRL_WRAP_TOP_LSB); + + /** + * Join TX + * Out right shift + */ + pio_sm->shiftctrl = PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS + | PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS; return 0; } -static int pio_uart_rx_init(PIO pio, uint32_t sm, uint32_t rx_pin, float div) +static int pio_uart_init_rx(const struct device *dev) { - pio_sm_config sm_config; - uint32_t offset; + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + pio_sm_hw_t *pio_sm; + int rc; + + /* Allocate state machine */ + rc = pio_rpi_pico_alloc_sm(config->parent, 1, &data->rx_sm); + if (rc < 0) { + return rc; + } - if (!pio_can_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_rx))) { - return -EBUSY; + pio_sm = &pio_hw->sm[data->rx_sm]; + data->rx_sm_mask = BIT(data->rx_sm); + + /* Allocate shared instructions */ + rc = pio_rpi_pico_alloc_shared_instr(config->parent, STRINGIFY(DT_DRV_COMPAT), + ARRAY_SIZE(pio_uart_rx_prg), &data->rx_prg); + if ((rc < 0) && (rc != -EALREADY)) { + return rc; } - offset = pio_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_rx)); - sm_config = pio_get_default_sm_config(); + if (rc != -EALREADY) { + /* Load initial program */ + pio_rpi_pico_util_load_prg(pio_hw->instr_mem, data->rx_prg, + pio_uart_rx_prg, ARRAY_SIZE(pio_uart_rx_prg)); + } - 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)); + /** + * rx_gpio in base + * rx_gpio set base + */ + pio_sm->pinctrl = (1u << PIO_SM0_PINCTRL_SET_COUNT_LSB) + | (config->rx_gpio << PIO_SM0_PINCTRL_SET_BASE_LSB) + | (config->rx_gpio << PIO_SM0_PINCTRL_IN_BASE_LSB); - pio_sm_init(pio, sm, offset, &sm_config); - pio_sm_set_enabled(pio, sm, true); + /* Force direction to input */ + pio_sm->instr = PIO_ASM_SET(PIO_ASM_SET_DST_PINDIRS, 0, PIO_UART_RX_DSS(0)); + + /** + * Set JMP pin + * Set wraps + */ + pio_sm->execctrl = (config->rx_gpio << PIO_SM0_EXECCTRL_JMP_PIN_LSB) + | (data->rx_prg << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) + | ((data->rx_prg + ARRAY_SIZE(pio_uart_rx_prg) - 1) + << PIO_SM0_EXECCTRL_WRAP_TOP_LSB); + + /** + * Join RX + * In right shift + */ + pio_sm->shiftctrl = PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS + | PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS; return 0; } -static int pio_uart_poll_in(const struct device *dev, unsigned char *c) +static int pio_uart_configure_txrx(const struct device *dev, const struct uart_config *cfg) { 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; + pio_hw_t *pio_hw = config->pio_regs; + pio_sm_hw_t *tx_sm = &pio_hw->sm[data->tx_sm]; + pio_sm_hw_t *rx_sm = &pio_hw->sm[data->rx_sm]; + uint32_t scratch_y, tx_delay, patch; + uint8_t rx_shift; + + /* Flow control not supported */ + if (cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE) { + return -ENOTSUP; + } - /* - * 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; + /* Check parity */ + switch (cfg->parity) { + case UART_CFG_PARITY_NONE: + break; + + default: + return -ENOTSUP; } - /* Accessing the FIFO pops the read word from it */ - *c = (char)*uart_rx_fifo_msb; + /* Baud cannot be 0 */ + if (cfg->baudrate == 0) { + return -EINVAL; + } + + /* Check and set data bits (see program description) */ + switch (cfg->data_bits) { + case UART_CFG_DATA_BITS_5: + scratch_y = PIO_UART_TXRX_BITS_CNT(5); + rx_shift = PIO_UART_RX_SHIFT_CNT(5); + break; + + case UART_CFG_DATA_BITS_6: + scratch_y = PIO_UART_TXRX_BITS_CNT(6); + rx_shift = PIO_UART_RX_SHIFT_CNT(6); + break; + + case UART_CFG_DATA_BITS_7: + scratch_y = PIO_UART_TXRX_BITS_CNT(7); + rx_shift = PIO_UART_RX_SHIFT_CNT(7); + break; + + case UART_CFG_DATA_BITS_8: + scratch_y = PIO_UART_TXRX_BITS_CNT(8); + rx_shift = PIO_UART_RX_SHIFT_CNT(8); + break; + + default: + return -ENOTSUP; + } + + /* Check and set stop bits (see program description) */ + switch (cfg->stop_bits) { + case UART_CFG_STOP_BITS_0_5: + tx_delay = PIO_UART_TX_STOP_0_5; + break; + + case UART_CFG_STOP_BITS_1: + tx_delay = PIO_UART_TX_STOP_1; + break; + + case UART_CFG_STOP_BITS_1_5: + tx_delay = PIO_UART_TX_STOP_1_5; + break; + + case UART_CFG_STOP_BITS_2: + tx_delay = PIO_UART_TX_STOP_2; + break; + + default: + return -ENOTSUP; + } + + /* TX configuration (including stop bits patch) */ + tx_sm->clkdiv = PIO_SM_CLKDIV(config->clock_frequency, (cfg->baudrate * 4)); + tx_sm->instr = PIO_ASM_SET(PIO_ASM_SET_DST_Y, scratch_y, PIO_UART_TX_DSS(0, 0, 0)); + patch = PIO_ASM_PULL(0, 1, PIO_UART_TX_DSS(1, 1, tx_delay)); + pio_hw->instr_mem[PIO_UART_TX_STOP_OFFSET + data->tx_prg] = patch; + + /* RX configuration */ + rx_sm->clkdiv = PIO_SM_CLKDIV(config->clock_frequency, (cfg->baudrate * 8)); + rx_sm->instr = PIO_ASM_SET(PIO_ASM_SET_DST_Y, scratch_y, PIO_UART_RX_DSS(0)); + data->rx_shift = rx_shift; + return 0; } +static void pio_uart_enable_txrx(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + pio_sm_hw_t *tx_sm = &pio_hw->sm[data->tx_sm]; + pio_sm_hw_t *rx_sm = &pio_hw->sm[data->rx_sm]; + uint32_t mask = data->tx_sm_mask | data->rx_sm_mask; + + /* Go to start */ + tx_sm->instr = PIO_ASM_JMP(PIO_ASM_JMP_COND_ALWAYS, data->tx_prg, PIO_UART_TX_DSS(0, 0, 0)); + rx_sm->instr = PIO_ASM_JMP(PIO_ASM_JMP_COND_ALWAYS, data->rx_prg, PIO_UART_RX_DSS(0)); + + /* Restart clocks */ + PIO_ATOMIC_SET(pio_hw->ctrl, mask << PIO_CTRL_CLKDIV_RESTART_LSB); + + /* Restart SMs */ + PIO_ATOMIC_SET(pio_hw->ctrl, mask << PIO_CTRL_SM_RESTART_LSB); + + /* Enable SMs */ + PIO_ATOMIC_SET(pio_hw->ctrl, mask << PIO_CTRL_SM_ENABLE_LSB); +} + +static void pio_uart_disable_txrx(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + uint32_t mask = data->tx_sm_mask | data->rx_sm_mask; + + /* Disable SMs */ + PIO_ATOMIC_CLR(pio_hw->ctrl, mask << PIO_CTRL_SM_ENABLE_LSB); +} + +#if CONFIG_UART_INTERRUPT_DRIVEN +static int pio_uart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + uint32_t fmask = (uint32_t)data->tx_sm_mask << PIO_FSTAT_TXFULL_LSB; + int cnt = 0; + + while (((len - cnt) > 0) && !(pio_hw->fstat & fmask)) { + pio_hw->txf[data->tx_sm] = (uint32_t)tx_data[cnt++]; + } + + return cnt; +} + +static int pio_uart_fifo_read(const struct device *dev, uint8_t *rx_data, const int len) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + uint32_t fmask = (uint32_t)data->rx_sm_mask << PIO_FSTAT_RXEMPTY_LSB; + io_ro_16 *fifo = (io_ro_16 *)&pio_hw->rxf[data->rx_sm] + 1; /* Upper 16 bits */ + int cnt = 0; + + while (((len - cnt) > 0) && !(pio_hw->fstat & fmask)) { + rx_data[cnt++] = (unsigned char)(*fifo >> data->rx_shift); + } + + return cnt; +} + +static inline void pio_uart_irq_generic_enable(const struct device *dev, uint32_t mask) +{ + const struct pio_uart_config *config = dev->config; + struct pio_irq_hw *irq_hw = PIO_IRQ_HW_INDEX(config->pio_regs, config->irq_idx); + + PIO_ATOMIC_SET(irq_hw->inte, mask); +} + +static inline void pio_uart_irq_generic_disable(const struct device *dev, uint32_t mask) +{ + const struct pio_uart_config *config = dev->config; + struct pio_irq_hw *irq_hw = PIO_IRQ_HW_INDEX(config->pio_regs, config->irq_idx); + + PIO_ATOMIC_CLR(irq_hw->inte, mask); +} + +static void pio_uart_irq_tx_enable(const struct device *dev) +{ + struct pio_uart_data *data = dev->data; + uint32_t mask = (uint32_t)data->tx_sm_mask << PIO_IRQ0_INTE_SM0_TXNFULL_LSB; + + pio_uart_irq_generic_enable(dev, mask); +} + +static void pio_uart_irq_rx_enable(const struct device *dev) +{ + struct pio_uart_data *data = dev->data; + uint32_t mask = (uint32_t)data->rx_sm_mask << PIO_IRQ0_INTE_SM0_RXNEMPTY_LSB; + + pio_uart_irq_generic_enable(dev, mask); +} + +static void pio_uart_irq_error_enable(const struct device *dev) +{ + struct pio_uart_data *data = dev->data; + uint32_t mask = (uint32_t)data->rx_sm_mask << PIO_IRQ0_INTE_SM0_LSB; + + pio_uart_irq_generic_enable(dev, mask); +} + +static void pio_uart_irq_tx_disable(const struct device *dev) +{ + struct pio_uart_data *data = dev->data; + uint32_t mask = (uint32_t)data->tx_sm_mask << PIO_IRQ0_INTE_SM0_TXNFULL_LSB; + + pio_uart_irq_generic_disable(dev, mask); +} + +static void pio_uart_irq_rx_disable(const struct device *dev) +{ + struct pio_uart_data *data = dev->data; + uint32_t mask = (uint32_t)data->rx_sm_mask << PIO_IRQ0_INTE_SM0_RXNEMPTY_LSB; + + pio_uart_irq_generic_disable(dev, mask); +} + +static void pio_uart_irq_error_disable(const struct device *dev) +{ + struct pio_uart_data *data = dev->data; + uint32_t mask = (uint32_t)data->rx_sm_mask << PIO_IRQ0_INTE_SM0_LSB; + + pio_uart_irq_generic_disable(dev, mask); +} + +static int pio_uart_irq_tx_ready(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + struct pio_irq_hw *irq_hw = PIO_IRQ_HW_INDEX(config->pio_regs, config->irq_idx); + uint32_t imask = (uint32_t)data->tx_sm_mask << PIO_IRQ0_INTE_SM0_TXNFULL_LSB; + uint32_t fmask = (uint32_t)data->tx_sm_mask << PIO_FSTAT_TXFULL_LSB; + + /* True only if interrupts enabled and fifo not full */ + return ((irq_hw->inte & imask) && !(pio_hw->fstat & fmask)) ? 1 : 0; +} + +static int pio_uart_irq_rx_ready(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + uint32_t fmask = (uint32_t)data->rx_sm_mask << PIO_FSTAT_RXEMPTY_LSB; + + /* True if fifo not empty */ + return !(pio_hw->fstat & fmask) ? 1 : 0; +} + +static int pio_uart_irq_tx_complete(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + uint32_t mask = (uint32_t)data->tx_sm_mask << PIO_FSTAT_TXEMPTY_LSB; + + /* True if fifo empty */ + return (pio_hw->fstat & mask) ? 1 : 0; +} + +static int pio_uart_irq_is_pending(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + uint32_t mask; + + mask = (uint32_t)data->tx_sm_mask << PIO_INTR_SM0_TXNFULL_LSB + | (uint32_t)data->rx_sm_mask << PIO_INTR_SM0_RXNEMPTY_LSB + | (uint32_t)data->rx_sm_mask << PIO_INTR_SM0_LSB; + + /* True if any IRQ is pending */ + return (pio_hw->intr & mask) ? 1 : 0; +} + +static int pio_uart_irq_update(const struct device *dev) +{ + return 1; +} + +static void pio_uart_irq_callback_set(const struct device *dev, + uart_irq_callback_user_data_t cb, + void *cb_data) +{ + struct pio_uart_data *data = dev->data; + + data->irq_cb = cb; + data->irq_cb_data = cb_data; +} + +static void pio_uart_irq(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + uint32_t mask; + + /* Filter events */ + mask = (uint32_t)data->tx_sm_mask << PIO_INTR_SM0_TXNFULL_LSB + | (uint32_t)data->rx_sm_mask << PIO_INTR_SM0_RXNEMPTY_LSB + | (uint32_t)data->rx_sm_mask << PIO_INTR_SM0_LSB; + + if ((pio_hw->intr & mask) && (data->irq_cb)) { + data->irq_cb(dev, data->irq_cb_data); + } +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +#if CONFIG_UART_USE_RUNTIME_CONFIGURE +static int pio_uart_configure(const struct device *dev, const struct uart_config *cfg) +{ + struct pio_uart_data *data = dev->data; + int rc; + + __ASSERT_NO_MSG(cfg); + + /* No point if no changes */ + rc = memcmp(cfg, &data->uart_config, sizeof(struct uart_config)); + if (rc != 0) { + /* Disable / config / enable. Will retain current config if invalid */ + pio_uart_disable_txrx(dev); + rc = pio_uart_configure_txrx(dev, cfg); + if (rc == 0) { + data->uart_config = *cfg; + } + pio_uart_enable_txrx(dev); + } + + return rc; +} + +static int pio_uart_config_get(const struct device *dev, struct uart_config *cfg) +{ + struct pio_uart_data *data = dev->data; + + *cfg = data->uart_config; + + return 0; +} +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ + +static int pio_uart_err_check(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + pio_hw_t *pio_hw = config->pio_regs; + int err = 0; + + if (pio_hw->fdebug & ((uint32_t)data->rx_sm_mask << PIO_FDEBUG_RXSTALL_LSB)) { + pio_hw->fdebug = (uint32_t)data->rx_sm_mask << PIO_FDEBUG_RXSTALL_LSB; + err |= UART_ERROR_OVERRUN; + } + + if (pio_hw->irq & data->rx_sm_mask) { + pio_hw->irq = data->rx_sm_mask; + err |= UART_ERROR_FRAMING; + } + + return err; +} + 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_hw_t *pio_hw = config->pio_regs; + + while (pio_hw->fstat & ((uint32_t)data->tx_sm_mask << PIO_FSTAT_TXFULL_LSB)) { + } - pio_sm_put_blocking(pio_rpi_pico_get_pio(config->piodev), data->tx_sm, (uint32_t)c); + pio_hw->txf[data->tx_sm] = (uint32_t)c; } -static int pio_uart_init(const struct device *dev) +static int pio_uart_poll_in(const struct device *dev, unsigned char *c) { 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_hw_t *pio_hw = config->pio_regs; + io_ro_16 *fifo = (io_ro_16 *)&pio_hw->rxf[data->rx_sm] + 1; /* Upper 16 bits */ - pio = pio_rpi_pico_get_pio(config->piodev); - sm_clock_div = (float)clock_get_hz(clk_sys) / (CYCLES_PER_BIT * config->baudrate); + if (pio_hw->fstat & ((uint32_t)data->rx_sm_mask << PIO_FSTAT_RXEMPTY_LSB)) { + return -1; + } - retval = pio_rpi_pico_allocate_sm(config->piodev, &tx_sm); - retval |= pio_rpi_pico_allocate_sm(config->piodev, &rx_sm); + *c = (unsigned char)(*fifo >> data->rx_shift); - if (retval < 0) { - return retval; + return 0; +} + +static int pio_uart_init(const struct device *dev) +{ + const struct pio_uart_config *config = dev->config; + struct pio_uart_data *data = dev->data; + int rc; + + if (!device_is_ready(config->parent)) { + return -ENODEV; } - data->tx_sm = tx_sm; - data->rx_sm = rx_sm; + rc = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (rc < 0) { + return rc; + } + + rc = pio_uart_init_tx(dev); + if (rc < 0) { + return rc; + } - retval = pio_uart_tx_init(pio, tx_sm, config->tx_pin, sm_clock_div); - if (retval < 0) { - return retval; + rc = pio_uart_init_rx(dev); + if (rc < 0) { + return rc; } - retval = pio_uart_rx_init(pio, rx_sm, config->rx_pin, sm_clock_div); - if (retval < 0) { - return retval; + rc = pio_uart_configure_txrx(dev, &data->uart_config); + if (rc < 0) { + return rc; } - return pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); +#if CONFIG_UART_INTERRUPT_DRIVEN + config->irq_config(dev); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + + pio_uart_enable_txrx(dev); + + return 0; } static const struct uart_driver_api pio_uart_driver_api = { .poll_in = pio_uart_poll_in, .poll_out = pio_uart_poll_out, + .err_check = pio_uart_err_check, +#if CONFIG_UART_USE_RUNTIME_CONFIGURE + .configure = pio_uart_configure, + .config_get = pio_uart_config_get, +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ +#if CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = pio_uart_fifo_fill, + .fifo_read = pio_uart_fifo_read, + .irq_tx_enable = pio_uart_irq_tx_enable, + .irq_tx_disable = pio_uart_irq_tx_disable, + .irq_tx_ready = pio_uart_irq_tx_ready, + .irq_rx_enable = pio_uart_irq_rx_enable, + .irq_rx_disable = pio_uart_irq_rx_disable, + .irq_tx_complete = pio_uart_irq_tx_complete, + .irq_rx_ready = pio_uart_irq_rx_ready, + .irq_err_enable = pio_uart_irq_error_enable, + .irq_err_disable = pio_uart_irq_error_disable, + .irq_is_pending = pio_uart_irq_is_pending, + .irq_update = pio_uart_irq_update, + .irq_callback_set = pio_uart_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ }; -#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), \ +#if CONFIG_UART_INTERRUPT_DRIVEN +#define PIO_UART_IRQ_CONFIG(inst) \ +static void pio_uart_irq_config##inst(const struct device *dev) \ +{ \ + ARG_UNUSED(dev); \ + static struct pio_rpi_pico_irq_cfg pio_uart_irq_cfg##inst = { \ + .irq_func = pio_uart_irq, \ + .irq_param = DEVICE_DT_GET(DT_DRV_INST(inst)), \ + .irq_idx = DT_INST_PROP_BY_IDX(inst, pio_interrupts, 0), \ + }; \ + pio_rpi_pico_irq_register(DEVICE_DT_GET(DT_INST_PARENT(inst)), &pio_uart_irq_cfg##inst);\ + pio_rpi_pico_irq_enable(DEVICE_DT_GET(DT_INST_PARENT(inst)), &pio_uart_irq_cfg##inst); \ +} + +#define PIO_UART_GEN_IRQ_CONFIG(inst) \ + .irq_config = pio_uart_irq_config##inst, \ + .irq_idx = DT_INST_PROP_BY_IDX(inst, pio_interrupts, 0), + +#else +#define PIO_UART_IRQ_CONFIG(inst) +#define PIO_UART_GEN_IRQ_CONFIG(inst) +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +#define PIO_UART_INIT(inst) \ + BUILD_ASSERT(DT_INST_ON_BUS(inst, pio_rpi_pico), \ + "node " DT_NODE_PATH(DT_DRV_INST(inst)) " is not assigned to a PIO instance"); \ + \ + PIO_UART_IRQ_CONFIG(inst) \ + \ + PINCTRL_DT_INST_DEFINE(inst); \ + \ + static struct pio_uart_data pio_uart_data##inst = { \ + .uart_config = { \ + .baudrate = DT_INST_PROP_OR(inst, current_speed, 115200), \ + .parity = DT_INST_ENUM_IDX_OR(inst, parity, UART_CFG_PARITY_NONE), \ + .stop_bits = DT_INST_ENUM_IDX_OR(inst, stop_bits, UART_CFG_STOP_BITS_1),\ + .data_bits = DT_INST_ENUM_IDX_OR(inst, data_bits, UART_CFG_DATA_BITS_8),\ + .flow_ctrl = 0, \ + }, \ + }; \ + \ + static const struct pio_uart_config pio_uart_config##inst = { \ + .parent = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ + .pio_regs = (pio_hw_t *)DT_INST_PIO_RPI_PICO_REG_ADDR(inst), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ + .clock_frequency = DT_INST_PIO_RPI_PICO_CLOCK_FREQ_HZ(inst), \ + .tx_gpio = DT_INST_PIO_RPI_PICO_PIN_BY_NAME(inst, default, 0, tx_gpio, 0), \ + .rx_gpio = DT_INST_PIO_RPI_PICO_PIN_BY_NAME(inst, default, 0, rx_gpio, 0), \ + PIO_UART_GEN_IRQ_CONFIG(inst) \ }; \ - 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); + DEVICE_DT_INST_DEFINE(inst, &pio_uart_init, \ + NULL, \ + &pio_uart_data##inst, \ + &pio_uart_config##inst, \ + PRE_KERNEL_1, \ + CONFIG_SERIAL_INIT_PRIORITY, \ + &pio_uart_driver_api); DT_INST_FOREACH_STATUS_OKAY(PIO_UART_INIT) diff --git a/dts/bindings/misc/raspberrypi,pico-pio-device.yaml b/dts/bindings/misc/raspberrypi,pico-pio-device.yaml index 8cc3e1300dfef..09dfff5a79f63 100644 --- a/dts/bindings/misc/raspberrypi,pico-pio-device.yaml +++ b/dts/bindings/misc/raspberrypi,pico-pio-device.yaml @@ -5,7 +5,7 @@ description: Raspberry Pi Pico PIO device -on-bus: rpi-pico-pio +on-bus: pio-rpi-pico properties: pio,interrupts: diff --git a/dts/bindings/serial/raspberrypi,pico-uart-pio.yaml b/dts/bindings/serial/raspberrypi,pico-uart-pio.yaml index bedbf4f590124..81ea3f0d1130e 100644 --- a/dts/bindings/serial/raspberrypi,pico-uart-pio.yaml +++ b/dts/bindings/serial/raspberrypi,pico-uart-pio.yaml @@ -2,4 +2,4 @@ description: Raspberry Pi Pico PIO UART compatible: "raspberrypi,pico-uart-pio" -include: [uart-controller.yaml, "raspberrypi,pico-pio-device.yaml"] +include: ["raspberrypi,pico-pio-device.yaml", uart-controller.yaml, pinctrl-device.yaml] From 1c399a58018d9d916e8df1d4c6a0384fdc4839e5 Mon Sep 17 00:00:00 2001 From: Ionut Catalin Pavel Date: Sun, 9 Apr 2023 14:59:40 +0300 Subject: [PATCH 3/3] samples: boards: rpi_pico: Added PIO shell uart example Simple example showing how to use the PIO based uart as a console / log backend. Signed-off-by: TOKITA Hiroshi Signed-off-by: Yonatan Schachter Signed-off-by: Ionut Catalin Pavel --- .../boards/rpi_pico/uart_pio/CMakeLists.txt | 6 +-- .../boards/rpi_pico/uart_pio/app-pinctrl.dtsi | 22 +++++++++ samples/boards/rpi_pico/uart_pio/app.overlay | 30 +++++++++++++ .../rpi_pico/uart_pio/boards/rpi_pico.overlay | 45 ------------------- samples/boards/rpi_pico/uart_pio/prj.conf | 6 ++- samples/boards/rpi_pico/uart_pio/src/main.c | 31 +++---------- 6 files changed, 66 insertions(+), 74 deletions(-) create mode 100644 samples/boards/rpi_pico/uart_pio/app-pinctrl.dtsi create mode 100644 samples/boards/rpi_pico/uart_pio/app.overlay delete mode 100644 samples/boards/rpi_pico/uart_pio/boards/rpi_pico.overlay diff --git a/samples/boards/rpi_pico/uart_pio/CMakeLists.txt b/samples/boards/rpi_pico/uart_pio/CMakeLists.txt index 56520b0d85de7..011872491e85d 100644 --- a/samples/boards/rpi_pico/uart_pio/CMakeLists.txt +++ b/samples/boards/rpi_pico/uart_pio/CMakeLists.txt @@ -1,7 +1,5 @@ -# SPDX-License-Identifier: Apache-2.0 - -cmake_minimum_required(VERSION 3.20.0) +cmake_minimum_required(VERSION 3.15.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) -project(uart_pio) +project(pio_shell_uart) target_sources(app PRIVATE src/main.c) diff --git a/samples/boards/rpi_pico/uart_pio/app-pinctrl.dtsi b/samples/boards/rpi_pico/uart_pio/app-pinctrl.dtsi new file mode 100644 index 0000000000000..8b6387f85f1f2 --- /dev/null +++ b/samples/boards/rpi_pico/uart_pio/app-pinctrl.dtsi @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023, Ionut Pavel + * Copyright (c) 2023 Yonatan Schachter + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +&pinctrl { + pio0_uart0_default: pio0_uart0_default { + tx_gpio { + pinmux = ; + }; + + rx_gpio { + pinmux = ; + input-enable; + bias-pull-up; + }; + }; +}; diff --git a/samples/boards/rpi_pico/uart_pio/app.overlay b/samples/boards/rpi_pico/uart_pio/app.overlay new file mode 100644 index 0000000000000..345345cdd13b1 --- /dev/null +++ b/samples/boards/rpi_pico/uart_pio/app.overlay @@ -0,0 +1,30 @@ +#include "app-pinctrl.dtsi" + +/ { + chosen { + zephyr,console = &pio0_uart0; + zephyr,shell-uart = &pio0_uart0; + }; +}; + +&uart0 { + status = "disabled"; +}; + +&pio0 { + status = "okay"; + + pio0_uart0: pio0_uart0 { + compatible = "raspberrypi,pico-uart-pio"; + + status = "okay"; + + pio,interrupts = <0>; + pio,interrupt-names = "shared"; + + pinctrl-0 = <&pio0_uart0_default>; + pinctrl-names = "default"; + + current-speed = <115200>; + }; +}; diff --git a/samples/boards/rpi_pico/uart_pio/boards/rpi_pico.overlay b/samples/boards/rpi_pico/uart_pio/boards/rpi_pico.overlay deleted file mode 100644 index 3b18c49d56cda..0000000000000 --- a/samples/boards/rpi_pico/uart_pio/boards/rpi_pico.overlay +++ /dev/null @@ -1,45 +0,0 @@ -&pinctrl { - pio1_uart0_default: pio1_uart0_default { - rx_pins { - pinmux = ; - input-enable; - bias-pull-up; - }; - tx_pins { - pinmux = ; - }; - }; - - pio1_uart1_default: pio1_uart1_default { - rx_pins { - pinmux = ; - input-enable; - bias-pull-up; - }; - tx_pins { - pinmux = ; - }; - }; -}; - -&pio1 { - status = "okay"; - - pio1_uart0: uart0 { - pinctrl-0 = <&pio1_uart0_default>; - pinctrl-names = "default"; - - compatible = "raspberrypi,pico-uart-pio"; - current-speed = <115200>; - status = "okay"; - }; - - pio1_uart1: uart1 { - pinctrl-0 = <&pio1_uart1_default>; - pinctrl-names = "default"; - - compatible = "raspberrypi,pico-uart-pio"; - current-speed = <115200>; - status = "okay"; - }; -}; diff --git a/samples/boards/rpi_pico/uart_pio/prj.conf b/samples/boards/rpi_pico/uart_pio/prj.conf index b2a4ba591044e..95f17cdfc6c02 100644 --- a/samples/boards/rpi_pico/uart_pio/prj.conf +++ b/samples/boards/rpi_pico/uart_pio/prj.conf @@ -1 +1,5 @@ -# nothing here +CONFIG_LOG=y +CONFIG_SHELL=y +CONFIG_SERIAL=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_CONSOLE=y diff --git a/samples/boards/rpi_pico/uart_pio/src/main.c b/samples/boards/rpi_pico/uart_pio/src/main.c index 6e3fdd727991d..776d16bacc4f7 100644 --- a/samples/boards/rpi_pico/uart_pio/src/main.c +++ b/samples/boards/rpi_pico/uart_pio/src/main.c @@ -1,36 +1,19 @@ /* + * Copyright (c) 2023 Ionut Pavel * Copyright (c) 2023 Yonatan Schachter * * SPDX-License-Identifier: Apache-2.0 */ #include -#include -#include -int main(void) -{ - char data; - const struct device *uart0 = DEVICE_DT_GET(DT_NODELABEL(pio1_uart0)); - const struct device *uart1 = DEVICE_DT_GET(DT_NODELABEL(pio1_uart1)); - - if (!device_is_ready(uart0)) { - return 0; - } - - if (!device_is_ready(uart1)) { - return 0; - } +#include +LOG_MODULE_REGISTER(main); +void main(void) +{ while (1) { - if (!uart_poll_in(uart0, &data)) { - uart_poll_out(uart0, data); - } - - if (!uart_poll_in(uart1, &data)) { - uart_poll_out(uart1, data); - } + k_msleep(1000); + LOG_INF("One second passed..."); } - - return 0; }