blob: 8dec65b6c1055f02a12debf3337ec0d909f8933a [file] [log] [blame]
/*
* 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 <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/swdp.h>
#include "swdp_ll_pin.h"
#include <zephyr/logging/log.h>
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;
}
if (config->reset.port) {
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)
{
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)