[Из песочницы] Разгоняем JTAG роутер

246ed659739644049ad207725286591d.png
/**
 * @file
 * This driver implements a bitbang jtag interface using gpio lines via
 * router ob BCM63XX SoC.
 * The aim of this driver implementation is use system GPIOs but avoid the
 * need for a additional kernel driver.
 * (Note memory mapped IO is another option, however it doesn't mix well with
 * the kernel gpiolib driver - which makes sense I guess.)
 *
 * A gpio is required for tck, tms, tdi and tdo. One or both of srst and trst
 * must be also be specified. The required jtag gpios are specified via the
 * bcm63xx_gpio_jtag_nums command or the relevant bcm63xx_gpio_XXX_num commang.
 * The srst and trst gpios are set via the bcm63xx_gpio_srst_num and
 * bcm63xx_gpio_trst_num respectively. GPIO numbering follows the kernel
 * convention of starting from 0.
 *
 * The gpios should not be in use by another entity, and must not be requested
 * by a kernel driver without also being exported by it (otherwise they can't
 * be exported by bcm63xx_).
 *
 * The bcm63xx gpio interface can only manipulate one gpio at a time, so the
 * bitbang write handler remembers the last state for tck, tms, tdi to avoid
 * superfluous writes.
 * For speed the bcm63xx "value" entry is opened at init and held open.
 * This results in considerable gains over open-write-close (45s vs 900s)
 *
 * Further work could address:
 *  -srst and trst open drain/ push pull
 *  -configurable active high/low for srst & trst
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include 
#include "bitbang.h"

#include 

/*
 * Helper func to determine if gpio number valid
 *
 * Assume here that there will be less than 1000 gpios on a system
 */
static int is_gpio_valid(int gpio)
{
        return gpio >= 0 && gpio < 32;
}

off_t address_dir = NULL;
off_t address_val = NULL;

static int dev_mem_fd = -1;
static volatile uint32_t *pio_base = NULL;
static volatile uint32_t *pval_base = NULL;
static volatile uint32_t *pads_base = NULL;
static unsigned int jtag_delay = 0;


static void set_dir_gpio(const int gpio, const int direction)
{
        if(direction)
                *pio_base |= 1 << gpio;
        else
                *pio_base &= ~(1 << gpio);
}


static void set_value_gpio(const int gpio, const int value)
{
        if(value)
                *pval_base |= 1 << gpio;
        else
                *pval_base &= ~(1 << gpio);

        for (unsigned int i = 0; i < jtag_delay; i++)
                asm volatile ("");
}

static int read_gpio(const int gpio)
{
        uint32_t val = *pval_base & (1 << gpio);
        val = val ? 1 : 0;
        return val;
}

static int setup_bcm63xx_gpio(int gpio, int is_output, int init_high)
{
        char buf[40];
        char gpiostr[4];
        int ret;

        if (!is_gpio_valid(gpio))
                return ERROR_OK;

        if((address_dir == NULL) || (address_val == NULL)){
                perror("address of gpio register don't set");
                return ERROR_FAIL; 
        }


        if( dev_mem_fd < 0 )
        {

                dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
                if (dev_mem_fd < 0) {
                        perror("open");
                        return ERROR_FAIL;
                }

                const uint32_t mapped_size = getpagesize();

                const off_t target_mmap = address_dir & ~(off_t)(mapped_size - 1);

                pads_base = mmap(NULL, mapped_size, PROT_READ | PROT_WRITE,
                                        MAP_SHARED, dev_mem_fd, target_mmap);

                if (pads_base == MAP_FAILED) {
                        perror("mmap. Check correct register address.");
                        close(dev_mem_fd);
                        return ERROR_FAIL;
                }

                pio_base        = (char*)pads_base + (unsigned)(address_dir -  target_mmap);
                pval_base       = (char*)pads_base + (unsigned)(address_val - target_mmap);
        }

        set_dir_gpio(gpio, is_output);
        set_value_gpio(gpio, init_high);

        return 0;
}

/* gpio numbers for each gpio. Negative values are invalid */
static int tck_gpio = -1;
static int tms_gpio = -1;
static int tdi_gpio = -1;
static int tdo_gpio = -1;
static int trst_gpio = -1;
static int srst_gpio = -1;
static int swclk_gpio = -1;
static int swdio_gpio = -1;

/*
 * file descriptors for /sys/class/gpio/gpioXX/value
 * Set up during init.
 */
static int tck_fd = -1;
static int tms_fd = -1;
static int tdi_fd = -1;
static int tdo_fd = -1;
static int trst_fd = -1;
static int srst_fd = -1;
static int swclk_fd = -1;
static int swdio_fd = -1;

