/* MSPDebug - debugging tool for MSP430 MCUs * Copyright (C) 2021 sys64738@disroot.org * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "util.h" #include "output.h" #include "ctrlc.h" #include "jtaglib.h" #include "mehfet_proto.h" #include "mehfet_xport.h" #include "mehfet.h" struct mehfet_device { struct jtdev jtag; transport_t trans; enum mehfet_conn connstat; }; /*extern void __builtin_trap(void);*/ #define NO_LL_JTAG_BP 0 #define no_ll_jtag() \ do { \ printc_err("mehfet: %s: low-lewel JTAG function not implemented!\n", __func__); \ p->failed = 1; \ if (NO_LL_JTAG_BP) /*__builtin_trap()*/; \ } while (0) \ #define JTMF_GET_DEV(p) ((struct mehfet_device*)((size_t)(p) - \ offsetof(struct mehfet_device, jtag))) \ static int jtmf_open(struct jtdev* p, const char* device) { printc_err("mehfet: %s should not get called.\n", __func__); p->failed = 1; return -1; } static void jtmf_close(struct jtdev* p) { printc_err("mehfet: %s should not get called.\n", __func__); p->failed = 1; } static void jtmf_power_on(struct jtdev* p) { printc_err("mehfet: %s should not get called.\n", __func__); p->failed = 1; } static void jtmf_power_off(struct jtdev* p) { printc_err("mehfet: %s should not get called.\n", __func__); p->failed = 1; } static void jtmf_connect(struct jtdev* p) { printc_err("mehfet: %s should not get called.\n", __func__); p->failed = 1; } static void jtmf_release(struct jtdev* p) { printc_err("mehfet: %s should not get called.\n", __func__); p->failed = 1; } static void jtmf_tck(struct jtdev* p, int _) { (void)_; no_ll_jtag(); } static void jtmf_tms(struct jtdev* p, int _) { (void)_; no_ll_jtag(); } static void jtmf_tdi(struct jtdev* p, int _) { (void)_; no_ll_jtag(); } static void jtmf_rst(struct jtdev* p, int _) { (void)_; no_ll_jtag(); } static void jtmf_tst(struct jtdev* p, int _) { (void)_; no_ll_jtag(); } static int jtmf_tdo_get(struct jtdev* p) { no_ll_jtag(); return 0; } static void jtmf_tclk(struct jtdev* p, int out) { struct mehfet_device* d = JTMF_GET_DEV(p); int r = mehfet_cmd_tclk_edge(d->trans, out); if (r < 0) p->failed = 1; } static int jtmf_tclk_get(struct jtdev* p) { struct mehfet_device* d = JTMF_GET_DEV(p); int ret = 1; enum mehfet_lines lines = 0; int r = mehfet_cmd_get_old_lines(d->trans, &lines); if (r < 0) { p->failed = 1; } else { ret = (lines & mehfet_line_tclk) ? 1 : 0; } return ret; } static void jtmf_tclk_strobe(struct jtdev* p, unsigned int count) { struct mehfet_device* d = JTMF_GET_DEV(p); int r = mehfet_cmd_tclk_burst(d->trans, count); if (r < 0) p->failed = 1; } /* MehFET has no leds */ static void jtmf_led_green(struct jtdev* p, int out) { (void)p; (void)out; } static void jtmf_led_red (struct jtdev* p, int out) { (void)p; (void)out; } static uint8_t jtmf_ir_shift(struct jtdev *p, uint8_t ir) { struct mehfet_device* d = JTMF_GET_DEV(p); uint8_t ret = 0; int r = mehfet_cmd_irshift(d->trans, ir, &ret); if (r < 0) p->failed = 1; return ret; } static uint8_t jtmf_dr_shift_8(struct jtdev *p, uint8_t dr) { struct mehfet_device* d = JTMF_GET_DEV(p); uint8_t ret = 0; int r = mehfet_cmd_drshift(d->trans, 8, &dr, &ret); if (r < 0) p->failed = 1; return ret; } static uint16_t jtmf_dr_shift_16(struct jtdev *p, uint16_t dr) { struct mehfet_device* d = JTMF_GET_DEV(p); uint8_t inbuf[2] = { dr & 0xff, (dr >> 8) & 0xff }; uint8_t outbuf[2]; int r = mehfet_cmd_drshift(d->trans, 16, inbuf, outbuf); if (r < 0) p->failed = 1; return outbuf[0] | ((uint16_t)outbuf[1] << 8); } static uint32_t jtmf_dr_shift_20(struct jtdev *p, uint32_t dr) { struct mehfet_device* d = JTMF_GET_DEV(p); uint8_t inbuf[3] = { dr & 0xff, (dr >> 8) & 0xff, (dr >> 16) & 0x0f }; uint8_t outbuf[3]; int r = mehfet_cmd_drshift(d->trans, 20, inbuf, outbuf); if (r < 0) p->failed = 1; return outbuf[0] | ((uint32_t)outbuf[1] << 8) | ((uint32_t)outbuf[2] << 16); } static void jtmf_tms_sequence(struct jtdev *p, int bits, unsigned int value) { struct mehfet_device* d = JTMF_GET_DEV(p); enum mehfet_lines lines = 0; int r = mehfet_cmd_get_old_lines(d->trans, &lines); if (r < 0) { p->failed = 1; return; } uint8_t dbuf[4] = { value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, (value >> 24) & 0xff }; r = mehfet_cmd_tms_seq(d->trans, lines & mehfet_line_tdi, bits, dbuf); if (r < 0) p->failed = 1; } static void jtmf_init_dap(struct jtdev *p) { struct mehfet_device* dev = JTMF_GET_DEV(p); enum mehfet_resettap_status stat = 0; int r = mehfet_cmd_reset_tap(dev->trans, mehfet_rsttap_do_reset | mehfet_rsttap_fuse_do, &stat); if (r < 0) p->failed = 1; } static const struct jtdev_func jtdev_func_mehfet = { .jtdev_open = jtmf_open, .jtdev_close = jtmf_close, .jtdev_power_on = jtmf_power_on, .jtdev_power_off = jtmf_power_off, .jtdev_connect = jtmf_connect, .jtdev_release = jtmf_release, .jtdev_tck = jtmf_tck, .jtdev_tms = jtmf_tms, .jtdev_tdi = jtmf_tdi, .jtdev_rst = jtmf_rst, .jtdev_tst = jtmf_tst, .jtdev_tdo_get = jtmf_tdo_get, .jtdev_tclk = jtmf_tclk, .jtdev_tclk_get = jtmf_tclk_get, .jtdev_tclk_strobe = jtmf_tclk_strobe, .jtdev_led_green = jtmf_led_green, .jtdev_led_red = jtmf_led_red, .jtdev_ir_shift = jtmf_ir_shift, .jtdev_dr_shift_8 = jtmf_dr_shift_8, .jtdev_dr_shift_16 = jtmf_dr_shift_16, .jtdev_dr_shift_20 = jtmf_dr_shift_20, .jtdev_tms_sequence= jtmf_tms_sequence, .jtdev_init_dap = jtmf_init_dap }; /*---------------------------------------------------------------------------*/ static int check_dev_ok(struct mehfet_device* dev, const struct device_args* args, enum mehfet_conn* useconn) { transport_t t = dev->trans; struct mehfet_info info; int r; r = mehfet_cmd_info(t, &info); if (r < 0) return r; printc_dbg("mehfet: MehFET %s\n", info.devicename); free(info.devicename); if (info.proto_version < MEHFET_PROTO_VER_MIN_SUPPORTED) { printc_err("mehfet: device has protocol version %04x, " "need at least %04x\n", info.proto_version, MEHFET_PROTO_VER_MIN_SUPPORTED); return -1; } if (info.proto_version > MEHFET_PROTO_VER) { printc_err("mehfet: device has newer protocol version %04x " "supporting at most %04x\n", info.proto_version, MEHFET_PROTO_VER); return -1; } if (args->flags & DEVICE_FLAG_JTAG) { if (!(info.caps & (mehfet_cap_jtag_noentry | mehfet_cap_jtag_entryseq))) { printc_err("mehfet: Cannot do JTAG, device doesn't have the capability\n"); return -1; } // devices that don't need one will probably work when given an entry // sequence. no good way of doing a proper autodetect sadly if (info.caps & mehfet_cap_jtag_entryseq) *useconn = mehfet_conn_jtag_entryseq; else *useconn = mehfet_conn_jtag_noentry; } else { if (!(info.caps & mehfet_cap_sbw_entryseq)) { printc_err("mehfet: Cannot do Spy-Bi-Wire, device doesn't have the capability\n"); return -1; } *useconn = mehfet_conn_sbw_entryseq; } if (args->flags & DEVICE_FLAG_FORCE_RESET) *useconn |= mehfet_conn_nrstmask; mehfet_transport_set_buf_size(t, (int)info.packet_buf_size); return 0; } static void mehfet_destroy(device_t dev_base) { struct mehfet_device* dev = (struct mehfet_device*)dev_base; if (!dev) return; #ifdef DEBUG_MEHFET_DRIVER printc_dbg("mehfet: releasing device & disconnecting\n"); #endif if (dev->trans) { jtag_release_device(&dev->jtag, 0xfffe); // 0xfffe=reset address : POR mehfet_cmd_disconnect(dev->trans); dev->trans->ops->destroy(dev->trans); dev->trans = NULL; } free(dev); } static device_t mehfet_open(const struct device_args* args) { struct mehfet_device* dev; int r; if ((args->flags & DEVICE_FLAG_TTY)) { printc_err("mehfet: this driver does not support TTY access\n"); return NULL; } dev = calloc(1, sizeof(*dev)); if (!dev) { printc_err("mehfet: malloc: %s\n", last_error()); return NULL; } dev->jtag.base.type = &device_mehfet; dev->jtag.base.max_breakpoints = 2; // TODO dev->jtag.base.need_probe = 1; // TODO dev->jtag.f = &jtdev_func_mehfet; // the MehFET currently doesn't have a designated PID, so we can't really // default to one. dev->trans = mehfet_transport_open(args->path, ((args->flags & DEVICE_FLAG_HAS_VID_PID) ? &args->vid : NULL), ((args->flags & DEVICE_FLAG_HAS_VID_PID) ? &args->pid : NULL), args->requested_serial); if (!dev->trans) goto FAIL; enum mehfet_conn useconn; r = check_dev_ok(dev, args, &useconn); if (r < 0) goto FAIL; r = mehfet_cmd_connect(dev->trans, useconn); if (r < 0) goto FAIL; r = mehfet_cmd_status(dev->trans, &dev->connstat); if (r < 0) goto FAIL; if (dev->connstat != (useconn & mehfet_conn_typemask)) { printc_err("mehfet: could not create connection to device\n"); goto FAIL; } r = jtag_dev_init(&dev->jtag); if (r < 0) goto FAIL; // JTAG fuse check has been performed, so we can now switch to a // higher-speed physical transport suitable for ~350 kHz TCLK strobes used // in (and required for) flash programming r = mehfet_cmd_set_clkspeed(dev->trans, true); if (r < 0) goto FAIL; #ifdef DEBUG_MEHFET_DRIVER printc_dbg("mehfet: device opened\n"); #endif return (device_t)dev; FAIL: mehfet_destroy((device_t)dev); return NULL; } const struct device_class device_mehfet = { .name = "mehfet", .help = "MehFET USB JTAG/SBW device", .open = mehfet_open, .destroy = mehfet_destroy, .readmem = jtag_dev_readmem, .writemem = jtag_dev_writemem, .getregs = jtag_dev_getregs, .setregs = jtag_dev_setregs, .ctl = jtag_dev_ctl, .poll = jtag_dev_poll, .erase = jtag_dev_erase, .getconfigfuses = jtag_dev_getconfigfuses };