openocd: fix SPDX tag format for files .c
[fw/openocd] / src / jtag / drivers / sysfsgpio.c
index 77b727c2708a1090dcf63d93c2b709e7e8de20d3..ee254d66a9854877574f318e03fe056a77494979 100644 (file)
@@ -1,18 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   Copyright (C) 2012 by Creative Product Design, marc @ cpdesign.com.au *
- *                                                                         *
- *   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, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 /* 2014-12: Addition of the SWD protocol support is based on the initial work
@@ -29,7 +18,7 @@
  *
  * 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
- * sysfsgpio_jtag_nums command or the relevant sysfsgpio_XXX_num commang.
+ * sysfsgpio_jtag_nums command or the relevant sysfsgpio_XXX_num commands.
  * The srst and trst gpios are set via the sysfsgpio_srst_num and
  * sysfsgpio_trst_num respectively. GPIO numbering follows the kernel
  * convention of starting from 0.
 #include "config.h"
 #endif
 
+#include <helper/time_support.h>
 #include <jtag/interface.h>
+#include <transport/transport.h>
 #include "bitbang.h"
 
 /*
  * Helper func to determine if gpio number valid
  *
- * Assume here that there will be less than 1000 gpios on a system
+ * Assume here that there will be less than 10000 gpios on a system
  */
-static int is_gpio_valid(int gpio)
+static bool is_gpio_valid(int gpio)
 {
-       return gpio >= 0 && gpio < 1000;
+       return gpio >= 0 && gpio < 10000;
 }
 
 /*
@@ -89,7 +80,7 @@ static int open_write_close(const char *name, const char *valstr)
  */
 static void unexport_sysfs_gpio(int gpio)
 {
-       char gpiostr[4];
+       char gpiostr[5];
 
        if (!is_gpio_valid(gpio))
                return;
@@ -97,8 +88,6 @@ static void unexport_sysfs_gpio(int gpio)
        snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
        if (open_write_close("/sys/class/gpio/unexport", gpiostr) < 0)
                LOG_ERROR("Couldn't unexport gpio %d", gpio);
-
-       return;
 }
 
 /*
@@ -112,8 +101,9 @@ static void unexport_sysfs_gpio(int gpio)
  */
 static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
 {
+       struct timeval timeout, now;
        char buf[40];
-       char gpiostr[4];
+       char gpiostr[5];
        int ret;
 
        if (!is_gpio_valid(gpio))
@@ -126,25 +116,44 @@ static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
                        LOG_WARNING("gpio %d is already exported", gpio);
                } else {
                        LOG_ERROR("Couldn't export gpio %d", gpio);
-                       perror("sysfsgpio: ");
+                       LOG_ERROR("sysfsgpio: %s", strerror(errno));
                        return ERROR_FAIL;
                }
        }
 
+       gettimeofday(&timeout, NULL);
+       timeval_add_time(&timeout, 0, 500000);
+
        snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
-       ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
+       for (;;) {
+               ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
+               if (ret >= 0 || errno != EACCES)
+                       break;
+               gettimeofday(&now, NULL);
+               if (timeval_compare(&now, &timeout) >= 0)
+                       break;
+               jtag_sleep(10000);
+       }
        if (ret < 0) {
                LOG_ERROR("Couldn't set direction for gpio %d", gpio);
-               perror("sysfsgpio: ");
+               LOG_ERROR("sysfsgpio: %s", strerror(errno));
                unexport_sysfs_gpio(gpio);
                return ERROR_FAIL;
        }
 
        snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
-       ret = open(buf, O_RDWR | O_NONBLOCK | O_SYNC);
+       for (;;) {
+               ret = open(buf, O_RDWR | O_NONBLOCK | O_SYNC);
+               if (ret >= 0 || errno != EACCES)
+                       break;
+               gettimeofday(&now, NULL);
+               if (timeval_compare(&now, &timeout) >= 0)
+                       break;
+               jtag_sleep(10000);
+       }
        if (ret < 0) {
                LOG_ERROR("Couldn't open value for gpio %d", gpio);
-               perror("sysfsgpio: ");
+               LOG_ERROR("sysfsgpio: %s", strerror(errno));
                unexport_sysfs_gpio(gpio);
        }
 