static int last_swclk;
static int last_swdio;
static bool last_stored;
static bool swdio_input;

static void bcm63xx_gpio_swdio_drive(bool is_output)
{
        set_dir_gpio(swdio_gpio, is_output ? 1 : 0);

        last_stored = false;
        swdio_input = !is_output;
}

static int bcm63xx_gpio_swdio_read(void)
{
        return read_gpio(swdio_gpio);
}

static void bcm63xx_gpio_swdio_write(int swclk, int swdio)
{

        const char one[] = "1";
        const char zero[] = "0";

        size_t bytes_written;

        if (!swdio_input) {
                if (!last_stored || (swdio != last_swdio)) {
                        set_value_gpio(swdio_gpio, swdio ? 1 : 0);
                }

        }

        /* write swclk last */
        if (!last_stored || (swclk != last_swclk)) {
                        set_value_gpio(swclk_gpio, swclk ? 1 : 0);
        }

        last_swdio = swdio;
        last_swclk = swclk;
        last_stored = true;
}

/*
 * Bitbang interface read of TDO
 *
 * The bcm63xx value will read back either '0' or '1'. The trick here is to call
 * lseek to bypass buffering in the bcm63xx kernel driver.
 */
static int bcm63xx_gpio_read(void)
{
        return read_gpio(tdo_gpio);
}

/*
 * Bitbang interface write of TCK, TMS, TDI
 *
 * Seeing as this is the only function where the outputs are changed,
 * we can cache the old value to avoid needlessly writing it.
 */
static void bcm63xx_gpio_write(int tck, int tms, int tdi)
{
        if (swd_mode) {
                bcm63xx_gpio_swdio_write(tck, tdi);
                return;
        }

        const char one[] = "1";
        const char zero[] = "0";

        static int last_tck;
        static int last_tms;
        static int last_tdi;

        static int first_time;
        size_t bytes_written;

        if (!first_time) {
                last_tck = !tck;
                last_tms = !tms;
                last_tdi = !tdi;
                first_time = 1;
        }

        if (tdi != last_tdi) {
                set_value_gpio(tdi_gpio,tdi);
        }

        if (tms != last_tms) {
                set_value_gpio(tms_gpio,tms);
        }

        /* write clk last */
        if (tck != last_tck) {
                set_value_gpio(tck_gpio,tck);

        }

        last_tdi = tdi;
        last_tms = tms;
        last_tck = tck;
}

/*
 * Bitbang interface to manipulate reset lines SRST and TRST
 *
 * (1) assert or (0) deassert reset lines
 */
static void bcm63xx_gpio_reset(int trst, int srst)
{
        LOG_DEBUG("bcm63xx_gpio_reset");
        const char one[] = "1";
        const char zero[] = "0";
        size_t bytes_written;

        /* assume active low */
        if (srst_fd >= 0) {
                set_value_gpio(srst_gpio,srst);
        }

        /* assume active low */
        if (trst_fd >= 0) {
                set_value_gpio(trst_gpio,trst);
        }
}

COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionums)
{
        if (CMD_ARGC == 4) {
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tms_gpio);
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], tdi_gpio);
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], tdo_gpio);
        } else if (CMD_ARGC != 0) {
                return ERROR_COMMAND_SYNTAX_ERROR;
        }

        command_print(CMD_CTX,
                        "bcm63xx_GPIO nums: tck = %d, tms = %d, tdi = %d, tdo = %d",
                        tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);

        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_tck)
{
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);

        command_print(CMD_CTX, "bcm63xx_GPIO num: tck = %d", tck_gpio);
        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_tms)
{
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);

        command_print(CMD_CTX, "bcm63xx_GPIO num: tms = %d", tms_gpio);
        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_tdo)
{
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);

        command_print(CMD_CTX, "bcm63xx_GPIO num: tdo = %d", tdo_gpio);
        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_tdi)
{
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);

        command_print(CMD_CTX, "bcm63xx_GPIO num: tdi = %d", tdi_gpio);
        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_srst)
{
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);

        command_print(CMD_CTX, "bcm63xx_GPIO num: srst = %d", srst_gpio);
        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_trst)
{
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);

        command_print(CMD_CTX, "bcm63xx_GPIO num: trst = %d", trst_gpio);
        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_handle_swd_gpionums)
{
        if (CMD_ARGC == 2) {
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], swdio_gpio);
        } else if (CMD_ARGC != 0) {
                return ERROR_COMMAND_SYNTAX_ERROR;
        }

        command_print(CMD_CTX,
                        "bcm63xx_GPIO nums: swclk = %d, swdio = %d",
                        swclk_gpio, swdio_gpio);

        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_handle_swd_gpionum_swclk)
{
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);

        command_print(CMD_CTX, "bcm63xx_GPIO num: swclk = %d", swclk_gpio);
        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_handle_swd_gpionum_swdio)
{
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_gpio);

        command_print(CMD_CTX, "bcm63xx_GPIO num: swdio = %d", swdio_gpio);
        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_jtag_delay)
{
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], jtag_delay);

        command_print(CMD_CTX, "bcm63xx_GPIO jtag_delay:= %d tics", jtag_delay);
        return ERROR_OK;
}

