272 lines
8.1 KiB
C
272 lines
8.1 KiB
C
/*
|
|
* This file is part of the Black Magic Debug project.
|
|
*
|
|
* Copyright (C) 2020 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de)
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/* Base on code from git://repo.or.cz/libjaylink.git
|
|
* and https://github.com/afaerber/jlink.git*/
|
|
|
|
#include "general.h"
|
|
#include "gdb_if.h"
|
|
#include "adiv5.h"
|
|
#include "exception.h"
|
|
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <ctype.h>
|
|
#include <sys/time.h>
|
|
|
|
#include "cl_utils.h"
|
|
#include "jlink.h"
|
|
|
|
#define USB_PID_SEGGER 0x1366
|
|
|
|
/* Only two devices PIDS tested so long */
|
|
#define USB_VID_SEGGER_0101 0x0101
|
|
#define USB_VID_SEGGER_0105 0x0105
|
|
#define USB_VID_SEGGER_1020 0x1020
|
|
|
|
static uint32_t emu_caps;
|
|
static uint32_t emu_speed_kHz;
|
|
static uint16_t emu_min_divisor;
|
|
static uint16_t emu_current_divisor;
|
|
|
|
static void jlink_print_caps(bmp_info_t *info)
|
|
{
|
|
uint8_t cmd[1] = {CMD_GET_CAPS};
|
|
uint8_t res[4];
|
|
send_recv(info->usb_link, cmd, 1, res, sizeof(res));
|
|
emu_caps = res[0] | (res[1] << 8) | (res[2] << 16) | (res[3] << 24);
|
|
DEBUG_INFO("Caps %" PRIx32 "\n", emu_caps);
|
|
if (emu_caps & JLINK_CAP_GET_HW_VERSION) {
|
|
uint8_t cmd[1] = {CMD_GET_HW_VERSION};
|
|
send_recv(info->usb_link, cmd, 1, NULL, 0);
|
|
send_recv(info->usb_link, NULL, 0, res, sizeof(res));
|
|
DEBUG_INFO("HW: Type %d, Major %d, Minor %d, Rev %d\n",
|
|
res[3], res[2], res[1], res[0]);
|
|
}
|
|
}
|
|
static void jlink_print_speed(bmp_info_t *info)
|
|
{
|
|
uint8_t cmd[1] = {CMD_GET_SPEEDS};
|
|
uint8_t res[6];
|
|
send_recv(info->usb_link, cmd, 1, res, sizeof(res));
|
|
emu_speed_kHz = (res[0] | (res[1] << 8) | (res[2] << 16) | (res[3] << 24)) /
|
|
1000;
|
|
emu_min_divisor = res[4] | (res[5] << 8);
|
|
DEBUG_INFO("Emulator speed %d kHz, Mindiv %d%s\n", emu_speed_kHz,
|
|
emu_min_divisor,
|
|
(emu_caps & JLINK_CAP_GET_SPEEDS) ? "" : ", fixed");
|
|
}
|
|
|
|
static void jlink_print_version(bmp_info_t *info)
|
|
{
|
|
uint8_t cmd[1] = {CMD_GET_VERSION};
|
|
uint8_t len_str[2];
|
|
send_recv(info->usb_link, cmd, 1, len_str, sizeof(len_str));
|
|
uint8_t version[0x70];
|
|
send_recv(info->usb_link, NULL, 0, version, sizeof(version));
|
|
DEBUG_INFO("%s\n", version );
|
|
}
|
|
|
|
static void jlink_print_interfaces(bmp_info_t *info)
|
|
{
|
|
uint8_t cmd[2] = {CMD_GET_SELECT_IF, JLINK_IF_GET_ACTIVE};
|
|
uint8_t res[4];
|
|
send_recv(info->usb_link, cmd, 2, res, sizeof(res));
|
|
cmd[1] = JLINK_IF_GET_AVAILABLE;
|
|
uint8_t res1[4];
|
|
send_recv(info->usb_link, cmd, 2, res1, sizeof(res1));
|
|
DEBUG_INFO("%s active", (res[0] == SELECT_IF_SWD) ? "SWD":
|
|
(res[0] == SELECT_IF_JTAG) ? "JTAG" : "NONE");
|
|
uint8_t other_interface = res1[0] - (res[0] + 1);
|
|
if (other_interface)
|
|
DEBUG_INFO(", %s available\n",
|
|
(other_interface == JLINK_IF_SWD) ? "SWD": "JTAG");
|
|
else
|
|
DEBUG_INFO(", %s not available\n",
|
|
((res[0] + 1) == JLINK_IF_SWD) ? "JTAG": "SWD");
|
|
}
|
|
|
|
static void jlink_info(bmp_info_t *info)
|
|
{
|
|
jlink_print_version(info);
|
|
jlink_print_caps(info);
|
|
jlink_print_speed(info);
|
|
jlink_print_interfaces(info);
|
|
}
|
|
|
|
/* On success endpoints are set and return 0, !0 else */
|
|
static int initialize_handle(bmp_info_t *info, libusb_device *dev)
|
|
{
|
|
struct libusb_config_descriptor *config;
|
|
int ret = libusb_get_active_config_descriptor(dev, &config);
|
|
if (ret != LIBUSB_SUCCESS) {
|
|
DEBUG_WARN( "Failed to get configuration descriptor: %s.",
|
|
libusb_error_name(ret));
|
|
return -1;
|
|
}
|
|
const struct libusb_interface *interface;
|
|
bool found_interface = false;
|
|
const struct libusb_interface_descriptor *desc;
|
|
for (int i = 0; i < config->bNumInterfaces; i++) {
|
|
interface = &config->interface[i];
|
|
desc = &interface->altsetting[0];
|
|
if (desc->bInterfaceClass != LIBUSB_CLASS_VENDOR_SPEC)
|
|
continue;
|
|
if (desc->bInterfaceSubClass != LIBUSB_CLASS_VENDOR_SPEC)
|
|
continue;
|
|
if (desc->bNumEndpoints < 2)
|
|
continue;
|
|
found_interface = true;
|
|
if (libusb_claim_interface (
|
|
info->usb_link->ul_libusb_device_handle, i)) {
|
|
DEBUG_WARN( " Can not claim handle\n");
|
|
found_interface = false;
|
|
}
|
|
break;
|
|
}
|
|
if (!found_interface) {
|
|
DEBUG_WARN( "No suitable interface found.");
|
|
libusb_free_config_descriptor(config);
|
|
return -1;
|
|
}
|
|
for (int i = 0; i < desc->bNumEndpoints; i++) {
|
|
const struct libusb_endpoint_descriptor *epdesc = &desc->endpoint[i];
|
|
if (epdesc->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
|
|
info->usb_link->ep_rx = epdesc->bEndpointAddress;
|
|
} else {
|
|
info->usb_link->ep_tx = epdesc->bEndpointAddress;
|
|
}
|
|
}
|
|
libusb_free_config_descriptor(config);
|
|
return 0;
|
|
}
|
|
/* Return 0 if single J-Link device connected or
|
|
* serial given matches one of several J-Link devices.
|
|
*/
|
|
int jlink_init(bmp_info_t *info)
|
|
{
|
|
usb_link_t *jl = calloc(1, sizeof(usb_link_t));
|
|
if (!jl)
|
|
return -1;
|
|
info->usb_link = jl;
|
|
jl->ul_libusb_ctx = info->libusb_ctx;
|
|
int ret = -1;
|
|
libusb_device **devs;
|
|
if (libusb_get_device_list(info->libusb_ctx, &devs) < 0) {
|
|
DEBUG_WARN( "libusb_get_device_list() failed");
|
|
return ret;
|
|
}
|
|
int i = 0;
|
|
for (; devs[i]; i++) {
|
|
libusb_device *dev = devs[i];
|
|
struct libusb_device_descriptor desc;
|
|
if (libusb_get_device_descriptor(dev, &desc) < 0) {
|
|
DEBUG_WARN( "libusb_get_device_descriptor() failed");
|
|
goto error;;
|
|
}
|
|
if (desc.idVendor != USB_PID_SEGGER)
|
|
continue;
|
|
if ((desc.idProduct != USB_VID_SEGGER_0101) &&
|
|
(desc.idProduct != USB_VID_SEGGER_0105) &&
|
|
(desc.idProduct != USB_VID_SEGGER_1020))
|
|
continue;
|
|
int res = libusb_open(dev, &jl->ul_libusb_device_handle);
|
|
if (res != LIBUSB_SUCCESS)
|
|
continue;
|
|
char buf[32];
|
|
res = libusb_get_string_descriptor_ascii(jl->ul_libusb_device_handle,
|
|
desc.iSerialNumber, (uint8_t*) buf, sizeof(buf));
|
|
if ((res <= 0) || (!strstr(buf, info->serial))) {
|
|
libusb_close(jl->ul_libusb_device_handle);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (!devs[i])
|
|
goto error;
|
|
if (initialize_handle(info, devs[i]))
|
|
goto error;
|
|
jl->req_trans = libusb_alloc_transfer(0);
|
|
jl->rep_trans = libusb_alloc_transfer(0);
|
|
if (!jl->req_trans || !jl->rep_trans ||
|
|
!jl->ep_tx || !jl->ep_rx) {
|
|
DEBUG_WARN("Device setup failed\n");
|
|
goto error;
|
|
}
|
|
libusb_free_device_list(devs, 1);
|
|
jlink_info(info);
|
|
return 0;
|
|
error:
|
|
libusb_free_device_list(devs, 1);
|
|
return -1;
|
|
|
|
}
|
|
|
|
const char *jlink_target_voltage(bmp_info_t *info)
|
|
{
|
|
uint8_t cmd[1] = {CMD_GET_HW_STATUS};
|
|
uint8_t res[8];
|
|
send_recv(info->usb_link, cmd, 1, res, sizeof(res));
|
|
uint16_t mVolt = res[0] | (res[1] << 8);
|
|
static char ret[7];
|
|
sprintf(ret, "%2d.%03d", mVolt / 1000, mVolt % 1000);
|
|
return ret;
|
|
}
|
|
|
|
static bool srst_status = false;
|
|
void jlink_srst_set_val(bmp_info_t *info, bool assert)
|
|
{
|
|
uint8_t cmd[1];
|
|
cmd[0]= (assert)? CMD_HW_RESET0: CMD_HW_RESET1;
|
|
send_recv(info->usb_link, cmd, 1, NULL, 0);
|
|
platform_delay(2);
|
|
srst_status = assert;
|
|
}
|
|
|
|
bool jlink_srst_get_val(bmp_info_t *info) {
|
|
uint8_t cmd[1] = {CMD_GET_HW_STATUS};
|
|
uint8_t res[8];
|
|
send_recv(info->usb_link, cmd, 1, res, sizeof(res));
|
|
return !(res[6]);
|
|
}
|
|
|
|
void jlink_max_frequency_set(bmp_info_t *info, uint32_t freq)
|
|
{
|
|
if (!(emu_caps & JLINK_CAP_GET_SPEEDS))
|
|
return;
|
|
if (!info->is_jtag)
|
|
return;
|
|
uint16_t freq_kHz = freq /1000;
|
|
uint16_t divisor = (emu_speed_kHz + freq_kHz - 1) / freq_kHz;
|
|
if (divisor < emu_min_divisor)
|
|
divisor = emu_min_divisor;
|
|
emu_current_divisor = divisor;
|
|
uint16_t speed_kHz = emu_speed_kHz / divisor;
|
|
uint8_t cmd[3] = {CMD_SET_SPEED, speed_kHz & 0xff, speed_kHz >> 8};
|
|
DEBUG_WARN("Set Speed %d\n", speed_kHz);
|
|
send_recv(info->usb_link, cmd, 3, NULL, 0);
|
|
}
|
|
|
|
uint32_t jlink_max_frequency_get(bmp_info_t *info)
|
|
{
|
|
if ((emu_caps & JLINK_CAP_GET_SPEEDS) && (info->is_jtag))
|
|
return (emu_speed_kHz * 1000L)/ emu_current_divisor;
|
|
return FREQ_FIXED;
|
|
}
|