@@ -188,7 +197,7 @@ static void sysfsgpio_swdio_drive(bool is_output)
        ret = open_write_close(buf, is_output ? "high" : "in");
        if (ret < 0) {
                LOG_ERROR("Couldn't set direction for gpio %d", swdio_gpio);
-               perror("sysfsgpio: ");
+               LOG_ERROR("sysfsgpio: %s", strerror(errno));
        }
 
        last_stored = false;
@@ -211,7 +220,7 @@ static int sysfsgpio_swdio_read(void)
        return buf[0] != '0';
 }
 
-static void sysfsgpio_swdio_write(int swclk, int swdio)
+static int sysfsgpio_swd_write(int swclk, int swdio)
 {
        const char one[] = "1";
        const char zero[] = "0";
@@ -236,6 +245,8 @@ static void sysfsgpio_swdio_write(int swclk, int swdio)
        last_swdio = swdio;
        last_swclk = swclk;
        last_stored = true;
+
+       return ERROR_OK;
 }
 
 /*
@@ -244,7 +255,7 @@ static void sysfsgpio_swdio_write(int swclk, int swdio)
  * The sysfs value will read back either '0' or '1'. The trick here is to call
  * lseek to bypass buffering in the sysfs kernel driver.
  */
-static int sysfsgpio_read(void)
+static bb_value_t sysfsgpio_read(void)
 {
        char buf[1];
 
@@ -257,7 +268,7 @@ static int sysfsgpio_read(void)
                return 0;
        }
 
-       return buf[0] != '0';
+       return buf[0] == '0' ? BB_LOW : BB_HIGH;
 }
 
 /*
@@ -266,13 +277,8 @@ static int sysfsgpio_read(void)
  * 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 sysfsgpio_write(int tck, int tms, int tdi)
+static int sysfsgpio_write(int tck, int tms, int tdi)
 {
-       if (swd_mode) {
-               sysfsgpio_swdio_write(tck, tdi);
-               return;
-       }
-
        const char one[] = "1";
        const char zero[] = "0";
 
@@ -312,6 +318,8 @@ static void sysfsgpio_write(int tck, int tms, int tdi)
        last_tdi = tdi;
        last_tms = tms;
        last_tck = tck;
+
+       return ERROR_OK;
 }
 
 /*
@@ -319,7 +327,7 @@ static void sysfsgpio_write(int tck, int tms, int tdi)
  *
  * (1) assert or (0) deassert reset lines
  */
-static void sysfsgpio_reset(int trst, int srst)
+static int sysfsgpio_reset(int trst, int srst)
 {
        LOG_DEBUG("sysfsgpio_reset");
        const char one[] = "1";
@@ -339,6 +347,8 @@ static void sysfsgpio_reset(int trst, int srst)
                if (bytes_written != 1)
                        LOG_WARNING("writing trst failed");
        }
+
+       return ERROR_OK;
 }
 
 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionums)
@@ -352,7 +362,7 @@ COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionums)
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
-       command_print(CMD_CTX,
+       command_print(CMD,
                        "SysfsGPIO nums: tck = %d, tms = %d, tdi = %d, tdo = %d",
                        tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
 
@@ -364,7 +374,7 @@ COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tck)
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
 
-       command_print(CMD_CTX, "SysfsGPIO num: tck = %d", tck_gpio);
+       command_print(CMD, "SysfsGPIO num: tck = %d", tck_gpio);
        return ERROR_OK;
 }
 
@@ -373,7 +383,7 @@ COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tms)
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);
 
-       command_print(CMD_CTX, "SysfsGPIO num: tms = %d", tms_gpio);
+       command_print(CMD, "SysfsGPIO num: tms = %d", tms_gpio);
        return ERROR_OK;
 }
 