COMMAND_HANDLER(bcm63xx_gpio_adresses)
{
        if (CMD_ARGC == 2) {
                COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address_dir);
                COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], address_val);
        } else if (CMD_ARGC != 0) {
                return ERROR_COMMAND_SYNTAX_ERROR;
        }

        command_print(CMD_CTX,
                        "bcm63xx_GPIO address: direction = %x, value = %x",
                        address_dir, address_val);

        return ERROR_OK;
}


static const struct command_registration bcm63xx_gpio_command_handlers[] = {
        {
                .name = "bcm63xx_gpio_jtag_nums",
                .handler = &bcm63xx_gpio_handle_jtag_gpionums,
                .mode = COMMAND_CONFIG,
                .help = "gpio numbers for tck, tms, tdi, tdo. (in that order)",
                .usage = "(tck tms tdi tdo)* ",
        },
        {
                .name = "bcm63xx_gpio_tck_num",
                .handler = &bcm63xx_gpio_handle_jtag_gpionum_tck,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for tck.",
        },
        {
                .name = "bcm63xx_gpio_tms_num",
                .handler = &bcm63xx_gpio_handle_jtag_gpionum_tms,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for tms.",
        },
        {
                .name = "bcm63xx_gpio_tdo_num",
                .handler = &bcm63xx_gpio_handle_jtag_gpionum_tdo,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for tdo.",
        },
        {
                .name = "bcm63xx_gpio_tdi_num",
                .handler = &bcm63xx_gpio_handle_jtag_gpionum_tdi,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for tdi.",
        },
        {
                .name = "bcm63xx_gpio_srst_num",
                .handler = &bcm63xx_gpio_handle_jtag_gpionum_srst,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for srst.",
        },
        {
                .name = "bcm63xx_gpio_trst_num",
                .handler = &bcm63xx_gpio_handle_jtag_gpionum_trst,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for trst.",
        },
        {
                .name = "bcm63xx_gpio_swd_nums",
                .handler = &bcm63xx_gpio_handle_swd_gpionums,
                .mode = COMMAND_CONFIG,
                .help = "gpio numbers for swclk, swdio. (in that order)",
                .usage = "(swclk swdio)* ",
        },
        {
                .name = "bcm63xx_gpio_swclk_num",
                .handler = &bcm63xx_gpio_handle_swd_gpionum_swclk,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for swclk.",
        },
        {
                .name = "bcm63xx_gpio_swdio_num",
                .handler = &bcm63xx_gpio_handle_swd_gpionum_swdio,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for swdio.",
        },
        {
                .name = "bcm63xx_gpio_jtag_delay",
                .handler = &bcm63xx_gpio_jtag_delay,
                .mode = COMMAND_CONFIG,
                .help = "qty tics gpio delay.",
        },
        {
                .name = "bcm63xx_gpio_adresses",
                .handler = &bcm63xx_gpio_adresses,
                .mode = COMMAND_CONFIG,
                .help = "addresses for direction and value setup. (in that order)",
                .usage = "(address_dir address_val)* ",
        },
        COMMAND_REGISTRATION_DONE
};

static int bcm63xx_gpio_init(void);
static int bcm63xx_gpio_quit(void);

static const char * const bcm63xx_gpio_transports[] = { "jtag", "swd", NULL };

struct jtag_interface bcm63xxgpio_interface = {
        .name = "bcm63xxgpio",
        .supported = DEBUG_CAP_TMS_SEQ,
        .execute_queue = bitbang_execute_queue,
        .transports = bcm63xx_gpio_transports,
        .swd = &bitbang_swd,
        .commands = bcm63xx_gpio_command_handlers,
        .init = bcm63xx_gpio_init,
        .quit = bcm63xx_gpio_quit,
};

