From 87f78286794f6d8fbf885715b01b685f98885d9d Mon Sep 17 00:00:00 2001 From: Tomas Vanek Date: Sun, 5 Nov 2023 06:49:18 +0100 Subject: [PATCH] drivers/ch347: add SWD speed settings SWD has completely different speed setting than JTAG. There is no official chip documentation. The trial and error method on SWD init USB packet discovered a byte parameter used as a divisor from 1 MHz maximal clock. Prevent setting too low clocks as it may trigger USB disconnects. CH347T/F versions 5.44/1.1 add 5 MHz SWD speed at divisor/index 0. CH347F related parts co-authored by ZhiYuanNJ <871238103@qq.com> Signed-off-by: Tomas Vanek Change-Id: I5a990a43dcd7a6d73c71b795283a9fbdff489bf4 Reviewed-on: https://review.openocd.org/c/openocd/+/8743 Reviewed-by: ZhiYuanNJ <871238103@qq.com> Tested-by: jenkins --- src/jtag/drivers/ch347.c | 85 ++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/src/jtag/drivers/ch347.c b/src/jtag/drivers/ch347.c index f4f013a79..ecec155f7 100644 --- a/src/jtag/drivers/ch347.c +++ b/src/jtag/drivers/ch347.c @@ -119,6 +119,9 @@ #define CH347_MAX_SEND_BUF 0X200 #define CH347_MAX_RECV_BUF 0X200 #define CH347_MAX_CMD_BUF 128 +#define CH347_SWD_CLOCK_MAX 5000 +#define CH347_SWD_CLOCK_BASE 1000 +#define CH347_SWD_CLOCK_MAX_DIVISOR 8 #define CH347_EPOUT 0x06u // the usb endpoint number for writing #define CH347_EPIN 0x86u // the usb endpoint number for reading @@ -205,6 +208,7 @@ struct ch347_info { bool use_bitwise_mode; enum pack_size pack_size; // see: pack_size for explanation int max_len; // in STANDARD_PACK or bitwise mode we can send only one USBC_PACKET_USBHS sized package + bool swclk_5mhz_supported; // a "scratchpad" where we record all bytes for one command uint8_t scratchpad_cmd_type; // command type @@ -1506,6 +1510,8 @@ static int ch347_open_device(void) if (ch347.chip_variant == CH347T) { if (swd_mode) { + ch347.swclk_5mhz_supported = ch347_device_descriptor.bcdDevice >= 0x544; + if (ch347_device_descriptor.bcdDevice < 0x441) LOG_WARNING("CH347T version older than 4.41 probably does not support SWD transport"); @@ -1520,6 +1526,8 @@ static int ch347_open_device(void) LOG_INFO("Please upgrade CH347T firmware to a production version >= 5.44"); } else if (ch347.chip_variant == CH347F) { + ch347.swclk_5mhz_supported = ch347_device_descriptor.bcdDevice >= 0x101; + if (ch347_device_descriptor.bcdDevice < 0x101) LOG_INFO("Please upgrade CH347F firmware to a production version >= 1.1"); } @@ -1608,16 +1616,44 @@ static int ch347_adapter_set_speed(uint8_t clock_index) return ch347_adapter_init(clock_index, &unused); } +/** + * @brief swd init function + * @param clock_divisor Divisor of base SWD frequency 1 MHz or 0 for fast 5 MHz clock + * + * @return ERROR_OK on success + */ +static int ch347_swd_init_cmd(uint8_t clock_divisor) +{ + int retval = ch347_cmd_start_next(CH347_CMD_SWD_INIT); + if (retval != ERROR_OK) + return retval; + + uint8_t cmd_data[] = {0x40, 0x42, 0x0f, 0x00, clock_divisor, 0x00, 0x00, 0x00 }; + retval = ch347_scratchpad_add_bytes(cmd_data, ARRAY_SIZE(cmd_data)); + if (retval != ERROR_OK) + return retval; + + /* TODO: CH347_CMD_SWD_INIT reads one data byte. + But how can we decide if SWD init was successfully executed? + Return an error code if init was failed */ + uint8_t init_result = 0; + retval = ch347_single_read_get_byte(0, &init_result); + LOG_DEBUG("SWD init clk div %" PRIu8 ", result %02" PRIx8, + clock_divisor, init_result); + return retval; +} + /** * @brief Initializes the JTAG interface and set CH347 TCK frequency * * @param speed_index speed index for JTAG_INIT command - * @return Success returns ERROR_OK,failed returns ERROR_FAIL + * @return Success returns ERROR_OK, failed returns ERROR_FAIL */ static int ch347_speed_set(int speed_index) { if (swd_mode) - return ERROR_OK; + return ch347_swd_init_cmd(speed_index); + int retval = ch347_adapter_set_speed(speed_index); if (retval != ERROR_OK) { LOG_ERROR("Couldn't set CH347 speed"); @@ -1660,6 +1696,14 @@ static int ch347_init_pack_size(void) */ static int ch347_speed_get(int speed_idx, int *khz) { + if (swd_mode) { + if (speed_idx) + *khz = DIV_ROUND_UP(CH347_SWD_CLOCK_BASE, speed_idx); + else + *khz = CH347_SWD_CLOCK_MAX; + return ERROR_OK; + } + int retval = ch347_init_pack_size(); if (retval != ERROR_OK) return retval; @@ -1682,6 +1726,19 @@ static int ch347_speed_get_index(int khz, int *speed_idx) LOG_ERROR("Adaptive clocking not supported"); return ERROR_FAIL; } + + if (swd_mode) { + if (khz >= CH347_SWD_CLOCK_MAX && ch347.swclk_5mhz_supported) { + *speed_idx = 0; + } else { + // Don't allow too low clk speeds: packet processing is limited to ~8 msec + // or triggers host USB disconnect + *speed_idx = MIN(DIV_ROUND_UP(CH347_SWD_CLOCK_BASE, khz), + CH347_SWD_CLOCK_MAX_DIVISOR); + } + return ERROR_OK; + } + // when checking with speed index 9 we can see if the device supports STANDARD_PACK or LARGER_PACK mode int retval = ch347_init_pack_size(); if (retval != ERROR_OK) @@ -1809,27 +1866,6 @@ static const struct command_registration ch347_command_handlers[] = { COMMAND_REGISTRATION_DONE }; -/** - * @brief swd init function - * - * @return ERROR_OK on success - */ -static int ch347_swd_init_cmd(void) -{ - int retval = ch347_cmd_start_next(CH347_CMD_SWD_INIT); - if (retval != ERROR_OK) - return retval; - uint8_t cmd_data[] = {0x40, 0x42, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 }; - retval = ch347_scratchpad_add_bytes(cmd_data, ARRAY_SIZE(cmd_data)); - if (retval != ERROR_OK) - return retval; - /* TODO: CH347_CMD_SWD_INIT reads one data byte. - But how can we decide if SWD init was successfully executed? - Return an error code if init was failed */ - uint8_t unused; - return ch347_single_read_get_byte(0, &unused); -} - /** * @brief CH347 Initialization function * @@ -1862,7 +1898,8 @@ static int ch347_init(void) retval = ch347_init_pack_size(); if (retval != ERROR_OK) return retval; - retval = ch347_swd_init_cmd(); + + retval = ch347_swd_init_cmd(1); } return retval; }