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..b811529bcb5 --- /dev/null +++ b/drivers/dp/Kconfig @@ -0,0 +1,31 @@ +# 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 + depends on GPIO + 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..42f6b02f1fe --- /dev/null +++ b/drivers/dp/swdp_bitbang.c @@ -0,0 +1,730 @@ +/* + * 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 "swdp_ll_pin.h" + +#include +LOG_MODULE_REGISTER(swdp, CONFIG_DP_DRIVER_LOG_LEVEL); + +#define MAX_SWJ_CLOCK(delay_cycles, port_write_cycles) \ + ((CPU_CLOCK / 2U) / (port_write_cycles + delay_cycles)) + +/* + * Default SWCLK frequency in Hz. + * sw_clock can be used to overwrite this default value. + */ +#define SWDP_DEFAULT_SWCLK_FREQUENCY 1000000U + +#define DELAY_FAST_CYCLES 2U +#define DELAY_SLOW_CYCLES 3U + +struct sw_config { + struct gpio_dt_spec clk; + struct gpio_dt_spec dout; + 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; +}; + +struct sw_cfg_data { + uint32_t clock_delay; + uint8_t turnaround; + bool data_phase; + bool fast_clock; +}; + +/* + * 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) +{ + data ^= data >> 16; + data ^= data >> 8; + data ^= data >> 4; + data ^= data >> 2; + data ^= data >> 1; + + return data & 1U; +} + +/* 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; + + 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; + + 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; + + 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; + + 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) +{ + if (bit & 1U) { + pin_swdio_set(dev); + } else { + pin_swdio_clr(dev); + } +} + +/* 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; + + if (FAST_BITBANG_HW_SUPPORT) { + return swdp_ll_pin_get(config->dio_reg, config->dio.pin); + } else { + return gpio_pin_get_dt(&config->dio); + } +} + +/* + * 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; + + 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); + } + } +} + +/* + * 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; + + 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) \ + 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_output_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; /* current byte */ + uint32_t n = 0; /* bit counter */ + + 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++; + 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 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; + 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; + + 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); + 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 (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 (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); + } + } + } + + 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; + + if (config->reset.port) { + val = gpio_pin_get_dt(&config->reset); + *state = val ? BIT(SWDP_nRESET_PIN) : 0; + } + + val = gpio_pin_get_dt(&config->dio); + *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; + + 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; + + 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; + int ret; + + if (config->dnoe.port) { + ret = gpio_pin_configure_dt(&config->dnoe, GPIO_OUTPUT_ACTIVE); + 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->dio, GPIO_INPUT); + if (ret) { + return ret; + } + if (config->noe.port) { + ret = gpio_pin_configure_dt(&config->noe, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + } + ret = gpio_pin_configure_dt(&config->clk, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + + return 0; +} + +static int sw_port_off(const struct device *dev) +{ + const struct sw_config *config = dev->config; + int ret; + + /* 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->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_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; + sw_set_clock(dev, SWDP_DEFAULT_SWCLK_FREQUENCY); + return 0; +} + +static struct swdp_api swdp_bitbang_api = { + .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, + .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) \ + 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..3fb45560cda --- /dev/null +++ b/drivers/dp/swdp_ll_pin.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#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 +#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 new file mode 100644 index 00000000000..c6d6aa55e7f --- /dev/null +++ b/dts/bindings/misc/zephyr,swdp-gpio.yaml @@ -0,0 +1,102 @@ +# Copyright (c) 2019 Phytec Messtechnik GmbH +# Copyright (c) 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +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 + +properties: + clk-gpios: + type: phandle-array + required: true + description: GPIO pin used for SWCLK output + + dio-gpios: + type: phandle-array + required: true + description: | + GPIO pin used for SWDIO input. This pin is also used for the SWDIO output + if separate output pin is not defined. + + dout-gpios: + type: phandle-array + description: | + Optional GPIO pin used for SWDIO output. + + dnoe-gpios: + type: phandle-array + description: | + GPIO pin used to disable the SWDIO output buffer behind optional + pin dout-gpios. + + noe-gpios: + type: phandle-array + description: | + Optional pin to disable all bus transceivers if any are present. + + reset-gpios: + type: phandle-array + 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.For example, the + GPIO clock may be different from the CPU clock. This can usually be + found in the SoC documentation. diff --git a/include/zephyr/drivers/swdp.h b/include/zephyr/drivers/swdp.h new file mode 100644 index 00000000000..d742280e883 --- /dev/null +++ b/include/zephyr/drivers/swdp.h @@ -0,0 +1,151 @@ +/* + * 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 { + /** + * @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); + + /** + * @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); + + /** + * @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, + uint32_t *data, + uint8_t idle_cycles, + uint8_t *response); + + /** + * @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); + + /** + * @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); + + /** + * @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); + + /** + * @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); + + /** + * @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); + + /** + * @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); +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_SWDP_H_ */ 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(); +} 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..2436ba71c4e --- /dev/null +++ b/subsys/dap/Kconfig @@ -0,0 +1,55 @@ +# 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. + +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. + +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" + +endif # DAP diff --git a/subsys/dap/cmsis_dap.c b/subsys/dap/cmsis_dap.c new file mode 100644 index 00000000000..4148346e15f --- /dev/null +++ b/subsys/dap/cmsis_dap.c @@ -0,0 +1,1060 @@ +/* + * 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 + +#include +LOG_MODULE_REGISTER(dap, CONFIG_DAP_LOG_LEVEL); + +#define DAP_STATE_CONNECTED 0 + +struct dap_context { + struct device *swdp_dev; + atomic_t state; + uint8_t debug_port; + uint8_t capabilities; + uint16_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]; + +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, + 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"); + 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"); + 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 = sizeof(DAP_FW_VER); + break; + case DAP_ID_DEVICE_VENDOR: + 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"); + 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; + case DAP_ID_PACKET_SIZE: + LOG_DBG("ID_PACKET_SIZE"); + sys_put_le16(ctx->pkt_size, &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] = 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"); + 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]); + k_timepoint_t end = sys_timepoint_calc(K_USEC(wait)); + 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); + } + + 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 (!sys_timepoint_expired(end)); + + 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_output_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; +} + +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. + * 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_SWDP_SEQUENCE: + retval = dap_swdp_sequence(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; + 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; + 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 = 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; + 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..d8869e2160a --- /dev/null +++ b/subsys/dap/cmsis_dap.h @@ -0,0 +1,136 @@ +/* + * 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 "2.1.0" + +/* 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_SWDP_SEQUENCE 0x1DU + +#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_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 + +/* 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_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 + +/* 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_ */