static struct bitbang_interface bcm63xx_gpio_bitbang = {
        .read = bcm63xx_gpio_read,
        .write = bcm63xx_gpio_write,
        .reset = bcm63xx_gpio_reset,
        .swdio_read = bcm63xx_gpio_swdio_read,
        .swdio_drive = bcm63xx_gpio_swdio_drive,
        .blink = 0
};

static void unusing_all_gpio(void)
{
        munmap(pads_base, sysconf(_SC_PAGE_SIZE));
        close(dev_mem_fd);
        LOG_INFO("unusing_all_gpio\n");
}

static bool bcm63xx_gpio_jtag_mode_possible(void)
{
        if (!is_gpio_valid(tck_gpio))
                return 0;
        if (!is_gpio_valid(tms_gpio))
                return 0;
        if (!is_gpio_valid(tdi_gpio))
                return 0;
        if (!is_gpio_valid(tdo_gpio))
                return 0;
        return 1;
}

static bool bcm63xx_gpio_swd_mode_possible(void)
{
        if (!is_gpio_valid(swclk_gpio))
                return 0;
        if (!is_gpio_valid(swdio_gpio))
                return 0;
        return 1;
}

static int bcm63xx_gpio_init(void)
{
        bitbang_interface = &bcm63xx_gpio_bitbang;

        LOG_INFO("bcm63xx_gpio JTAG/SWD bitbang driver");

        if (bcm63xx_gpio_jtag_mode_possible()) {
                if (bcm63xx_gpio_swd_mode_possible())
                        LOG_INFO("JTAG and SWD modes enabled");
                else
                        LOG_INFO("JTAG only mode enabled (specify swclk and swdio gpio to add SWD mode)");
                if (!is_gpio_valid(trst_gpio) && !is_gpio_valid(srst_gpio)) {
                        LOG_ERROR("Require at least one of trst or srst gpios to be specified");
                        return ERROR_JTAG_INIT_FAILED;
                }
        } else if (bcm63xx_gpio_swd_mode_possible()) {
                LOG_INFO("SWD only mode enabled (specify tck, tms, tdi and tdo gpios to add JTAG mode)");
        } else {
                LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode and/or swclk and swdio gpio for SWD mode");
                return ERROR_JTAG_INIT_FAILED;
        }


        /*
         * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
         * as outputs.  Drive TDI and TCK low, and TMS/TRST/SRST high.
         * For SWD, SWCLK and SWDIO are configures as output high.
         */
        if (tck_gpio >= 0) {
                tck_fd = setup_bcm63xx_gpio(tck_gpio, 1, 0);
                if (tck_fd < 0)
                        goto out_error;
        }

        if (tms_gpio >= 0) {
                tms_fd = setup_bcm63xx_gpio(tms_gpio, 1, 1);
                if (tms_fd < 0)
                        goto out_error;
        }

        if (tdi_gpio >= 0) {
                tdi_fd = setup_bcm63xx_gpio(tdi_gpio, 1, 0);
                if (tdi_fd < 0)
                        goto out_error;
        }

        if (tdo_gpio >= 0) {
                tdo_fd = setup_bcm63xx_gpio(tdo_gpio, 0, 0);
                if (tdo_fd < 0)
                        goto out_error;
        }

        /* assume active low*/
        if (trst_gpio >= 0) {
                trst_fd = setup_bcm63xx_gpio(trst_gpio, 1, 1);
                if (trst_fd < 0)
                        goto out_error;
        }

        /* assume active low*/
        if (srst_gpio >= 0) {
                srst_fd = setup_bcm63xx_gpio(srst_gpio, 1, 1);
                if (srst_fd < 0)
                        goto out_error;
        }

        if (swclk_gpio >= 0) {
                swclk_fd = setup_bcm63xx_gpio(swclk_gpio, 1, 0);
                if (swclk_fd < 0)
                        goto out_error;
        }

        if (swdio_gpio >= 0) {
                swdio_fd = setup_bcm63xx_gpio(swdio_gpio, 1, 0);
                if (swdio_fd < 0)
                        goto out_error;
        }

        if (bcm63xx_gpio_swd_mode_possible()) {
                if (swd_mode)
                        bitbang_swd_switch_seq(JTAG_TO_SWD);
                else
                        bitbang_swd_switch_seq(SWD_TO_JTAG);
        }

        return ERROR_OK;

out_error:
        unusing_all_gpio();
        return ERROR_JTAG_INIT_FAILED;
}

static int bcm63xx_gpio_quit(void)
{
        unusing_all_gpio();
        return ERROR_OK;
}


© Geektimes