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_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) \ - 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_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 + 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 - required: true - description: GPIO pin used for SWDIO output - - din-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.