From 082ee5344037860a9089c5f2f01a4daffc4a9ed4 Mon Sep 17 00:00:00 2001 From: Wjatscheslaw Stoljarski Date: Thu, 9 Aug 2012 14:44:49 +0200 Subject: Add JTAG bitbang driver Signed-off-by: Wjatscheslaw Stoljarski Signed-off-by: Sascha Hauer --- drivers/misc/Kconfig | 17 +++ drivers/misc/Makefile | 5 + drivers/misc/jtag.c | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 414 insertions(+) create mode 100644 drivers/misc/Kconfig create mode 100644 drivers/misc/Makefile create mode 100644 drivers/misc/jtag.c (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig new file mode 100644 index 0000000000..c3d31ec9a2 --- /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 0000000000..b0855777a9 --- /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 0000000000..99fd081b31 --- /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 + * + * Ported to barebox Jul 2012 by + * Wjatscheslaw Stoljarski + * + * 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 +#include +//#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_AUTHOR("Wjatscheslaw Stoljarski "); +MODULE_DESCRIPTION("JTAG bitbang driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3