Message ID | 20210101122328.43510-12-tmaimon77@gmail.com |
---|---|
State | New |
Headers | show |
Series | Add NPCM7xx patches to dev-5.8 | expand |
> > Add NPCM7xx JTAG master driver, > The NPCM7xx JTAG master usign GPIO lines > and NPCM PSPI bus. > Signed-off-by: Stanley Chu <yschu@nuvoton.com> > Signed-off-by: Tomer Maimon <tmaimon77@gmail.com> > --- > drivers/misc/Kconfig | 6 + > drivers/misc/Makefile | 1 + > drivers/misc/npcm7xx-jtag-master.c | 840 > +++++++++++++++++++++++++++++ > 3 files changed, 847 insertions(+) > create mode 100644 drivers/misc/npcm7xx-jtag-master.c > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig > index d8626a0d3e31..1b1876284fa6 100644 > --- a/drivers/misc/Kconfig > +++ b/drivers/misc/Kconfig > @@ -478,6 +478,12 @@ config NPCM7XX_PCI_MBOX > Expose the NPCM750/730/715/705 PCI MBOX registers found on > Nuvoton SOCs to userspace. > > +config NPCM7XX_JTAG_MASTER > +tristate "NPCM7xx JTAG Master driver" > +depends on (ARCH_NPCM7XX || COMPILE_TEST) > +help > + Control PSPI/GPIO to transmit jtag signals to support jtag master > function. > + > source "drivers/misc/c2port/Kconfig" > source "drivers/misc/eeprom/Kconfig" > source "drivers/misc/cb710/Kconfig" > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile > index 183970192ced..b11d3c21fa03 100644 > --- a/drivers/misc/Makefile > +++ b/drivers/misc/Makefile > @@ -59,4 +59,5 @@ obj-$(CONFIG_UACCE)+= uacce/ > obj-$(CONFIG_XILINX_SDFEC)+= xilinx_sdfec.o > obj-$(CONFIG_NPCM7XX_LPC_BPC)+= npcm7xx-lpc-bpc.o > obj-$(CONFIG_NPCM7XX_PCI_MBOX)+= npcm7xx-pci-mbox.o > +obj-$(CONFIG_NPCM7XX_JTAG_MASTER)+= npcm7xx-jtag-master.o > obj-$(CONFIG_MCTP_LPC)+= mctp-lpc.o > diff --git a/drivers/misc/npcm7xx-jtag-master.c > b/drivers/misc/npcm7xx-jtag-master.c > new file mode 100644 > index 000000000000..441bc1620551 > --- /dev/null > +++ b/drivers/misc/npcm7xx-jtag-master.c > @@ -0,0 +1,840 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Description : JTAG Master driver > + * > + * Copyright (C) 2019 NuvoTon Corporation > + * > + */ > + > +#include <linux/module.h> > +#include <linux/uaccess.h> > +#include <linux/miscdevice.h> > +#include <linux/spi/spi.h> > +#include <linux/pinctrl/consumer.h> > +#include <linux/gpio/consumer.h> > + > +#define JTAG_PSPI_SPEED(10 * 1000000) > +#define JTAG_SCAN_LEN256 > +#define JTAG_MAX_XFER_DATA_LEN65535 > + > +struct tck_bitbang { > +unsigned char tms; > +unsigned char tdi; /* TDI bit value to write */ > +unsigned char tdo; /* TDO bit value to read */ > +}; > + > +struct bitbang_packet { > +struct tck_bitbang *data; > +__u32length; > +} __attribute__((__packed__)); > + > +struct scan_xfer { > +unsigned int length; /* number of bits */ > +unsigned char tdi[JTAG_SCAN_LEN]; > +unsigned int tdi_bytes; > +unsigned char tdo[JTAG_SCAN_LEN]; > +unsigned int tdo_bytes; > +unsigned int end_tap_state; > +}; > + > +struct jtag_xfer { > +__u8type; > +__u8direction; > +__u8from; > +__u8endstate; > +__u32padding; > +__u32length; > +__u64tdio; > +}; > + > +struct jtag_tap_state { > +__u8reset; > +__u8from; > +__u8endstate; > +__u8tck; > +}; > + > +enum jtagstates { > +jtagtlr, > +jtagrti, > +jtagseldr, > +jtagcapdr, > +jtagshfdr, > +jtagex1dr, > +jtagpaudr, > +jtagex2dr, > +jtagupddr, > +jtagselir, > +jtagcapir, > +jtagshfir, > +jtagex1ir, > +jtagpauir, > +jtagex2ir, > +jtagupdir, > +JTAG_STATE_CURRENT > +}; > + > +enum JTAG_PIN { > +pin_TCK, > +pin_TDI, > +pin_TDO, > +pin_TMS, > +pin_NUM, > +}; > + > +enum jtag_reset { > +JTAG_NO_RESET = 0, > +JTAG_FORCE_RESET = 1, > +}; > + > +enum jtag_xfer_type { > +JTAG_SIR_XFER = 0, > +JTAG_SDR_XFER = 1, > +JTAG_RUNTEST_XFER, > +}; > + > +enum jtag_xfer_direction { > +JTAG_READ_XFER = 1, > +JTAG_WRITE_XFER = 2, > +JTAG_READ_WRITE_XFER = 3, > +}; > + > +#define __JTAG_IOCTL_MAGIC0xb2 > +#define JTAG_SIOCSTATE_IOW(__JTAG_IOCTL_MAGIC, 0, struct > jtag_tap_state) > +#define JTAG_SIOCFREQ_IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int) > +#define JTAG_GIOCFREQ_IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int) > +#define JTAG_IOCXFER_IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer) > +#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum > jtagstates) > +#define JTAG_SIOCMODE_IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int) > +#define JTAG_IOCBITBANG_IOW(__JTAG_IOCTL_MAGIC, 6, unsigned int) > +#define JTAG_RUNTEST _IOW(__JTAG_IOCTL_MAGIC, 7, unsigned int) > + > +static DEFINE_IDA(jtag_ida); > + > +static unsigned char reverse[16] = { > +0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, > +0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF > +}; > + > +#define REVERSE(x) ((reverse[((x) & 0x0f)] << 4) | reverse[((x) & 0xf0) >> 4]) > + > +static DEFINE_SPINLOCK(jtag_file_lock); > + > +struct jtag_info { > +struct device *dev; > +struct spi_device*spi; > +struct miscdevice miscdev; > +struct gpio_desc*pins[pin_NUM]; > +struct pinctrl*pinctrl; > +u32 freq; > +u8 tms_level; > +u8 tapstate; > +bool is_open; > +int id; > + > +/* transmit tck/tdi/tdo by pspi */ > +#define MODE_PSPI0 > +/* transmit all signals by gpio */ > +#define MODE_GPIO1 > +u8 mode; > +}; > + > +/* this structure represents a TMS cycle, as expressed in a set of bits and > + * a count of bits (note: there are no start->end state transitions that > + * require more than 1 byte of TMS cycles) > + */ > +struct tmscycle { > +unsigned char tmsbits; > +unsigned char count; > +}; > + > +/* this is the complete set TMS cycles for going from any TAP state to > + * any other TAP state, following a “shortest path” rule > + */ > +const struct tmscycle _tmscyclelookup[][16] = { > +/* TLR RTI SelDR CapDR SDR */ > +/* Ex1DR PDR Ex2DR UpdDR SelIR */ > +/* CapIR SIR Ex1IR PIR Ex2IR */ > +/* UpdIR */ > +/* TLR */ > +{ > +{0x01, 1}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x02, 4}, > +{0x0a, 4}, {0x0a, 5}, {0x2a, 6}, {0x1a, 5}, {0x06, 3}, > +{0x06, 4}, {0x06, 5}, {0x16, 5}, {0x16, 6}, {0x56, 7}, > +{0x36, 6} > +}, > +/* RTI */ > +{ > +{0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, > +{0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, > +{0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, > +{0x1b, 5} > +}, > +/* SelDR */ > +{ > +{0x03, 2}, {0x03, 3}, {0x00, 0}, {0x00, 1}, {0x00, 2}, > +{0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3}, {0x01, 1}, > +{0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, > +{0x0d, 4} > +}, > +/* CapDR */ > +{ > +{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x00, 0}, {0x00, 1}, > +{0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, > +{0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, > +{0x6f, 7} > +}, > +/* SDR */ > +{ > +{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x00, 0}, > +{0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, > +{0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, > +{0x6f, 7} > +}, > +/* Ex1DR */ > +{ > +{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x02, 3}, > +{0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1}, {0x07, 3}, > +{0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, > +{0x37, 6} > +}, > +/* PDR */ > +{ > +{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x01, 2}, > +{0x05, 3}, {0x00, 1}, {0x01, 1}, {0x03, 2}, {0x0f, 4}, > +{0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, > +{0x6f, 7} > +}, > +/* Ex2DR */ > +{ > +{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x00, 1}, > +{0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1}, {0x07, 3}, > +{0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, > +{0x37, 6} > +}, > +/* UpdDR */ > +{ > +{0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, > +{0x05, 3}, {0x05, 4}, {0x15, 5}, {0x00, 0}, {0x03, 2}, > +{0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, > +{0x1b, 5} > +}, > +/* SelIR */ > +{ > +{0x01, 1}, {0x01, 2}, {0x05, 3}, {0x05, 4}, {0x05, 5}, > +{0x15, 5}, {0x15, 6}, {0x55, 7}, {0x35, 6}, {0x00, 0}, > +{0x00, 1}, {0x00, 2}, {0x02, 2}, {0x02, 3}, {0x0a, 4}, > +{0x06, 3} > +}, > +/* CapIR */ > +{ > +{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, > +{0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, > +{0x00, 0}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x05, 3}, > +{0x03, 2} > +}, > +/* SIR */ > +{ > +{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, > +{0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, > +{0x0f, 5}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x05, 3}, > +{0x03, 2} > +}, > +/* Ex1IR */ > +{ > +{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, > +{0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, > +{0x07, 4}, {0x02, 3}, {0x00, 0}, {0x00, 1}, {0x02, 2}, > +{0x01, 1} > +}, > +/* PIR */ > +{ > +{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, > +{0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, > +{0x0f, 5}, {0x01, 2}, {0x05, 3}, {0x00, 1}, {0x01, 1}, > +{0x03, 2} > +}, > +/* Ex2IR */ > +{ > +{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, > +{0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, > +{0x07, 4}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x00, 0}, > +{0x01, 1} > +}, > +/* UpdIR */ > +{ > +{0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, > +{0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, > +{0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, > +{0x00, 0} > +}, > +}; > + > +static u8 TCK_cycle(struct jtag_info *jtag, > + unsigned char no_tdo, unsigned char TMS, > + unsigned char TDI) > +{ > +u32 tdo = 0; > + > +/* IEEE 1149.1 > + * TMS & TDI shall be sampled by the test logic on the rising edge > + * test logic shall change TDO on the falling edge > + */ > +gpiod_set_value(jtag->pins[pin_TDI], (int)TDI); > +if (jtag->tms_level != (int)TMS) { > +gpiod_set_value(jtag->pins[pin_TMS], (int)TMS); > +jtag->tms_level = (int)TMS; > +} > +gpiod_set_value(jtag->pins[pin_TCK], 1); > +if (!no_tdo) > +tdo = gpiod_get_value(jtag->pins[pin_TDO]); > +gpiod_set_value(jtag->pins[pin_TCK], 0); > + > +return tdo; > +} > + > +static inline void npcm7xx_jtag_bitbangs(struct jtag_info *jtag, > + struct bitbang_packet *bitbangs, > + struct tck_bitbang *bitbang_data) > +{ > +int i; > + > +for (i = 0; i < bitbangs->length; i++) { > +bitbang_data[i].tdo = > +TCK_cycle(jtag, 0, bitbang_data[i].tms, > + bitbang_data[i].tdi); > +cond_resched(); > +} > +} > + > +static int npcm7xx_jtag_set_tapstate(struct jtag_info *jtag, > + enum jtagstates from, enum jtagstates to) > +{ > +unsigned char i; > +unsigned char tmsbits; > +unsigned char count; > + > +if (from == to) > +return 0; > +if (from == JTAG_STATE_CURRENT) > +from = jtag->tapstate; > + > +if (from > JTAG_STATE_CURRENT || to > JTAG_STATE_CURRENT) > +return -1; > + > +if (to == jtagtlr) { > +for (i = 0; i < 9; i++) > +TCK_cycle(jtag, 1, 1, 1); > +jtag->tapstate = jtagtlr; > +return 0; > +} > + > +tmsbits = _tmscyclelookup[from][to].tmsbits; > +count = _tmscyclelookup[from][to].count; > + > +if (count == 0) > +return 0; > + > +for (i = 0; i < count; i++) { > +TCK_cycle(jtag, 1, (tmsbits & 1), 1); > +tmsbits >>= 1; > +} > +pr_debug("jtag: change state %d -> %d\n", from, to); > +jtag->tapstate = to; > +return 0; > +} > + > +static int npcm7xx_jtag_switch_pin_func(struct jtag_info *jtag, u8 mode) > +{ > +struct pinctrl_state*state; > + > +if (mode == MODE_PSPI) { > +state = pinctrl_lookup_state(jtag->pinctrl, "pspi"); > +if (IS_ERR(state)) > +return -ENOENT; > + > +pinctrl_gpio_free(desc_to_gpio(jtag->pins[pin_TCK])); > +pinctrl_gpio_free(desc_to_gpio(jtag->pins[pin_TDI])); > +pinctrl_gpio_free(desc_to_gpio(jtag->pins[pin_TDO])); > +pinctrl_select_state(jtag->pinctrl, state); > +} else if (mode == MODE_GPIO) { > +state = pinctrl_lookup_state(jtag->pinctrl, "gpio"); > +if (IS_ERR(state)) > +return -ENOENT; > + > +pinctrl_select_state(jtag->pinctrl, state); > +pinctrl_gpio_request(desc_to_gpio(jtag->pins[pin_TCK])); > +pinctrl_gpio_request(desc_to_gpio(jtag->pins[pin_TDI])); > +pinctrl_gpio_request(desc_to_gpio(jtag->pins[pin_TDO])); > +jtag->tms_level = gpiod_get_value(jtag->pins[pin_TMS]); > +} > + > +return 0; > +} > + > +static int npcm7xx_jtag_xfer_spi(struct jtag_info *jtag, u32 xfer_bytes, > + u8 *out, u8 *in) > +{ > +struct spi_message m; > +struct spi_transfer spi_xfer; > +int err; > +int i; > + > +err = npcm7xx_jtag_switch_pin_func(jtag, MODE_PSPI); > +if (err) > +return err; > + > +for (i = 0; i < xfer_bytes; i++) > +out[i] = REVERSE(out[i]); > + > +memset(&spi_xfer, 0, sizeof(spi_xfer)); > +spi_xfer.speed_hz = jtag->freq; > +spi_xfer.tx_buf = out; > +spi_xfer.rx_buf = in; > +spi_xfer.len = xfer_bytes; > + > +spi_message_init(&m); > +spi_message_add_tail(&spi_xfer, &m); > +err = spi_sync(jtag->spi, &m); > + > +for (i = 0; i < xfer_bytes; i++) > +in[i] = REVERSE(in[i]); > + > +err = npcm7xx_jtag_switch_pin_func(jtag, MODE_GPIO); > + > +return err; > +} > + > +static int npcm7xx_jtag_xfer_gpio(struct jtag_info *jtag, > + struct jtag_xfer *xfer, u8 *out, u8 *in) > +{ > +unsigned long *bitmap_tdi = (unsigned long *)out; > +unsigned long *bitmap_tdo = (unsigned long *)in; > +u32 xfer_bits = xfer->length; > +u32 bit_index = 0; > +u8 tdi, tdo, tms; > + > +while (bit_index < xfer_bits) { > +tdi = 0; > +tms = 0; > + > +if (test_bit(bit_index, bitmap_tdi)) > +tdi = 1; > + > +/* If this is the last bit, leave TMS high */ > +if ((bit_index == xfer_bits - 1) && xfer->endstate != jtagshfdr && > + xfer->endstate != jtagshfir && xfer->endstate != > JTAG_STATE_CURRENT) > +tms = 1; > + > +/* shift 1 bit */ > +tdo = TCK_cycle(jtag, 0, tms, tdi); > +cond_resched(); > +/* If it was the last bit in the scan and the end_tap_state is > + * something other than shiftDR or shiftIR then go to Exit1. > + * IMPORTANT Note: if the end_tap_state is ShiftIR/DR and the > + * next call to this function is a shiftDR/IR then the driver > + * will not change state! > + */ > +if (tms) > +jtag->tapstate = (jtag->tapstate == jtagshfdr) ? > +jtagex1dr : jtagex1ir; > + > +if (tdo) > +bitmap_set(bitmap_tdo, bit_index, 1); > + > +bit_index++; > +} > + > +return 0; > +} > + > +static int npcm7xx_jtag_readwrite_scan(struct jtag_info *jtag, > + struct jtag_xfer *xfer, u8 *tdi, u8 *tdo) > +{ > +u32 xfer_bytes = DIV_ROUND_UP(xfer->length, BITS_PER_BYTE); > +u32 remain_bits = xfer->length; > +u32 spi_xfer_bytes = 0; > + > +if (xfer_bytes > 1 && jtag->mode == MODE_PSPI) { > +/* The last byte should be sent using gpio bitbang > + * (TMS needed) > + */ > +spi_xfer_bytes = xfer_bytes - 1; > +if (npcm7xx_jtag_xfer_spi(jtag, spi_xfer_bytes, tdi, tdo)) > +return -EIO; > +remain_bits -= spi_xfer_bytes * 8; > +} > + > +if (remain_bits) { > +xfer->length = remain_bits; > +npcm7xx_jtag_xfer_gpio(jtag, xfer, tdi + spi_xfer_bytes, > + tdo + spi_xfer_bytes); > +} > + > +npcm7xx_jtag_set_tapstate(jtag, JTAG_STATE_CURRENT, xfer->endstate); > + > +return 0; > +} > + > +static int npcm7xx_jtag_xfer(struct jtag_info *npcm7xx_jtag, > + struct jtag_xfer *xfer, u8 *data, u32 bytes) > +{ > +u8 *tdo; > +int ret; > + > +if (xfer->length == 0) > +return 0; > + > +tdo = kzalloc(bytes, GFP_KERNEL); > +if (!tdo) > +return -ENOMEM; > + > +if (xfer->type == JTAG_SIR_XFER) > +npcm7xx_jtag_set_tapstate(npcm7xx_jtag, xfer->from, jtagshfir); > +else if (xfer->type == JTAG_SDR_XFER) > +npcm7xx_jtag_set_tapstate(npcm7xx_jtag, xfer->from, jtagshfdr); > + > +ret = npcm7xx_jtag_readwrite_scan(npcm7xx_jtag, xfer, data, tdo); > +memcpy(data, tdo, bytes); > +kfree(tdo); > + > +return ret; > +} > + > +/* Run in current state for specific number of tcks */ > +static int npcm7xx_jtag_runtest(struct jtag_info *jtag, unsigned int tcks) > +{ > +struct jtag_xfer xfer; > +u32 bytes = DIV_ROUND_UP(tcks, BITS_PER_BYTE); > +u8 *buf; > +u32 i; > +int err; > + > +if (jtag->mode != MODE_PSPI) { > +for (i = 0; i < tcks; i++) { > +TCK_cycle(jtag, 0, 0, 1); > +cond_resched(); > +} > +return 0; > +} > + > +buf = kzalloc(bytes, GFP_KERNEL); > +xfer.type = JTAG_RUNTEST_XFER; > +xfer.direction = JTAG_WRITE_XFER; > +xfer.from = JTAG_STATE_CURRENT; > +xfer.endstate = JTAG_STATE_CURRENT; > +xfer.length = tcks; > + > +err = npcm7xx_jtag_xfer(jtag, &xfer, buf, bytes); > +kfree(buf); > + > +return err; > +} > + > +static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg) > +{ > +struct jtag_info *priv = file->private_data; > +struct jtag_tap_state tapstate; > +void __user *argp = (void __user *)arg; > +struct jtag_xfer xfer; > +struct bitbang_packet bitbang; > +struct tck_bitbang *bitbang_data; > +u8 *xfer_data; > +u32 data_size; > +u32 value; > +int ret = 0; > + > +switch (cmd) { > +case JTAG_SIOCFREQ: > +if (get_user(value, (__u32 __user *)arg)) > +return -EFAULT; > +if (value <= priv->spi->max_speed_hz) { > +priv->freq = value; > +} else { > +dev_err(priv->dev, "%s: invalid jtag freq %u\n", > +__func__, value); > +ret = -EINVAL; > +} > +break; > +case JTAG_GIOCFREQ: > +if (put_user(priv->freq, (__u32 __user *)arg)) > +return -EFAULT; > +break; > +case JTAG_IOCBITBANG: > +if (copy_from_user(&bitbang, (const void __user *)arg, > + sizeof(struct bitbang_packet))) > +return -EFAULT; > + > +if (bitbang.length >= JTAG_MAX_XFER_DATA_LEN) > +return -EINVAL; > + > +data_size = bitbang.length * sizeof(struct tck_bitbang); > +bitbang_data = memdup_user((void __user *)bitbang.data, > + data_size); > +if (IS_ERR(bitbang_data)) > +return -EFAULT; > + > +npcm7xx_jtag_bitbangs(priv, &bitbang, bitbang_data); > +ret = copy_to_user((void __user *)bitbang.data, > + (void *)bitbang_data, data_size); > +kfree(bitbang_data); > +if (ret) > +return -EFAULT; > +break; > +case JTAG_SIOCSTATE: > +if (copy_from_user(&tapstate, (const void __user *)arg, > + sizeof(struct jtag_tap_state))) > +return -EFAULT; > + > +if (tapstate.from > JTAG_STATE_CURRENT) > +return -EINVAL; > + > +if (tapstate.endstate > JTAG_STATE_CURRENT) > +return -EINVAL; > + > +if (tapstate.reset > JTAG_FORCE_RESET) > +return -EINVAL; > +if (tapstate.reset == JTAG_FORCE_RESET) > +npcm7xx_jtag_set_tapstate(priv, JTAG_STATE_CURRENT, > + jtagtlr); > +npcm7xx_jtag_set_tapstate(priv, tapstate.from, > + tapstate.endstate); > +break; > +case JTAG_GIOCSTATUS: > +ret = put_user(priv->tapstate, (__u32 __user *)arg); > +break; > +case JTAG_IOCXFER: > +if (copy_from_user(&xfer, argp, sizeof(struct jtag_xfer))) > +return -EFAULT; > + > +if (xfer.length >= JTAG_MAX_XFER_DATA_LEN) > +return -EINVAL; > + > +if (xfer.type > JTAG_SDR_XFER) > +return -EINVAL; > + > +if (xfer.direction > JTAG_READ_WRITE_XFER) > +return -EINVAL; > + > +if (xfer.from > JTAG_STATE_CURRENT) > +return -EINVAL; > + > +if (xfer.endstate > JTAG_STATE_CURRENT) > +return -EINVAL; > + > +data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE); > +xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio), data_size); > +if (IS_ERR(xfer_data)) > +return -EFAULT; > +ret = npcm7xx_jtag_xfer(priv, &xfer, xfer_data, data_size); > +if (ret) { > +kfree(xfer_data); > +return -EIO; > +} > +ret = copy_to_user(u64_to_user_ptr(xfer.tdio), > + (void *)xfer_data, data_size); > +kfree(xfer_data); > +if (ret) > +return -EFAULT; > + > +if (copy_to_user((void __user *)arg, (void *)&xfer, > + sizeof(struct jtag_xfer))) > +return -EFAULT; > +break; > +case JTAG_SIOCMODE: > +if (get_user(value, (__u32 __user *)arg)) > +return -EFAULT; > +if (value != MODE_GPIO && value != MODE_PSPI) > +return -EINVAL; > +priv->mode = value; > +break; > +case JTAG_RUNTEST: > +ret = npcm7xx_jtag_runtest(priv, (unsigned int)arg); > +break; > +default: > +return -EINVAL; > +} > + > +return ret; > +} > + > +static int jtag_open(struct inode *inode, struct file *file) > +{ > +struct jtag_info *jtag; > + > +jtag = container_of(file->private_data, struct jtag_info, miscdev); > + > +spin_lock(&jtag_file_lock); > +if (jtag->is_open) { > +spin_unlock(&jtag_file_lock); > +return -EBUSY; > +} > + > +jtag->is_open = true; > +file->private_data = jtag; > + > +spin_unlock(&jtag_file_lock); > + > +return 0; > +} > + > +static int jtag_release(struct inode *inode, struct file *file) > +{ > +struct jtag_info *jtag = file->private_data; > + > +spin_lock(&jtag_file_lock); > +jtag->is_open = false; > +spin_unlock(&jtag_file_lock); > + > +return 0; > +} > + > +const struct file_operations npcm_jtag_fops = { > +.open = jtag_open, > +.unlocked_ioctl = jtag_ioctl, > +.release = jtag_release, > +}; > + > +static int jtag_register_device(struct jtag_info *jtag) > +{ > +struct device *dev = jtag->dev; > +int err; > +int id; > + > +if (!dev) > +return -ENODEV; > + > +id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL); > +if (id < 0) > +return id; > + > +jtag->id = id; > +/* register miscdev */ > +jtag->miscdev.parent = dev; > +jtag->miscdev.fops = &npcm_jtag_fops; > +jtag->miscdev.minor = MISC_DYNAMIC_MINOR; > +jtag->miscdev.name = kasprintf(GFP_KERNEL, "jtag%d", id); > +if (!jtag->miscdev.name) { > +err = -ENOMEM; > +goto err; > +} > + > +err = misc_register(&jtag->miscdev); > +if (err) { > +dev_err(jtag->miscdev.parent, > +"Unable to register device, err %d\n", err); > +kfree(jtag->miscdev.name); > +goto err; > +} > + > +return 0; > + > +err: > +ida_simple_remove(&jtag_ida, id); > +return err; > +} > + > +static int npcm7xx_jtag_init(struct device *dev, struct jtag_info > *npcm7xx_jtag) > +{ > +struct pinctrl*pinctrl; > +int i; > + > +pinctrl = devm_pinctrl_get(dev); > +if (IS_ERR(pinctrl)) > +return PTR_ERR(pinctrl); > + > +npcm7xx_jtag->pinctrl = pinctrl; > + > +/* jtag pins */ > +npcm7xx_jtag->pins[pin_TCK] = gpiod_get(dev, "tck", GPIOD_OUT_LOW); > +npcm7xx_jtag->pins[pin_TDI] = gpiod_get(dev, "tdi", GPIOD_OUT_HIGH); > +npcm7xx_jtag->pins[pin_TDO] = gpiod_get(dev, "tdo", GPIOD_IN); > +npcm7xx_jtag->pins[pin_TMS] = gpiod_get(dev, "tms", > GPIOD_OUT_HIGH); > +for (i = 0; i < pin_NUM; i++) { > +if (IS_ERR(npcm7xx_jtag->pins[i])) > +return PTR_ERR(npcm7xx_jtag->pins[i]); > +} > + > +npcm7xx_jtag->freq = JTAG_PSPI_SPEED; > +npcm7xx_jtag->tms_level = > gpiod_get_value(npcm7xx_jtag->pins[pin_TMS]); > +npcm7xx_jtag_set_tapstate(npcm7xx_jtag, JTAG_STATE_CURRENT, > jtagtlr); > +npcm7xx_jtag->mode = MODE_PSPI; > + > +return 0; > +} > + > +static int npcm7xx_jtag_probe(struct spi_device *spi) > +{ > +struct jtag_info *npcm_jtag; > +int ret; > + > +dev_info(&spi->dev, "%s", __func__); > + > +npcm_jtag = kzalloc(sizeof(struct jtag_info), GFP_KERNEL); > +if (!npcm_jtag) > +return -ENOMEM; > + > +npcm_jtag->dev = &spi->dev; > +npcm_jtag->spi = spi; > +spi->mode = SPI_MODE_0 | SPI_NO_CS; > + > +/* Initialize device*/ > +ret = npcm7xx_jtag_init(&spi->dev, npcm_jtag); > +if (ret) > +goto err; > + > +/* Register a misc device */ > +ret = jtag_register_device(npcm_jtag); > +if (ret) { > +dev_err(&spi->dev, "failed to create device\n"); > +goto err; > +} > +spi_set_drvdata(spi, npcm_jtag); > + > +return 0; > +err: > +kfree(npcm_jtag); > +return ret; > +} > + > +static int npcm7xx_jtag_remove(struct spi_device *spi) > +{ > +struct jtag_info *jtag = spi_get_drvdata(spi); > +int i; > + > +if (!jtag) > +return 0; > + > +misc_deregister(&jtag->miscdev); > +kfree(jtag->miscdev.name); > +for (i = 0; i < pin_NUM; i++) { > +gpiod_direction_input(jtag->pins[i]); > +gpiod_put(jtag->pins[i]); > +} > +kfree(jtag); > +ida_simple_remove(&jtag_ida, jtag->id); > + > +return 0; > +} > + > +static const struct of_device_id npcm7xx_jtag_of_match[] = { > +{ .compatible = "nuvoton,npcm750-jtag-master", }, > +{}, > +}; > +MODULE_DEVICE_TABLE(of, npcm7xx_jtag_of_match); > + > +static struct spi_driver npcm7xx_jtag_driver = { > +.driver = { > +.name= "npcm7xx_jtag", > +.of_match_table = npcm7xx_jtag_of_match, > +}, > +.probe= npcm7xx_jtag_probe, > +.remove= npcm7xx_jtag_remove, > +}; > + > +module_spi_driver(npcm7xx_jtag_driver); > + > +MODULE_AUTHOR("Stanley Chu <yschu@nuvoton.com>"); > +MODULE_DESCRIPTION("NPCM7xx JTAG Master Driver"); > +MODULE_LICENSE("GPL"); > + > -- > 2.22.0
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d8626a0d3e31..1b1876284fa6 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -478,6 +478,12 @@ config NPCM7XX_PCI_MBOX Expose the NPCM750/730/715/705 PCI MBOX registers found on Nuvoton SOCs to userspace. +config NPCM7XX_JTAG_MASTER + tristate "NPCM7xx JTAG Master driver" + depends on (ARCH_NPCM7XX || COMPILE_TEST) + help + Control PSPI/GPIO to transmit jtag signals to support jtag master function. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 183970192ced..b11d3c21fa03 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -59,4 +59,5 @@ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o obj-$(CONFIG_NPCM7XX_LPC_BPC) += npcm7xx-lpc-bpc.o obj-$(CONFIG_NPCM7XX_PCI_MBOX) += npcm7xx-pci-mbox.o +obj-$(CONFIG_NPCM7XX_JTAG_MASTER) += npcm7xx-jtag-master.o obj-$(CONFIG_MCTP_LPC) += mctp-lpc.o diff --git a/drivers/misc/npcm7xx-jtag-master.c b/drivers/misc/npcm7xx-jtag-master.c new file mode 100644 index 000000000000..441bc1620551 --- /dev/null +++ b/drivers/misc/npcm7xx-jtag-master.c @@ -0,0 +1,840 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Description : JTAG Master driver + * + * Copyright (C) 2019 NuvoTon Corporation + * + */ + +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/spi/spi.h> +#include <linux/pinctrl/consumer.h> +#include <linux/gpio/consumer.h> + +#define JTAG_PSPI_SPEED (10 * 1000000) +#define JTAG_SCAN_LEN 256 +#define JTAG_MAX_XFER_DATA_LEN 65535 + +struct tck_bitbang { + unsigned char tms; + unsigned char tdi; /* TDI bit value to write */ + unsigned char tdo; /* TDO bit value to read */ +}; + +struct bitbang_packet { + struct tck_bitbang *data; + __u32 length; +} __attribute__((__packed__)); + +struct scan_xfer { + unsigned int length; /* number of bits */ + unsigned char tdi[JTAG_SCAN_LEN]; + unsigned int tdi_bytes; + unsigned char tdo[JTAG_SCAN_LEN]; + unsigned int tdo_bytes; + unsigned int end_tap_state; +}; + +struct jtag_xfer { + __u8 type; + __u8 direction; + __u8 from; + __u8 endstate; + __u32 padding; + __u32 length; + __u64 tdio; +}; + +struct jtag_tap_state { + __u8 reset; + __u8 from; + __u8 endstate; + __u8 tck; +}; + +enum jtagstates { + jtagtlr, + jtagrti, + jtagseldr, + jtagcapdr, + jtagshfdr, + jtagex1dr, + jtagpaudr, + jtagex2dr, + jtagupddr, + jtagselir, + jtagcapir, + jtagshfir, + jtagex1ir, + jtagpauir, + jtagex2ir, + jtagupdir, + JTAG_STATE_CURRENT +}; + +enum JTAG_PIN { + pin_TCK, + pin_TDI, + pin_TDO, + pin_TMS, + pin_NUM, +}; + +enum jtag_reset { + JTAG_NO_RESET = 0, + JTAG_FORCE_RESET = 1, +}; + +enum jtag_xfer_type { + JTAG_SIR_XFER = 0, + JTAG_SDR_XFER = 1, + JTAG_RUNTEST_XFER, +}; + +enum jtag_xfer_direction { + JTAG_READ_XFER = 1, + JTAG_WRITE_XFER = 2, + JTAG_READ_WRITE_XFER = 3, +}; + +#define __JTAG_IOCTL_MAGIC 0xb2 +#define JTAG_SIOCSTATE _IOW(__JTAG_IOCTL_MAGIC, 0, struct jtag_tap_state) +#define JTAG_SIOCFREQ _IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int) +#define JTAG_GIOCFREQ _IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int) +#define JTAG_IOCXFER _IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer) +#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtagstates) +#define JTAG_SIOCMODE _IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int) +#define JTAG_IOCBITBANG _IOW(__JTAG_IOCTL_MAGIC, 6, unsigned int) +#define JTAG_RUNTEST _IOW(__JTAG_IOCTL_MAGIC, 7, unsigned int) + +static DEFINE_IDA(jtag_ida); + +static unsigned char reverse[16] = { + 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, + 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF +}; + +#define REVERSE(x) ((reverse[((x) & 0x0f)] << 4) | reverse[((x) & 0xf0) >> 4]) + +static DEFINE_SPINLOCK(jtag_file_lock); + +struct jtag_info { + struct device *dev; + struct spi_device *spi; + struct miscdevice miscdev; + struct gpio_desc *pins[pin_NUM]; + struct pinctrl *pinctrl; + u32 freq; + u8 tms_level; + u8 tapstate; + bool is_open; + int id; + + /* transmit tck/tdi/tdo by pspi */ + #define MODE_PSPI 0 + /* transmit all signals by gpio */ + #define MODE_GPIO 1 + u8 mode; +}; + +/* this structure represents a TMS cycle, as expressed in a set of bits and + * a count of bits (note: there are no start->end state transitions that + * require more than 1 byte of TMS cycles) + */ +struct tmscycle { + unsigned char tmsbits; + unsigned char count; +}; + +/* this is the complete set TMS cycles for going from any TAP state to + * any other TAP state, following a “shortest path” rule + */ +const struct tmscycle _tmscyclelookup[][16] = { +/* TLR RTI SelDR CapDR SDR */ +/* Ex1DR PDR Ex2DR UpdDR SelIR */ +/* CapIR SIR Ex1IR PIR Ex2IR */ +/* UpdIR */ +/* TLR */ + { + {0x01, 1}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x02, 4}, + {0x0a, 4}, {0x0a, 5}, {0x2a, 6}, {0x1a, 5}, {0x06, 3}, + {0x06, 4}, {0x06, 5}, {0x16, 5}, {0x16, 6}, {0x56, 7}, + {0x36, 6} + }, +/* RTI */ + { + {0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, + {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, + {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, + {0x1b, 5} + }, +/* SelDR */ + { + {0x03, 2}, {0x03, 3}, {0x00, 0}, {0x00, 1}, {0x00, 2}, + {0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3}, {0x01, 1}, + {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, + {0x0d, 4} + }, +/* CapDR */ + { + {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x00, 0}, {0x00, 1}, + {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, + {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, + {0x6f, 7} + }, +/* SDR */ + { + {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x00, 0}, + {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, + {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, + {0x6f, 7} + }, +/* Ex1DR */ + { + {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x02, 3}, + {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1}, {0x07, 3}, + {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, + {0x37, 6} + }, +/* PDR */ + { + {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x01, 2}, + {0x05, 3}, {0x00, 1}, {0x01, 1}, {0x03, 2}, {0x0f, 4}, + {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, + {0x6f, 7} + }, +/* Ex2DR */ + { + {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x00, 1}, + {0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1}, {0x07, 3}, + {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, + {0x37, 6} + }, +/* UpdDR */ + { + {0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, + {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x00, 0}, {0x03, 2}, + {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, + {0x1b, 5} + }, +/* SelIR */ + { + {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x05, 4}, {0x05, 5}, + {0x15, 5}, {0x15, 6}, {0x55, 7}, {0x35, 6}, {0x00, 0}, + {0x00, 1}, {0x00, 2}, {0x02, 2}, {0x02, 3}, {0x0a, 4}, + {0x06, 3} + }, +/* CapIR */ + { + {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, + {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, + {0x00, 0}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x05, 3}, + {0x03, 2} + }, +/* SIR */ + { + {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, + {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, + {0x0f, 5}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x05, 3}, + {0x03, 2} + }, +/* Ex1IR */ + { + {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, + {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, + {0x07, 4}, {0x02, 3}, {0x00, 0}, {0x00, 1}, {0x02, 2}, + {0x01, 1} + }, +/* PIR */ + { + {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, + {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, + {0x0f, 5}, {0x01, 2}, {0x05, 3}, {0x00, 1}, {0x01, 1}, + {0x03, 2} + }, +/* Ex2IR */ + { + {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, + {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, + {0x07, 4}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x00, 0}, + {0x01, 1} + }, +/* UpdIR */ + { + {0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, + {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, + {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, + {0x00, 0} + }, +}; + +static u8 TCK_cycle(struct jtag_info *jtag, + unsigned char no_tdo, unsigned char TMS, + unsigned char TDI) +{ + u32 tdo = 0; + + /* IEEE 1149.1 + * TMS & TDI shall be sampled by the test logic on the rising edge + * test logic shall change TDO on the falling edge + */ + gpiod_set_value(jtag->pins[pin_TDI], (int)TDI); + if (jtag->tms_level != (int)TMS) { + gpiod_set_value(jtag->pins[pin_TMS], (int)TMS); + jtag->tms_level = (int)TMS; + } + gpiod_set_value(jtag->pins[pin_TCK], 1); + if (!no_tdo) + tdo = gpiod_get_value(jtag->pins[pin_TDO]); + gpiod_set_value(jtag->pins[pin_TCK], 0); + + return tdo; +} + +static inline void npcm7xx_jtag_bitbangs(struct jtag_info *jtag, + struct bitbang_packet *bitbangs, + struct tck_bitbang *bitbang_data) +{ + int i; + + for (i = 0; i < bitbangs->length; i++) { + bitbang_data[i].tdo = + TCK_cycle(jtag, 0, bitbang_data[i].tms, + bitbang_data[i].tdi); + cond_resched(); + } +} + +static int npcm7xx_jtag_set_tapstate(struct jtag_info *jtag, + enum jtagstates from, enum jtagstates to) +{ + unsigned char i; + unsigned char tmsbits; + unsigned char count; + + if (from == to) + return 0; + if (from == JTAG_STATE_CURRENT) + from = jtag->tapstate; + + if (from > JTAG_STATE_CURRENT || to > JTAG_STATE_CURRENT) + return -1; + + if (to == jtagtlr) { + for (i = 0; i < 9; i++) + TCK_cycle(jtag, 1, 1, 1); + jtag->tapstate = jtagtlr; + return 0; + } + + tmsbits = _tmscyclelookup[from][to].tmsbits; + count = _tmscyclelookup[from][to].count; + + if (count == 0) + return 0; + + for (i = 0; i < count; i++) { + TCK_cycle(jtag, 1, (tmsbits & 1), 1); + tmsbits >>= 1; + } + pr_debug("jtag: change state %d -> %d\n", from, to); + jtag->tapstate = to; + return 0; +} + +static int npcm7xx_jtag_switch_pin_func(struct jtag_info *jtag, u8 mode) +{ + struct pinctrl_state *state; + + if (mode == MODE_PSPI) { + state = pinctrl_lookup_state(jtag->pinctrl, "pspi"); + if (IS_ERR(state)) + return -ENOENT; + + pinctrl_gpio_free(desc_to_gpio(jtag->pins[pin_TCK])); + pinctrl_gpio_free(desc_to_gpio(jtag->pins[pin_TDI])); + pinctrl_gpio_free(desc_to_gpio(jtag->pins[pin_TDO])); + pinctrl_select_state(jtag->pinctrl, state); + } else if (mode == MODE_GPIO) { + state = pinctrl_lookup_state(jtag->pinctrl, "gpio"); + if (IS_ERR(state)) + return -ENOENT; + + pinctrl_select_state(jtag->pinctrl, state); + pinctrl_gpio_request(desc_to_gpio(jtag->pins[pin_TCK])); + pinctrl_gpio_request(desc_to_gpio(jtag->pins[pin_TDI])); + pinctrl_gpio_request(desc_to_gpio(jtag->pins[pin_TDO])); + jtag->tms_level = gpiod_get_value(jtag->pins[pin_TMS]); + } + + return 0; +} + +static int npcm7xx_jtag_xfer_spi(struct jtag_info *jtag, u32 xfer_bytes, + u8 *out, u8 *in) +{ + struct spi_message m; + struct spi_transfer spi_xfer; + int err; + int i; + + err = npcm7xx_jtag_switch_pin_func(jtag, MODE_PSPI); + if (err) + return err; + + for (i = 0; i < xfer_bytes; i++) + out[i] = REVERSE(out[i]); + + memset(&spi_xfer, 0, sizeof(spi_xfer)); + spi_xfer.speed_hz = jtag->freq; + spi_xfer.tx_buf = out; + spi_xfer.rx_buf = in; + spi_xfer.len = xfer_bytes; + + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + err = spi_sync(jtag->spi, &m); + + for (i = 0; i < xfer_bytes; i++) + in[i] = REVERSE(in[i]); + + err = npcm7xx_jtag_switch_pin_func(jtag, MODE_GPIO); + + return err; +} + +static int npcm7xx_jtag_xfer_gpio(struct jtag_info *jtag, + struct jtag_xfer *xfer, u8 *out, u8 *in) +{ + unsigned long *bitmap_tdi = (unsigned long *)out; + unsigned long *bitmap_tdo = (unsigned long *)in; + u32 xfer_bits = xfer->length; + u32 bit_index = 0; + u8 tdi, tdo, tms; + + while (bit_index < xfer_bits) { + tdi = 0; + tms = 0; + + if (test_bit(bit_index, bitmap_tdi)) + tdi = 1; + + /* If this is the last bit, leave TMS high */ + if ((bit_index == xfer_bits - 1) && xfer->endstate != jtagshfdr && + xfer->endstate != jtagshfir && xfer->endstate != JTAG_STATE_CURRENT) + tms = 1; + + /* shift 1 bit */ + tdo = TCK_cycle(jtag, 0, tms, tdi); + cond_resched(); + /* If it was the last bit in the scan and the end_tap_state is + * something other than shiftDR or shiftIR then go to Exit1. + * IMPORTANT Note: if the end_tap_state is ShiftIR/DR and the + * next call to this function is a shiftDR/IR then the driver + * will not change state! + */ + if (tms) + jtag->tapstate = (jtag->tapstate == jtagshfdr) ? + jtagex1dr : jtagex1ir; + + if (tdo) + bitmap_set(bitmap_tdo, bit_index, 1); + + bit_index++; + } + + return 0; +} + +static int npcm7xx_jtag_readwrite_scan(struct jtag_info *jtag, + struct jtag_xfer *xfer, u8 *tdi, u8 *tdo) +{ + u32 xfer_bytes = DIV_ROUND_UP(xfer->length, BITS_PER_BYTE); + u32 remain_bits = xfer->length; + u32 spi_xfer_bytes = 0; + + if (xfer_bytes > 1 && jtag->mode == MODE_PSPI) { + /* The last byte should be sent using gpio bitbang + * (TMS needed) + */ + spi_xfer_bytes = xfer_bytes - 1; + if (npcm7xx_jtag_xfer_spi(jtag, spi_xfer_bytes, tdi, tdo)) + return -EIO; + remain_bits -= spi_xfer_bytes * 8; + } + + if (remain_bits) { + xfer->length = remain_bits; + npcm7xx_jtag_xfer_gpio(jtag, xfer, tdi + spi_xfer_bytes, + tdo + spi_xfer_bytes); + } + + npcm7xx_jtag_set_tapstate(jtag, JTAG_STATE_CURRENT, xfer->endstate); + + return 0; +} + +static int npcm7xx_jtag_xfer(struct jtag_info *npcm7xx_jtag, + struct jtag_xfer *xfer, u8 *data, u32 bytes) +{ + u8 *tdo; + int ret; + + if (xfer->length == 0) + return 0; + + tdo = kzalloc(bytes, GFP_KERNEL); + if (!tdo) + return -ENOMEM; + + if (xfer->type == JTAG_SIR_XFER) + npcm7xx_jtag_set_tapstate(npcm7xx_jtag, xfer->from, jtagshfir); + else if (xfer->type == JTAG_SDR_XFER) + npcm7xx_jtag_set_tapstate(npcm7xx_jtag, xfer->from, jtagshfdr); + + ret = npcm7xx_jtag_readwrite_scan(npcm7xx_jtag, xfer, data, tdo); + memcpy(data, tdo, bytes); + kfree(tdo); + + return ret; +} + +/* Run in current state for specific number of tcks */ +static int npcm7xx_jtag_runtest(struct jtag_info *jtag, unsigned int tcks) +{ + struct jtag_xfer xfer; + u32 bytes = DIV_ROUND_UP(tcks, BITS_PER_BYTE); + u8 *buf; + u32 i; + int err; + + if (jtag->mode != MODE_PSPI) { + for (i = 0; i < tcks; i++) { + TCK_cycle(jtag, 0, 0, 1); + cond_resched(); + } + return 0; + } + + buf = kzalloc(bytes, GFP_KERNEL); + xfer.type = JTAG_RUNTEST_XFER; + xfer.direction = JTAG_WRITE_XFER; + xfer.from = JTAG_STATE_CURRENT; + xfer.endstate = JTAG_STATE_CURRENT; + xfer.length = tcks; + + err = npcm7xx_jtag_xfer(jtag, &xfer, buf, bytes); + kfree(buf); + + return err; +} + +static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct jtag_info *priv = file->private_data; + struct jtag_tap_state tapstate; + void __user *argp = (void __user *)arg; + struct jtag_xfer xfer; + struct bitbang_packet bitbang; + struct tck_bitbang *bitbang_data; + u8 *xfer_data; + u32 data_size; + u32 value; + int ret = 0; + + switch (cmd) { + case JTAG_SIOCFREQ: + if (get_user(value, (__u32 __user *)arg)) + return -EFAULT; + if (value <= priv->spi->max_speed_hz) { + priv->freq = value; + } else { + dev_err(priv->dev, "%s: invalid jtag freq %u\n", + __func__, value); + ret = -EINVAL; + } + break; + case JTAG_GIOCFREQ: + if (put_user(priv->freq, (__u32 __user *)arg)) + return -EFAULT; + break; + case JTAG_IOCBITBANG: + if (copy_from_user(&bitbang, (const void __user *)arg, + sizeof(struct bitbang_packet))) + return -EFAULT; + + if (bitbang.length >= JTAG_MAX_XFER_DATA_LEN) + return -EINVAL; + + data_size = bitbang.length * sizeof(struct tck_bitbang); + bitbang_data = memdup_user((void __user *)bitbang.data, + data_size); + if (IS_ERR(bitbang_data)) + return -EFAULT; + + npcm7xx_jtag_bitbangs(priv, &bitbang, bitbang_data); + ret = copy_to_user((void __user *)bitbang.data, + (void *)bitbang_data, data_size); + kfree(bitbang_data); + if (ret) + return -EFAULT; + break; + case JTAG_SIOCSTATE: + if (copy_from_user(&tapstate, (const void __user *)arg, + sizeof(struct jtag_tap_state))) + return -EFAULT; + + if (tapstate.from > JTAG_STATE_CURRENT) + return -EINVAL; + + if (tapstate.endstate > JTAG_STATE_CURRENT) + return -EINVAL; + + if (tapstate.reset > JTAG_FORCE_RESET) + return -EINVAL; + if (tapstate.reset == JTAG_FORCE_RESET) + npcm7xx_jtag_set_tapstate(priv, JTAG_STATE_CURRENT, + jtagtlr); + npcm7xx_jtag_set_tapstate(priv, tapstate.from, + tapstate.endstate); + break; + case JTAG_GIOCSTATUS: + ret = put_user(priv->tapstate, (__u32 __user *)arg); + break; + case JTAG_IOCXFER: + if (copy_from_user(&xfer, argp, sizeof(struct jtag_xfer))) + return -EFAULT; + + if (xfer.length >= JTAG_MAX_XFER_DATA_LEN) + return -EINVAL; + + if (xfer.type > JTAG_SDR_XFER) + return -EINVAL; + + if (xfer.direction > JTAG_READ_WRITE_XFER) + return -EINVAL; + + if (xfer.from > JTAG_STATE_CURRENT) + return -EINVAL; + + if (xfer.endstate > JTAG_STATE_CURRENT) + return -EINVAL; + + data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE); + xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio), data_size); + if (IS_ERR(xfer_data)) + return -EFAULT; + ret = npcm7xx_jtag_xfer(priv, &xfer, xfer_data, data_size); + if (ret) { + kfree(xfer_data); + return -EIO; + } + ret = copy_to_user(u64_to_user_ptr(xfer.tdio), + (void *)xfer_data, data_size); + kfree(xfer_data); + if (ret) + return -EFAULT; + + if (copy_to_user((void __user *)arg, (void *)&xfer, + sizeof(struct jtag_xfer))) + return -EFAULT; + break; + case JTAG_SIOCMODE: + if (get_user(value, (__u32 __user *)arg)) + return -EFAULT; + if (value != MODE_GPIO && value != MODE_PSPI) + return -EINVAL; + priv->mode = value; + break; + case JTAG_RUNTEST: + ret = npcm7xx_jtag_runtest(priv, (unsigned int)arg); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int jtag_open(struct inode *inode, struct file *file) +{ + struct jtag_info *jtag; + + jtag = container_of(file->private_data, struct jtag_info, miscdev); + + spin_lock(&jtag_file_lock); + if (jtag->is_open) { + spin_unlock(&jtag_file_lock); + return -EBUSY; + } + + jtag->is_open = true; + file->private_data = jtag; + + spin_unlock(&jtag_file_lock); + + return 0; +} + +static int jtag_release(struct inode *inode, struct file *file) +{ + struct jtag_info *jtag = file->private_data; + + spin_lock(&jtag_file_lock); + jtag->is_open = false; + spin_unlock(&jtag_file_lock); + + return 0; +} + +const struct file_operations npcm_jtag_fops = { + .open = jtag_open, + .unlocked_ioctl = jtag_ioctl, + .release = jtag_release, +}; + +static int jtag_register_device(struct jtag_info *jtag) +{ + struct device *dev = jtag->dev; + int err; + int id; + + if (!dev) + return -ENODEV; + + id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL); + if (id < 0) + return id; + + jtag->id = id; + /* register miscdev */ + jtag->miscdev.parent = dev; + jtag->miscdev.fops = &npcm_jtag_fops; + jtag->miscdev.minor = MISC_DYNAMIC_MINOR; + jtag->miscdev.name = kasprintf(GFP_KERNEL, "jtag%d", id); + if (!jtag->miscdev.name) { + err = -ENOMEM; + goto err; + } + + err = misc_register(&jtag->miscdev); + if (err) { + dev_err(jtag->miscdev.parent, + "Unable to register device, err %d\n", err); + kfree(jtag->miscdev.name); + goto err; + } + + return 0; + +err: + ida_simple_remove(&jtag_ida, id); + return err; +} + +static int npcm7xx_jtag_init(struct device *dev, struct jtag_info *npcm7xx_jtag) +{ + struct pinctrl *pinctrl; + int i; + + pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pinctrl)) + return PTR_ERR(pinctrl); + + npcm7xx_jtag->pinctrl = pinctrl; + + /* jtag pins */ + npcm7xx_jtag->pins[pin_TCK] = gpiod_get(dev, "tck", GPIOD_OUT_LOW); + npcm7xx_jtag->pins[pin_TDI] = gpiod_get(dev, "tdi", GPIOD_OUT_HIGH); + npcm7xx_jtag->pins[pin_TDO] = gpiod_get(dev, "tdo", GPIOD_IN); + npcm7xx_jtag->pins[pin_TMS] = gpiod_get(dev, "tms", GPIOD_OUT_HIGH); + for (i = 0; i < pin_NUM; i++) { + if (IS_ERR(npcm7xx_jtag->pins[i])) + return PTR_ERR(npcm7xx_jtag->pins[i]); + } + + npcm7xx_jtag->freq = JTAG_PSPI_SPEED; + npcm7xx_jtag->tms_level = gpiod_get_value(npcm7xx_jtag->pins[pin_TMS]); + npcm7xx_jtag_set_tapstate(npcm7xx_jtag, JTAG_STATE_CURRENT, jtagtlr); + npcm7xx_jtag->mode = MODE_PSPI; + + return 0; +} + +static int npcm7xx_jtag_probe(struct spi_device *spi) +{ + struct jtag_info *npcm_jtag; + int ret; + + dev_info(&spi->dev, "%s", __func__); + + npcm_jtag = kzalloc(sizeof(struct jtag_info), GFP_KERNEL); + if (!npcm_jtag) + return -ENOMEM; + + npcm_jtag->dev = &spi->dev; + npcm_jtag->spi = spi; + spi->mode = SPI_MODE_0 | SPI_NO_CS; + + /* Initialize device*/ + ret = npcm7xx_jtag_init(&spi->dev, npcm_jtag); + if (ret) + goto err; + + /* Register a misc device */ + ret = jtag_register_device(npcm_jtag); + if (ret) { + dev_err(&spi->dev, "failed to create device\n"); + goto err; + } + spi_set_drvdata(spi, npcm_jtag); + + return 0; +err: + kfree(npcm_jtag); + return ret; +} + +static int npcm7xx_jtag_remove(struct spi_device *spi) +{ + struct jtag_info *jtag = spi_get_drvdata(spi); + int i; + + if (!jtag) + return 0; + + misc_deregister(&jtag->miscdev); + kfree(jtag->miscdev.name); + for (i = 0; i < pin_NUM; i++) { + gpiod_direction_input(jtag->pins[i]); + gpiod_put(jtag->pins[i]); + } + kfree(jtag); + ida_simple_remove(&jtag_ida, jtag->id); + + return 0; +} + +static const struct of_device_id npcm7xx_jtag_of_match[] = { + { .compatible = "nuvoton,npcm750-jtag-master", }, + {}, +}; +MODULE_DEVICE_TABLE(of, npcm7xx_jtag_of_match); + +static struct spi_driver npcm7xx_jtag_driver = { + .driver = { + .name = "npcm7xx_jtag", + .of_match_table = npcm7xx_jtag_of_match, + }, + .probe = npcm7xx_jtag_probe, + .remove = npcm7xx_jtag_remove, +}; + +module_spi_driver(npcm7xx_jtag_driver); + +MODULE_AUTHOR("Nuvoton Technology Corp."); +MODULE_DESCRIPTION("NPCM7xx JTAG Master Driver"); +MODULE_LICENSE("GPL"); +
Add NPCM7xx JTAG master driver, The NPCM7xx JTAG master usign GPIO lines and NPCM PSPI bus. Signed-off-by: Tomer Maimon <tmaimon77@gmail.com> --- drivers/misc/Kconfig | 6 + drivers/misc/Makefile | 1 + drivers/misc/npcm7xx-jtag-master.c | 840 +++++++++++++++++++++++++++++ 3 files changed, 847 insertions(+) create mode 100644 drivers/misc/npcm7xx-jtag-master.c