From 9d783c5f20e9980bea763e8170bddd27abd828f7 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Sat, 2 Jun 2018 00:48:08 +0200 Subject: [PATCH 01/17] [nrf fromtree] driver: add SWDP driver interface API and bit-bang driver Add Serial Wire Debug Port interface driver API and bit-bang driver. The driver requires a simple Hardware Interface Circuits (HICs), where signals CLK, DOUT, DIN, ENn, OE_ENn, RESETn are connected to board GPIOs and buffered signals SWD_CLK and SWD_DIO to the target. Signal OE_ENn controls the direction of the Serial Wire (SWD_DIO), ENn the buffers SWD_CLK possibly others and enables/disables HIC. Signed-off-by: Johann Fischer Signed-off-by: Johann Fischer (cherry picked from commit 3cf630fb0bcf5d95a735170a43608aea8e619d31) --- drivers/CMakeLists.txt | 1 + drivers/Kconfig | 1 + drivers/dp/CMakeLists.txt | 5 + drivers/dp/Kconfig | 30 ++ drivers/dp/swdp_bitbang.c | 633 ++++++++++++++++++++++++ dts/bindings/misc/zephyr,swdp-gpio.yaml | 46 ++ include/zephyr/drivers/swdp.h | 92 ++++ 7 files changed, 808 insertions(+) create mode 100644 drivers/dp/CMakeLists.txt create mode 100644 drivers/dp/Kconfig create mode 100644 drivers/dp/swdp_bitbang.c create mode 100644 dts/bindings/misc/zephyr,swdp-gpio.yaml create mode 100644 include/zephyr/drivers/swdp.h diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index e1dbab7e1a2..7332e1e3541 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -31,6 +31,7 @@ add_subdirectory_ifdef(CONFIG_DAC dac) add_subdirectory_ifdef(CONFIG_DAI dai) add_subdirectory_ifdef(CONFIG_DISPLAY display) add_subdirectory_ifdef(CONFIG_DMA dma) +add_subdirectory_ifdef(CONFIG_DP_DRIVER dp) add_subdirectory_ifdef(CONFIG_EDAC edac) add_subdirectory_ifdef(CONFIG_EEPROM eeprom) add_subdirectory_ifdef(CONFIG_ENTROPY_GENERATOR entropy) diff --git a/drivers/Kconfig b/drivers/Kconfig index ac07add3c1f..b1aec9de129 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -24,6 +24,7 @@ source "drivers/dai/Kconfig" source "drivers/disk/Kconfig" source "drivers/display/Kconfig" source "drivers/dma/Kconfig" +source "drivers/dp/Kconfig" source "drivers/edac/Kconfig" source "drivers/eeprom/Kconfig" source "drivers/entropy/Kconfig" diff --git a/drivers/dp/CMakeLists.txt b/drivers/dp/CMakeLists.txt new file mode 100644 index 00000000000..dd49b759ab8 --- /dev/null +++ b/drivers/dp/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2019, PHYTEC Messtechnik GmbH +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources_ifdef(CONFIG_SWDP_BITBANG_DRIVER swdp_bitbang.c) diff --git a/drivers/dp/Kconfig b/drivers/dp/Kconfig new file mode 100644 index 00000000000..77e33811a39 --- /dev/null +++ b/drivers/dp/Kconfig @@ -0,0 +1,30 @@ +# Copyright (c) 2019 Phytec Messtechnik GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +menuconfig DP_DRIVER + bool "Debug Port interface driver [EXPERIMENTAL]" + select EXPERIMENTAL + help + Enable Debug Port interface driver + +if DP_DRIVER + +module = DP_DRIVER +module-str = dp drv +source "subsys/logging/Kconfig.template.log_config" + +config DP_DRIVER_INIT_PRIO + int "Debug Port driver initialization priority" + default 80 + help + Set the initialization priority number. + +config SWDP_BITBANG_DRIVER + bool "Serial Wire Debug Port bit-bang driver" + default y + depends on DT_HAS_ZEPHYR_SWDP_GPIO_ENABLED + help + Serial Wire Debug Port bit-bang driver. + +endif # DP_DRIVER diff --git a/drivers/dp/swdp_bitbang.c b/drivers/dp/swdp_bitbang.c new file mode 100644 index 00000000000..6da04655cc0 --- /dev/null +++ b/drivers/dp/swdp_bitbang.c @@ -0,0 +1,633 @@ +/* + * Copyright (c) 2018-2019 PHYTEC Messtechnik GmbH + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This file is based on SW_DP.c from CMSIS-DAP Source (Revision: V2.0.0) + * https://github.com/ARM-software/CMSIS_5/tree/develop/CMSIS/DAP/Firmware + * Copyright (c) 2013-2017, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + + +/* Serial Wire Debug Port interface bit-bang driver */ + +#define DT_DRV_COMPAT zephyr_swdp_gpio + +#include +#include +#include + +#include +LOG_MODULE_REGISTER(swdp, CONFIG_DP_DRIVER_LOG_LEVEL); + +#if defined(CONFIG_SOC_SERIES_NRF52X) +#define CPU_CLOCK 64000000U +#else +#define CPU_CLOCK CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC +#endif + +#define CLOCK_DELAY(swclk_freq, port_write_cycles) \ + ((CPU_CLOCK / 2 / swclk_freq) - port_write_cycles) + +/* + * Default SWCLK frequency in Hz. + * sw_clock can be used to overwrite this default value. + */ +#define SWDP_DEFAULT_SWCLK_FREQUENCY 1000000U + +#define DELAY_SLOW_CYCLES 3U + +struct sw_config { + struct gpio_dt_spec clk; + struct gpio_dt_spec dout; + struct gpio_dt_spec din; + struct gpio_dt_spec dnoe; + struct gpio_dt_spec noe; + struct gpio_dt_spec reset; + uint32_t port_write_cycles; + void *clk_reg; + void *dout_reg; + void *din_reg; + void *dnoe_reg; +}; + +struct sw_cfg_data { + uint32_t clock_delay; + uint8_t turnaround; + bool data_phase; + bool fast_clock; +}; + +static uint8_t sw_request_lut[16] = {0U}; + +static void mk_sw_request_lut(void) +{ + uint32_t parity = 0U; + + for (int request = 0; request < sizeof(sw_request_lut); request++) { + parity = request; + parity ^= parity >> 2; + parity ^= parity >> 1; + + /* + * Move A[3:3], RnW, APnDP bits to their position, + * add start bit, stop bit(6), and park bit. + */ + sw_request_lut[request] = BIT(7) | (request << 1) | BIT(0); + /* Add parity bit */ + if (parity & 0x01U) { + sw_request_lut[request] |= BIT(5); + } + } + + LOG_HEXDUMP_DBG(sw_request_lut, sizeof(sw_request_lut), "request lut"); +} + +static ALWAYS_INLINE uint32_t sw_get32bit_parity(uint32_t data) +{ + data ^= data >> 16; + data ^= data >> 8; + data ^= data >> 4; + data ^= data >> 2; + data ^= data >> 1; + + return data & 1U; +} + +static ALWAYS_INLINE void pin_delay_asm(uint32_t delay) +{ +#if defined(CONFIG_SOC_SERIES_NRF52X) + __asm volatile ("movs r3, %[p]\n" + ".start_%=:\n" + "subs r3, #1\n" + "bne .start_%=\n" + : + : [p] "r" (delay) + : "r3", "cc" + ); +#else +#error "Not defined for this SoC family" +#endif +} + +static ALWAYS_INLINE void pin_platform_set(void *base, uint8_t pin) +{ +#if defined(CONFIG_SOC_SERIES_NRF52X) + NRF_GPIO_Type * reg = base; + + reg->OUTSET = BIT(pin); +#else +#error "Not defined for this SoC family" +#endif +} + +static ALWAYS_INLINE void pin_platform_clr(void *base, uint8_t pin) +{ +#if defined(CONFIG_SOC_SERIES_NRF52X) + NRF_GPIO_Type * reg = base; + + reg->OUTCLR = BIT(pin); +#else +#error "Not defined for this SoC family" +#endif +} + +static ALWAYS_INLINE uint32_t pin_platform_get(void *base, uint8_t pin) +{ +#if defined(CONFIG_SOC_SERIES_NRF52X) + NRF_GPIO_Type * reg = base; + + return ((reg->IN >> pin) & 1); +#else +#error "Not defined for this SoC family" +#endif +} + +/* Set SWCLK DAP hardware output pin to high level */ +static ALWAYS_INLINE void pin_swclk_set(const struct device *dev) +{ + const struct sw_config *config = dev->config; + const struct gpio_dt_spec *dt_spec = &config->clk; + + pin_platform_set(config->clk_reg, dt_spec->pin); +} + +/* Set SWCLK DAP hardware output pin to low level */ +static ALWAYS_INLINE void pin_swclk_clr(const struct device *dev) +{ + const struct sw_config *config = dev->config; + const struct gpio_dt_spec *dt_spec = &config->clk; + + pin_platform_clr(config->clk_reg, dt_spec->pin); +} + +/* Set the SWDIO DAP hardware output pin to high level */ +static ALWAYS_INLINE void pin_swdio_set(const struct device *dev) +{ + const struct sw_config *config = dev->config; + const struct gpio_dt_spec *dt_spec = &config->dout; + + pin_platform_set(config->dout_reg, dt_spec->pin); +} + +/* Set the SWDIO DAP hardware output pin to low level */ +static ALWAYS_INLINE void pin_swdio_clr(const struct device *dev) +{ + const struct sw_config *config = dev->config; + const struct gpio_dt_spec *dt_spec = &config->dout; + + pin_platform_clr(config->dout_reg, dt_spec->pin); +} + +/* Set the SWDIO DAP hardware output pin to bit level */ +static ALWAYS_INLINE void pin_swdio_out(const struct device *dev, + const uint32_t bit) +{ + const struct sw_config *config = dev->config; + const struct gpio_dt_spec *dt_spec = &config->dout; + + if (bit & 1U) { + pin_platform_set(config->dout_reg, dt_spec->pin); + } else { + pin_platform_clr(config->dout_reg, dt_spec->pin); + } +} + +/* Return current level of the SWDIO DAP hardware input pin */ +static ALWAYS_INLINE uint32_t pin_swdio_in(const struct device *dev) +{ + const struct sw_config *config = dev->config; + const struct gpio_dt_spec *dt_spec = &config->din; + + return pin_platform_get(config->din_reg, dt_spec->pin); +} + +/* + * Configure the SWDIO DAP hardware to output mode. + * This is default configuration for every transfer. + */ +static ALWAYS_INLINE void pin_swdio_out_enable(const struct device *dev) +{ + const struct sw_config *config = dev->config; + const struct gpio_dt_spec *dt_spec = &config->dnoe; + + pin_platform_set(config->dnoe_reg, dt_spec->pin); +} + +/* + * Configure the SWDIO DAP hardware to input mode. + */ +static ALWAYS_INLINE void pin_swdio_out_disable(const struct device *dev) +{ + const struct sw_config *config = dev->config; + const struct gpio_dt_spec *dt_spec = &config->dnoe; + + pin_platform_clr(config->dnoe_reg, dt_spec->pin); +} + +#define SW_CLOCK_CYCLE(dev, delay) \ + do { \ + pin_swclk_clr(dev); \ + pin_delay_asm(delay); \ + pin_swclk_set(dev); \ + pin_delay_asm(delay); \ + } while (0) + +#define SW_WRITE_BIT(dev, bit, delay) \ + do { \ + pin_swdio_out(dev, bit); \ + pin_swclk_clr(dev); \ + pin_delay_asm(delay); \ + pin_swclk_set(dev); \ + pin_delay_asm(delay); \ + } while (0) + +#define SW_READ_BIT(dev, bit, delay) \ + do { \ + pin_swclk_clr(dev); \ + pin_delay_asm(delay); \ + bit = pin_swdio_in(dev); \ + pin_swclk_set(dev); \ + pin_delay_asm(delay); \ + } while (0) + +static int sw_sequence(const struct device *dev, uint32_t count, + const uint8_t *data) +{ + struct sw_cfg_data *sw_data = dev->data; + unsigned int key; + uint32_t val = 0; + uint32_t n = 0; + + LOG_DBG("count %u", count); + LOG_HEXDUMP_DBG(data, count, "sequence bit data"); + key = irq_lock(); + + while (count--) { + if (n == 0U) { + val = *data++; + n = 8U; + } + if (val & 1U) { + pin_swdio_set(dev); + } else { + pin_swdio_clr(dev); + } + SW_CLOCK_CYCLE(dev, sw_data->clock_delay); + val >>= 1; + n--; + } + + irq_unlock(key); + + return 0; +} + +static ALWAYS_INLINE void sw_cycle_turnaround(const struct device *dev) +{ + struct sw_cfg_data *sw_data = dev->data; + uint32_t n; + + for (n = sw_data->turnaround; n; n--) { + SW_CLOCK_CYCLE(dev, sw_data->clock_delay); + } +} + +static int sw_transfer(const struct device *dev, + const uint8_t request, uint32_t *const data, + const uint8_t idle_cycles, uint8_t *const response) +{ + struct sw_cfg_data *sw_data = dev->data; + unsigned int key; + uint32_t ack; + uint32_t bit; + uint32_t val; + uint32_t parity = 0; + uint32_t n; + + LOG_DBG("request 0x%02x idle %u", request, idle_cycles); + if (!(request & SWDP_REQUEST_RnW)) { + LOG_DBG("write data 0x%08x", *data); + parity = sw_get32bit_parity(*data); + } + + key = irq_lock(); + + val = sw_request_lut[request & 0xFU]; + for (n = 8U; n; n--) { + SW_WRITE_BIT(dev, val, sw_data->clock_delay); + val >>= 1; + } + + pin_swdio_out_disable(dev); + sw_cycle_turnaround(dev); + + /* Acknowledge response */ + SW_READ_BIT(dev, bit, sw_data->clock_delay); + ack = bit << 0; + SW_READ_BIT(dev, bit, sw_data->clock_delay); + ack |= bit << 1; + SW_READ_BIT(dev, bit, sw_data->clock_delay); + ack |= bit << 2; + + if (ack == SWDP_ACK_OK) { + /* Data transfer */ + if (request & SWDP_REQUEST_RnW) { + /* Read data */ + val = 0U; + for (n = 32U; n; n--) { + /* Read RDATA[0:31] */ + SW_READ_BIT(dev, bit, sw_data->clock_delay); + val >>= 1; + val |= bit << 31; + } + + /* Read parity bit */ + SW_READ_BIT(dev, bit, sw_data->clock_delay); + sw_cycle_turnaround(dev); + pin_swdio_out_enable(dev); + + if ((sw_get32bit_parity(val) ^ bit) & 1U) { + ack = SWDP_TRANSFER_ERROR; + } + + if (data) { + *data = val; + } + + } else { + sw_cycle_turnaround(dev); + + pin_swdio_out_enable(dev); + /* Write data */ + val = *data; + for (n = 32U; n; n--) { + SW_WRITE_BIT(dev, val, sw_data->clock_delay); + val >>= 1; + } + + /* Write parity bit */ + SW_WRITE_BIT(dev, parity, sw_data->clock_delay); + } + /* Idle cycles */ + n = idle_cycles; + if (n) { + pin_swdio_out(dev, 0U); + for (; n; n--) { + SW_CLOCK_CYCLE(dev, sw_data->clock_delay); + } + } + + pin_swdio_out(dev, 1U); + irq_unlock(key); + if (request & SWDP_REQUEST_RnW) { + LOG_DBG("read data 0x%08x", *data); + } + + if (response) { + *response = (uint8_t)ack; + } + + return 0; + } + + if ((ack == SWDP_ACK_WAIT) || (ack == SWDP_ACK_FAULT)) { + /* WAIT OR fault response */ + if (sw_data->data_phase) { + for (n = 32U + 1U + sw_data->turnaround; n; n--) { + /* Dummy Read RDATA[0:31] + Parity */ + SW_CLOCK_CYCLE(dev, sw_data->clock_delay); + } + } else { + sw_cycle_turnaround(dev); + } + + pin_swdio_out_enable(dev); + pin_swdio_out(dev, 1U); + irq_unlock(key); + LOG_DBG("Transfer wait or fault"); + if (response) { + *response = (uint8_t)ack; + } + + return 0; + } + + /* Protocol error */ + for (n = sw_data->turnaround + 32U + 1U; n; n--) { + /* Back off data phase */ + SW_CLOCK_CYCLE(dev, sw_data->clock_delay); + } + + pin_swdio_out_enable(dev); + pin_swdio_out(dev, 1U); + irq_unlock(key); + LOG_INF("Protocol error"); + if (response) { + *response = (uint8_t)ack; + } + + return 0; +} + +static int sw_set_pins(const struct device *dev, + const uint8_t pins, const uint8_t value) +{ + const struct sw_config *config = dev->config; + + LOG_DBG("pins 0x%02x value 0x%02x", pins, value); + + if (pins & BIT(SWDP_SWCLK_PIN)) { + if (value & BIT(SWDP_SWCLK_PIN)) { + gpio_pin_set_dt(&config->clk, 1); + } else { + gpio_pin_set_dt(&config->clk, 0); + } + } + + if (pins & BIT(SWDP_SWDIO_PIN)) { + if (value & BIT(SWDP_SWDIO_PIN)) { + gpio_pin_set_dt(&config->dout, 1); + } else { + gpio_pin_set_dt(&config->dout, 0); + } + } + + if (pins & BIT(SWDP_nRESET_PIN)) { + if (value & BIT(SWDP_nRESET_PIN)) { + gpio_pin_set_dt(&config->reset, 1); + } else { + gpio_pin_set_dt(&config->reset, 0); + } + } + + return 0; +} + +static int sw_get_pins(const struct device *dev, uint8_t *const state) +{ + const struct sw_config *config = dev->config; + uint32_t val; + + val = gpio_pin_get_dt(&config->reset); + *state = val ? BIT(SWDP_nRESET_PIN) : 0; + + val = gpio_pin_get_dt(&config->din); + *state |= val ? BIT(SWDP_SWDIO_PIN) : 0; + + val = gpio_pin_get_dt(&config->clk); + *state |= val ? BIT(SWDP_SWCLK_PIN) : 0; + + LOG_DBG("pins state 0x%02x", *state); + + return 0; +} + +static int sw_set_clock(const struct device *dev, const uint32_t clock) +{ + const struct sw_config *config = dev->config; + struct sw_cfg_data *sw_data = dev->data; + uint32_t delay; + + sw_data->fast_clock = false; + delay = ((CPU_CLOCK / 2U) + (clock - 1U)) / clock; + + if (delay > config->port_write_cycles) { + delay -= config->port_write_cycles; + delay = (delay + (DELAY_SLOW_CYCLES - 1U)) / DELAY_SLOW_CYCLES; + } else { + delay = 1U; + } + + sw_data->clock_delay = delay; + + LOG_WRN("cpu_clock %d, delay %d", CPU_CLOCK, sw_data->clock_delay); + + return 0; +} + +static int sw_configure(const struct device *dev, + const uint8_t turnaround, const bool data_phase) +{ + struct sw_cfg_data *sw_data = dev->data; + + sw_data->turnaround = turnaround; + sw_data->data_phase = data_phase; + + LOG_INF("turnaround %d, data_phase %d", + sw_data->turnaround, sw_data->data_phase); + + return 0; +} + +static int sw_port_on(const struct device *dev) +{ + const struct sw_config *config = dev->config; + + gpio_pin_set_dt(&config->clk, 1); + gpio_pin_set_dt(&config->dout, 1); + gpio_pin_set_dt(&config->dnoe, 1); + gpio_pin_set_dt(&config->noe, 1); + gpio_pin_set_dt(&config->reset, 1); + + return 0; +} + +static int sw_port_off(const struct device *dev) +{ + const struct sw_config *config = dev->config; + + gpio_pin_set_dt(&config->dnoe, 0); + gpio_pin_set_dt(&config->noe, 0); + gpio_pin_set_dt(&config->reset, 1); + + return 0; +} + +static int sw_gpio_init(const struct device *dev) +{ + const struct sw_config *config = dev->config; + struct sw_cfg_data *sw_data = dev->data; + int ret; + + ret = gpio_pin_configure_dt(&config->clk, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + + ret = gpio_pin_configure_dt(&config->dout, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + + ret = gpio_pin_configure_dt(&config->din, GPIO_INPUT); + if (ret) { + return ret; + } + + ret = gpio_pin_configure_dt(&config->dnoe, GPIO_OUTPUT_INACTIVE); + if (ret) { + return ret; + } + + ret = gpio_pin_configure_dt(&config->noe, GPIO_OUTPUT_INACTIVE); + if (ret) { + return ret; + } + + ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE); + if (ret) { + return ret; + } + + sw_data->turnaround = 1U; + sw_data->data_phase = false; + sw_data->fast_clock = false; + sw_data->clock_delay = CLOCK_DELAY(SWDP_DEFAULT_SWCLK_FREQUENCY, + config->port_write_cycles); + mk_sw_request_lut(); + + return 0; +} + +static struct swdp_api swdp_bitbang_api = { + .swdp_sequence = sw_sequence, + .swdp_transfer = sw_transfer, + .swdp_set_pins = sw_set_pins, + .swdp_get_pins = sw_get_pins, + .swdp_set_clock = sw_set_clock, + .swdp_configure = sw_configure, + .swdp_port_on = sw_port_on, + .swdp_port_off = sw_port_off, +}; + +#define SW_GPIOS_GET_REG(n, gpios) \ + INT_TO_POINTER(DT_REG_ADDR(DT_PHANDLE(DT_DRV_INST(n), gpios))) + +#define SW_DEVICE_DEFINE(n) \ + static const struct sw_config sw_cfg_##n = { \ + .clk = GPIO_DT_SPEC_INST_GET(n, clk_gpios), \ + .dout = GPIO_DT_SPEC_INST_GET(n, dout_gpios), \ + .din = GPIO_DT_SPEC_INST_GET(n, din_gpios), \ + .dnoe = GPIO_DT_SPEC_INST_GET(n, dnoe_gpios), \ + .noe = GPIO_DT_SPEC_INST_GET(n, noe_gpios), \ + .reset = GPIO_DT_SPEC_INST_GET(n, reset_gpios), \ + .port_write_cycles = DT_INST_PROP(n, port_write_cycles), \ + .clk_reg = SW_GPIOS_GET_REG(n, clk_gpios), \ + .dout_reg = SW_GPIOS_GET_REG(n, dout_gpios), \ + .din_reg = SW_GPIOS_GET_REG(n, din_gpios), \ + .dnoe_reg = SW_GPIOS_GET_REG(n, dnoe_gpios), \ + }; \ + \ + static struct sw_cfg_data sw_data_##n; \ + \ + DEVICE_DT_INST_DEFINE(n, sw_gpio_init, NULL, \ + &sw_data_##n, &sw_cfg_##n, \ + POST_KERNEL, CONFIG_DP_DRIVER_INIT_PRIO, \ + &swdp_bitbang_api); \ + +DT_INST_FOREACH_STATUS_OKAY(SW_DEVICE_DEFINE) diff --git a/dts/bindings/misc/zephyr,swdp-gpio.yaml b/dts/bindings/misc/zephyr,swdp-gpio.yaml new file mode 100644 index 00000000000..3264dd58001 --- /dev/null +++ b/dts/bindings/misc/zephyr,swdp-gpio.yaml @@ -0,0 +1,46 @@ +# Copyright (c) 2019, Phytec Messtechnik GmbH +# SPDX-License-Identifier: Apache-2.0 + +description: > + This is a representation of the Serial Wire Debug Port interface + implementation by GPIO bit-banging. + +compatible: "zephyr,swdp-gpio" + +include: base.yaml + +properties: + clk-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWCLK output + + dout-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWDIO output + + din-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWDIO input + + dnoe-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWDIO NOE output + + noe-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWD NOE output + + reset-gpios: + type: phandle-array + required: true + description: GPIO pin used for RESET output + + port-write-cycles: + type: int + required: true + description: Number of processor cycles for I/O Port write operations diff --git a/include/zephyr/drivers/swdp.h b/include/zephyr/drivers/swdp.h new file mode 100644 index 00000000000..f15a4b40ecd --- /dev/null +++ b/include/zephyr/drivers/swdp.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 PHYTEC Messtechnik GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Serial Wire Debug Port interface driver API + */ + +#ifndef ZEPHYR_INCLUDE_SWDP_H_ +#define ZEPHYR_INCLUDE_SWDP_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* SWDP packet request bits */ +#define SWDP_REQUEST_APnDP BIT(0) +#define SWDP_REQUEST_RnW BIT(1) +#define SWDP_REQUEST_A2 BIT(2) +#define SWDP_REQUEST_A3 BIT(3) + +/* SWDP acknowledge response bits */ +#define SWDP_ACK_OK BIT(0) +#define SWDP_ACK_WAIT BIT(1) +#define SWDP_ACK_FAULT BIT(2) + +/* SWDP transfer or parity error */ +#define SWDP_TRANSFER_ERROR BIT(3) + +/* SWDP Interface pins */ +#define SWDP_SWCLK_PIN 0U +#define SWDP_SWDIO_PIN 1U +#define SWDP_nRESET_PIN 7U + +/* + * Serial Wire Interface (SWDP) driver API. + * This is the mandatory API any Serial Wire driver needs to expose. + */ +struct swdp_api { + /* Generate SWJ Sequence according to sequence bit count and bit data */ + int (*swdp_sequence)(const struct device *dev, + uint32_t count, + const uint8_t *data); + + /* + * Perform SWDP transfer based on host request value and store + * acknowledge response bits ACK[0:2]. + */ + int (*swdp_transfer)(const struct device *dev, + uint8_t request, + uint32_t *data, + uint8_t idle_cycles, + uint8_t *response); + + /* Set SWCLK, SWDPIO, and nRESET pins state */ + int (*swdp_set_pins)(const struct device *dev, + uint8_t pins, uint8_t value); + + /* Get SWCLK, SWDPIO, and nRESET pins state */ + int (*swdp_get_pins)(const struct device *dev, uint8_t *state); + + /* Set SWCLK frequency */ + int (*swdp_set_clock)(const struct device *dev, uint32_t clock); + + /* + * Configure interface, line turnaround and whether data phase is + * forced after WAIN and FAULT response. + */ + int (*swdp_configure)(const struct device *dev, + uint8_t turnaround, + bool data_phase); + + /* + * Enable interface, set SWDPIO to output mode + * and set SWCLK and nRESET to default high level. + */ + int (*swdp_port_on)(const struct device *dev); + + /* Disables interface, set SWCLK, SWDPIO, nRESET to High-Z mode. */ + int (*swdp_port_off)(const struct device *dev); +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_SWDP_H_ */ From 70f8c07c3686c75813fb96695c6b5f9b09ab50fc Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Wed, 9 Oct 2019 00:02:30 +0200 Subject: [PATCH 02/17] [nrf fromtree] dap: add CMSIS-DAP compatible controller Add CMSIS-DAP compatible controller which is a handler between the host interface and SWD driver. The controller follows CMSIS-DAP reference implementation. It expects a request buffer from the host interface, splits it to simple transfers and forwards to the DP driver, and finally returns a response buffer to the host. Interface to the host can be implemented with USB HID device support. Controller implements only SW-DP support and is tested with pyOCD and ADIv5.x. Signed-off-by: Johann Fischer (cherry picked from commit 7c9259abbceb84880906e4d8e594308db0ae00f0) --- dts/bindings/misc/zephyr,swdp-gpio.yaml | 72 +- subsys/CMakeLists.txt | 1 + subsys/Kconfig | 1 + subsys/dap/CMakeLists.txt | 7 + subsys/dap/Kconfig | 24 + subsys/dap/cmsis_dap.c | 925 ++++++++++++++++++++++++ subsys/dap/cmsis_dap.h | 119 +++ 7 files changed, 1113 insertions(+), 36 deletions(-) create mode 100644 subsys/dap/CMakeLists.txt create mode 100644 subsys/dap/Kconfig create mode 100644 subsys/dap/cmsis_dap.c create mode 100644 subsys/dap/cmsis_dap.h diff --git a/dts/bindings/misc/zephyr,swdp-gpio.yaml b/dts/bindings/misc/zephyr,swdp-gpio.yaml index 3264dd58001..5d38449ad49 100644 --- a/dts/bindings/misc/zephyr,swdp-gpio.yaml +++ b/dts/bindings/misc/zephyr,swdp-gpio.yaml @@ -2,45 +2,45 @@ # SPDX-License-Identifier: Apache-2.0 description: > - This is a representation of the Serial Wire Debug Port interface - implementation by GPIO bit-banging. + This is a representation of the Serial Wire Debug Port interface + implementation by GPIO bit-banging. compatible: "zephyr,swdp-gpio" include: base.yaml properties: - clk-gpios: - type: phandle-array - required: true - description: GPIO pin used for SWCLK output - - dout-gpios: - type: phandle-array - required: true - description: GPIO pin used for SWDIO output - - din-gpios: - type: phandle-array - required: true - description: GPIO pin used for SWDIO input - - dnoe-gpios: - type: phandle-array - required: true - description: GPIO pin used for SWDIO NOE output - - noe-gpios: - type: phandle-array - required: true - description: GPIO pin used for SWD NOE output - - reset-gpios: - type: phandle-array - required: true - description: GPIO pin used for RESET output - - port-write-cycles: - type: int - required: true - description: Number of processor cycles for I/O Port write operations + clk-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWCLK output + + dout-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWDIO output + + din-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWDIO input + + dnoe-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWDIO NOE output + + noe-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWD NOE output + + reset-gpios: + type: phandle-array + required: true + description: GPIO pin used for RESET output + + port-write-cycles: + type: int + required: true + description: Number of processor cycles for I/O Port write operations diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index ccd2c8c88bf..584559fa50e 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -37,6 +37,7 @@ add_subdirectory_ifdef(CONFIG_ARM_SIP_SVC_SUBSYS sip_svc) add_subdirectory_ifdef(CONFIG_BINDESC bindesc) add_subdirectory_ifdef(CONFIG_BT bluetooth) add_subdirectory_ifdef(CONFIG_CONSOLE_SUBSYS console) +add_subdirectory_ifdef(CONFIG_DAP dap) add_subdirectory_ifdef(CONFIG_DEMAND_PAGING demand_paging) add_subdirectory_ifdef(CONFIG_DISK_ACCESS disk) add_subdirectory_ifdef(CONFIG_DSP dsp) diff --git a/subsys/Kconfig b/subsys/Kconfig index 2e083fb4c89..2c708e26d59 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -11,6 +11,7 @@ source "subsys/bindesc/Kconfig" source "subsys/bluetooth/Kconfig" source "subsys/canbus/Kconfig" source "subsys/console/Kconfig" +source "subsys/dap/Kconfig" source "subsys/debug/Kconfig" source "subsys/demand_paging/Kconfig" source "subsys/dfu/Kconfig" diff --git a/subsys/dap/CMakeLists.txt b/subsys/dap/CMakeLists.txt new file mode 100644 index 00000000000..c021d240711 --- /dev/null +++ b/subsys/dap/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2019, PHYTEC Messtechnik GmbH +# SPDX-License-Identifier: Apache-2.0 + +zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +zephyr_library() +zephyr_library_sources(cmsis_dap.c) diff --git a/subsys/dap/Kconfig b/subsys/dap/Kconfig new file mode 100644 index 00000000000..43485958102 --- /dev/null +++ b/subsys/dap/Kconfig @@ -0,0 +1,24 @@ +# Copyright (c) 2018-2019 PHYTEC Messtechnik GmbH +# SPDX-License-Identifier: Apache-2.0 + +menuconfig DAP + bool "Debug Access Port support [EXPERIMENTAL]" + select EXPERIMENTAL + select DP_DRIVER + help + Debug Access Port support (currently CMSIS DAP only) + +if DAP + +config CMSIS_DAP_PACKET_COUNT + int "Maximum packet buffers for request and response data." + default 4 + range 1 255 + help + Maximum packet buffers for request and response data. + +module = DAP +module-str = dap +source "subsys/logging/Kconfig.template.log_config" + +endif # DAP diff --git a/subsys/dap/cmsis_dap.c b/subsys/dap/cmsis_dap.c new file mode 100644 index 00000000000..f1fc537366e --- /dev/null +++ b/subsys/dap/cmsis_dap.c @@ -0,0 +1,925 @@ +/* + * Copyright (c) 2018-2019 PHYTEC Messtechnik GmbH + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This file is based on DAP.c from CMSIS-DAP Source (Revision: V2.0.0) + * https://github.com/ARM-software/CMSIS_5/tree/develop/CMSIS/DAP/Firmware + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +LOG_MODULE_REGISTER(dap, CONFIG_DAP_LOG_LEVEL); + +const char dap_fw_ver[] = DAP_FW_VER; + +#define DAP_STATE_CONNECTED 0 + +struct dap_context { + struct device *swdp_dev; + atomic_t state; + uint8_t debug_port; + uint8_t capabilities; + uint8_t pkt_size; + struct { + /* Idle cycles after transfer */ + uint8_t idle_cycles; + /* Number of retries after WAIT response */ + uint16_t retry_count; + /* Number of retries if read value does not match */ + uint16_t match_retry; + /* Match Mask */ + uint32_t match_mask; + } transfer; +}; + +static struct dap_context dap_ctx[1]; + +/* Get DAP Information */ +static uint16_t dap_info(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + uint8_t *info = response + 1; + uint8_t id = request[0]; + uint8_t length = 0U; + + switch (id) { + case DAP_ID_VENDOR: + LOG_DBG("ID_VENDOR unsupported"); + break; + case DAP_ID_PRODUCT: + LOG_DBG("ID_PRODUCT unsupported"); + break; + case DAP_ID_SER_NUM: + LOG_DBG("ID_SER_NUM unsupported"); + break; + case DAP_ID_FW_VER: + LOG_DBG("ID_FW_VER"); + memcpy(info, dap_fw_ver, sizeof(dap_fw_ver)); + length = (uint8_t)sizeof(dap_fw_ver); + break; + case DAP_ID_DEVICE_VENDOR: + LOG_DBG("ID_DEVICE_VENDOR unsupported"); + break; + case DAP_ID_DEVICE_NAME: + LOG_DBG("ID_DEVICE_NAME unsupported"); + break; + case DAP_ID_CAPABILITIES: + info[0] = ctx->capabilities; + LOG_DBG("ID_CAPABILITIES 0x%0x", info[0]); + length = 1U; + break; + case DAP_ID_SWO_BUFFER_SIZE: + LOG_DBG("ID_SWO_BUFFER_SIZE unsupported"); + break; + case DAP_ID_PACKET_SIZE: + LOG_DBG("ID_PACKET_SIZE"); + sys_put_le16(ctx->capabilities, &info[0]); + length = 2U; + break; + case DAP_ID_PACKET_COUNT: + LOG_DBG("ID_PACKET_COUNT"); + info[0] = CONFIG_CMSIS_DAP_PACKET_COUNT; + length = 1U; + break; + default: + LOG_DBG("unsupported ID"); + break; + } + + response[0] = (uint8_t)length; + + return length + 1U; +} + +/* Process Host Status command and prepare response */ +static uint16_t dap_host_status(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + switch (request[0]) { + case DAP_DEBUGGER_CONNECTED: + if (request[1]) { + LOG_INF("Debugger connected"); + } else { + LOG_INF("Debugger disconnected"); + } + break; + case DAP_TARGET_RUNNING: + LOG_DBG("unsupported"); + break; + default: + *response = DAP_ERROR; + return 1U; + } + + response[0] = DAP_OK; + return 1U; +} + +/* Process Connect command and prepare response */ +static uint16_t dap_connect(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + const struct swdp_api *api = ctx->swdp_dev->api; + uint8_t port; + + if (request[0] == DAP_PORT_AUTODETECT) { + port = DAP_PORT_SWD; + } else { + port = request[0]; + } + + switch (port) { + case DAP_PORT_SWD: + LOG_INF("port swd"); + ctx->debug_port = DAP_PORT_SWD; + + if (atomic_test_and_set_bit(&ctx->state, + DAP_STATE_CONNECTED)) { + LOG_ERR("DAP device is already connected"); + port = DAP_ERROR; + break; + } + + api->swdp_port_on(ctx->swdp_dev); + break; + case DAP_PORT_JTAG: + LOG_ERR("port unsupported"); + port = DAP_ERROR; + break; + default: + LOG_DBG("port disabled"); + port = DAP_PORT_DISABLED; + break; + } + + response[0] = port; + return 1U; +} + +/* Process Disconnect command and prepare response */ +static uint16_t dap_disconnect(struct dap_context *const ctx, + uint8_t *const response) +{ + const struct swdp_api *api = ctx->swdp_dev->api; + + LOG_DBG(""); + + ctx->debug_port = DAP_PORT_DISABLED; + + if (atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) { + api->swdp_port_off(ctx->swdp_dev); + } else { + LOG_WRN("DAP device is not connected"); + } + + response[0] = DAP_OK; + atomic_clear_bit(&ctx->state, DAP_STATE_CONNECTED); + + return 1; +} + +/* Process Delay command and prepare response */ +static uint16_t dap_delay(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + uint16_t delay = sys_get_le16(&request[0]); + + LOG_DBG("dap delay %u ms", delay); + + k_busy_wait(delay * USEC_PER_MSEC); + response[0] = DAP_OK; + + return 1U; +} + +/* Process Reset Target command and prepare response */ +static uint16_t dap_reset_target(struct dap_context *const ctx, + uint8_t *const response) +{ + response[0] = DAP_OK; + response[1] = 0U; + LOG_WRN("unsupported"); + + return 2; +} + +/* Process SWJ Pins command and prepare response */ +static uint16_t dap_swj_pins(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + const struct swdp_api *api = ctx->swdp_dev->api; + uint8_t value = request[0]; + uint8_t select = request[1]; + uint32_t wait = sys_get_le32(&request[2]); + uint8_t state; + + if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) { + LOG_ERR("DAP device is not connected"); + response[0] = DAP_ERROR; + return 1U; + } + + /* Skip if nothing selected. */ + if (select) { + api->swdp_set_pins(ctx->swdp_dev, select, value); + } + + /* TODO: implement wait */ + api->swdp_get_pins(ctx->swdp_dev, &state); + LOG_ERR("select 0x%02x, value 0x%02x, wait %u, state 0x%02x", + select, value, wait, state); + + response[0] = state; + + return sizeof(state); +} + +/* Process SWJ Clock command and prepare response */ +static uint16_t dap_swj_clock(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + const struct swdp_api *api = ctx->swdp_dev->api; + uint32_t clk = sys_get_le32(&request[0]); + + LOG_DBG("clock %d", clk); + + if (atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) { + if (clk) { + api->swdp_set_clock(ctx->swdp_dev, clk); + response[0] = DAP_OK; + } else { + response[0] = DAP_ERROR; + } + } else { + LOG_WRN("DAP device is not connected"); + response[0] = DAP_OK; + } + + return 1U; +} + +/* Process SWJ Sequence command and prepare response */ +static uint16_t dap_swj_sequence(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + const struct swdp_api *api = ctx->swdp_dev->api; + uint16_t count = request[0]; + + LOG_DBG("count %u", count); + + if (count == 0U) { + count = 256U; + } + + if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) { + LOG_ERR("DAP device is not connected"); + response[0] = DAP_ERROR; + return 1U; + } + + api->swdp_sequence(ctx->swdp_dev, count, &request[1]); + response[0] = DAP_OK; + + return 1U; +} + +/* Process SWD Configure command and prepare response */ +static uint16_t dap_swdp_configure(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + const struct swdp_api *api = ctx->swdp_dev->api; + uint8_t turnaround = (request[0] & 0x03U) + 1U; + bool data_phase = (request[0] & 0x04U) ? true : false; + + if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) { + LOG_ERR("DAP device is not connected"); + response[0] = DAP_ERROR; + return 1U; + } + + api->swdp_configure(ctx->swdp_dev, turnaround, data_phase); + response[0] = DAP_OK; + + return 1U; +} + +/* Process Transfer Configure command and prepare response */ +static uint16_t dap_transfer_cfg(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + + ctx->transfer.idle_cycles = request[0]; + ctx->transfer.retry_count = sys_get_le16(&request[1]); + ctx->transfer.match_retry = sys_get_le16(&request[3]); + LOG_DBG("idle_cycles %d, retry_count %d, match_retry %d", + ctx->transfer.idle_cycles, + ctx->transfer.retry_count, + ctx->transfer.match_retry); + + response[0] = DAP_OK; + return 1U; +} + +static inline uint8_t do_swdp_transfer(struct dap_context *const ctx, + const uint8_t req_val, + uint32_t *data) +{ + const struct swdp_api *api = ctx->swdp_dev->api; + uint32_t retry = ctx->transfer.retry_count; + uint8_t rspns_val; + + do { + api->swdp_transfer(ctx->swdp_dev, + req_val, + data, + ctx->transfer.idle_cycles, + &rspns_val); + } while ((rspns_val == SWDP_ACK_WAIT) && retry--); + + return rspns_val; +} + +static uint8_t swdp_transfer_match(struct dap_context *const ctx, + const uint8_t req_val, + const uint32_t match_val) +{ + uint32_t match_retry = ctx->transfer.match_retry; + uint32_t data; + uint8_t rspns_val; + + if (req_val & SWDP_REQUEST_APnDP) { + /* Post AP read, result will be returned on the next transfer */ + rspns_val = do_swdp_transfer(ctx, req_val, NULL); + if (rspns_val != SWDP_ACK_OK) { + return rspns_val; + } + } + + do { + /* + * Read register until its value matches + * or retry counter expires + */ + rspns_val = do_swdp_transfer(ctx, req_val, &data); + if (rspns_val != SWDP_ACK_OK) { + return rspns_val; + } + + } while (((data & ctx->transfer.match_mask) != match_val) && + match_retry--); + + if ((data & ctx->transfer.match_mask) != match_val) { + rspns_val |= DAP_TRANSFER_MISMATCH; + } + + return rspns_val; +} + +/* + * Process SWD Transfer command and prepare response + * pyOCD counterpart is _encode_transfer_data. + * Packet format: one byte DAP_index (ignored) + * one bytes transfer_count + * following by number transfer_count pairs of + * one byte request (register) + * four byte data (for write request only) + */ +static uint16_t dap_swdp_transfer(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + uint8_t *rspns_buf; + const uint8_t *req_buf; + uint8_t rspns_cnt = 0; + uint8_t rspns_val = 0; + bool post_read = false; + uint32_t check_write = 0; + uint8_t req_cnt; + uint8_t req_val; + uint32_t match_val; + uint32_t data; + + /* Ignore DAP index request[0] */ + req_cnt = request[1]; + req_buf = request + sizeof(req_cnt) + 1; + rspns_buf = response + (sizeof(rspns_cnt) + sizeof(rspns_val)); + + for (; req_cnt; req_cnt--) { + req_val = *req_buf++; + if (req_val & SWDP_REQUEST_RnW) { + /* Read register */ + if (post_read) { + /* + * Read was posted before, read previous AP + * data or post next AP read. + */ + if ((req_val & (SWDP_REQUEST_APnDP | + DAP_TRANSFER_MATCH_VALUE)) != + SWDP_REQUEST_APnDP) { + req_val = DP_RDBUFF | SWDP_REQUEST_RnW; + post_read = false; + } + + rspns_val = do_swdp_transfer(ctx, req_val, &data); + if (rspns_val != SWDP_ACK_OK) { + break; + } + + /* Store previous AP data */ + sys_put_le32(data, rspns_buf); + rspns_buf += sizeof(data); + } + if (req_val & DAP_TRANSFER_MATCH_VALUE) { + LOG_INF("match value read"); + /* Read with value match */ + match_val = sys_get_le32(req_buf); + req_buf += sizeof(match_val); + + rspns_val = swdp_transfer_match(ctx, req_val, match_val); + if (rspns_val != SWDP_ACK_OK) { + break; + } + + } else if (req_val & SWDP_REQUEST_APnDP) { + /* Normal read */ + if (!post_read) { + /* Post AP read */ + rspns_val = do_swdp_transfer(ctx, req_val, NULL); + if (rspns_val != SWDP_ACK_OK) { + break; + } + post_read = true; + } + } else { + /* Read DP register */ + rspns_val = do_swdp_transfer(ctx, req_val, &data); + if (rspns_val != SWDP_ACK_OK) { + break; + } + /* Store data */ + sys_put_le32(data, rspns_buf); + rspns_buf += sizeof(data); + } + check_write = 0U; + } else { + /* Write register */ + if (post_read) { + /* Read previous data */ + rspns_val = do_swdp_transfer(ctx, + DP_RDBUFF | SWDP_REQUEST_RnW, + &data); + if (rspns_val != SWDP_ACK_OK) { + break; + } + + /* Store previous data */ + sys_put_le32(data, rspns_buf); + rspns_buf += sizeof(data); + post_read = false; + } + /* Load data */ + data = sys_get_le32(req_buf); + req_buf += sizeof(data); + if (req_val & DAP_TRANSFER_MATCH_MASK) { + /* Write match mask */ + ctx->transfer.match_mask = data; + rspns_val = SWDP_ACK_OK; + } else { + /* Write DP/AP register */ + rspns_val = do_swdp_transfer(ctx, req_val, &data); + if (rspns_val != SWDP_ACK_OK) { + break; + } + + check_write = 1U; + } + } + rspns_cnt++; + } + + if (rspns_val == SWDP_ACK_OK) { + if (post_read) { + /* Read previous data */ + rspns_val = do_swdp_transfer(ctx, + DP_RDBUFF | SWDP_REQUEST_RnW, + &data); + if (rspns_val != SWDP_ACK_OK) { + goto end; + } + + /* Store previous data */ + sys_put_le32(data, rspns_buf); + rspns_buf += sizeof(data); + } else if (check_write) { + /* Check last write */ + rspns_val = do_swdp_transfer(ctx, + DP_RDBUFF | SWDP_REQUEST_RnW, + NULL); + } + } + +end: + response[0] = rspns_cnt; + response[1] = rspns_val; + + return (rspns_buf - response); +} + +/* Delegate DAP Transfer command */ +static uint16_t dap_transfer(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + uint16_t retval; + + if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) { + LOG_ERR("DAP device is not connected"); + response[0] = DAP_ERROR; + return 1U; + } + + switch (ctx->debug_port) { + case DAP_PORT_SWD: + retval = dap_swdp_transfer(ctx, request, response); + break; + case DAP_PORT_JTAG: + default: + LOG_ERR("port unsupported"); + response[0] = DAP_ERROR; + retval = 1U; + } + + return retval; +} + +/* + * Process SWD DAP_TransferBlock command and prepare response. + * pyOCD counterpart is _encode_transfer_block_data. + * Packet format: one byte DAP_index (ignored) + * two bytes transfer_count + * one byte block_request (register) + * data[transfer_count * sizeof(uint32_t)] + */ +static uint16_t dap_swdp_transferblock(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + uint32_t data; + uint8_t *rspns_buf; + const uint8_t *req_buf; + uint16_t rspns_cnt = 0; + uint16_t req_cnt; + uint8_t rspns_val = 0; + uint8_t req_val; + + req_cnt = sys_get_le16(&request[1]); + req_val = request[3]; + req_buf = request + (sizeof(req_cnt) + sizeof(req_val) + 1); + rspns_buf = response + (sizeof(rspns_cnt) + sizeof(rspns_val)); + + if (req_cnt == 0U) { + goto end; + } + + if (req_val & SWDP_REQUEST_RnW) { + /* Read register block */ + if (req_val & SWDP_REQUEST_APnDP) { + /* Post AP read */ + rspns_val = do_swdp_transfer(ctx, req_val, NULL); + if (rspns_val != SWDP_ACK_OK) { + goto end; + } + } + + while (req_cnt--) { + /* Read DP/AP register */ + if ((req_cnt == 0U) && + (req_val & SWDP_REQUEST_APnDP)) { + /* Last AP read */ + req_val = DP_RDBUFF | SWDP_REQUEST_RnW; + } + + rspns_val = do_swdp_transfer(ctx, req_val, &data); + if (rspns_val != SWDP_ACK_OK) { + goto end; + } + + /* Store data */ + sys_put_le32(data, rspns_buf); + rspns_buf += sizeof(data); + rspns_cnt++; + } + } else { + /* Write register block */ + while (req_cnt--) { + /* Load data */ + data = sys_get_le32(req_buf); + req_buf += sizeof(data); + /* Write DP/AP register */ + rspns_val = do_swdp_transfer(ctx, req_val, &data); + if (rspns_val != SWDP_ACK_OK) { + goto end; + } + + rspns_cnt++; + } + /* Check last write */ + rspns_val = do_swdp_transfer(ctx, DP_RDBUFF | SWDP_REQUEST_RnW, NULL); + } + +end: + sys_put_le16(rspns_cnt, &response[0]); + response[2] = rspns_val; + + LOG_DBG("Received %u, to transmit %u, response count %u", + req_buf - request, + rspns_buf - response, + rspns_cnt * 4); + + return (rspns_buf - response); +} + +/* Delegate Transfer Block command */ +static uint16_t dap_transferblock(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + uint16_t retval; + + if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) { + LOG_ERR("DAP device is not connected"); + /* Clear response count */ + sys_put_le16(0U, &response[0]); + /* Clear DAP response (ACK) value */ + response[2] = 0U; + return 3U; + } + + switch (ctx->debug_port) { + case DAP_PORT_SWD: + retval = dap_swdp_transferblock(ctx, request, response); + break; + case DAP_PORT_JTAG: + default: + LOG_ERR("port unsupported"); + /* Clear response count */ + sys_put_le16(0U, &response[0]); + /* Clear DAP response (ACK) value */ + response[2] = 0U; + retval = 3U; + } + + return retval; +} + +/* Process SWD Write ABORT command and prepare response */ +static uint16_t dap_swdp_writeabort(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + const struct swdp_api *api = ctx->swdp_dev->api; + /* Load data (Ignore DAP index in request[0]) */ + uint32_t data = sys_get_le32(&request[1]); + + /* Write Abort register */ + api->swdp_transfer(ctx->swdp_dev, DP_ABORT, &data, + ctx->transfer.idle_cycles, NULL); + + response[0] = DAP_OK; + return 1U; +} + +/* Delegate DAP Write ABORT command */ +static uint16_t dap_writeabort(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + uint16_t retval; + + if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) { + LOG_ERR("DAP device is not connected"); + response[0] = DAP_ERROR; + return 1U; + } + + switch (ctx->debug_port) { + case DAP_PORT_SWD: + retval = dap_swdp_writeabort(ctx, request, response); + break; + case DAP_PORT_JTAG: + default: + LOG_ERR("port unsupported"); + response[0] = DAP_ERROR; + retval = 1U; + } + return retval; +} + +/* Process DAP Vendor command request */ +static uint16_t dap_process_vendor_cmd(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + response[0] = ID_DAP_INVALID; + return 1U; +} + +/* + * Process DAP command request and prepare response + * request: pointer to request data + * response: pointer to response data + * return: number of bytes in response + * + * All the subsequent command functions have the same parameter + * and return value structure. + */ +static uint16_t dap_process_cmd(struct dap_context *const ctx, + const uint8_t *request, + uint8_t *response) +{ + uint16_t retval; + + LOG_HEXDUMP_DBG(request, 8, "req"); + + if ((*request >= ID_DAP_VENDOR0) && (*request <= ID_DAP_VENDOR31)) { + return dap_process_vendor_cmd(ctx, request, response); + } + + *response++ = *request; + LOG_DBG("request 0x%02x", *request); + + switch (*request++) { + case ID_DAP_INFO: + retval = dap_info(ctx, request, response); + break; + case ID_DAP_HOST_STATUS: + retval = dap_host_status(ctx, request, response); + break; + case ID_DAP_CONNECT: + retval = dap_connect(ctx, request, response); + break; + case ID_DAP_DISCONNECT: + retval = dap_disconnect(ctx, response); + break; + case ID_DAP_DELAY: + retval = dap_delay(ctx, request, response); + break; + case ID_DAP_RESET_TARGET: + retval = dap_reset_target(ctx, response); + break; + case ID_DAP_SWJ_PINS: + retval = dap_swj_pins(ctx, request, response); + break; + case ID_DAP_SWJ_CLOCK: + retval = dap_swj_clock(ctx, request, response); + break; + case ID_DAP_SWJ_SEQUENCE: + retval = dap_swj_sequence(ctx, request, response); + break; + case ID_DAP_SWDP_CONFIGURE: + retval = dap_swdp_configure(ctx, request, response); + break; + case ID_DAP_JTAG_SEQUENCE: + LOG_ERR("JTAG sequence unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_JTAG_CONFIGURE: + LOG_ERR("JTAG configure unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_JTAG_IDCODE: + LOG_ERR("JTAG IDCODE unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_TRANSFER_CONFIGURE: + retval = dap_transfer_cfg(ctx, request, response); + break; + case ID_DAP_TRANSFER: + retval = dap_transfer(ctx, request, response); + break; + case ID_DAP_TRANSFER_BLOCK: + retval = dap_transferblock(ctx, request, response); + break; + case ID_DAP_WRITE_ABORT: + retval = dap_writeabort(ctx, request, response); + break; + case ID_DAP_SWO_TRANSPORT: + LOG_ERR("SWO Transport unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_SWO_MODE: + LOG_ERR("SWO Mode unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_SWO_BAUDRATE: + LOG_ERR("SWO Baudrate unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_SWO_CONTROL: + LOG_ERR("SWO Control unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_SWO_STATUS: + LOG_ERR("SWO Status unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_SWO_DATA: + LOG_ERR("SWO Data unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + + default: + *(response - 1) = ID_DAP_INVALID; + return 1U; + } + + return (1U + retval); +} + +/* + * Execute DAP command (process request and prepare response) + * request: pointer to request data + * response: pointer to response data + * return: number of bytes in response + */ +uint32_t dap_execute_cmd(const uint8_t *request, + uint8_t *response) +{ + uint32_t retval; + uint16_t n; + uint8_t count; + + if (request[0] == ID_DAP_EXECUTE_COMMANDS) { + /* copy command and increment */ + *response++ = *request++; + count = request[0]; + request += sizeof(count); + response[0] = count; + response += sizeof(count); + retval = sizeof(count) + 1U; + LOG_WRN("(untested) ID DAP EXECUTE_COMMANDS count %u", count); + while (count--) { + n = dap_process_cmd(&dap_ctx[0], request, response); + retval += n; + request += n; + response += n; + } + return retval; + } + + return dap_process_cmd(&dap_ctx[0], request, response); +} + +int dap_setup(const struct device *const dev) +{ + dap_ctx[0].swdp_dev = (void *)dev; + + if (!device_is_ready(dap_ctx[0].swdp_dev)) { + LOG_ERR("SWD driver not ready"); + return -ENODEV; + } + + /* Default settings */ + dap_ctx[0].pkt_size = 64U; + dap_ctx[0].debug_port = 0U; + dap_ctx[0].transfer.idle_cycles = 0U; + dap_ctx[0].transfer.retry_count = 100U; + dap_ctx[0].transfer.match_retry = 0U; + dap_ctx[0].transfer.match_mask = 0U; + dap_ctx[0].capabilities = DAP_SUPPORTS_ATOMIC_COMMANDS | + DAP_DP_SUPPORTS_SWD; + + return 0; +} diff --git a/subsys/dap/cmsis_dap.h b/subsys/dap/cmsis_dap.h new file mode 100644 index 00000000000..0117ec1ab56 --- /dev/null +++ b/subsys/dap/cmsis_dap.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018-2019 PHYTEC Messtechnik GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This file is based on DAP.c from CMSIS-DAP Source (Revision: V2.0.0) + * https://github.com/ARM-software/CMSIS_5/tree/develop/CMSIS/DAP/Firmware + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief DAP controller private header + */ + +#ifndef ZEPHYR_INCLUDE_CMSIS_DAP_H_ +#define ZEPHYR_INCLUDE_CMSIS_DAP_H_ + +#include + +/* Firmware Version */ +#define DAP_FW_VER "1.10" + +/* DAP Command IDs */ +#define ID_DAP_INFO 0x00U +#define ID_DAP_HOST_STATUS 0x01U +#define ID_DAP_CONNECT 0x02U +#define ID_DAP_DISCONNECT 0x03U +#define ID_DAP_TRANSFER_CONFIGURE 0x04U +#define ID_DAP_TRANSFER 0x05U +#define ID_DAP_TRANSFER_BLOCK 0x06U +#define ID_DAP_TRANSFER_ABORT 0x07U +#define ID_DAP_WRITE_ABORT 0x08U +#define ID_DAP_DELAY 0x09U +#define ID_DAP_RESET_TARGET 0x0AU +#define ID_DAP_SWJ_PINS 0x10U +#define ID_DAP_SWJ_CLOCK 0x11U +#define ID_DAP_SWJ_SEQUENCE 0x12U +#define ID_DAP_SWDP_CONFIGURE 0x13U +#define ID_DAP_JTAG_SEQUENCE 0x14U +#define ID_DAP_JTAG_CONFIGURE 0x15U +#define ID_DAP_JTAG_IDCODE 0x16U +#define ID_DAP_SWO_TRANSPORT 0x17U +#define ID_DAP_SWO_MODE 0x18U +#define ID_DAP_SWO_BAUDRATE 0x19U +#define ID_DAP_SWO_CONTROL 0x1AU +#define ID_DAP_SWO_STATUS 0x1BU +#define ID_DAP_SWO_DATA 0x1CU + +#define ID_DAP_QUEUE_COMMANDS 0x7EU +#define ID_DAP_EXECUTE_COMMANDS 0x7FU + +/* DAP Vendor Command IDs */ +#define ID_DAP_VENDOR0 0x80U +#define ID_DAP_VENDOR31 0x9FU +#define ID_DAP_INVALID 0xFFU + +/* DAP Status Code */ +#define DAP_OK 0U +#define DAP_ERROR 0xFFU + +/* DAP ID */ +#define DAP_ID_VENDOR 0x01U +#define DAP_ID_PRODUCT 0x02U +#define DAP_ID_SER_NUM 0x03U +#define DAP_ID_FW_VER 0x04U +#define DAP_ID_DEVICE_VENDOR 0x05U +#define DAP_ID_DEVICE_NAME 0x06U +#define DAP_ID_CAPABILITIES 0xF0U +#define DAP_ID_SWO_BUFFER_SIZE 0xFDU +#define DAP_ID_PACKET_COUNT 0xFEU +#define DAP_ID_PACKET_SIZE 0xFFU + +/* DAP Host Status */ +#define DAP_DEBUGGER_CONNECTED 0U +#define DAP_TARGET_RUNNING 1U + +/* DAP Port */ +#define DAP_PORT_AUTODETECT 0U +#define DAP_PORT_DISABLED 0U +#define DAP_PORT_SWD 1U +#define DAP_PORT_JTAG 2U + +/* DAP transfer request bits */ +#define DAP_TRANSFER_MATCH_VALUE BIT(4) +#define DAP_TRANSFER_MATCH_MASK BIT(5) + +/* DAP transfer response bits */ +#define DAP_TRANSFER_MISMATCH BIT(4) + +/* DAP controller capabilities */ +#define DAP_DP_SUPPORTS_SWD BIT(0) +#define DAP_DP_SUPPORTS_JTAG BIT(1) +#define DAP_SWO_SUPPORTS_UART BIT(2) +#define DAP_SWO_SUPPORTS_MANCHESTER BIT(3) +#define DAP_SUPPORTS_ATOMIC_COMMANDS BIT(4) +#define DAP_SUPPORTS_TIMESTAMP_CLOCK BIT(5) +#define DAP_SWO_SUPPORTS_STREAM BIT(6) + +/* DP Register (DPv1) */ +#define DP_IDCODE 0x00U +#define DP_ABORT 0x00U +#define DP_CTRL_STAT 0x04U +#define DP_SELECT 0x08U +#define DP_RESEND 0x08U +#define DP_RDBUFF 0x0CU + +#define DAP_MBMSG_REGISTER_IFACE 0x0U +#define DAP_MBMSG_FROM_IFACE 0x1U +#define DAP_MBMSG_FROM_CONTROLLER 0x2U + +/* Keep it internal until an other interface has been implemented. */ +int dap_setup(const struct device *const dev); +uint32_t dap_execute_cmd(const uint8_t *request, uint8_t *response); + +#endif /* ZEPHYR_INCLUDE_CMSIS_DAP_H_ */ From c51178c608db6caa85b287f4f8e7e1da503d32a8 Mon Sep 17 00:00:00 2001 From: Johan Carlsson Date: Fri, 21 Apr 2023 14:47:09 +0200 Subject: [PATCH 03/17] [nrf fromtree] dap: add Konfig option to set maximum packet size This also fixes a bug where the packet size returned was only 17 byte. Signed-off-by: Johan Carlsson (cherry picked from commit e46e72320e61139383816c43f8ae81f6987a57bf) --- subsys/dap/Kconfig | 7 +++++++ subsys/dap/cmsis_dap.c | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/subsys/dap/Kconfig b/subsys/dap/Kconfig index 43485958102..71c2b6034b9 100644 --- a/subsys/dap/Kconfig +++ b/subsys/dap/Kconfig @@ -17,6 +17,13 @@ config CMSIS_DAP_PACKET_COUNT help Maximum packet buffers for request and response data. +config CMSIS_DAP_PACKET_SIZE + int "Maximum packet size for request and response data." + default 64 + range 64 512 + help + Maximum packet size for request and response data. + module = DAP module-str = dap source "subsys/logging/Kconfig.template.log_config" diff --git a/subsys/dap/cmsis_dap.c b/subsys/dap/cmsis_dap.c index f1fc537366e..2bdd23378ea 100644 --- a/subsys/dap/cmsis_dap.c +++ b/subsys/dap/cmsis_dap.c @@ -33,7 +33,7 @@ struct dap_context { atomic_t state; uint8_t debug_port; uint8_t capabilities; - uint8_t pkt_size; + uint16_t pkt_size; struct { /* Idle cycles after transfer */ uint8_t idle_cycles; @@ -88,7 +88,7 @@ static uint16_t dap_info(struct dap_context *const ctx, break; case DAP_ID_PACKET_SIZE: LOG_DBG("ID_PACKET_SIZE"); - sys_put_le16(ctx->capabilities, &info[0]); + sys_put_le16(ctx->pkt_size, &info[0]); length = 2U; break; case DAP_ID_PACKET_COUNT: @@ -912,7 +912,7 @@ int dap_setup(const struct device *const dev) } /* Default settings */ - dap_ctx[0].pkt_size = 64U; + dap_ctx[0].pkt_size = CONFIG_CMSIS_DAP_PACKET_SIZE; dap_ctx[0].debug_port = 0U; dap_ctx[0].transfer.idle_cycles = 0U; dap_ctx[0].transfer.retry_count = 100U; From 7224d6d3cc588a6eec4dde86fc3b62c48aae7a96 Mon Sep 17 00:00:00 2001 From: Johan Carlsson Date: Sun, 23 Apr 2023 23:35:33 +0200 Subject: [PATCH 04/17] [nrf fromtree] dap: do not return error in DAP connect command If a host client crashes or fails to disconnect, the probe will hang. Allow a new connection without raising errors. Signed-off-by: Johan Carlsson Signed-off-by: Johann Fischer (cherry picked from commit f5ccc3c82ccd428bfbc4f6b1f10290ac0ddd75e2) --- subsys/dap/cmsis_dap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/subsys/dap/cmsis_dap.c b/subsys/dap/cmsis_dap.c index 2bdd23378ea..dba65495e43 100644 --- a/subsys/dap/cmsis_dap.c +++ b/subsys/dap/cmsis_dap.c @@ -153,7 +153,6 @@ static uint16_t dap_connect(struct dap_context *const ctx, if (atomic_test_and_set_bit(&ctx->state, DAP_STATE_CONNECTED)) { LOG_ERR("DAP device is already connected"); - port = DAP_ERROR; break; } From 08a580dc4c0ca8553855ba6614e1ee2e4a2be737 Mon Sep 17 00:00:00 2001 From: Maximilian Deubel Date: Fri, 7 Jul 2023 15:22:22 +0200 Subject: [PATCH 05/17] [nrf fromtree] driver: swdp_bitbang: rework pin configurations Move low-level GPIO functions to a separate file and use GPIO driver API if low-level GPIO support is not available for the platform. Allows alternative pin configuration using only two pins, clk and dio. Improve binding description. Signed-off-by: Maximilian Deubel Signed-off-by: Johann Fischer (cherry picked from commit 3ef2c66a8ad0d347e68b6fe5f74b8e67b8f60e9e) --- drivers/dp/Kconfig | 1 + drivers/dp/swdp_bitbang.c | 340 ++++++++++++++---------- drivers/dp/swdp_ll_pin.h | 84 ++++++ dts/bindings/misc/zephyr,swdp-gpio.yaml | 84 +++++- 4 files changed, 357 insertions(+), 152 deletions(-) create mode 100644 drivers/dp/swdp_ll_pin.h diff --git a/drivers/dp/Kconfig b/drivers/dp/Kconfig index 77e33811a39..b811529bcb5 100644 --- a/drivers/dp/Kconfig +++ b/drivers/dp/Kconfig @@ -24,6 +24,7 @@ config SWDP_BITBANG_DRIVER bool "Serial Wire Debug Port bit-bang driver" default y depends on DT_HAS_ZEPHYR_SWDP_GPIO_ENABLED + depends on GPIO help Serial Wire Debug Port bit-bang driver. diff --git a/drivers/dp/swdp_bitbang.c b/drivers/dp/swdp_bitbang.c index 6da04655cc0..e283c76c3e6 100644 --- a/drivers/dp/swdp_bitbang.c +++ b/drivers/dp/swdp_bitbang.c @@ -21,15 +21,11 @@ #include #include +#include "swdp_ll_pin.h" + #include LOG_MODULE_REGISTER(swdp, CONFIG_DP_DRIVER_LOG_LEVEL); -#if defined(CONFIG_SOC_SERIES_NRF52X) -#define CPU_CLOCK 64000000U -#else -#define CPU_CLOCK CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC -#endif - #define CLOCK_DELAY(swclk_freq, port_write_cycles) \ ((CPU_CLOCK / 2 / swclk_freq) - port_write_cycles) @@ -44,15 +40,15 @@ LOG_MODULE_REGISTER(swdp, CONFIG_DP_DRIVER_LOG_LEVEL); struct sw_config { struct gpio_dt_spec clk; struct gpio_dt_spec dout; - struct gpio_dt_spec din; + struct gpio_dt_spec dio; struct gpio_dt_spec dnoe; + void *dout_reg; + void *dio_reg; + void *dnoe_reg; struct gpio_dt_spec noe; struct gpio_dt_spec reset; uint32_t port_write_cycles; void *clk_reg; - void *dout_reg; - void *din_reg; - void *dnoe_reg; }; struct sw_cfg_data { @@ -98,102 +94,78 @@ static ALWAYS_INLINE uint32_t sw_get32bit_parity(uint32_t data) return data & 1U; } -static ALWAYS_INLINE void pin_delay_asm(uint32_t delay) -{ -#if defined(CONFIG_SOC_SERIES_NRF52X) - __asm volatile ("movs r3, %[p]\n" - ".start_%=:\n" - "subs r3, #1\n" - "bne .start_%=\n" - : - : [p] "r" (delay) - : "r3", "cc" - ); -#else -#error "Not defined for this SoC family" -#endif -} - -static ALWAYS_INLINE void pin_platform_set(void *base, uint8_t pin) -{ -#if defined(CONFIG_SOC_SERIES_NRF52X) - NRF_GPIO_Type * reg = base; - - reg->OUTSET = BIT(pin); -#else -#error "Not defined for this SoC family" -#endif -} - -static ALWAYS_INLINE void pin_platform_clr(void *base, uint8_t pin) -{ -#if defined(CONFIG_SOC_SERIES_NRF52X) - NRF_GPIO_Type * reg = base; - - reg->OUTCLR = BIT(pin); -#else -#error "Not defined for this SoC family" -#endif -} - -static ALWAYS_INLINE uint32_t pin_platform_get(void *base, uint8_t pin) -{ -#if defined(CONFIG_SOC_SERIES_NRF52X) - NRF_GPIO_Type * reg = base; - - return ((reg->IN >> pin) & 1); -#else -#error "Not defined for this SoC family" -#endif -} - /* Set SWCLK DAP hardware output pin to high level */ static ALWAYS_INLINE void pin_swclk_set(const struct device *dev) { const struct sw_config *config = dev->config; - const struct gpio_dt_spec *dt_spec = &config->clk; - pin_platform_set(config->clk_reg, dt_spec->pin); + if (FAST_BITBANG_HW_SUPPORT) { + swdp_ll_pin_set(config->clk_reg, config->clk.pin); + } else { + gpio_pin_set_dt(&config->clk, 1); + } } /* Set SWCLK DAP hardware output pin to low level */ static ALWAYS_INLINE void pin_swclk_clr(const struct device *dev) { const struct sw_config *config = dev->config; - const struct gpio_dt_spec *dt_spec = &config->clk; - pin_platform_clr(config->clk_reg, dt_spec->pin); + if (FAST_BITBANG_HW_SUPPORT) { + swdp_ll_pin_clr(config->clk_reg, config->clk.pin); + } else { + gpio_pin_set_dt(&config->clk, 0); + } } /* Set the SWDIO DAP hardware output pin to high level */ static ALWAYS_INLINE void pin_swdio_set(const struct device *dev) { const struct sw_config *config = dev->config; - const struct gpio_dt_spec *dt_spec = &config->dout; - pin_platform_set(config->dout_reg, dt_spec->pin); + if (config->dout.port) { + if (FAST_BITBANG_HW_SUPPORT) { + swdp_ll_pin_set(config->dout_reg, config->dout.pin); + } else { + gpio_pin_set_dt(&config->dout, 1); + } + } else { + if (FAST_BITBANG_HW_SUPPORT) { + swdp_ll_pin_set(config->dio_reg, config->dio.pin); + } else { + gpio_pin_set_dt(&config->dio, 1); + } + } } /* Set the SWDIO DAP hardware output pin to low level */ static ALWAYS_INLINE void pin_swdio_clr(const struct device *dev) { const struct sw_config *config = dev->config; - const struct gpio_dt_spec *dt_spec = &config->dout; - pin_platform_clr(config->dout_reg, dt_spec->pin); + if (config->dout.port) { + if (FAST_BITBANG_HW_SUPPORT) { + swdp_ll_pin_clr(config->dout_reg, config->dout.pin); + } else { + gpio_pin_set_dt(&config->dout, 0); + } + } else { + if (FAST_BITBANG_HW_SUPPORT) { + swdp_ll_pin_clr(config->dio_reg, config->dio.pin); + } else { + gpio_pin_set_dt(&config->dio, 0); + } + } } /* Set the SWDIO DAP hardware output pin to bit level */ static ALWAYS_INLINE void pin_swdio_out(const struct device *dev, const uint32_t bit) { - const struct sw_config *config = dev->config; - const struct gpio_dt_spec *dt_spec = &config->dout; - if (bit & 1U) { - pin_platform_set(config->dout_reg, dt_spec->pin); + pin_swdio_set(dev); } else { - pin_platform_clr(config->dout_reg, dt_spec->pin); + pin_swdio_clr(dev); } } @@ -201,9 +173,12 @@ static ALWAYS_INLINE void pin_swdio_out(const struct device *dev, static ALWAYS_INLINE uint32_t pin_swdio_in(const struct device *dev) { const struct sw_config *config = dev->config; - const struct gpio_dt_spec *dt_spec = &config->din; - return pin_platform_get(config->din_reg, dt_spec->pin); + if (FAST_BITBANG_HW_SUPPORT) { + return swdp_ll_pin_get(config->dio_reg, config->dio.pin); + } else { + return gpio_pin_get_dt(&config->dio); + } } /* @@ -213,9 +188,20 @@ static ALWAYS_INLINE uint32_t pin_swdio_in(const struct device *dev) static ALWAYS_INLINE void pin_swdio_out_enable(const struct device *dev) { const struct sw_config *config = dev->config; - const struct gpio_dt_spec *dt_spec = &config->dnoe; - pin_platform_set(config->dnoe_reg, dt_spec->pin); + if (config->dnoe.port) { + if (FAST_BITBANG_HW_SUPPORT) { + swdp_ll_pin_set(config->dnoe_reg, config->dnoe.pin); + } else { + gpio_pin_set_dt(&config->dnoe, 1); + } + } else { + if (FAST_BITBANG_HW_SUPPORT) { + swdp_ll_pin_output(config->dio_reg, config->dio.pin); + } else { + gpio_pin_configure_dt(&config->dio, GPIO_OUTPUT_ACTIVE); + } + } } /* @@ -224,9 +210,20 @@ static ALWAYS_INLINE void pin_swdio_out_enable(const struct device *dev) static ALWAYS_INLINE void pin_swdio_out_disable(const struct device *dev) { const struct sw_config *config = dev->config; - const struct gpio_dt_spec *dt_spec = &config->dnoe; - pin_platform_clr(config->dnoe_reg, dt_spec->pin); + if (config->dnoe.port) { + if (FAST_BITBANG_HW_SUPPORT) { + swdp_ll_pin_clr(config->dnoe_reg, config->dnoe.pin); + } else { + gpio_pin_set_dt(&config->dnoe, 0); + } + } else { + if (FAST_BITBANG_HW_SUPPORT) { + swdp_ll_pin_input(config->dio_reg, config->dio.pin); + } else { + gpio_pin_configure_dt(&config->dio, GPIO_INPUT); + } + } } #define SW_CLOCK_CYCLE(dev, delay) \ @@ -260,13 +257,14 @@ static int sw_sequence(const struct device *dev, uint32_t count, { struct sw_cfg_data *sw_data = dev->data; unsigned int key; - uint32_t val = 0; - uint32_t n = 0; + uint32_t val = 0; /* current byte */ + uint32_t n = 0; /* bit counter */ - LOG_DBG("count %u", count); + LOG_DBG("writing %u bits", count); LOG_HEXDUMP_DBG(data, count, "sequence bit data"); key = irq_lock(); + pin_swdio_out_enable(dev); while (count--) { if (n == 0U) { val = *data++; @@ -309,6 +307,8 @@ static int sw_transfer(const struct device *dev, uint32_t parity = 0; uint32_t n; + pin_swdio_out_enable(dev); + LOG_DBG("request 0x%02x idle %u", request, idle_cycles); if (!(request & SWDP_REQUEST_RnW)) { LOG_DBG("write data 0x%08x", *data); @@ -449,19 +449,31 @@ static int sw_set_pins(const struct device *dev, } } - if (pins & BIT(SWDP_SWDIO_PIN)) { - if (value & BIT(SWDP_SWDIO_PIN)) { - gpio_pin_set_dt(&config->dout, 1); - } else { - gpio_pin_set_dt(&config->dout, 0); + if (config->dout_reg != NULL) { + if (pins & BIT(SWDP_SWDIO_PIN)) { + if (value & BIT(SWDP_SWDIO_PIN)) { + gpio_pin_set_dt(&config->dout, 1); + } else { + gpio_pin_set_dt(&config->dout, 0); + } + } + } else { + if (pins & BIT(SWDP_SWDIO_PIN)) { + if (value & BIT(SWDP_SWDIO_PIN)) { + gpio_pin_set_dt(&config->dio, 1); + } else { + gpio_pin_set_dt(&config->dio, 0); + } } } - if (pins & BIT(SWDP_nRESET_PIN)) { - if (value & BIT(SWDP_nRESET_PIN)) { - gpio_pin_set_dt(&config->reset, 1); - } else { - gpio_pin_set_dt(&config->reset, 0); + if (config->reset.port) { + if (pins & BIT(SWDP_nRESET_PIN)) { + if (value & BIT(SWDP_nRESET_PIN)) { + gpio_pin_set_dt(&config->reset, 1); + } else { + gpio_pin_set_dt(&config->reset, 0); + } } } @@ -473,10 +485,12 @@ static int sw_get_pins(const struct device *dev, uint8_t *const state) const struct sw_config *config = dev->config; uint32_t val; - val = gpio_pin_get_dt(&config->reset); - *state = val ? BIT(SWDP_nRESET_PIN) : 0; + if (config->reset.port) { + val = gpio_pin_get_dt(&config->reset); + *state = val ? BIT(SWDP_nRESET_PIN) : 0; + } - val = gpio_pin_get_dt(&config->din); + val = gpio_pin_get_dt(&config->dio); *state |= val ? BIT(SWDP_SWDIO_PIN) : 0; val = gpio_pin_get_dt(&config->clk); @@ -529,10 +543,28 @@ static int sw_port_on(const struct device *dev) const struct sw_config *config = dev->config; gpio_pin_set_dt(&config->clk, 1); - gpio_pin_set_dt(&config->dout, 1); - gpio_pin_set_dt(&config->dnoe, 1); - gpio_pin_set_dt(&config->noe, 1); - gpio_pin_set_dt(&config->reset, 1); + + if (config->dnoe.port) { + gpio_pin_set_dt(&config->dnoe, 1); + } + + if (config->dout.port) { + gpio_pin_set_dt(&config->dout, 1); + } else { + int ret; + + ret = gpio_pin_configure_dt(&config->dio, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + } + + if (config->noe.port) { + gpio_pin_set_dt(&config->noe, 1); + } + if (config->reset.port) { + gpio_pin_set_dt(&config->reset, 1); + } return 0; } @@ -541,9 +573,27 @@ static int sw_port_off(const struct device *dev) { const struct sw_config *config = dev->config; - gpio_pin_set_dt(&config->dnoe, 0); - gpio_pin_set_dt(&config->noe, 0); - gpio_pin_set_dt(&config->reset, 1); + if (config->dnoe.port) { + gpio_pin_set_dt(&config->dnoe, 0); + } + + if (config->dout.port) { + gpio_pin_set_dt(&config->dout, 0); + } else { + int ret; + + ret = gpio_pin_configure_dt(&config->dio, GPIO_INPUT); + if (ret) { + return ret; + } + } + + if (config->noe.port) { + gpio_pin_set_dt(&config->noe, 0); + } + if (config->reset.port) { + gpio_pin_set_dt(&config->reset, 1); + } return 0; } @@ -559,29 +609,37 @@ static int sw_gpio_init(const struct device *dev) return ret; } - ret = gpio_pin_configure_dt(&config->dout, GPIO_OUTPUT_ACTIVE); + ret = gpio_pin_configure_dt(&config->dio, GPIO_INPUT); if (ret) { return ret; } - ret = gpio_pin_configure_dt(&config->din, GPIO_INPUT); - if (ret) { - return ret; + if (config->dout.port) { + ret = gpio_pin_configure_dt(&config->dout, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } } - ret = gpio_pin_configure_dt(&config->dnoe, GPIO_OUTPUT_INACTIVE); - if (ret) { - return ret; + if (config->dnoe.port) { + ret = gpio_pin_configure_dt(&config->dnoe, GPIO_OUTPUT_INACTIVE); + if (ret) { + return ret; + } } - ret = gpio_pin_configure_dt(&config->noe, GPIO_OUTPUT_INACTIVE); - if (ret) { - return ret; + if (config->noe.port) { + ret = gpio_pin_configure_dt(&config->noe, GPIO_OUTPUT_INACTIVE); + if (ret) { + return ret; + } } - ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE); - if (ret) { - return ret; + if (config->reset.port) { + ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } } sw_data->turnaround = 1U; @@ -605,29 +663,35 @@ static struct swdp_api swdp_bitbang_api = { .swdp_port_off = sw_port_off, }; -#define SW_GPIOS_GET_REG(n, gpios) \ - INT_TO_POINTER(DT_REG_ADDR(DT_PHANDLE(DT_DRV_INST(n), gpios))) - -#define SW_DEVICE_DEFINE(n) \ - static const struct sw_config sw_cfg_##n = { \ - .clk = GPIO_DT_SPEC_INST_GET(n, clk_gpios), \ - .dout = GPIO_DT_SPEC_INST_GET(n, dout_gpios), \ - .din = GPIO_DT_SPEC_INST_GET(n, din_gpios), \ - .dnoe = GPIO_DT_SPEC_INST_GET(n, dnoe_gpios), \ - .noe = GPIO_DT_SPEC_INST_GET(n, noe_gpios), \ - .reset = GPIO_DT_SPEC_INST_GET(n, reset_gpios), \ - .port_write_cycles = DT_INST_PROP(n, port_write_cycles), \ - .clk_reg = SW_GPIOS_GET_REG(n, clk_gpios), \ - .dout_reg = SW_GPIOS_GET_REG(n, dout_gpios), \ - .din_reg = SW_GPIOS_GET_REG(n, din_gpios), \ - .dnoe_reg = SW_GPIOS_GET_REG(n, dnoe_gpios), \ - }; \ - \ - static struct sw_cfg_data sw_data_##n; \ - \ - DEVICE_DT_INST_DEFINE(n, sw_gpio_init, NULL, \ - &sw_data_##n, &sw_cfg_##n, \ - POST_KERNEL, CONFIG_DP_DRIVER_INIT_PRIO, \ - &swdp_bitbang_api); \ +#define SW_GPIOS_GET_REG(n, gpios) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(n, gpios), \ + (INT_TO_POINTER(DT_REG_ADDR(DT_PHANDLE(DT_DRV_INST(n), gpios)))), \ + (NULL)) + +#define SW_DEVICE_DEFINE(n) \ + BUILD_ASSERT((DT_INST_NODE_HAS_PROP(n, dout_gpios)) == \ + (DT_INST_NODE_HAS_PROP(n, dnoe_gpios)), \ + "Either the dout-gpios or dnoe-gpios property is missing."); \ + \ + static const struct sw_config sw_cfg_##n = { \ + .clk = GPIO_DT_SPEC_INST_GET(n, clk_gpios), \ + .clk_reg = SW_GPIOS_GET_REG(n, clk_gpios), \ + .dio = GPIO_DT_SPEC_INST_GET(n, dio_gpios), \ + .dio_reg = SW_GPIOS_GET_REG(n, dio_gpios), \ + .dout = GPIO_DT_SPEC_INST_GET_OR(n, dout_gpios, {0}), \ + .dout_reg = SW_GPIOS_GET_REG(n, dout_gpios), \ + .dnoe = GPIO_DT_SPEC_INST_GET_OR(n, dnoe_gpios, {0}), \ + .dnoe_reg = SW_GPIOS_GET_REG(n, dnoe_gpios), \ + .noe = GPIO_DT_SPEC_INST_GET_OR(n, noe_gpios, {0}), \ + .reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}), \ + .port_write_cycles = DT_INST_PROP(n, port_write_cycles), \ + }; \ + \ + static struct sw_cfg_data sw_data_##n; \ + \ + DEVICE_DT_INST_DEFINE(n, sw_gpio_init, NULL, \ + &sw_data_##n, &sw_cfg_##n, \ + POST_KERNEL, CONFIG_DP_DRIVER_INIT_PRIO, \ + &swdp_bitbang_api); DT_INST_FOREACH_STATUS_OKAY(SW_DEVICE_DEFINE) diff --git a/drivers/dp/swdp_ll_pin.h b/drivers/dp/swdp_ll_pin.h new file mode 100644 index 00000000000..3044931a228 --- /dev/null +++ b/drivers/dp/swdp_ll_pin.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#if defined(CONFIG_SOC_SERIES_NRF52X) +#define CPU_CLOCK 64000000U +#else +#define CPU_CLOCK CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC +#endif + +#if defined(CONFIG_SOC_SERIES_NRF52X) || defined(CONFIG_SOC_SERIES_NRF53X) +#define FAST_BITBANG_HW_SUPPORT 1 +#else +#define FAST_BITBANG_HW_SUPPORT 0 +#endif + +static ALWAYS_INLINE void pin_delay_asm(uint32_t delay) +{ +#if defined(CONFIG_CPU_CORTEX_M) + __asm volatile ("movs r3, %[p]\n" + ".start_%=:\n" + "subs r3, #1\n" + "bne .start_%=\n" + : + : [p] "r" (delay) + : "r3", "cc" + ); +#else +#warning "Pin delay is not defined" +#endif +} + +static ALWAYS_INLINE void swdp_ll_pin_input(void *const base, uint8_t pin) +{ +#if defined(CONFIG_SOC_SERIES_NRF52X) || defined(CONFIG_SOC_SERIES_NRF53X) + NRF_GPIO_Type * reg = base; + + reg->PIN_CNF[pin] = 0b0000; +#endif +} + +static ALWAYS_INLINE void swdp_ll_pin_output(void *const base, uint8_t pin) +{ +#if defined(CONFIG_SOC_SERIES_NRF52X) || defined(CONFIG_SOC_SERIES_NRF53X) + NRF_GPIO_Type * reg = base; + + reg->PIN_CNF[pin] = 0b0001; +#endif +} + + +static ALWAYS_INLINE void swdp_ll_pin_set(void *const base, uint8_t pin) +{ +#if defined(CONFIG_SOC_SERIES_NRF52X) || defined(CONFIG_SOC_SERIES_NRF53X) + NRF_GPIO_Type * reg = base; + + reg->OUTSET = BIT(pin); +#endif +} + +static ALWAYS_INLINE void swdp_ll_pin_clr(void *const base, uint8_t pin) +{ +#if defined(CONFIG_SOC_SERIES_NRF52X) || defined(CONFIG_SOC_SERIES_NRF53X) + NRF_GPIO_Type * reg = base; + + reg->OUTCLR = BIT(pin); +#endif +} + +static ALWAYS_INLINE uint32_t swdp_ll_pin_get(void *const base, uint8_t pin) +{ +#if defined(CONFIG_SOC_SERIES_NRF52X) || defined(CONFIG_SOC_SERIES_NRF53X) + NRF_GPIO_Type * reg = base; + + return ((reg->IN >> pin) & 1); +#else + return 0UL; +#endif +} diff --git a/dts/bindings/misc/zephyr,swdp-gpio.yaml b/dts/bindings/misc/zephyr,swdp-gpio.yaml index 5d38449ad49..c6d6aa55e7f 100644 --- a/dts/bindings/misc/zephyr,swdp-gpio.yaml +++ b/dts/bindings/misc/zephyr,swdp-gpio.yaml @@ -1,10 +1,60 @@ -# Copyright (c) 2019, Phytec Messtechnik GmbH +# Copyright (c) 2019 Phytec Messtechnik GmbH +# Copyright (c) 2023 Nordic Semiconductor ASA # SPDX-License-Identifier: Apache-2.0 -description: > +description: | This is a representation of the Serial Wire Debug Port interface implementation by GPIO bit-banging. + Schematic using dual-supply bus transceiver and separate dout and dnoe pins + + VCC_3V3 VCC_REF + ^ ^ + | +-------------+ | + +-------|vcca vccb|-----+ + | | + clk-gpios -------|a b|-------------- SWD CLK + | | + noe-gpios -------|dir gnd|-----+ + +-------------+ | + 74LVC1T45 v + GND + + + VCC_3V3 VCC_REF + ^ ^ + | +-------------+ | + +-------|vcca vccb|-----+ + | | + dio-gpios -------|a b|------------*- SWD DIO + | | | + +-------|dir gnd|-----+ | + | +-------------+ | | + v 74LVC1T45 v | + GND GND | + | + | + VCC_3V3 VCC_REF | + ^ ^ | + | +-------------+ | | + +-------|vcca vccb|-----+ | + | | | + dout-gpios -------|a b|------------+ + | | + dnoe-gpios -------|dir gnd|-----+ + +-------------+ | + 74LVC1T45 v + GND + + Direct connection using only dio pin for SWD DIO. + + clk-gpios ------------------------------------ SWD CLK + + dio-gpios ------------------------------------ SWD DIO + + Of course, bidirectional bus transceiver between dio and SWD DIO can also be + used together with noe pin to enable/disable transceivers. + compatible: "zephyr,swdp-gpio" include: base.yaml @@ -15,32 +65,38 @@ properties: required: true description: GPIO pin used for SWCLK output - dout-gpios: + dio-gpios: type: phandle-array required: true - description: GPIO pin used for SWDIO output + description: | + GPIO pin used for SWDIO input. This pin is also used for the SWDIO output + if separate output pin is not defined. - din-gpios: + dout-gpios: type: phandle-array - required: true - description: GPIO pin used for SWDIO input + description: | + Optional GPIO pin used for SWDIO output. dnoe-gpios: type: phandle-array - required: true - description: GPIO pin used for SWDIO NOE output + description: | + GPIO pin used to disable the SWDIO output buffer behind optional + pin dout-gpios. noe-gpios: type: phandle-array - required: true - description: GPIO pin used for SWD NOE output + description: | + Optional pin to disable all bus transceivers if any are present. reset-gpios: type: phandle-array - required: true - description: GPIO pin used for RESET output + description: | + Optional GPIO pin used for RESET output. port-write-cycles: type: int required: true - description: Number of processor cycles for I/O Port write operations + description: | + Number of processor cycles for I/O Port write operations.For example, the + GPIO clock may be different from the CPU clock. This can usually be + found in the SoC documentation. From 10e8bdc74f971b1911fa41476fe6cb32e973297f Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Thu, 31 Aug 2023 17:10:00 +0200 Subject: [PATCH 06/17] [nrf fromtree] drivers: swdp_bitbang: add SW output sequence Add API to read count bits from SWDIO into data LSB first. Signed-off-by: Maximilian Deubel Signed-off-by: Johann Fischer (cherry picked from commit 3e8f9fb7dfff9e717da8c67b0519db23352112be) --- drivers/dp/swdp_bitbang.c | 40 +++++++++++++++++++++++++++++++---- include/zephyr/drivers/swdp.h | 11 +++++++--- subsys/dap/cmsis_dap.c | 2 +- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/drivers/dp/swdp_bitbang.c b/drivers/dp/swdp_bitbang.c index e283c76c3e6..f3c5fcc50d2 100644 --- a/drivers/dp/swdp_bitbang.c +++ b/drivers/dp/swdp_bitbang.c @@ -252,8 +252,8 @@ static ALWAYS_INLINE void pin_swdio_out_disable(const struct device *dev) pin_delay_asm(delay); \ } while (0) -static int sw_sequence(const struct device *dev, uint32_t count, - const uint8_t *data) +static int sw_output_sequence(const struct device *dev, uint32_t count, + const uint8_t *data) { struct sw_cfg_data *sw_data = dev->data; unsigned int key; @@ -285,6 +285,37 @@ static int sw_sequence(const struct device *dev, uint32_t count, return 0; } +static int sw_input_sequence(const struct device *dev, uint32_t count, + uint8_t *data) +{ + struct sw_cfg_data *sw_data = dev->data; + unsigned int key; + uint32_t val = 0U; /* current byte */ + uint32_t n = 8U; /* bit counter */ + uint32_t bit; + + LOG_DBG("reading %u bits", count); + key = irq_lock(); + + pin_swdio_out_disable(dev); + while (count--) { + if (n == 0U) { + *data++ = val; + val = 0; + n = 8U; + } + SW_READ_BIT(dev, bit, sw_data->clock_delay); + LOG_DBG("Read bit: %d", bit); + val = (val << 1 | bit); + n--; + } + + *data = val; /* write last byte */ + irq_unlock(key); + + return 0; +} + static ALWAYS_INLINE void sw_cycle_turnaround(const struct device *dev) { struct sw_cfg_data *sw_data = dev->data; @@ -653,7 +684,8 @@ static int sw_gpio_init(const struct device *dev) } static struct swdp_api swdp_bitbang_api = { - .swdp_sequence = sw_sequence, + .swdp_output_sequence = sw_output_sequence, + .swdp_input_sequence = sw_input_sequence, .swdp_transfer = sw_transfer, .swdp_set_pins = sw_set_pins, .swdp_get_pins = sw_get_pins, @@ -669,7 +701,7 @@ static struct swdp_api swdp_bitbang_api = { (NULL)) #define SW_DEVICE_DEFINE(n) \ - BUILD_ASSERT((DT_INST_NODE_HAS_PROP(n, dout_gpios)) == \ + BUILD_ASSERT((DT_INST_NODE_HAS_PROP(n, dout_gpios)) == \ (DT_INST_NODE_HAS_PROP(n, dnoe_gpios)), \ "Either the dout-gpios or dnoe-gpios property is missing."); \ \ diff --git a/include/zephyr/drivers/swdp.h b/include/zephyr/drivers/swdp.h index f15a4b40ecd..c1f06e52151 100644 --- a/include/zephyr/drivers/swdp.h +++ b/include/zephyr/drivers/swdp.h @@ -43,9 +43,14 @@ extern "C" { */ struct swdp_api { /* Generate SWJ Sequence according to sequence bit count and bit data */ - int (*swdp_sequence)(const struct device *dev, - uint32_t count, - const uint8_t *data); + int (*swdp_output_sequence)(const struct device *dev, + uint32_t count, + const uint8_t *data); + + /* Read count bits from SWDIO into data LSB first */ + int (*swdp_input_sequence)(const struct device *dev, + uint32_t count, + uint8_t *data); /* * Perform SWDP transfer based on host request value and store diff --git a/subsys/dap/cmsis_dap.c b/subsys/dap/cmsis_dap.c index dba65495e43..2e345f99962 100644 --- a/subsys/dap/cmsis_dap.c +++ b/subsys/dap/cmsis_dap.c @@ -297,7 +297,7 @@ static uint16_t dap_swj_sequence(struct dap_context *const ctx, return 1U; } - api->swdp_sequence(ctx->swdp_dev, count, &request[1]); + api->swdp_output_sequence(ctx->swdp_dev, count, &request[1]); response[0] = DAP_OK; return 1U; From 50664f956a0b1a9f4b871f84d90707a0f595d1a2 Mon Sep 17 00:00:00 2001 From: Maximilian Deubel Date: Fri, 7 Jul 2023 15:23:55 +0200 Subject: [PATCH 07/17] [nrf fromtree] dap: implement wait for SWJ pins command Implement wait for SWJ pins command. Signed-off-by: Maximilian Deubel (cherry picked from commit 6522dd7f5395bd4f65d1d608d7dbf4339ea7c9ed) --- subsys/dap/cmsis_dap.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/subsys/dap/cmsis_dap.c b/subsys/dap/cmsis_dap.c index 2e345f99962..455fa367604 100644 --- a/subsys/dap/cmsis_dap.c +++ b/subsys/dap/cmsis_dap.c @@ -229,6 +229,7 @@ static uint16_t dap_swj_pins(struct dap_context *const ctx, uint8_t value = request[0]; uint8_t select = request[1]; uint32_t wait = sys_get_le32(&request[2]); + int64_t timeout_ticks; uint8_t state; if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) { @@ -242,10 +243,17 @@ static uint16_t dap_swj_pins(struct dap_context *const ctx, api->swdp_set_pins(ctx->swdp_dev, select, value); } - /* TODO: implement wait */ - api->swdp_get_pins(ctx->swdp_dev, &state); - LOG_ERR("select 0x%02x, value 0x%02x, wait %u, state 0x%02x", - select, value, wait, state); + timeout_ticks = k_uptime_ticks() + (CONFIG_SYS_CLOCK_TICKS_PER_SEC / 1000000 * wait); + + do { + api->swdp_get_pins(ctx->swdp_dev, &state); + LOG_INF("select 0x%02x, value 0x%02x, wait %u, state 0x%02x", + select, value, wait, state); + if ((value & select) == (state & select)) { + LOG_DBG("swdp_get_pins succeeded before timeout"); + break; + } + } while (k_uptime_ticks() - timeout_ticks > 0); response[0] = state; From 4709e3a09c5fed464dfeb66a2ae722f542ca6ded Mon Sep 17 00:00:00 2001 From: Maximilian Deubel Date: Fri, 7 Jul 2023 15:25:55 +0200 Subject: [PATCH 08/17] [nrf fromtree] dap: add support for DAP_INFO string elements Add support for DAP_INFO string elements. Signed-off-by: Maximilian Deubel (cherry picked from commit 58d9d889aa11ca88871b755256c6cb9298b62922) --- subsys/dap/Kconfig | 24 ++++++++++++++ subsys/dap/cmsis_dap.c | 74 +++++++++++++++++++++++++++++++++++++----- subsys/dap/cmsis_dap.h | 10 ++++++ 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/subsys/dap/Kconfig b/subsys/dap/Kconfig index 71c2b6034b9..2436ba71c4e 100644 --- a/subsys/dap/Kconfig +++ b/subsys/dap/Kconfig @@ -24,6 +24,30 @@ config CMSIS_DAP_PACKET_SIZE help Maximum packet size for request and response data. +config CMSIS_DAP_PROBE_VENDOR + string "Probe vendor" + default "Zephyr" + +config CMSIS_DAP_PROBE_NAME + string "Probe name" + default "CMSIS-DAP" + +config CMSIS_DAP_BOARD_VENDOR + string "Target board vendor" + default "" + +config CMSIS_DAP_BOARD_NAME + string "Target board name" + default "" + +config CMSIS_DAP_DEVICE_VENDOR + string "Target device vendor" + default "" + +config CMSIS_DAP_DEVICE_NAME + string "Target device name" + default "" + module = DAP module-str = dap source "subsys/logging/Kconfig.template.log_config" diff --git a/subsys/dap/cmsis_dap.c b/subsys/dap/cmsis_dap.c index 455fa367604..bab57301209 100644 --- a/subsys/dap/cmsis_dap.c +++ b/subsys/dap/cmsis_dap.c @@ -18,14 +18,13 @@ #include #include #include +#include #include #include LOG_MODULE_REGISTER(dap, CONFIG_DAP_LOG_LEVEL); -const char dap_fw_ver[] = DAP_FW_VER; - #define DAP_STATE_CONNECTED 0 struct dap_context { @@ -48,6 +47,25 @@ struct dap_context { static struct dap_context dap_ctx[1]; +BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_PROBE_VENDOR) <= + MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2), + "PROBE_VENDOR string is too long."); +BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_PROBE_NAME) <= + MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2), + "PROBE_NAME string is too long."); +BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_BOARD_VENDOR) <= + MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2), + "BOARD_VENDOR string is too long."); +BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_BOARD_NAME) <= + MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2), + "BOARD_NAME string is too long."); +BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_DEVICE_VENDOR) <= + MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2), + "DEVICE_VENDOR string is too long."); +BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_DEVICE_NAME) <= + MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2), + "DEVICE_NAME string is too long."); + /* Get DAP Information */ static uint16_t dap_info(struct dap_context *const ctx, const uint8_t *const request, @@ -59,30 +77,68 @@ static uint16_t dap_info(struct dap_context *const ctx, switch (id) { case DAP_ID_VENDOR: - LOG_DBG("ID_VENDOR unsupported"); + LOG_DBG("ID_VENDOR"); + memcpy(info, CONFIG_CMSIS_DAP_PROBE_VENDOR, + sizeof(CONFIG_CMSIS_DAP_PROBE_VENDOR)); + length = sizeof(CONFIG_CMSIS_DAP_PROBE_VENDOR); break; case DAP_ID_PRODUCT: - LOG_DBG("ID_PRODUCT unsupported"); + LOG_DBG("ID_PRODUCT"); + memcpy(info, CONFIG_CMSIS_DAP_PROBE_NAME, + sizeof(CONFIG_CMSIS_DAP_PROBE_NAME)); + length = sizeof(CONFIG_CMSIS_DAP_PROBE_NAME); break; case DAP_ID_SER_NUM: + /* optional to implement */ LOG_DBG("ID_SER_NUM unsupported"); break; case DAP_ID_FW_VER: LOG_DBG("ID_FW_VER"); - memcpy(info, dap_fw_ver, sizeof(dap_fw_ver)); - length = (uint8_t)sizeof(dap_fw_ver); + memcpy(info, DAP_FW_VER, sizeof(DAP_FW_VER)); + length = sizeof(DAP_FW_VER); break; case DAP_ID_DEVICE_VENDOR: - LOG_DBG("ID_DEVICE_VENDOR unsupported"); + LOG_DBG("ID_DEVICE_VENDOR"); + memcpy(info, CONFIG_CMSIS_DAP_DEVICE_VENDOR, + sizeof(CONFIG_CMSIS_DAP_DEVICE_VENDOR)); + length = sizeof(CONFIG_CMSIS_DAP_DEVICE_VENDOR); break; case DAP_ID_DEVICE_NAME: - LOG_DBG("ID_DEVICE_NAME unsupported"); + LOG_DBG("ID_DEVICE_NAME"); + memcpy(info, CONFIG_CMSIS_DAP_DEVICE_NAME, + sizeof(CONFIG_CMSIS_DAP_DEVICE_NAME)); + length = sizeof(CONFIG_CMSIS_DAP_DEVICE_NAME); + break; + case DAP_ID_BOARD_VENDOR: + LOG_DBG("ID_BOARD_VENDOR"); + memcpy(info, CONFIG_CMSIS_DAP_BOARD_VENDOR, + sizeof(CONFIG_CMSIS_DAP_BOARD_VENDOR)); + length = sizeof(CONFIG_CMSIS_DAP_BOARD_VENDOR); + break; + case DAP_ID_BOARD_NAME: + memcpy(info, CONFIG_CMSIS_DAP_BOARD_NAME, + sizeof(CONFIG_CMSIS_DAP_BOARD_NAME)); + length = sizeof(CONFIG_CMSIS_DAP_BOARD_NAME); + LOG_DBG("ID_BOARD_NAME"); + break; + case DAP_ID_PRODUCT_FW_VER: + /* optional to implement */ + LOG_DBG("ID_PRODUCT_FW_VER unsupported"); break; case DAP_ID_CAPABILITIES: info[0] = ctx->capabilities; LOG_DBG("ID_CAPABILITIES 0x%0x", info[0]); length = 1U; break; + case DAP_ID_TIMESTAMP_CLOCK: + LOG_DBG("ID_TIMESTAMP_CLOCK unsupported"); + break; + case DAP_ID_UART_RX_BUFFER_SIZE: + LOG_DBG("ID_UART_RX_BUFFER_SIZE unsupported"); + break; + case DAP_ID_UART_TX_BUFFER_SIZE: + LOG_DBG("ID_UART_TX_BUFFER_SIZE unsupported"); + break; case DAP_ID_SWO_BUFFER_SIZE: LOG_DBG("ID_SWO_BUFFER_SIZE unsupported"); break; @@ -101,7 +157,7 @@ static uint16_t dap_info(struct dap_context *const ctx, break; } - response[0] = (uint8_t)length; + response[0] = length; return length + 1U; } diff --git a/subsys/dap/cmsis_dap.h b/subsys/dap/cmsis_dap.h index 0117ec1ab56..2e1fe10e836 100644 --- a/subsys/dap/cmsis_dap.h +++ b/subsys/dap/cmsis_dap.h @@ -36,13 +36,17 @@ #define ID_DAP_WRITE_ABORT 0x08U #define ID_DAP_DELAY 0x09U #define ID_DAP_RESET_TARGET 0x0AU + #define ID_DAP_SWJ_PINS 0x10U #define ID_DAP_SWJ_CLOCK 0x11U #define ID_DAP_SWJ_SEQUENCE 0x12U + #define ID_DAP_SWDP_CONFIGURE 0x13U + #define ID_DAP_JTAG_SEQUENCE 0x14U #define ID_DAP_JTAG_CONFIGURE 0x15U #define ID_DAP_JTAG_IDCODE 0x16U + #define ID_DAP_SWO_TRANSPORT 0x17U #define ID_DAP_SWO_MODE 0x18U #define ID_DAP_SWO_BAUDRATE 0x19U @@ -69,7 +73,13 @@ #define DAP_ID_FW_VER 0x04U #define DAP_ID_DEVICE_VENDOR 0x05U #define DAP_ID_DEVICE_NAME 0x06U +#define DAP_ID_BOARD_VENDOR 0x07U +#define DAP_ID_BOARD_NAME 0x08U +#define DAP_ID_PRODUCT_FW_VER 0x09U #define DAP_ID_CAPABILITIES 0xF0U +#define DAP_ID_TIMESTAMP_CLOCK 0xF1U +#define DAP_ID_UART_RX_BUFFER_SIZE 0xFBU +#define DAP_ID_UART_TX_BUFFER_SIZE 0xFCU #define DAP_ID_SWO_BUFFER_SIZE 0xFDU #define DAP_ID_PACKET_COUNT 0xFEU #define DAP_ID_PACKET_SIZE 0xFFU From cb734c11b07891aa85d7ff17eb6bd3f05c73addb Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Thu, 31 Aug 2023 18:01:15 +0200 Subject: [PATCH 09/17] [nrf fromtree] dap: react properly to unsupported UART commands Add ID_DAP_UART_* command definitions and react properly to unsupported UART commands. Signed-off-by: Maximilian Deubel Signed-off-by: Johann Fischer (cherry picked from commit e31cdc00a0a28bc246c75cdf7dd27366b31b09ba) --- subsys/dap/cmsis_dap.c | 25 +++++++++++++++++++++++++ subsys/dap/cmsis_dap.h | 6 ++++++ 2 files changed, 31 insertions(+) diff --git a/subsys/dap/cmsis_dap.c b/subsys/dap/cmsis_dap.c index bab57301209..76127c801d4 100644 --- a/subsys/dap/cmsis_dap.c +++ b/subsys/dap/cmsis_dap.c @@ -922,6 +922,31 @@ static uint16_t dap_process_cmd(struct dap_context *const ctx, retval = 1; *response = DAP_ERROR; break; + case ID_DAP_UART_TRANSPORT: + LOG_ERR("UART Transport unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_UART_CONFIGURE: + LOG_ERR("UART Configure unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_UART_CONTROL: + LOG_ERR("UART Control unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_UART_STATUS: + LOG_ERR("UART Status unsupported"); + retval = 1; + *response = DAP_ERROR; + break; + case ID_DAP_UART_TRANSFER: + LOG_ERR("UART Transfer unsupported"); + retval = 1; + *response = DAP_ERROR; + break; default: *(response - 1) = ID_DAP_INVALID; diff --git a/subsys/dap/cmsis_dap.h b/subsys/dap/cmsis_dap.h index 2e1fe10e836..787dc822b79 100644 --- a/subsys/dap/cmsis_dap.h +++ b/subsys/dap/cmsis_dap.h @@ -54,6 +54,12 @@ #define ID_DAP_SWO_STATUS 0x1BU #define ID_DAP_SWO_DATA 0x1CU +#define ID_DAP_UART_TRANSPORT 0x1FU +#define ID_DAP_UART_CONFIGURE 0x20U +#define ID_DAP_UART_CONTROL 0x22U +#define ID_DAP_UART_STATUS 0x23U +#define ID_DAP_UART_TRANSFER 0x21U + #define ID_DAP_QUEUE_COMMANDS 0x7EU #define ID_DAP_EXECUTE_COMMANDS 0x7FU From cc5ea5b5a3242cfee53914ee8c22e756b4c61f6c Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Thu, 31 Aug 2023 18:01:51 +0200 Subject: [PATCH 10/17] [nrf fromtree] dap: implement DAP SWD sequence command DAP SWD sequence command is a requirement to support CMSIS-DAPv2. Raise supported version to "2.1.0". Signed-off-by: Maximilian Deubel Signed-off-by: Johann Fischer (cherry picked from commit 9a9afb0b54324ef28b6ca0a1c4ee5e66b51805ec) --- subsys/dap/cmsis_dap.c | 49 ++++++++++++++++++++++++++++++++++++++++++ subsys/dap/cmsis_dap.h | 3 ++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/subsys/dap/cmsis_dap.c b/subsys/dap/cmsis_dap.c index 76127c801d4..5bf3f01976f 100644 --- a/subsys/dap/cmsis_dap.c +++ b/subsys/dap/cmsis_dap.c @@ -638,6 +638,52 @@ static uint16_t dap_transfer(struct dap_context *const ctx, return retval; } +static uint16_t dap_swdp_sequence(struct dap_context *const ctx, + const uint8_t *const request, + uint8_t *const response) +{ + const struct swdp_api *api = ctx->swdp_dev->api; + const uint8_t *request_data = request + 1; + uint8_t *response_data = response + 1; + uint8_t count = request[0]; + uint8_t num_cycles; + uint32_t num_bytes; + bool input; + + switch (ctx->debug_port) { + case DAP_PORT_SWD: + response[0] = DAP_OK; + break; + case DAP_PORT_JTAG: + default: + LOG_ERR("port unsupported"); + response[0] = DAP_ERROR; + return 1U; + } + + for (size_t i = 0; i < count; ++i) { + input = *request_data & BIT(7); + num_cycles = *request_data & BIT_MASK(7); + num_bytes = (num_cycles + 7) >> 3; /* rounded up to full bytes */ + + if (num_cycles == 0) { + num_cycles = 64; + } + + request_data += 1; + + if (input) { + api->swdp_input_sequence(ctx->swdp_dev, num_cycles, response_data); + response_data += num_bytes; + } else { + api->swdp_output_sequence(ctx->swdp_dev, num_cycles, request_data); + request_data += num_bytes; + } + } + + return response_data - response; +} + /* * Process SWD DAP_TransferBlock command and prepare response. * pyOCD counterpart is _encode_transfer_block_data. @@ -865,6 +911,9 @@ static uint16_t dap_process_cmd(struct dap_context *const ctx, case ID_DAP_SWDP_CONFIGURE: retval = dap_swdp_configure(ctx, request, response); break; + case ID_DAP_SWDP_SEQUENCE: + retval = dap_swdp_sequence(ctx, request, response); + break; case ID_DAP_JTAG_SEQUENCE: LOG_ERR("JTAG sequence unsupported"); retval = 1; diff --git a/subsys/dap/cmsis_dap.h b/subsys/dap/cmsis_dap.h index 787dc822b79..d8869e2160a 100644 --- a/subsys/dap/cmsis_dap.h +++ b/subsys/dap/cmsis_dap.h @@ -22,7 +22,7 @@ #include /* Firmware Version */ -#define DAP_FW_VER "1.10" +#define DAP_FW_VER "2.1.0" /* DAP Command IDs */ #define ID_DAP_INFO 0x00U @@ -42,6 +42,7 @@ #define ID_DAP_SWJ_SEQUENCE 0x12U #define ID_DAP_SWDP_CONFIGURE 0x13U +#define ID_DAP_SWDP_SEQUENCE 0x1DU #define ID_DAP_JTAG_SEQUENCE 0x14U #define ID_DAP_JTAG_CONFIGURE 0x15U From 09b557442c53e6290a293d519abe68f0636abcd0 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Fri, 13 Jan 2023 00:41:34 +0100 Subject: [PATCH 11/17] [nrf fromtree] samples: debug: add CMSIS DAP sample using USB as interface Add CMSIS DAP sample using USB as interface. Signed-off-by: Maximilian Deubel Signed-off-by: Johann Fischer (cherry picked from commit 94f48c7a78ed657f79958ae3ff5e602d617ff910) --- samples/subsys/dap/CMakeLists.txt | 9 + samples/subsys/dap/README.rst | 57 +++ samples/subsys/dap/app.overlay | 17 + .../dap/boards/nrf52840dk_nrf52840.overlay | 19 + samples/subsys/dap/prj.conf | 19 + samples/subsys/dap/sample.yaml | 16 + samples/subsys/dap/src/main.c | 370 ++++++++++++++++++ 7 files changed, 507 insertions(+) create mode 100644 samples/subsys/dap/CMakeLists.txt create mode 100644 samples/subsys/dap/README.rst create mode 100644 samples/subsys/dap/app.overlay create mode 100644 samples/subsys/dap/boards/nrf52840dk_nrf52840.overlay create mode 100644 samples/subsys/dap/prj.conf create mode 100644 samples/subsys/dap/sample.yaml create mode 100644 samples/subsys/dap/src/main.c diff --git a/samples/subsys/dap/CMakeLists.txt b/samples/subsys/dap/CMakeLists.txt new file mode 100644 index 00000000000..72ea1953b3c --- /dev/null +++ b/samples/subsys/dap/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(dap) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/samples/subsys/dap/README.rst b/samples/subsys/dap/README.rst new file mode 100644 index 00000000000..dcecb9440b5 --- /dev/null +++ b/samples/subsys/dap/README.rst @@ -0,0 +1,57 @@ +.. _dap-sample: + +DAP Sample Application +###################### + +Overview +******** + +This sample app demonstrates use of a SWDP interface driver and CMSIS DAP +controller through USB Bulk interface. + +Requirements +************ + +This sample supports multiple hardware configurations: + +The simplest configuration would be to connect `SWDIO` to `dio`, `SWDCLK` to `clk` +and optionally `nRESET` to `reset`. The optional `noe` pin is used to enable the port, +e.g. if the SWD connections are multiplexed. + +Building and Running +******************** + +In order for our debug adapter to be recognized by pyOCD we need to change +Zephyr's VID/PID to IDs known to pyOCD, this is up to the user. +The following commands build and flash DAP sample. + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/dap + :board: nrf52840dk_nrf52840 + :goals: flash + :compact: + +Connect HIC to the target and try some pyOCD commands, for example: + +.. code-block:: console + + pyocd commander -t nrf52840 + + 0029527 W Board ID FE5D is not recognized [mbed_board] + Connected to NRF52840 [Sleeping]: FE5D244DFE1F33DB + pyocd> read32 0x20004f18 32 + 20004f18: 20001160 2000244c 00000000 0000e407 | ..` .$L........| + 20004f28: ffffffff ffffffff 00000000 aaaaaaaa |................| + pyocd> halt + Successfully halted device + pyocd> reg + general registers: + lr: 0x00009cdd r7: 0x00000000 (0) + pc: 0x000033ca r8: 0x00000000 (0) + r0: 0x00000000 (0) r9: 0x00000000 (0) + r1: 0x20002854 (536881236) r10: 0x00000000 (0) + r2: 0x20000be4 (536873956) r11: 0x00000000 (0) + r3: 0x00000000 (0) r12: 0x00000000 (0) + r4: 0x200017e8 (536877032) sp: 0x20002898 + r5: 0x20001867 (536877159) xpsr: 0x61000000 (1627389952) + r6: 0x00000000 (0) diff --git a/samples/subsys/dap/app.overlay b/samples/subsys/dap/app.overlay new file mode 100644 index 00000000000..a0ab85fccd6 --- /dev/null +++ b/samples/subsys/dap/app.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + dp0 { + compatible = "zephyr,swdp-gpio"; + status = "okay"; + + clk-gpios = <&arduino_header 10 GPIO_ACTIVE_HIGH>; /* D4 */ + noe-gpios = <&arduino_header 9 GPIO_ACTIVE_HIGH>; /* D3 */ + dio-gpios = <&arduino_header 8 GPIO_PULL_UP>; /* D2 */ + port-write-cycles = <2>; + }; +}; diff --git a/samples/subsys/dap/boards/nrf52840dk_nrf52840.overlay b/samples/subsys/dap/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 00000000000..82438d36d92 --- /dev/null +++ b/samples/subsys/dap/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + dp0 { + compatible = "zephyr,swdp-gpio"; + status = "okay"; + clk-gpios = <&arduino_header 10 GPIO_ACTIVE_HIGH>; /* D4 */ + dio-gpios = <&arduino_header 8 GPIO_PULL_UP>; /* D2 */ + dout-gpios = <&arduino_header 9 GPIO_ACTIVE_HIGH>; /* D3 */ + dnoe-gpios = <&arduino_header 12 GPIO_ACTIVE_HIGH>; /* D6 */ + noe-gpios = <&arduino_header 11 GPIO_ACTIVE_HIGH>; /* D5 */ + reset-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */ + port-write-cycles = <2>; + }; +}; diff --git a/samples/subsys/dap/prj.conf b/samples/subsys/dap/prj.conf new file mode 100644 index 00000000000..85e26acafa9 --- /dev/null +++ b/samples/subsys/dap/prj.conf @@ -0,0 +1,19 @@ +CONFIG_STDOUT_CONSOLE=y +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_BOS=y +CONFIG_SERIAL=y +CONFIG_USB_DEVICE_PRODUCT="Zephyr CMSIS-DAP" +CONFIG_USB_DEVICE_PID=0x0204 +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y +CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n + +CONFIG_LOG=y + +CONFIG_USB_DRIVER_LOG_LEVEL_INF=y +CONFIG_USB_DEVICE_LOG_LEVEL_INF=y +CONFIG_DAP_LOG_LEVEL_INF=y +CONFIG_DP_DRIVER_LOG_LEVEL_INF=y +CONFIG_DAP=y +CONFIG_GPIO=y +CONFIG_NET_BUF=y diff --git a/samples/subsys/dap/sample.yaml b/samples/subsys/dap/sample.yaml new file mode 100644 index 00000000000..65264fa2429 --- /dev/null +++ b/samples/subsys/dap/sample.yaml @@ -0,0 +1,16 @@ +sample: + name: DAP USB +tests: + sample.dap.bulk: + build_only: true + depends_on: arduino_gpio usb_device + platform_allow: + - nrf52840dk/nrf52840 + - frdm_k64f + tags: dap + sample.dap.bulk.nrf: + build_only: true + depends_on: gpio usb_device + platform_allow: + - nrf52840dk/nrf52840 + tags: dap diff --git a/samples/subsys/dap/src/main.c b/samples/subsys/dap/src/main.c new file mode 100644 index 00000000000..d680f89a233 --- /dev/null +++ b/samples/subsys/dap/src/main.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +LOG_MODULE_REGISTER(dap_sample, LOG_LEVEL_INF); + +NET_BUF_POOL_FIXED_DEFINE(dapusb_rx_pool, CONFIG_CMSIS_DAP_PACKET_COUNT, + CONFIG_CMSIS_DAP_PACKET_SIZE, 0, NULL); + +static uint8_t rx_buf[CONFIG_CMSIS_DAP_PACKET_SIZE]; +static uint8_t tx_buf[CONFIG_CMSIS_DAP_PACKET_SIZE]; + +static K_FIFO_DEFINE(dap_rx_queue); + +#define DAP_IFACE_STR_DESC "CMSIS-DAP v2" + +struct dap_iface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bString[USB_BSTRING_LENGTH(DAP_IFACE_STR_DESC)]; +} __packed; + +USBD_STRING_DESCR_USER_DEFINE(primary) struct dap_iface_descriptor dap_iface_desc = { + .bLength = USB_STRING_DESCRIPTOR_LENGTH(DAP_IFACE_STR_DESC), + .bDescriptorType = USB_DESC_STRING, + .bString = DAP_IFACE_STR_DESC +}; + +#define DAP_USB_EP_IN 0x81 +#define DAP_USB_EP_OUT 0x01 +#define DAP_USB_EP_IN_IDX 0 +#define DAP_USB_EP_OUT_IDX 1 + +#define WEBUSB_VENDOR_CODE 0x21 +#define WINUSB_VENDOR_CODE 0x20 + +/* {CDB3B5AD-293B-4663-AA36-1AAE46463776} */ +#define CMSIS_DAP_V2_DEVICE_INTERFACE_GUID \ + '{', 0x00, 'C', 0x00, 'D', 0x00, 'B', 0x00, '3', 0x00, 'B', 0x00, \ + '5', 0x00, 'A', 0x00, 'D', 0x00, '-', 0x00, '2', 0x00, '9', 0x00, \ + '3', 0x00, 'B', 0x00, '-', 0x00, '4', 0x00, '6', 0x00, '6', 0x00, \ + '3', 0x00, '-', 0x00, 'A', 0x00, 'A', 0x00, '3', 0x00, '6', 0x00, \ + '-', 0x00, '1', 0x00, 'A', 0x00, 'A', 0x00, 'E', 0x00, '4', 0x00, \ + '6', 0x00, '4', 0x00, '6', 0x00, '3', 0x00, '7', 0x00, '7', 0x00, \ + '6', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 + +#define COMPATIBLE_ID_WINUSB \ + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00 + +static struct msosv2_descriptor { + struct msosv2_descriptor_set_header header; +#if defined(CONFIG_USB_COMPOSITE_DEVICE) + struct msosv2_function_subset_header subset_header; +#endif + struct msosv2_compatible_id compatible_id; + struct msosv2_guids_property guids_property; +} __packed msosv2_cmsis_dap_desc = { + /* + * Microsoft OS 2.0 descriptor set. This tells Windows what kind + * of device this is and to install the WinUSB driver. + */ + .header = { + .wLength = sizeof(struct msosv2_descriptor_set_header), + .wDescriptorType = MS_OS_20_SET_HEADER_DESCRIPTOR, + .dwWindowsVersion = 0x06030000, + .wTotalLength = sizeof(struct msosv2_descriptor), + }, +#if defined(CONFIG_USB_COMPOSITE_DEVICE) + .subset_header = { + .wLength = sizeof(struct msosv2_function_subset_header), + .wDescriptorType = MS_OS_20_SUBSET_HEADER_FUNCTION, + .wSubsetLength = sizeof(struct msosv2_function_subset_header) + + sizeof(struct msosv2_compatible_id) + + sizeof(struct msosv2_guids_property), + }, +#endif + .compatible_id = { + .wLength = sizeof(struct msosv2_compatible_id), + .wDescriptorType = MS_OS_20_FEATURE_COMPATIBLE_ID, + .CompatibleID = {COMPATIBLE_ID_WINUSB}, + }, + .guids_property = { + .wLength = sizeof(struct msosv2_guids_property), + .wDescriptorType = MS_OS_20_FEATURE_REG_PROPERTY, + .wPropertyDataType = MS_OS_20_PROPERTY_DATA_REG_MULTI_SZ, + .wPropertyNameLength = 42, + .PropertyName = {DEVICE_INTERFACE_GUIDS_PROPERTY_NAME}, + .wPropertyDataLength = 80, + .bPropertyData = {CMSIS_DAP_V2_DEVICE_INTERFACE_GUID}, + }, +}; + +USB_DEVICE_BOS_DESC_DEFINE_CAP struct usb_bos_msosv2_desc { + struct usb_bos_platform_descriptor platform; + struct usb_bos_capability_msos cap; +} __packed bos_cap_msosv2 = { + /* Microsoft OS 2.0 Platform Capability Descriptor */ + .platform = { + .bLength = sizeof(struct usb_bos_platform_descriptor) + + sizeof(struct usb_bos_capability_msos), + .bDescriptorType = USB_DESC_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_BOS_CAPABILITY_PLATFORM, + .bReserved = 0, + .PlatformCapabilityUUID = { + /** + * MS OS 2.0 Platform Capability ID + * D8DD60DF-4589-4CC7-9CD2-659D9E648A9F + */ + 0xDF, 0x60, 0xDD, 0xD8, + 0x89, 0x45, + 0xC7, 0x4C, + 0x9C, 0xD2, + 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F, + }, + }, + .cap = { + /* Windows version (8.1) (0x06030000) */ + .dwWindowsVersion = sys_cpu_to_le32(0x06030000), + .wMSOSDescriptorSetTotalLength = + sys_cpu_to_le16(sizeof(msosv2_cmsis_dap_desc)), + .bMS_VendorCode = WINUSB_VENDOR_CODE, + .bAltEnumCode = 0x00 + }, +}; + +USB_DEVICE_BOS_DESC_DEFINE_CAP struct usb_bos_webusb_desc { + struct usb_bos_platform_descriptor platform; + struct usb_bos_capability_webusb cap; +} __packed bos_cap_webusb = { + /* WebUSB Platform Capability Descriptor: + * https://wicg.github.io/webusb/#webusb-platform-capability-descriptor + */ + .platform = { + .bLength = sizeof(struct usb_bos_platform_descriptor) + + sizeof(struct usb_bos_capability_webusb), + .bDescriptorType = USB_DESC_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_BOS_CAPABILITY_PLATFORM, + .bReserved = 0, + /* WebUSB Platform Capability UUID + * 3408b638-09a9-47a0-8bfd-a0768815b665 + */ + .PlatformCapabilityUUID = { + 0x38, 0xB6, 0x08, 0x34, + 0xA9, 0x09, + 0xA0, 0x47, + 0x8B, 0xFD, + 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65, + }, + }, + .cap = { + .bcdVersion = sys_cpu_to_le16(0x0100), + .bVendorCode = WEBUSB_VENDOR_CODE, + .iLandingPage = 0x01 + } +}; + +/* URL Descriptor: https://wicg.github.io/webusb/#url-descriptor */ +static const uint8_t webusb_origin_url[] = { + /* Length, DescriptorType, Scheme */ + 24, 0x03, 0x01, + 'w', 'w', 'w', '.', + 'z', 'e', 'p', 'h', 'y', 'r', 'p', 'r', 'o', 'j', 'e', 'c', 't', '.', + 'o', 'r', 'g', '/', +}; + +static int msosv2_vendor_handle_req(struct usb_setup_packet *setup, + int32_t *len, uint8_t **data) +{ + if (usb_reqtype_is_to_device(setup)) { + return -ENOTSUP; + } + + if (setup->bRequest == WEBUSB_VENDOR_CODE && setup->wIndex == 0x02) { + *data = (uint8_t *)(&webusb_origin_url); + *len = sizeof(webusb_origin_url); + + LOG_DBG("Get URL request"); + + return 0; + } + + if (setup->bRequest == WINUSB_VENDOR_CODE && + setup->wIndex == MS_OS_20_DESCRIPTOR_INDEX) { + *data = (uint8_t *)(&msosv2_cmsis_dap_desc); + *len = sizeof(msosv2_cmsis_dap_desc); + + LOG_DBG("Get MS OS Descriptors v2"); + + return 0; + } + + return -ENOTSUP; +} + +USBD_CLASS_DESCR_DEFINE(primary, 0) struct { + struct usb_if_descriptor if0; + struct usb_ep_descriptor if0_out_ep; + struct usb_ep_descriptor if0_in_ep; +} __packed dapusb_desc = { + .if0 = { + .bLength = sizeof(struct usb_if_descriptor), + .bDescriptorType = USB_DESC_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_BCC_VENDOR, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + }, + .if0_out_ep = { + .bLength = sizeof(struct usb_ep_descriptor), + .bDescriptorType = USB_DESC_ENDPOINT, + .bEndpointAddress = DAP_USB_EP_OUT, + .bmAttributes = USB_DC_EP_BULK, + .wMaxPacketSize = sys_cpu_to_le16(CONFIG_CMSIS_DAP_PACKET_SIZE), + .bInterval = 0, + }, + .if0_in_ep = { + .bLength = sizeof(struct usb_ep_descriptor), + .bDescriptorType = USB_DESC_ENDPOINT, + .bEndpointAddress = DAP_USB_EP_IN, + .bmAttributes = USB_DC_EP_BULK, + .wMaxPacketSize = sys_cpu_to_le16(CONFIG_CMSIS_DAP_PACKET_SIZE), + .bInterval = 0, + }, +}; + +static struct usb_ep_cfg_data dapusb_ep_data[] = { + { + .ep_cb = usb_transfer_ep_callback, + .ep_addr = DAP_USB_EP_OUT + }, + { + .ep_cb = usb_transfer_ep_callback, + .ep_addr = DAP_USB_EP_IN + } +}; + +static void iface_string_desc_init(struct usb_cfg_data *bulk_cfg) +{ + struct usb_if_descriptor *bulk_if = bulk_cfg->interface_descriptor; + + bulk_if->iInterface = usb_get_str_descriptor_idx(&dap_iface_desc); +} + +static void dapusb_read_cb(uint8_t ep, int size, void *priv) +{ + struct usb_cfg_data *cfg = priv; + struct net_buf *buf; + + LOG_DBG("cfg %p ep %x size %u", cfg, ep, size); + + if (size <= 0) { + goto read_cb_done; + } + + buf = net_buf_alloc(&dapusb_rx_pool, K_FOREVER); + net_buf_add_mem(buf, rx_buf, MIN(size, CONFIG_CMSIS_DAP_PACKET_SIZE)); + k_fifo_put(&dap_rx_queue, buf); + +read_cb_done: + usb_transfer(ep, rx_buf, sizeof(rx_buf), USB_TRANS_READ, dapusb_read_cb, cfg); +} + +static void dapusb_dev_status_cb(struct usb_cfg_data *cfg, + enum usb_dc_status_code status, + const uint8_t *param) +{ + ARG_UNUSED(param); + + if (status == USB_DC_CONFIGURED) { + dapusb_read_cb(cfg->endpoint[DAP_USB_EP_IN_IDX].ep_addr, 0, cfg); + } +} + +static void dapusb_interface_config(struct usb_desc_header *head, + uint8_t bInterfaceNumber) +{ + ARG_UNUSED(head); + + dapusb_desc.if0.bInterfaceNumber = bInterfaceNumber; +#if defined(CONFIG_USB_COMPOSITE_DEVICE) + msosv2_cmsis_dap_desc.subset_header.bFirstInterface = bInterfaceNumber; +#endif +} + +USBD_DEFINE_CFG_DATA(dapusb_config) = { + .usb_device_description = NULL, + .interface_config = dapusb_interface_config, + .interface_descriptor = &dapusb_desc.if0, + .cb_usb_status = dapusb_dev_status_cb, + .interface = { + .class_handler = NULL, + .custom_handler = NULL, + .vendor_handler = msosv2_vendor_handle_req, + }, + .num_endpoints = ARRAY_SIZE(dapusb_ep_data), + .endpoint = dapusb_ep_data +}; + +static int dap_usb_process(void) +{ + uint8_t ep = dapusb_config.endpoint[DAP_USB_EP_OUT_IDX].ep_addr; + struct net_buf *buf; + size_t len; + int err; + + buf = k_fifo_get(&dap_rx_queue, K_FOREVER); + + len = dap_execute_cmd(buf->data, tx_buf); + LOG_DBG("response length %u, starting with [0x%02X, 0x%02X]", + len, tx_buf[0], tx_buf[1]); + net_buf_unref(buf); + + err = usb_transfer_sync(ep, tx_buf, len, USB_TRANS_WRITE | USB_TRANS_NO_ZLP); + if (err < 0 || err != len) { + LOG_ERR("usb_transfer_sync failed, %d", err); + return -EIO; + } + + return 0; +} + +int main(void) +{ + const struct device *const swd_dev = DEVICE_DT_GET_ONE(zephyr_swdp_gpio); + int ret; + + if (!device_is_ready(swd_dev)) { + LOG_ERR("SWD device is not ready"); + return -ENODEV; + } + + ret = dap_setup(swd_dev); + if (ret) { + LOG_ERR("Failed to initialize DAP controller, %d", ret); + return ret; + } + + /* Add MS OS 2.0 BOS descriptor to BOS structure */ + usb_bos_register_cap((void *)&bos_cap_msosv2); + /* Point interface index to string descriptor */ + iface_string_desc_init(&dapusb_config); + + ret = usb_enable(NULL); + if (ret != 0) { + LOG_ERR("Failed to enable USB"); + return 0; + } + + while (!dap_usb_process()) { + } + + return usb_disable(); +} From 30f4aa50977ca49b8bb361ab9910b67288131fa5 Mon Sep 17 00:00:00 2001 From: Maximilian Deubel Date: Fri, 24 May 2024 15:43:16 +0200 Subject: [PATCH 12/17] [nrf fromtree] dap: fix timeout handling Use k_timepoint_t for timeout handling in the swj_pins function. Signed-off-by: Maximilian Deubel (cherry picked from commit 05a45a19e35a793afa7e68b65822d4cddcc139a9) --- subsys/dap/cmsis_dap.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/subsys/dap/cmsis_dap.c b/subsys/dap/cmsis_dap.c index 5bf3f01976f..4148346e15f 100644 --- a/subsys/dap/cmsis_dap.c +++ b/subsys/dap/cmsis_dap.c @@ -285,7 +285,7 @@ static uint16_t dap_swj_pins(struct dap_context *const ctx, uint8_t value = request[0]; uint8_t select = request[1]; uint32_t wait = sys_get_le32(&request[2]); - int64_t timeout_ticks; + k_timepoint_t end = sys_timepoint_calc(K_USEC(wait)); uint8_t state; if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) { @@ -299,8 +299,6 @@ static uint16_t dap_swj_pins(struct dap_context *const ctx, api->swdp_set_pins(ctx->swdp_dev, select, value); } - timeout_ticks = k_uptime_ticks() + (CONFIG_SYS_CLOCK_TICKS_PER_SEC / 1000000 * wait); - do { api->swdp_get_pins(ctx->swdp_dev, &state); LOG_INF("select 0x%02x, value 0x%02x, wait %u, state 0x%02x", @@ -309,7 +307,7 @@ static uint16_t dap_swj_pins(struct dap_context *const ctx, LOG_DBG("swdp_get_pins succeeded before timeout"); break; } - } while (k_uptime_ticks() - timeout_ticks > 0); + } while (!sys_timepoint_expired(end)); response[0] = state; From 2ac4331771a825a77a5c2361b989e4f0b68e55c5 Mon Sep 17 00:00:00 2001 From: Maximilian Deubel Date: Fri, 24 May 2024 16:43:22 +0200 Subject: [PATCH 13/17] [nrf fromtree] driver: swdp_bitbang: hardcode request lut Hardcode the lookup table for SWDP requests. This is an optimization to save some space. Documentation was added to understand the values. Signed-off-by: Maximilian Deubel (cherry picked from commit f1b6073a278d6f7ed57fc28f3caf04062443fb24) --- drivers/dp/swdp_bitbang.c | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/drivers/dp/swdp_bitbang.c b/drivers/dp/swdp_bitbang.c index f3c5fcc50d2..ba46c629604 100644 --- a/drivers/dp/swdp_bitbang.c +++ b/drivers/dp/swdp_bitbang.c @@ -58,30 +58,20 @@ struct sw_cfg_data { bool fast_clock; }; -static uint8_t sw_request_lut[16] = {0U}; - -static void mk_sw_request_lut(void) -{ - uint32_t parity = 0U; - - for (int request = 0; request < sizeof(sw_request_lut); request++) { - parity = request; - parity ^= parity >> 2; - parity ^= parity >> 1; - - /* - * Move A[3:3], RnW, APnDP bits to their position, - * add start bit, stop bit(6), and park bit. - */ - sw_request_lut[request] = BIT(7) | (request << 1) | BIT(0); - /* Add parity bit */ - if (parity & 0x01U) { - sw_request_lut[request] |= BIT(5); - } - } - - LOG_HEXDUMP_DBG(sw_request_lut, sizeof(sw_request_lut), "request lut"); -} +/* + * Move A[2:3], RnW, APnDP bits to their position, + * add start bit, stop bit(6), park bit and parity bit. + * For example, reading IDCODE would be APnDP=0, RnW=1, A2=0, A3=0. + * The request would be 0xa5, which is 10100101 in binary. + * + * For more information, see: + * - CMSIS-DAP Command Specification, DAP_Transfer + * - ARM Debug Interface v5 Architecture Specification + */ +const static uint8_t sw_request_lut[16] = { + 0x81, 0xa3, 0xa5, 0x87, 0xa9, 0x8b, 0x8d, 0xaf, + 0xb1, 0x93, 0x95, 0xb7, 0x99, 0xbb, 0xbd, 0x9f +}; static ALWAYS_INLINE uint32_t sw_get32bit_parity(uint32_t data) { @@ -678,7 +668,6 @@ static int sw_gpio_init(const struct device *dev) sw_data->fast_clock = false; sw_data->clock_delay = CLOCK_DELAY(SWDP_DEFAULT_SWCLK_FREQUENCY, config->port_write_cycles); - mk_sw_request_lut(); return 0; } From d2a30d5564ef746aedef1d75ae4922266a045617 Mon Sep 17 00:00:00 2001 From: Maximilian Deubel Date: Fri, 24 May 2024 16:20:46 +0200 Subject: [PATCH 14/17] [nrf fromtree] drivers: swdp: document API This patch adds documentation for the SWDP API. Signed-off-by: Maximilian Deubel Signed-off-by: Johann Fischer (cherry picked from commit 96112ade749f6d65b5d5f051e253c98c354e9679) --- include/zephyr/drivers/swdp.h | 84 ++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 15 deletions(-) diff --git a/include/zephyr/drivers/swdp.h b/include/zephyr/drivers/swdp.h index c1f06e52151..d742280e883 100644 --- a/include/zephyr/drivers/swdp.h +++ b/include/zephyr/drivers/swdp.h @@ -42,19 +42,39 @@ extern "C" { * This is the mandatory API any Serial Wire driver needs to expose. */ struct swdp_api { - /* Generate SWJ Sequence according to sequence bit count and bit data */ + /** + * @brief Write count bits to SWDIO from data LSB first + * + * @param dev SWDP device + * @param count Number of bits to write + * @param data Bits to write + * @return 0 on success, or error code + */ int (*swdp_output_sequence)(const struct device *dev, uint32_t count, const uint8_t *data); - /* Read count bits from SWDIO into data LSB first */ + /** + * @brief Read count bits from SWDIO into data LSB first + * + * @param dev SWDP device + * @param count Number of bits to read + * @param data Buffer to store bits read + * @return 0 on success, or error code + */ int (*swdp_input_sequence)(const struct device *dev, uint32_t count, uint8_t *data); - /* - * Perform SWDP transfer based on host request value and store - * acknowledge response bits ACK[0:2]. + /** + * @brief Perform SWDP transfer and store response + * + * @param dev SWDP device + * @param request SWDP request bits + * @param data Data to be transferred with request + * @param idle_cycles Idle cycles between request and response + * @param response Buffer to store response (ACK/WAIT/FAULT) + * @return 0 on success, or error code */ int (*swdp_transfer)(const struct device *dev, uint8_t request, @@ -62,31 +82,65 @@ struct swdp_api { uint8_t idle_cycles, uint8_t *response); - /* Set SWCLK, SWDPIO, and nRESET pins state */ + /** + * @brief Set SWCLK, SWDPIO, and nRESET pins state + * @note The bit positions are defined by the SWDP_*_PIN macros. + * + * @param dev SWDP device + * @param pins Bitmask of pins to set + * @param value Value to set pins to + * @return 0 on success, or error code + */ int (*swdp_set_pins)(const struct device *dev, uint8_t pins, uint8_t value); - /* Get SWCLK, SWDPIO, and nRESET pins state */ + /** + * @brief Get SWCLK, SWDPIO, and nRESET pins state + * @note The bit positions are defined by the SWDP_*_PIN macros. + * + * @param dev SWDP device + * @param state Place to store pins state + * @return 0 on success, or error code + */ int (*swdp_get_pins)(const struct device *dev, uint8_t *state); - /* Set SWCLK frequency */ + /** + * @brief Set SWDP clock frequency + * + * @param dev SWDP device + * @param clock Clock frequency in Hz + * @return 0 on success, or error code + */ int (*swdp_set_clock)(const struct device *dev, uint32_t clock); - /* - * Configure interface, line turnaround and whether data phase is - * forced after WAIN and FAULT response. + /** + * @brief Configure SWDP interface + * + * @param dev SWDP device + * @param turnaround Line turnaround cycles + * @param data_phase Always generate Data Phase (also on WAIT/FAULT) + * @return 0 on success, or error code */ int (*swdp_configure)(const struct device *dev, uint8_t turnaround, bool data_phase); - /* - * Enable interface, set SWDPIO to output mode - * and set SWCLK and nRESET to default high level. + /** + * @brief Enable interface, set pins to default state + * + * @note SWDPIO is set to output mode, SWCLK and nRESET are set to high level. + * + * @param dev SWDP device + * @return 0 on success, or error code */ int (*swdp_port_on)(const struct device *dev); - /* Disables interface, set SWCLK, SWDPIO, nRESET to High-Z mode. */ + /** + * @brief Disable interface, set pins to High-Z mode + * + * @param dev SWDP device + * @return 0 on success, or error code + */ int (*swdp_port_off)(const struct device *dev); }; From bbea2576b91eef4ea6d5d870d678c32443ace22b Mon Sep 17 00:00:00 2001 From: Maximilian Deubel Date: Fri, 9 Aug 2024 16:57:37 +0200 Subject: [PATCH 15/17] [nrf fromlist] drivers: dp: swdp_bitbang: Update SWD clock calculation This patch updates the SWD clock calculation to the latest behavior of DAPLink. Upstream PR: https://github.com/zephyrproject-rtos/zephyr/pull/74969 Signed-off-by: Maximilian Deubel --- drivers/dp/swdp_bitbang.c | 28 ++++++++++++++++------------ drivers/dp/swdp_ll_pin.h | 1 + 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/dp/swdp_bitbang.c b/drivers/dp/swdp_bitbang.c index ba46c629604..c6a3b034f18 100644 --- a/drivers/dp/swdp_bitbang.c +++ b/drivers/dp/swdp_bitbang.c @@ -26,8 +26,8 @@ #include LOG_MODULE_REGISTER(swdp, CONFIG_DP_DRIVER_LOG_LEVEL); -#define CLOCK_DELAY(swclk_freq, port_write_cycles) \ - ((CPU_CLOCK / 2 / swclk_freq) - port_write_cycles) +#define MAX_SWJ_CLOCK(delay_cycles, port_write_cycles) \ + ((CPU_CLOCK / 2U) / (port_write_cycles + delay_cycles)) /* * Default SWCLK frequency in Hz. @@ -35,6 +35,7 @@ LOG_MODULE_REGISTER(swdp, CONFIG_DP_DRIVER_LOG_LEVEL); */ #define SWDP_DEFAULT_SWCLK_FREQUENCY 1000000U +#define DELAY_FAST_CYCLES 2U #define DELAY_SLOW_CYCLES 3U struct sw_config { @@ -528,14 +529,19 @@ static int sw_set_clock(const struct device *dev, const uint32_t clock) struct sw_cfg_data *sw_data = dev->data; uint32_t delay; - sw_data->fast_clock = false; - delay = ((CPU_CLOCK / 2U) + (clock - 1U)) / clock; - - if (delay > config->port_write_cycles) { - delay -= config->port_write_cycles; - delay = (delay + (DELAY_SLOW_CYCLES - 1U)) / DELAY_SLOW_CYCLES; - } else { + if (clock >= MAX_SWJ_CLOCK(DELAY_FAST_CYCLES, config->port_write_cycles)) { + sw_data->fast_clock = true; delay = 1U; + } else { + sw_data->fast_clock = false; + + delay = ((CPU_CLOCK / 2U) + (clock - 1U)) / clock; + if (delay > config->port_write_cycles) { + delay -= config->port_write_cycles; + delay = (delay + (DELAY_SLOW_CYCLES - 1U)) / DELAY_SLOW_CYCLES; + } else { + delay = 1U; + } } sw_data->clock_delay = delay; @@ -666,9 +672,7 @@ static int sw_gpio_init(const struct device *dev) sw_data->turnaround = 1U; sw_data->data_phase = false; sw_data->fast_clock = false; - sw_data->clock_delay = CLOCK_DELAY(SWDP_DEFAULT_SWCLK_FREQUENCY, - config->port_write_cycles); - + sw_set_clock(dev, SWDP_DEFAULT_SWCLK_FREQUENCY); return 0; } diff --git a/drivers/dp/swdp_ll_pin.h b/drivers/dp/swdp_ll_pin.h index 3044931a228..bcce71a108f 100644 --- a/drivers/dp/swdp_ll_pin.h +++ b/drivers/dp/swdp_ll_pin.h @@ -6,6 +6,7 @@ #include #include +#include #if defined(CONFIG_SOC_SERIES_NRF52X) #define CPU_CLOCK 64000000U From 2eeed99215922559bb4e170267db22140298e4f6 Mon Sep 17 00:00:00 2001 From: Maximilian Deubel Date: Fri, 9 Aug 2024 16:58:03 +0200 Subject: [PATCH 16/17] [nrf fromlist] drivers: dp: swdp_bitbang: hardcode nRF53 SYSCLK Since CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC is tied to the slow RTC clock for the nRF53, use the default SYSCLK of 64MHz instead. Upstream PR: https://github.com/zephyrproject-rtos/zephyr/pull/74969 Signed-off-by: Maximilian Deubel --- drivers/dp/swdp_ll_pin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dp/swdp_ll_pin.h b/drivers/dp/swdp_ll_pin.h index bcce71a108f..3fb45560cda 100644 --- a/drivers/dp/swdp_ll_pin.h +++ b/drivers/dp/swdp_ll_pin.h @@ -8,7 +8,7 @@ #include #include -#if defined(CONFIG_SOC_SERIES_NRF52X) +#if defined(CONFIG_SOC_SERIES_NRF52X) || defined(CONFIG_SOC_SERIES_NRF53X) #define CPU_CLOCK 64000000U #else #define CPU_CLOCK CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC From ebd054520daaf3cedd73d138937fa35c052ae8e2 Mon Sep 17 00:00:00 2001 From: Maximilian Deubel Date: Mon, 24 Jun 2024 16:50:42 +0200 Subject: [PATCH 17/17] drivers: dp: swdp_bitbang: power optimization This patch changes GPIO initialization to be in PORT_OFF state by default. Also, if no transceiver is attached to the signals, the GPIOs are configured to be disconnected to preserve power. I tested this on a prototype board where we are going to have a debugger in a low-power context. Signed-off-by: Maximilian Deubel --- drivers/dp/swdp_bitbang.c | 122 ++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 57 deletions(-) diff --git a/drivers/dp/swdp_bitbang.c b/drivers/dp/swdp_bitbang.c index c6a3b034f18..42f6b02f1fe 100644 --- a/drivers/dp/swdp_bitbang.c +++ b/drivers/dp/swdp_bitbang.c @@ -568,107 +568,115 @@ static int sw_configure(const struct device *dev, static int sw_port_on(const struct device *dev) { const struct sw_config *config = dev->config; - - gpio_pin_set_dt(&config->clk, 1); + int ret; if (config->dnoe.port) { - gpio_pin_set_dt(&config->dnoe, 1); + ret = gpio_pin_configure_dt(&config->dnoe, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } } - if (config->dout.port) { - gpio_pin_set_dt(&config->dout, 1); - } else { - int ret; - - ret = gpio_pin_configure_dt(&config->dio, GPIO_OUTPUT_ACTIVE); + ret = gpio_pin_configure_dt(&config->dout, GPIO_OUTPUT_ACTIVE); if (ret) { return ret; } } - - if (config->noe.port) { - gpio_pin_set_dt(&config->noe, 1); - } - if (config->reset.port) { - gpio_pin_set_dt(&config->reset, 1); - } - - return 0; -} - -static int sw_port_off(const struct device *dev) -{ - const struct sw_config *config = dev->config; - - if (config->dnoe.port) { - gpio_pin_set_dt(&config->dnoe, 0); + ret = gpio_pin_configure_dt(&config->dio, GPIO_INPUT); + if (ret) { + return ret; } - - if (config->dout.port) { - gpio_pin_set_dt(&config->dout, 0); - } else { - int ret; - - ret = gpio_pin_configure_dt(&config->dio, GPIO_INPUT); + if (config->noe.port) { + ret = gpio_pin_configure_dt(&config->noe, GPIO_OUTPUT_ACTIVE); if (ret) { return ret; } } - - if (config->noe.port) { - gpio_pin_set_dt(&config->noe, 0); + ret = gpio_pin_configure_dt(&config->clk, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; } - if (config->reset.port) { - gpio_pin_set_dt(&config->reset, 1); + ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; } return 0; } -static int sw_gpio_init(const struct device *dev) +static int sw_port_off(const struct device *dev) { const struct sw_config *config = dev->config; - struct sw_cfg_data *sw_data = dev->data; int ret; - ret = gpio_pin_configure_dt(&config->clk, GPIO_OUTPUT_ACTIVE); - if (ret) { - return ret; - } - - ret = gpio_pin_configure_dt(&config->dio, GPIO_INPUT); - if (ret) { - return ret; - } - - if (config->dout.port) { - ret = gpio_pin_configure_dt(&config->dout, GPIO_OUTPUT_ACTIVE); + /* If there is a transceiver connected to IO, pins should always be driven. */ + if (config->dnoe.port) { + ret = gpio_pin_configure_dt(&config->dnoe, GPIO_OUTPUT_INACTIVE); if (ret) { return ret; } - } - - if (config->dnoe.port) { - ret = gpio_pin_configure_dt(&config->dnoe, GPIO_OUTPUT_INACTIVE); + if (config->dout.port) { + ret = gpio_pin_configure_dt(&config->dout, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + } + ret = gpio_pin_configure_dt(&config->dio, GPIO_INPUT); + if (ret) { + return ret; + } + } else { + if (config->dout.port) { + ret = gpio_pin_configure_dt(&config->dout, GPIO_DISCONNECTED); + if (ret) { + return ret; + } + } + ret = gpio_pin_configure_dt(&config->dio, GPIO_DISCONNECTED); if (ret) { return ret; } } + /* If there is a transceiver connected to CLK, pins should always be driven. */ if (config->noe.port) { ret = gpio_pin_configure_dt(&config->noe, GPIO_OUTPUT_INACTIVE); if (ret) { return ret; } + ret = gpio_pin_configure_dt(&config->clk, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + } else { + ret = gpio_pin_configure_dt(&config->clk, GPIO_DISCONNECTED); + if (ret) { + return ret; + } } if (config->reset.port) { - ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_ACTIVE); + ret = gpio_pin_configure_dt(&config->reset, GPIO_DISCONNECTED); if (ret) { return ret; } } + return 0; +} + +static int sw_gpio_init(const struct device *dev) +{ + const struct sw_config *config = dev->config; + struct sw_cfg_data *sw_data = dev->data; + int ret; + + /* start with the port turned off */ + ret = sw_port_off(dev); + if (ret) { + return ret; + } + sw_data->turnaround = 1U; sw_data->data_phase = false; sw_data->fast_clock = false;