From c53589d9c8df8993099b244a1dd8c0f32c7d3a83 Mon Sep 17 00:00:00 2001
From: Stefano Babic <sbabic@denx.de>
Date: Thu, 29 Apr 2010 09:19:35 +0200
Subject: [PATCH 1/2] Support updating FPGA on QONG module

The QONG module has a FPGA on board and makes available
a JTAG interface to program it with GPIO pins.
The patch adds the cable driver to support
it. Note that the pins for gpios must be already set
in the iomux controller.
	MX31_PIN_SFS6 (gpio 25) as TCK
	MX31_PIN_SCK6 (gpio 26) as TMS
	MX31_PIN_CAPTURE (gpio 7) as TDO
	MX31_PIN_COMPARE (gpio 8) as TDI

Signed-off-by: Stefano Babic <sbabic@denx.de>
---
 configure.ac              |    3 +-
 po/POTFILES.in            |    1 +
 src/tap/Makefile.am       |    5 +
 src/tap/cable.c           |    4 +
 src/tap/cable/qong_gpio.c |  402 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 414 insertions(+), 1 deletions(-)
 create mode 100644 src/tap/cable/qong_gpio.c

diff --git a/configure.ac b/configure.ac
index 8737097..2561aa3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -468,7 +468,7 @@ CHECK_DRIVER([$busdrivers], [enabled_bus_drivers], [zefant_xs3],    [ENABLE_BUS_
 
 # Enable cable drivers
 AC_DEFUN([DEF_ENABLE_CABLEDRIVERS], [\
-arcom byteblaster dlc5 ea253 ei012 ft2232 igloo jlink keithkoep lattice mpcbdm triton usbblaster wiggler xpc])
+arcom byteblaster dlc5 ea253 ei012 ft2232 igloo jlink keithkoep lattice mpcbdm qong_gpio triton usbblaster wiggler xpc])
 AC_DEFUN([DEF_DISABLE_CABLEDRIVERS], [ep9307 jim ts7800])
 AC_ARG_ENABLE(cable,
 [AS_HELP_STRING([--enable-cable], [Enable default set or specific cable drivers.])]
@@ -507,6 +507,7 @@ CHECK_DRIVER([$cabledrivers], [enabled_cable_drivers], [jlink],       [ENABLE_CA
 CHECK_DRIVER([$cabledrivers], [enabled_cable_drivers], [keithkoep],   [ENABLE_CABLE_KEITHKOEP])
 CHECK_DRIVER([$cabledrivers], [enabled_cable_drivers], [lattice],     [ENABLE_CABLE_LATTICE])
 CHECK_DRIVER([$cabledrivers], [enabled_cable_drivers], [mpcbdm],      [ENABLE_CABLE_MPCBDM])
+CHECK_DRIVER([$cabledrivers], [enabled_cable_drivers], [qong_gpio],   [ENABLE_CABLE_QONG])
 CHECK_DRIVER([$cabledrivers], [enabled_cable_drivers], [triton],      [ENABLE_CABLE_TRITON])
 CHECK_DRIVER([$cabledrivers], [enabled_cable_drivers], [usbblaster],  [ENABLE_CABLE_USBBLASTER])
 CHECK_DRIVER([$cabledrivers], [enabled_cable_drivers], [wiggler],     [ENABLE_CABLE_WIGGLER])
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 37a4453..53c74f4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -116,6 +116,7 @@ src/tap/cable/jlink.c
 src/tap/cable/keithkoep.c
 src/tap/cable/lattice.c
 src/tap/cable/mpcbdm.c
+src/tap/cable/qong_gpio.c
 src/tap/cable/triton.c
 src/tap/cable/ts7800.c
 src/tap/cable/usbblaster.c
diff --git a/src/tap/Makefile.am b/src/tap/Makefile.am
index e0defd5..976ab8a 100644
--- a/src/tap/Makefile.am
+++ b/src/tap/Makefile.am
@@ -85,6 +85,11 @@ libtap_a_SOURCES += \
 	cable/mpcbdm.c
 endif
 
+if ENABLE_CABLE_QONG
+libtap_a_SOURCES += \
+	cable/qong_gpio.c
+endif
+
 if ENABLE_CABLE_TRITON
 libtap_a_SOURCES += \
 	cable/triton.c
diff --git a/src/tap/cable.c b/src/tap/cable.c
index c8945fd..34a8251 100644
--- a/src/tap/cable.c
+++ b/src/tap/cable.c
@@ -59,6 +59,7 @@ extern cable_driver_t igloo_cable_driver;
 extern cable_driver_t keithkoep_cable_driver;
 extern cable_driver_t lattice_cable_driver;
 extern cable_driver_t mpcbdm_cable_driver;
+extern cable_driver_t qong_cable_driver;
 extern cable_driver_t triton_cable_driver;
 extern cable_driver_t jim_cable_driver;
 extern cable_driver_t wiggler_cable_driver;
@@ -116,6 +117,9 @@ cable_driver_t *cable_drivers[] = {
 #ifdef ENABLE_CABLE_MPCBDM
 	&mpcbdm_cable_driver,
 #endif
+#ifdef ENABLE_CABLE_QONG
+	&qong_cable_driver,
+#endif
 #ifdef ENABLE_CABLE_TRITON
 	&triton_cable_driver,
 #endif
diff --git a/src/tap/cable/qong_gpio.c b/src/tap/cable/qong_gpio.c
new file mode 100644
index 0000000..ace1497
--- /dev/null
+++ b/src/tap/cable/qong_gpio.c
@@ -0,0 +1,402 @@
+/*
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
+ * QONG GPIO JTAG Cable Driver
+ *
+ * Based on TS7800 GPIO JTAG Cable Driver
+ * Copyright (C) 2008 Catalin Ionescu
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#include "sysdep.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "cable.h"
+#include "chain.h"
+
+#include "generic.h"
+
+#include <cmd.h>
+
+
+#define GPIO_PATH		"/sys/class/gpio/"
+#define GPIO_EXPORT_PATH	"/sys/class/gpio/export"
+#define GPIO_UNEXPORT_PATH	"/sys/class/gpio/unexport"
+
+/* pin mapping on qong (referred as fpga site) */
+#define TDO			7
+#define TDI			8
+#if 0
+#define TCK			25
+#define TMS			26
+#else
+#define TCK			26
+#define TMS			25
+#endif
+
+enum {
+	QONG_TDI = 0,
+	QONG_TCK,
+	QONG_TMS,
+	QONG_TDO
+};
+
+static int gpios[] = {
+	[QONG_TDI] = TDI,
+	[QONG_TCK] = TCK,
+	[QONG_TMS] = TMS,
+	[QONG_TDO] = TDO
+};
+
+typedef struct {
+	int		signals;
+	uint32_t	lastout;
+	int		fd_gpios[4];
+} qong_params_t;
+
+static int qong_gpio_export(int gpio, int export) {
+
+	int fd, ret;
+	char *fname;
+	char gpio_number[10];
+
+	if (export)
+		fname = GPIO_EXPORT_PATH;
+	else
+		fname = GPIO_UNEXPORT_PATH;
+
+	fd = open(fname, O_WRONLY);
+	if (!fd) {
+		printf("I cannot open %s to (un)export GPIO %d: %d\n", fname, gpio, errno);
+		return -1;
+	}
+
+	snprintf(gpio_number, sizeof(gpio_number) - 2, "%d\n", gpio);
+
+	ret = write(fd,gpio_number, strlen(gpio_number));
+	close(fd);
+
+	return 0;
+}
+
+static int qong_gpio_direction(int gpio_number, int out)
+{
+	int fd, ret;
+	char fname[50];
+	char buf[8];
+
+	memset(fname, 0, sizeof(fname));
+	memset(buf, 0, sizeof(buf));
+	snprintf(fname, sizeof(fname),
+		"%sgpio%d/direction", GPIO_PATH, gpio_number);
+
+	fd = open(fname, O_WRONLY);
+	if (!fd) {
+		printf("I cannot open %s to set GPIO\n", fname);
+		return -1;
+	}
+
+	if (out)
+		strcpy(buf, "out");
+	else
+		strcpy(buf, "in");
+
+	ret = write(fd,buf, strlen(buf));
+	close(fd);
+
+	if (ret != strlen(buf)) {
+		printf("Error setting direction gpio %d %s %d\n",
+			gpio_number, out ? "out" : "in", ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int qong_gpio_set_value(int fd, int value) {
+	int ret;
+	char buf[8];
+
+	buf[0] = value + '0';
+
+	ret = write(fd, buf, 1);
+
+	if (ret != 1) {
+		printf("Error %d setting value gpio\n", ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+int qong_gpio_get_value(int gpio) {
+	int ret;
+	char buf[8];
+	int fd;
+	char fname[50];
+
+	snprintf(fname, sizeof(fname),
+		"%sgpio%d/value", GPIO_PATH, gpio);
+
+	fd = open(fname, O_RDONLY);
+	if (!fd) {
+		printf("I cannot open %s to read GPIO %d\n", fname, gpio);
+		return -1;
+	}
+
+	ret = read(fd, buf, 8);
+	close(fd);
+
+	if (ret <= 0) {
+		printf("Error getting value gpio %d\n", errno);
+		return -1;
+	}
+
+	if (buf[0] != '0' && buf[0] != '1') {
+		printf("Erroneous value for gpio %d: 0x%x\n", gpio, buf[0]);
+		return -1;
+	}
+
+	return buf[0] - '0';
+}
+
+
+
+static int
+qong_gpio_open( cable_t *cable )
+{
+	qong_params_t *p = cable->params;
+	char fname[50];
+	int i, ret;
+
+	/* Export all gpios */
+	for (i = 0; i < sizeof(gpios) / sizeof(gpios[0]); i++) {	
+		ret = qong_gpio_export(gpios[i], 1);
+
+		if (ret) {
+			printf ("I cannot export gpio (index) %d\n", i);
+			return -1;
+		}
+		qong_gpio_direction(gpios[i], (i == QONG_TDO) ? 0 : 1);
+		if (i != QONG_TDO) {
+			memset(fname, 0, sizeof(fname));
+			snprintf(fname, sizeof(fname),
+				"%sgpio%d/value", GPIO_PATH, gpios[i]);
+			p->fd_gpios[i] = open(fname, O_RDWR);
+			if (!p->fd_gpios[i]) {
+				printf ("I cannot open value for gpio(%d)\n", i);
+				return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int
+qong_gpio_close( cable_t *cable )
+{
+	int i;
+	qong_params_t *p = cable->params;
+
+	for (i = 0; i < sizeof(gpios) / sizeof(gpios[0]); i++) {	
+		if (p->fd_gpios[i]) close(p->fd_gpios[i]);
+		qong_gpio_export(gpios[i], 0);
+	}
+
+	return 0;
+}
+
+static int
+qong_connect( char *params[], cable_t *cable )
+{
+	qong_params_t *cable_params;
+
+	if ( cmd_params( params ) != 1) {
+	  printf( _("Error: This cable type does not accept parameters!\n") );
+	  return 1;
+	}
+
+	printf( _("Initializing QONG GPIO JTAG Chain\n") );
+
+	cable_params = malloc( sizeof(qong_params_t));
+	if (!cable_params) {
+		printf( _("%s(%d) Out of memory\n"), __FILE__, __LINE__ );
+		free( cable );
+		return 4;
+	}
+	memset(cable_params, 0, sizeof(qong_params_t));
+
+	cable->params = cable_params;
+	cable->chain = NULL;
+	cable->delay = 1000;
+
+	return 0;
+}
+
+static void
+qong_disconnect( cable_t *cable )
+{
+	qong_gpio_close( cable );
+	chain_disconnect( cable->chain );
+}
+
+static void
+qong_cable_free( cable_t *cable )
+{
+	free( cable->params );
+	free( cable );
+}
+
+static int
+qong_init( cable_t *cable )
+{
+	qong_params_t *p = cable->params;
+
+	printf("%s:\n", __func__);
+
+	if (qong_gpio_open( cable ))
+		return -1;
+
+	p->signals = CS_TRST;
+
+	return 0;
+}
+
+static void
+qong_done( cable_t *cable )
+{
+	qong_gpio_close( cable );
+}
+
+static void
+qong_clock( cable_t *cable, int tms, int tdi, int n )
+{
+	qong_params_t *p = cable->params;
+	int bit_mask;
+	int i;
+
+	tms = tms ? 1 : 0;
+	tdi = tdi ? 1 : 0;
+
+	qong_gpio_set_value(p->fd_gpios[QONG_TMS], tms);
+	qong_gpio_set_value(p->fd_gpios[QONG_TDI], tdi);
+
+	for (i = 0; i < n; i++) {
+		qong_gpio_set_value(p->fd_gpios[QONG_TCK], 0);
+		qong_gpio_set_value(p->fd_gpios[QONG_TCK], 1);
+		qong_gpio_set_value(p->fd_gpios[QONG_TCK], 0);
+	}
+}
+
+/*
+ * NOTE: This also lowers the TDI and TMS lines; is this intended?
+ */
+static int
+qong_get_tdo( cable_t *cable )
+{
+	qong_params_t *p = cable->params;
+
+	qong_gpio_set_value(p->fd_gpios[QONG_TCK], 0);
+	qong_gpio_set_value(p->fd_gpios[QONG_TDI], 0);
+	qong_gpio_set_value(p->fd_gpios[QONG_TMS], 0);
+	p->lastout &= ~(CS_TMS | CS_TDI | CS_TCK);
+
+	cable_wait( cable );
+
+	return (qong_gpio_get_value(gpios[QONG_TDO]));
+}
+
+static int
+qong_current_signals( cable_t *cable )
+{
+	qong_params_t *p = cable->params;
+
+	int sigs = p->signals & ~(CS_TMS | CS_TDI | CS_TCK);
+
+	if (p->lastout & CS_TCK) sigs |= CS_TCK;
+	if (p->lastout & CS_TDI) sigs |= CS_TDI;
+	if (p->lastout & CS_TMS) sigs |= CS_TMS;
+
+	return sigs;
+}
+
+static int
+qong_set_signal( cable_t *cable, int mask, int val )
+{
+	int prev_sigs = qong_current_signals( cable );
+	qong_params_t *p = cable->params;
+
+	mask &= (CS_TDI | CS_TCK | CS_TMS); // only these can be modified
+
+	if (mask != 0) {
+		if (mask & CS_TMS)
+			qong_gpio_set_value(p->fd_gpios[QONG_TMS], val & CS_TMS);
+		if (mask & CS_TDI)
+			qong_gpio_set_value(p->fd_gpios[QONG_TDI], val & CS_TDI);
+		if (mask & CS_TCK)
+			qong_gpio_set_value(p->fd_gpios[QONG_TCK], val & CS_TCK);
+	}
+
+	p->lastout = val & mask;
+
+	return prev_sigs;
+}
+
+static int
+qong_get_signal( cable_t *cable, pod_sigsel_t sig )
+{
+	return (qong_current_signals( cable ) & sig) ? 1 : 0;
+}
+
+static void
+qong_help( const char *cablename )
+{
+	printf( _(
+		"Usage: cable %s\n"
+		"\n"
+	), cablename );
+}
+
+cable_driver_t qong_cable_driver = {
+	"qong_gpio",
+	N_("QONG GPIO JTAG Chain"),
+	qong_connect,
+	qong_disconnect,
+	qong_cable_free,
+	qong_init,
+	qong_done,
+	generic_set_frequency,
+	qong_clock,
+	qong_get_tdo,
+	generic_transfer,
+	qong_set_signal,
+	qong_get_signal,
+	generic_flush_one_by_one,
+	qong_help
+};
+
-- 
1.6.3.3


