719 lines
16 KiB
C
719 lines
16 KiB
C
/*
|
|
* 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 CLOCK_DELAY(swclk_freq, port_write_cycles) \
|
|
((CPU_CLOCK / 2 / swclk_freq) - port_write_cycles)
|
|
|
|
/*
|
|
* Default SWCLK frequency in Hz.
|
|
* sw_clock can be used to overwrite this default value.
|
|
*/
|
|
#define SWDP_DEFAULT_SWCLK_FREQUENCY 1000000U
|
|
|
|
#define DELAY_SLOW_CYCLES 3U
|
|
|
|
struct sw_config {
|
|
struct gpio_dt_spec clk;
|
|
struct gpio_dt_spec dout;
|
|
struct gpio_dt_spec 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;
|
|
|
|
sw_data->fast_clock = false;
|
|
delay = ((CPU_CLOCK / 2U) + (clock - 1U)) / clock;
|
|
|
|
if (delay > config->port_write_cycles) {
|
|
delay -= config->port_write_cycles;
|
|
delay = (delay + (DELAY_SLOW_CYCLES - 1U)) / DELAY_SLOW_CYCLES;
|
|
} else {
|
|
delay = 1U;
|
|
}
|
|
|
|
sw_data->clock_delay = delay;
|
|
|
|
LOG_WRN("cpu_clock %d, delay %d", CPU_CLOCK, sw_data->clock_delay);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sw_configure(const struct device *dev,
|
|
const uint8_t turnaround, const bool data_phase)
|
|
{
|
|
struct sw_cfg_data *sw_data = dev->data;
|
|
|
|
sw_data->turnaround = turnaround;
|
|
sw_data->data_phase = data_phase;
|
|
|
|
LOG_INF("turnaround %d, data_phase %d",
|
|
sw_data->turnaround, sw_data->data_phase);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sw_port_on(const struct device *dev)
|
|
{
|
|
const struct sw_config *config = dev->config;
|
|
|
|
gpio_pin_set_dt(&config->clk, 1);
|
|
|
|
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;
|
|
}
|
|
|
|
static int sw_port_off(const struct device *dev)
|
|
{
|
|
const struct sw_config *config = dev->config;
|
|
|
|
if (config->dnoe.port) {
|
|
gpio_pin_set_dt(&config->dnoe, 0);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int sw_gpio_init(const struct device *dev)
|
|
{
|
|
const struct sw_config *config = dev->config;
|
|
struct sw_cfg_data *sw_data = dev->data;
|
|
int ret;
|
|
|
|
ret = gpio_pin_configure_dt(&config->clk, GPIO_OUTPUT_ACTIVE);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = gpio_pin_configure_dt(&config->dio, GPIO_INPUT);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (config->dout.port) {
|
|
ret = gpio_pin_configure_dt(&config->dout, GPIO_OUTPUT_ACTIVE);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (config->dnoe.port) {
|
|
ret = gpio_pin_configure_dt(&config->dnoe, 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;
|
|
}
|
|
}
|
|
|
|
if (config->reset.port) {
|
|
ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_ACTIVE);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
sw_data->turnaround = 1U;
|
|
sw_data->data_phase = false;
|
|
sw_data->fast_clock = false;
|
|
sw_data->clock_delay = CLOCK_DELAY(SWDP_DEFAULT_SWCLK_FREQUENCY,
|
|
config->port_write_cycles);
|
|
|
|
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)
|