@@ -382,7 +392,7 @@ COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tdo)
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);
 
-       command_print(CMD_CTX, "SysfsGPIO num: tdo = %d", tdo_gpio);
+       command_print(CMD, "SysfsGPIO num: tdo = %d", tdo_gpio);
        return ERROR_OK;
 }
 
@@ -391,7 +401,7 @@ COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tdi)
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);
 
-       command_print(CMD_CTX, "SysfsGPIO num: tdi = %d", tdi_gpio);
+       command_print(CMD, "SysfsGPIO num: tdi = %d", tdi_gpio);
        return ERROR_OK;
 }
 
@@ -400,7 +410,7 @@ COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_srst)
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);
 
-       command_print(CMD_CTX, "SysfsGPIO num: srst = %d", srst_gpio);
+       command_print(CMD, "SysfsGPIO num: srst = %d", srst_gpio);
        return ERROR_OK;
 }
 
@@ -409,7 +419,7 @@ COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_trst)
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);
 
-       command_print(CMD_CTX, "SysfsGPIO num: trst = %d", trst_gpio);
+       command_print(CMD, "SysfsGPIO num: trst = %d", trst_gpio);
        return ERROR_OK;
 }
 
@@ -422,7 +432,7 @@ COMMAND_HANDLER(sysfsgpio_handle_swd_gpionums)
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
-       command_print(CMD_CTX,
+       command_print(CMD,
                        "SysfsGPIO nums: swclk = %d, swdio = %d",
                        swclk_gpio, swdio_gpio);
 
@@ -434,7 +444,7 @@ COMMAND_HANDLER(sysfsgpio_handle_swd_gpionum_swclk)
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
 
-       command_print(CMD_CTX, "SysfsGPIO num: swclk = %d", swclk_gpio);
+       command_print(CMD, "SysfsGPIO num: swclk = %d", swclk_gpio);
        return ERROR_OK;
 }
 
@@ -443,72 +453,91 @@ COMMAND_HANDLER(sysfsgpio_handle_swd_gpionum_swdio)
        if (CMD_ARGC == 1)
                COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_gpio);
 
-       command_print(CMD_CTX, "SysfsGPIO num: swdio = %d", swdio_gpio);
+       command_print(CMD, "SysfsGPIO num: swdio = %d", swdio_gpio);
        return ERROR_OK;
 }
 
