summaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorWjatscheslaw Stoljarski <wjatscheslaw.stoljarski@kiwigrid.com>2012-08-09 14:44:49 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2012-08-13 21:38:50 +0200
commit082ee5344037860a9089c5f2f01a4daffc4a9ed4 (patch)
tree2adc003b2209550626858b72ed345b433483595e /drivers/misc
parent3f96b4938c4046590be8a38fb114148c4bf1ca8e (diff)
downloadbarebox-082ee5344037860a9089c5f2f01a4daffc4a9ed4.tar.gz
Add JTAG bitbang driver
Signed-off-by: Wjatscheslaw Stoljarski <wjatscheslaw.stoljarski@kiwigrid.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig17
-rw-r--r--drivers/misc/Makefile5
-rw-r--r--drivers/misc/jtag.c392
3 files changed, 414 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
new file mode 100644
index 0000000..c3d31ec
--- /dev/null
+++ b/drivers/misc/Kconfig
@@ -0,0 +1,17 @@
+#
+# Misc strange devices
+#
+
+menuconfig MISC_DEVICES
+ bool "Misc devices "
+ help
+ Add support for misc strange devices
+
+if MISC_DEVICES
+
+config JTAG
+ tristate "JTAG Bitbang driver"
+ help
+ Controls JTAG chains connected to I/O pins
+
+endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
new file mode 100644
index 0000000..b085577
--- /dev/null
+++ b/drivers/misc/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for misc devices that really don't fit anywhere else.
+#
+
+obj-$(CONFIG_JTAG) += jtag.o
diff --git a/drivers/misc/jtag.c b/drivers/misc/jtag.c
new file mode 100644
index 0000000..99fd081
--- /dev/null
+++ b/drivers/misc/jtag.c
@@ -0,0 +1,392 @@
+/*
+ * drivers/misc/jtag.c - More infos in include/jtag.h
+ *
+ * Written Aug 2009 by Davide Rizzo <elpa.rizzo@gmail.com>
+ *
+ * Ported to barebox Jul 2012 by
+ * Wjatscheslaw Stoljarski <wjatscheslaw.stoljarski@kiwigrid.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <fs.h>
+#include <errno.h>
+//#include <linux/list.h>
+#include <jtag.h>
+#include <gpio.h>
+#include <driver.h>
+#include <malloc.h>
+#include <common.h>
+#include <init.h>
+#include <ioctl.h>
+
+#define VERSION_MAJ 1
+#define VERSION_MIN 0
+
+/* Max devices in the jtag chain */
+#define MAX_DEVICES 16
+
+struct jtag_info {
+ struct jtag_platdata *pdata;
+ struct cdev cdev;
+ unsigned int devices; /* Number of devices found in the jtag chain */
+ /* Instruction register length of every device in the chain */
+ unsigned int ir_len[MAX_DEVICES]; /* [devices] */
+};
+
+static const unsigned long bypass = 0xFFFFFFFF;
+
+static void pulse_tms0(const struct jtag_platdata *pdata)
+{
+ gpio_set_value(pdata->gpio_tms, 0);
+ gpio_set_value(pdata->gpio_tclk, 0);
+ gpio_set_value(pdata->gpio_tclk, 1);
+}
+
+static void pulse_tms1(const struct jtag_platdata *pdata)
+{
+ gpio_set_value(pdata->gpio_tms, 1);
+ gpio_set_value(pdata->gpio_tclk, 0);
+ gpio_set_value(pdata->gpio_tclk, 1);
+}
+
+static void jtag_reset(const struct jtag_platdata *pdata)
+{
+ gpio_set_value(pdata->gpio_tms, 1);
+ gpio_set_value(pdata->gpio_tclk, 0);
+ gpio_set_value(pdata->gpio_tclk, 1);
+ gpio_set_value(pdata->gpio_tclk, 0);
+ gpio_set_value(pdata->gpio_tclk, 1);
+ gpio_set_value(pdata->gpio_tclk, 0);
+ gpio_set_value(pdata->gpio_tclk, 1);
+ gpio_set_value(pdata->gpio_tclk, 0);
+ gpio_set_value(pdata->gpio_tclk, 1);
+ gpio_set_value(pdata->gpio_tclk, 0);
+ gpio_set_value(pdata->gpio_tclk, 1);
+}
+
+static void jtag_output(const struct jtag_platdata *pdata,
+ const unsigned long *data, unsigned int bitlen, int notlast)
+{
+ unsigned int a;
+ unsigned long mask;
+ gpio_set_value(pdata->gpio_tms, 0);
+ while (bitlen > 0) {
+ for (a = *data++, mask = 0x00000001; mask != 0 && bitlen > 0;
+ mask <<= 1, bitlen--) {
+ gpio_set_value(pdata->gpio_tdo, (a & mask) ? 1 : 0);
+ if ((bitlen == 1) && !notlast)
+ gpio_set_value(pdata->gpio_tms, 1);
+ gpio_set_value(pdata->gpio_tclk, 0);
+ gpio_set_value(pdata->gpio_tclk, 1);
+ }
+ }
+}
+
+static int jtag_ioctl(struct cdev *inode, int cmd, void *arg)
+{
+ int ret = 0;
+ struct jtag_info *info = (struct jtag_info *)inode->priv;
+ int devices = info->devices;
+ struct jtag_cmd *jcmd = (struct jtag_cmd *)arg;
+ struct jtag_platdata *pdata = info->pdata;
+
+ if (_IOC_TYPE(cmd) != JTAG_IOC_MAGIC) return -ENOTTY;
+ if (_IOC_NR(cmd) > JTAG_IOC_MAXNR) return -ENOTTY;
+
+ switch (cmd) {
+
+ case JTAG_GET_DEVICES:
+ /* Returns how many devices found in the chain */
+ ret = info->devices;
+ break;
+
+ case JTAG_GET_ID:
+ /* Returns ID register of selected device */
+ if ((((struct jtag_rd_id *)arg)->device < 0) ||
+ (((struct jtag_rd_id *)arg)->device >= devices)) {
+ ret = -EINVAL;
+ break;
+ }
+ jtag_reset(pdata);
+ pulse_tms0(pdata);
+ pulse_tms1(pdata);
+ pulse_tms0(pdata);
+ pulse_tms0(pdata);
+ while (devices-- > 0) {
+ unsigned long id = 0;
+ pulse_tms0(pdata);
+ if (gpio_get_value(pdata->gpio_tdi)) {
+ unsigned long mask;
+ for (id = 1, mask = 0x00000002; (mask != 0);
+ mask <<= 1) {
+ pulse_tms0(pdata);
+ if (gpio_get_value(pdata->gpio_tdi))
+ id |= mask;
+ }
+ }
+ if (devices == ((struct jtag_rd_id *)arg)->device) {
+ ((struct jtag_rd_id *)arg)->id = id;
+ ret = 0;
+ break;
+ }
+ }
+ jtag_reset(pdata);
+ break;
+
+ case JTAG_SET_IR_LENGTH:
+ /* Sets up IR length of one device */
+ if ((jcmd->device >= 0) && (jcmd->device < devices))
+ info->ir_len[jcmd->device] = jcmd->bitlen;
+ else
+ ret = -EINVAL;
+ break;
+
+ case JTAG_RESET:
+ /* Resets all JTAG states */
+ jtag_reset(pdata);
+ break;
+
+ case JTAG_IR_WR:
+ /*
+ * Writes Instruction Register
+ * If device == -1 writes same Instruction Register in
+ * all devices.
+ * If device >= 0 writes Instruction Register in selected
+ * device and loads BYPASS instruction in all others.
+ */
+ if ((jcmd->device < -1) || (jcmd->device >= devices)) {
+ ret = -EINVAL;
+ break;
+ }
+ pulse_tms0(pdata);
+ pulse_tms1(pdata);
+ pulse_tms1(pdata);
+ pulse_tms0(pdata);
+ pulse_tms0(pdata);
+ while (devices-- > 0) {
+ if ((jcmd->device == -1) || (jcmd->device == devices))
+ /* Loads desired instruction */
+ jtag_output(pdata, jcmd->data,
+ info->ir_len[devices], devices);
+ else
+ /* Loads BYPASS instruction */
+ jtag_output(pdata, &bypass,
+ info->ir_len[devices], devices);
+ }
+ pulse_tms1(pdata);
+ pulse_tms0(pdata);
+ break;
+
+ case JTAG_DR_WR:
+ /*
+ * Writes Data Register of all devices
+ * If device == -1 writes same Data Register in all devices.
+ * If device >= 0 writes Data Register in selected device
+ * and loads BYPASS instruction in all others.
+ */
+ if ((jcmd->device < -1) || (jcmd->device >= devices)) {
+ ret = -EINVAL;
+ break;
+ }
+ pulse_tms0(pdata);
+ pulse_tms1(pdata);
+ pulse_tms0(pdata);
+ pulse_tms0(pdata);
+ while (devices-- > 0) {
+ if ((jcmd->device == -1) || (devices == jcmd->device))
+ /* Loads desired data */
+ jtag_output(pdata, jcmd->data, jcmd->bitlen,
+ devices);
+ else
+ /* Loads 1 dummy bit in BYPASS data register */
+ jtag_output(pdata, &bypass, 1, devices);
+ }
+ pulse_tms1(pdata);
+ pulse_tms0(pdata);
+ break;
+
+ case JTAG_DR_RD:
+ /* Reads data register of selected device */
+ if ((jcmd->device < 0) || (jcmd->device >= devices))
+ ret = -EINVAL;
+ else {
+ unsigned long mask;
+ int bitlen = jcmd->bitlen;
+ unsigned long *data = jcmd->data;
+ pulse_tms0(pdata);
+ pulse_tms1(pdata);
+ pulse_tms0(pdata);
+ pulse_tms0(pdata);
+ devices -= (jcmd->device + 1);
+ while (devices-- > 0)
+ pulse_tms0(pdata);
+ while (bitlen > 0) {
+ for (*data = 0, mask = 0x00000001;
+ (mask != 0) && (bitlen > 0);
+ mask <<= 1, bitlen--) {
+ if (bitlen == 1)
+ pulse_tms1(pdata);
+ else
+ pulse_tms0(pdata);
+ if (gpio_get_value(pdata->gpio_tdi))
+ *data |= mask;
+ }
+ data++;
+ }
+ pulse_tms1(pdata);
+ pulse_tms0(pdata);
+ }
+ break;
+
+ case JTAG_CLK:
+ /* Generates arg clock pulses */
+ gpio_set_value(pdata->gpio_tms, 0);
+ while ((*(unsigned int *) arg)--) {
+ gpio_set_value(pdata->gpio_tclk, 0);
+ gpio_set_value(pdata->gpio_tclk, 1);
+ }
+ break;
+
+ default:
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+static struct file_operations jtag_operations = {
+ .ioctl = jtag_ioctl,
+};
+
+static int jtag_probe(struct device_d *pdev)
+{
+ int i, ret;
+ struct jtag_info *info;
+ struct jtag_platdata *pdata = pdev->platform_data;
+
+ /* Setup gpio pins */
+ gpio_direction_output(pdata->gpio_tms, 0);
+ gpio_direction_output(pdata->gpio_tclk, 1);
+ gpio_direction_output(pdata->gpio_tdo, 0);
+ gpio_direction_input(pdata->gpio_tdi);
+ if (pdata->use_gpio_trst) {
+ /*
+ * Keep fixed at 1 because some devices in the chain could
+ * not use it, to reset chain use jtag_reset()
+ */
+ gpio_direction_output(pdata->gpio_trst, 1);
+ }
+
+ /* Find how many devices in chain */
+ jtag_reset(pdata);
+ pulse_tms0(pdata);
+ pulse_tms1(pdata);
+ pulse_tms1(pdata);
+ pulse_tms0(pdata);
+ pulse_tms0(pdata);
+ gpio_set_value(pdata->gpio_tdo, 1);
+ /* Fills all IR with bypass instruction */
+ for (i = 0; i < 32 * MAX_DEVICES; i++)
+ pulse_tms0(pdata);
+ pulse_tms1(pdata);
+ pulse_tms1(pdata);
+ pulse_tms1(pdata);
+ pulse_tms0(pdata);
+ pulse_tms0(pdata);
+ gpio_set_value(pdata->gpio_tdo, 0);
+ /* Fills all 1-bit bypass register with 0 */
+ for (i = 0; i < MAX_DEVICES + 2; i++)
+ pulse_tms0(pdata);
+ gpio_set_value(pdata->gpio_tdo, 1);
+ /* Counts chain's bit length */
+ for (i = 0; i < MAX_DEVICES + 1; i++) {
+ pulse_tms0(pdata);
+ if (gpio_get_value(pdata->gpio_tdi))
+ break;
+ }
+ dev_notice(pdev, "%d devices found in chain\n", i);
+
+ /* Allocate structure with chain specific infos */
+ info = xzalloc(sizeof(struct jtag_info) + sizeof(info->ir_len[0]) * i);
+
+ info->devices = i;
+ info->pdata = pdata;
+ pdev->priv = info;
+
+ info->cdev.name = JTAG_NAME;
+ info->cdev.dev = pdev;
+ info->cdev.ops = &jtag_operations;
+ info->cdev.priv = info;
+ ret = devfs_create(&info->cdev);
+
+ if (ret)
+ goto fail_devfs_create;
+
+ return 0;
+
+fail_devfs_create:
+ pdev->priv = NULL;
+ free(info);
+ return ret;
+}
+
+static void jtag_info(struct device_d *pdev)
+{
+ int dn, ret;
+ struct jtag_rd_id jid;
+ struct jtag_info *info = pdev->priv;
+
+ printf(" JTAG:\n");
+ printf(" Devices found: %d\n", info->devices);
+ for (dn = 0; dn < info->devices; dn++) {
+ jid.device = dn;
+ ret = jtag_ioctl(&info->cdev, JTAG_GET_ID, &jid);
+ printf(" Device number: %d\n", dn);
+ if (ret == -1)
+ printf(" JTAG_GET_ID failed: %s\n", strerror(errno));
+ else
+ printf(" ID: 0x%lX\n", jid.id);
+ }
+}
+
+static void jtag_remove(struct device_d *pdev)
+{
+ struct jtag_info *info = (struct jtag_info *) pdev->priv;
+
+ devfs_remove(&info->cdev);
+ pdev->priv = NULL;
+ free(info);
+ dev_notice(pdev, "Device removed\n");
+}
+
+static struct driver_d jtag_driver = {
+ .name = JTAG_NAME,
+ .probe = jtag_probe,
+ .remove = jtag_remove,
+ .info = jtag_info,
+};
+
+static int jtag_module_init(void)
+{
+ return register_driver(&jtag_driver);
+}
+
+device_initcall(jtag_module_init);
+
+MODULE_AUTHOR("Davide Rizzo <elpa.rizzo@gmail.com>");
+MODULE_AUTHOR("Wjatscheslaw Stoljarski <wjatscheslaw.stoljarski@kiwigrid.com>");
+MODULE_DESCRIPTION("JTAG bitbang driver");
+MODULE_LICENSE("GPL");