-static const struct command_registration sysfsgpio_command_handlers[] = {
+static const struct command_registration sysfsgpio_subcommand_handlers[] = {
        {
-               .name = "sysfsgpio_jtag_nums",
+               .name = "jtag_nums",
                .handler = &sysfsgpio_handle_jtag_gpionums,
                .mode = COMMAND_CONFIG,
                .help = "gpio numbers for tck, tms, tdi, tdo. (in that order)",
-               .usage = "(tck tms tdi tdo)* ",
+               .usage = "[tck tms tdi tdo]",
        },
        {
-               .name = "sysfsgpio_tck_num",
+               .name = "tck_num",
                .handler = &sysfsgpio_handle_jtag_gpionum_tck,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for tck.",
+               .usage = "[tck]",
        },
        {
-               .name = "sysfsgpio_tms_num",
+               .name = "tms_num",
                .handler = &sysfsgpio_handle_jtag_gpionum_tms,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for tms.",
+               .usage = "[tms]",
        },
        {
-               .name = "sysfsgpio_tdo_num",
+               .name = "tdo_num",
                .handler = &sysfsgpio_handle_jtag_gpionum_tdo,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for tdo.",
+               .usage = "[tdo]",
        },
        {
-               .name = "sysfsgpio_tdi_num",
+               .name = "tdi_num",
                .handler = &sysfsgpio_handle_jtag_gpionum_tdi,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for tdi.",
+               .usage = "[tdi]",
        },
        {
-               .name = "sysfsgpio_srst_num",
+               .name = "srst_num",
                .handler = &sysfsgpio_handle_jtag_gpionum_srst,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for srst.",
+               .usage = "[srst]",
        },
        {
-               .name = "sysfsgpio_trst_num",
+               .name = "trst_num",
                .handler = &sysfsgpio_handle_jtag_gpionum_trst,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for trst.",
+               .usage = "[trst]",
        },
        {
-               .name = "sysfsgpio_swd_nums",
+               .name = "swd_nums",
                .handler = &sysfsgpio_handle_swd_gpionums,
                .mode = COMMAND_CONFIG,
                .help = "gpio numbers for swclk, swdio. (in that order)",
-               .usage = "(swclk swdio)* ",
+               .usage = "[swclk swdio]",
        },
        {
-               .name = "sysfsgpio_swclk_num",
+               .name = "swclk_num",
                .handler = &sysfsgpio_handle_swd_gpionum_swclk,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for swclk.",
+               .usage = "[swclk]",
        },
        {
-               .name = "sysfsgpio_swdio_num",
+               .name = "swdio_num",
                .handler = &sysfsgpio_handle_swd_gpionum_swdio,
                .mode = COMMAND_CONFIG,
                .help = "gpio number for swdio.",
+               .usage = "[swdio]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration sysfsgpio_command_handlers[] = {
+       {
+               .name = "sysfsgpio",
+               .mode = COMMAND_ANY,
+               .help = "perform sysfsgpio management",
+               .chain = sysfsgpio_subcommand_handlers,
+               .usage = "",
        },
        COMMAND_REGISTRATION_DONE
 };
@@ -518,23 +547,30 @@ static int sysfsgpio_quit(void);
 
 static const char * const sysfsgpio_transports[] = { "jtag", "swd", NULL };
 
-struct jtag_interface sysfsgpio_interface = {
-       .name = "sysfsgpio",
+static struct jtag_interface sysfsgpio_interface = {
        .supported = DEBUG_CAP_TMS_SEQ,
        .execute_queue = bitbang_execute_queue,
+};
+
+struct adapter_driver sysfsgpio_adapter_driver = {
+       .name = "sysfsgpio",
        .transports = sysfsgpio_transports,
-       .swd = &bitbang_swd,
        .commands = sysfsgpio_command_handlers,
+
        .init = sysfsgpio_init,
        .quit = sysfsgpio_quit,
+       .reset = sysfsgpio_reset,
+
+       .jtag_ops = &sysfsgpio_interface,
+       .swd_ops = &bitbang_swd,
 };
 
 static struct bitbang_interface sysfsgpio_bitbang = {
        .read = sysfsgpio_read,
        .write = sysfsgpio_write,
-       .reset = sysfsgpio_reset,
        .swdio_read = sysfsgpio_swdio_read,
        .swdio_drive = sysfsgpio_swdio_drive,
+       .swd_write = sysfsgpio_swd_write,
        .blink = 0
 };
 
@@ -551,34 +587,40 @@ static void cleanup_fd(int fd, int gpio)
 
 static void cleanup_all_fds(void)
 {
-       cleanup_fd(tck_fd, tck_gpio);
-       cleanup_fd(tms_fd, tms_gpio);
-       cleanup_fd(tdi_fd, tdi_gpio);
-       cleanup_fd(tdo_fd, tdo_gpio);
-       cleanup_fd(trst_fd, trst_gpio);
+       if (transport_is_jtag()) {
+               cleanup_fd(tck_fd, tck_gpio);
+               cleanup_fd(tms_fd, tms_gpio);
+               cleanup_fd(tdi_fd, tdi_gpio);
+               cleanup_fd(tdo_fd, tdo_gpio);
+               cleanup_fd(trst_fd, trst_gpio);
+       }
+       if (transport_is_swd()) {
+               cleanup_fd(swclk_fd, swclk_gpio);
+               cleanup_fd(swdio_fd, swdio_gpio);
+       }
        cleanup_fd(srst_fd, srst_gpio);
 }
 
 static bool sysfsgpio_jtag_mode_possible(void)
 {
        if (!is_gpio_valid(tck_gpio))
-               return 0;
+               return false;
        if (!is_gpio_valid(tms_gpio))
-               return 0;
+               return false;
        if (!is_gpio_valid(tdi_gpio))
-               return 0;
+               return false;
        if (!is_gpio_valid(tdo_gpio))
-               return 0;
-       return 1;
+               return false;
+       return true;
 }
 
 static bool sysfsgpio_swd_mode_possible(void)
 {
        if (!is_gpio_valid(swclk_gpio))
-               return 0;
+               return false;
        if (!is_gpio_valid(swdio_gpio))
-               return 0;
-       return 1;
+               return false;
+       return true;
 }
 
 static int sysfsgpio_init(void)
@@ -587,83 +629,62 @@ static int sysfsgpio_init(void)
 
        LOG_INFO("SysfsGPIO JTAG/SWD bitbang driver");
 
-       if (sysfsgpio_jtag_mode_possible()) {
-               if (sysfsgpio_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 (sysfsgpio_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) {
+
+       if (transport_is_jtag()) {
+               if (!sysfsgpio_jtag_mode_possible()) {
+                       LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode");
+                       return ERROR_JTAG_INIT_FAILED;
+               }
+
                tck_fd = setup_sysfs_gpio(tck_gpio, 1, 0);
                if (tck_fd < 0)
                        goto out_error;
-       }
 
-       if (tms_gpio >= 0) {
                tms_fd = setup_sysfs_gpio(tms_gpio, 1, 1);
                if (tms_fd < 0)
                        goto out_error;
-       }
 
-       if (tdi_gpio >= 0) {
                tdi_fd = setup_sysfs_gpio(tdi_gpio, 1, 0);
                if (tdi_fd < 0)
                        goto out_error;
-       }
 
-       if (tdo_gpio >= 0) {
                tdo_fd = setup_sysfs_gpio(tdo_gpio, 0, 0);
                if (tdo_fd < 0)
                        goto out_error;
-       }
 
-       /* assume active low*/
-       if (trst_gpio >= 0) {
-               trst_fd = setup_sysfs_gpio(trst_gpio, 1, 1);
-               if (trst_fd < 0)
-                       goto out_error;
+               /* assume active low*/
+               if (trst_gpio >= 0) {
+                       trst_fd = setup_sysfs_gpio(trst_gpio, 1, 1);
+                       if (trst_fd < 0)
+                               goto out_error;
+               }
        }
 
-       /* assume active low*/
-       if (srst_gpio >= 0) {
-               srst_fd = setup_sysfs_gpio(srst_gpio, 1, 1);
-               if (srst_fd < 0)
-                       goto out_error;
-       }
+       if (transport_is_swd()) {
+               if (!sysfsgpio_swd_mode_possible()) {
+                       LOG_ERROR("Require swclk and swdio gpio for SWD mode");
+                       return ERROR_JTAG_INIT_FAILED;
+               }
 
-       if (swclk_gpio >= 0) {
                swclk_fd = setup_sysfs_gpio(swclk_gpio, 1, 0);
                if (swclk_fd < 0)
                        goto out_error;
-       }
 
-       if (swdio_gpio >= 0) {
                swdio_fd = setup_sysfs_gpio(swdio_gpio, 1, 0);
                if (swdio_fd < 0)
                        goto out_error;
        }
 
-       if (sysfsgpio_swd_mode_possible()) {
-               if (swd_mode)
-                       bitbang_swd_switch_seq(JTAG_TO_SWD);
-               else
-                       bitbang_swd_switch_seq(SWD_TO_JTAG);
+       /* assume active low*/
+       if (srst_gpio >= 0) {
+               srst_fd = setup_sysfs_gpio(srst_gpio, 1, 1);
+               if (srst_fd < 0)
+                       goto out_error;
        }
 
        return ERROR_OK;
@@ -678,4 +699,3 @@ static int sysfsgpio_quit(void)
        cleanup_all_fds();
        return ERROR_OK;
 }
-