* [patch v4 2/3] drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master driver
From: Oleksandr Shamray @ 2017-08-18 9:49 UTC (permalink / raw)
To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, arnd-r2nGTMty4D4
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, openbmc-uLR06cmDAlY/bJ5BZ2RsiQ,
joel-U3u1mxZcP9KHXe+LvDLADg, jiri-rHqAuBHg3fBzbRFIqnYvSA,
tklauser-93Khv+1bN0NyDzI6CaY1VQ,
linux-serial-u79uwXL29TY76Z2rM5mHXA, mec-WqBc5aa1uDFeoWH0uzbU5w,
vadimp-45czdsxZ+A5DPfheJLI6IQ,
system-sw-low-level-VPRAkNaXOzVWk0Htik3J/w,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
openocd-devel-owner-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linux-api-u79uwXL29TY76Z2rM5mHXA, Oleksandr Shamray, Jiri Pirko
In-Reply-To: <1503049757-17243-1-git-send-email-oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Driver adds support of Aspeed 2500/2400 series SOC JTAG master controller.
Driver implements the following jtag ops:
- freq_get;
- freq_set;
- status_get;
- idle;
- xfer;
It has been tested on Mellanox system with BMC equipped with
Aspeed 2520 SoC for programming CPLD devices.
Signed-off-by: Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Jiri Pirko <jiri-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
v3->v4
Comments pointed by Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
- change transaction pointer tdio type to __u64
- change internal status type from enum to __u32
v2->v3
v1->v2
Comments pointed by Greg KH <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
- change license type from GPLv2/BSD to GPLv2
Comments pointed by Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
- Add clk_prepare_enable/clk_disable_unprepare in clock init/deinit
- Change .compatible to soc-specific compatible names
aspeed,aspeed4000-jtag/aspeed5000-jtag
- Added dt-bindings
Comments pointed by Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
- Reorder functions and removed the forward declarations
- Add static const qualifier to state machine states transitions
- Change .compatible to soc-specific compatible names
aspeed,aspeed4000-jtag/aspeed5000-jtag
- Add dt-bindings
Comments pointed by Randy Dunlap <rdunlap-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
- Change module name jtag-aspeed in description in Kconfig
Comments pointed by kbuild test robot <lkp-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
- Remove invalid include <asm/mach-types.h>
- add resource_size instead of calculation
---
drivers/jtag/Kconfig | 13 +
drivers/jtag/Makefile | 1 +
drivers/jtag/jtag-aspeed.c | 772 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 786 insertions(+), 0 deletions(-)
create mode 100644 drivers/jtag/jtag-aspeed.c
diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
index 0fad1a3..9f9bf94 100644
--- a/drivers/jtag/Kconfig
+++ b/drivers/jtag/Kconfig
@@ -14,3 +14,16 @@ menuconfig JTAG
To compile this driver as a module, choose M here: the module will
be called jtag.
+
+menuconfig JTAG_ASPEED
+ tristate "Aspeed SoC JTAG controller support"
+ depends on JTAG
+ ---help---
+ This provides a support for Aspeed JTAG device, equipped on
+ Aspeed SoC 24xx and 25xx families. Drivers allows programming
+ of hardware devices, connected to SoC through the JTAG interface.
+
+ If you want this support, you should say Y here.
+
+ To compile this driver as a module, choose M here: the module will
+ be called jtag-aspeed.
diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
index af37493..04a855e 100644
--- a/drivers/jtag/Makefile
+++ b/drivers/jtag/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_JTAG) += jtag.o
+obj-$(CONFIG_JTAG_ASPEED) += jtag-aspeed.o
diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c
new file mode 100644
index 0000000..252bd91
--- /dev/null
+++ b/drivers/jtag/jtag-aspeed.c
@@ -0,0 +1,772 @@
+/*
+ * drivers/jtag/jtag.c
+ *
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/jtag.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <uapi/linux/jtag.h>
+
+#define ASPEED_JTAG_DATA 0x00
+#define ASPEED_JTAG_INST 0x04
+#define ASPEED_JTAG_CTRL 0x08
+#define ASPEED_JTAG_ISR 0x0C
+#define ASPEED_JTAG_SW 0x10
+#define ASPEED_JTAG_TCK 0x14
+#define ASPEED_JTAG_EC 0x18
+
+#define ASPEED_JTAG_DATA_MSB 0x01
+#define ASPEED_JTAG_DATA_CHUNK_SIZE 0x20
+
+/* ASPEED_JTAG_CTRL: Engine Control */
+#define ASPEED_JTAG_CTL_ENG_EN BIT(31)
+#define ASPEED_JTAG_CTL_ENG_OUT_EN BIT(30)
+#define ASPEED_JTAG_CTL_FORCE_TMS BIT(29)
+#define ASPEED_JTAG_CTL_INST_LEN(x) ((x) << 20)
+#define ASPEED_JTAG_CTL_LASPEED_INST BIT(17)
+#define ASPEED_JTAG_CTL_INST_EN BIT(16)
+#define ASPEED_JTAG_CTL_DR_UPDATE BIT(10)
+#define ASPEED_JTAG_CTL_DATA_LEN(x) ((x) << 4)
+#define ASPEED_JTAG_CTL_LASPEED_DATA BIT(1)
+#define ASPEED_JTAG_CTL_DATA_EN BIT(0)
+
+/* ASPEED_JTAG_ISR : Interrupt status and enable */
+#define ASPEED_JTAG_ISR_INST_PAUSE BIT(19)
+#define ASPEED_JTAG_ISR_INST_COMPLETE BIT(18)
+#define ASPEED_JTAG_ISR_DATA_PAUSE BIT(17)
+#define ASPEED_JTAG_ISR_DATA_COMPLETE BIT(16)
+#define ASPEED_JTAG_ISR_INST_PAUSE_EN BIT(3)
+#define ASPEED_JTAG_ISR_INST_COMPLETE_EN BIT(2)
+#define ASPEED_JTAG_ISR_DATA_PAUSE_EN BIT(1)
+#define ASPEED_JTAG_ISR_DATA_COMPLETE_EN BIT(0)
+#define ASPEED_JTAG_ISR_INT_EN_MASK GENMASK(3, 0)
+#define ASPEED_JTAG_ISR_INT_MASK GENMASK(19, 16)
+
+/* ASPEED_JTAG_SW : Software Mode and Status */
+#define ASPEED_JTAG_SW_MODE_EN BIT(19)
+#define ASPEED_JTAG_SW_MODE_TCK BIT(18)
+#define ASPEED_JTAG_SW_MODE_TMS BIT(17)
+#define ASPEED_JTAG_SW_MODE_TDIO BIT(16)
+
+/* ASPEED_JTAG_TCK : TCK Control */
+#define ASPEED_JTAG_TCK_DIVISOR_MASK GENMASK(10, 0)
+#define ASPEED_JTAG_TCK_GET_DIV(x) ((x) & ASPEED_JTAG_TCK_DIVISOR_MASK)
+
+/* ASPEED_JTAG_EC : Controller set for go to IDLE */
+#define ASPEED_JTAG_EC_GO_IDLE BIT(0)
+
+#define ASPEED_JTAG_IOUT_LEN(len) (ASPEED_JTAG_CTL_ENG_EN |\
+ ASPEED_JTAG_CTL_ENG_OUT_EN |\
+ ASPEED_JTAG_CTL_INST_LEN(len))
+
+#define ASPEED_JTAG_DOUT_LEN(len) (ASPEED_JTAG_CTL_ENG_EN |\
+ ASPEED_JTAG_CTL_ENG_OUT_EN |\
+ ASPEED_JTAG_CTL_DATA_LEN(len))
+
+#define ASPEED_JTAG_TCK_WAIT 10
+#define ASPEED_JTAG_RESET_CNTR 10
+
+#define ASPEED_JTAG_NAME "jtag-aspeed"
+
+struct aspeed_jtag {
+ void __iomem *reg_base;
+ struct device *dev;
+ struct clk *pclk;
+ enum jtag_endstate status;
+ int irq;
+ u32 flag;
+ wait_queue_head_t jtag_wq;
+ bool is_open;
+};
+
+static char *end_status_str[] = {"idle", "ir pause", "drpause"};
+
+static u32 aspeed_jtag_read(struct aspeed_jtag *aspeed_jtag, u32 reg)
+{
+ return readl(aspeed_jtag->reg_base + reg);
+}
+
+static void
+aspeed_jtag_write(struct aspeed_jtag *aspeed_jtag, u32 val, u32 reg)
+{
+ writel(val, aspeed_jtag->reg_base + reg);
+}
+
+static int aspeed_jtag_freq_set(struct jtag *jtag, __u32 freq)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+ unsigned long apb_frq;
+ u32 tck_val;
+ u16 div;
+
+ apb_frq = clk_get_rate(aspeed_jtag->pclk);
+ div = (apb_frq % freq == 0) ? (apb_frq / freq) - 1 : (apb_frq / freq);
+ tck_val = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
+ aspeed_jtag_write(aspeed_jtag,
+ (tck_val & ASPEED_JTAG_TCK_DIVISOR_MASK) | div,
+ ASPEED_JTAG_TCK);
+ return 0;
+}
+
+static int aspeed_jtag_freq_get(struct jtag *jtag, __u32 *frq)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+ u32 pclk;
+ u32 tck;
+
+ pclk = clk_get_rate(aspeed_jtag->pclk);
+ tck = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
+ *frq = pclk / (ASPEED_JTAG_TCK_GET_DIV(tck) + 1);
+
+ return 0;
+}
+
+static void aspeed_jtag_sw_delay(struct aspeed_jtag *aspeed_jtag, int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++)
+ aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW);
+}
+
+static char aspeed_jtag_tck_cycle(struct aspeed_jtag *aspeed_jtag,
+ u8 tms, u8 tdi)
+{
+ char tdo = 0;
+
+ /* TCK = 0 */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ (tms * ASPEED_JTAG_SW_MODE_TMS) |
+ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+
+ aspeed_jtag_sw_delay(aspeed_jtag, ASPEED_JTAG_TCK_WAIT);
+
+ /* TCK = 1 */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TCK |
+ (tms * ASPEED_JTAG_SW_MODE_TMS) |
+ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+
+ if (aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW) &
+ ASPEED_JTAG_SW_MODE_TDIO)
+ tdo = 1;
+
+ aspeed_jtag_sw_delay(aspeed_jtag, ASPEED_JTAG_TCK_WAIT);
+
+ /* TCK = 0 */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ (tms * ASPEED_JTAG_SW_MODE_TMS) |
+ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+ return tdo;
+}
+
+static void aspeed_jtag_wait_instruction_pause(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_INST_PAUSE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_PAUSE;
+}
+
+static void
+aspeed_jtag_wait_instruction_complete(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_INST_COMPLETE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_COMPLETE;
+}
+
+static void
+aspeed_jtag_wait_data_pause_complete(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_DATA_PAUSE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_PAUSE;
+}
+
+static void aspeed_jtag_wait_data_complete(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_DATA_COMPLETE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_COMPLETE;
+}
+
+static void aspeed_jtag_sm_cycle(struct aspeed_jtag *aspeed_jtag, const u8 *tms,
+ int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ aspeed_jtag_tck_cycle(aspeed_jtag, tms[i], 0);
+}
+
+static void aspeed_jtag_run_test_idle_sw(struct aspeed_jtag *aspeed_jtag,
+ struct jtag_run_test_idle *runtest)
+{
+ static const char sm_pause_irpause[] = {1, 1, 1, 1, 0, 1, 0};
+ static const char sm_pause_drpause[] = {1, 1, 1, 0, 1, 0};
+ static const char sm_idle_irpause[] = {1, 1, 0, 1, 0};
+ static const char sm_idle_drpause[] = {1, 0, 1, 0};
+ static const char sm_pause_idle[] = {1, 1, 0};
+ int i;
+
+ /* SW mode from idle/pause-> to pause/idle */
+ if (runtest->reset) {
+ for (i = 0; i < ASPEED_JTAG_RESET_CNTR; i++)
+ aspeed_jtag_tck_cycle(aspeed_jtag, 1, 0);
+ }
+
+ switch (aspeed_jtag->status) {
+ case JTAG_STATE_IDLE:
+ switch (runtest->endstate) {
+ case JTAG_STATE_PAUSEIR:
+ /* ->DRSCan->IRSCan->IRCap->IRExit1->PauseIR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_idle_irpause,
+ sizeof(sm_idle_irpause));
+
+ aspeed_jtag->status = JTAG_STATE_PAUSEIR;
+ break;
+ case JTAG_STATE_PAUSEDR:
+ /* ->DRSCan->DRCap->DRExit1->PauseDR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_idle_drpause,
+ sizeof(sm_idle_drpause));
+
+ aspeed_jtag->status = JTAG_STATE_PAUSEDR;
+ break;
+ case JTAG_STATE_IDLE:
+ /* IDLE */
+ aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+ aspeed_jtag->status = JTAG_STATE_IDLE;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case JTAG_STATE_PAUSEIR:
+ /* Fall-through */
+ case JTAG_STATE_PAUSEDR:
+ /* From IR/DR Pause */
+ switch (runtest->endstate) {
+ case JTAG_STATE_PAUSEIR:
+ /*
+ * to Exit2 IR/DR->Updt IR/DR->DRSCan->IRSCan->IRCap->
+ * IRExit1->PauseIR
+ */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_irpause,
+ sizeof(sm_pause_irpause));
+
+ aspeed_jtag->status = JTAG_STATE_PAUSEIR;
+ break;
+ case JTAG_STATE_PAUSEDR:
+ /*
+ * to Exit2 IR/DR->Updt IR/DR->DRSCan->DRCap->
+ * DRExit1->PauseDR
+ */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_drpause,
+ sizeof(sm_pause_drpause));
+ aspeed_jtag->status = JTAG_STATE_PAUSEDR;
+ break;
+ case JTAG_STATE_IDLE:
+ /* to Exit2 IR/DR->Updt IR/DR->IDLE */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_idle,
+ sizeof(sm_pause_idle));
+ aspeed_jtag->status = JTAG_STATE_IDLE;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ dev_err(aspeed_jtag->dev, "aspeed_jtag_run_test_idle error\n");
+ break;
+ }
+
+ /* Stay on IDLE for at least TCK cycle */
+ for (i = 0; i < runtest->tck; i++)
+ aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+}
+
+/**
+ * aspeed_jtag_run_test_idle:
+ * JTAG reset: generates at least 9 TMS high and 1 TMS low to force
+ * devices into Run-Test/Idle State.
+ */
+static int aspeed_jtag_idle(struct jtag *jtag,
+ struct jtag_run_test_idle *runtest)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+ dev_dbg(aspeed_jtag->dev, "aspeed_jtag runtest, status:%d, mode:%s, state:%s, reset:%d, tck:%d\n",
+ aspeed_jtag->status, runtest->mode ? "SW" : "HW",
+ end_status_str[runtest->endstate], runtest->reset,
+ runtest->tck);
+
+ if (runtest->mode) {
+ aspeed_jtag_run_test_idle_sw(aspeed_jtag, runtest);
+ return 0;
+ }
+
+ /* Disable sw mode */
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
+ /* x TMS high + 1 TMS low */
+ if (runtest->reset)
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
+ ASPEED_JTAG_CTL_ENG_OUT_EN |
+ ASPEED_JTAG_CTL_FORCE_TMS, ASPEED_JTAG_CTRL);
+ else
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_EC_GO_IDLE,
+ ASPEED_JTAG_EC);
+
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+ aspeed_jtag->status = JTAG_STATE_IDLE;
+ return 0;
+}
+
+static void aspeed_jtag_xfer_sw(struct aspeed_jtag *aspeed_jtag,
+ struct jtag_xfer *xfer, unsigned long *data)
+{
+ unsigned long remain_xfer = xfer->length;
+ unsigned long shift_bits = 0;
+ unsigned long index = 0;
+ unsigned long tdi;
+ char tdo;
+
+ if (xfer->direction == JTAG_READ_XFER)
+ tdi = UINT_MAX;
+ else
+ tdi = data[index];
+
+ while (remain_xfer > 1) {
+ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 0,
+ tdi & ASPEED_JTAG_DATA_MSB);
+ data[index] |= tdo << (shift_bits %
+ ASPEED_JTAG_DATA_CHUNK_SIZE);
+
+ tdi >>= 1;
+ shift_bits++;
+ remain_xfer--;
+
+ if (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE == 0) {
+ dev_dbg(aspeed_jtag->dev, "R/W data[%lu]:%lx\n",
+ index, data[index]);
+
+ tdo = 0;
+ index++;
+
+ if (xfer->direction == JTAG_READ_XFER)
+ tdi = UINT_MAX;
+ else
+ tdi = data[index];
+ }
+ }
+
+ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 1, tdi & ASPEED_JTAG_DATA_MSB);
+ data[index] |= tdo << (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE);
+}
+
+static void aspeed_jtag_xfer_push_data(struct aspeed_jtag *aspeed_jtag,
+ enum jtag_xfer_type type, u32 bits_len)
+{
+ dev_dbg(aspeed_jtag->dev, "shift bits %d\n", bits_len);
+
+ if (type == JTAG_SIR_XFER) {
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_IOUT_LEN(bits_len),
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) |
+ ASPEED_JTAG_CTL_INST_EN, ASPEED_JTAG_CTRL);
+ } else {
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len),
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) |
+ ASPEED_JTAG_CTL_DATA_EN, ASPEED_JTAG_CTRL);
+ }
+}
+
+static void aspeed_jtag_xfer_push_data_last(struct aspeed_jtag *aspeed_jtag,
+ enum jtag_xfer_type type,
+ u32 shift_bits,
+ enum jtag_endstate endstate)
+{
+ if (endstate != JTAG_STATE_IDLE) {
+ if (type == JTAG_SIR_XFER) {
+ dev_dbg(aspeed_jtag->dev, "IR Keep Pause\n");
+
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits),
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_INST_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_instruction_pause(aspeed_jtag);
+ } else {
+ dev_dbg(aspeed_jtag->dev, "DR Keep Pause\n");
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_DR_UPDATE,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_DR_UPDATE |
+ ASPEED_JTAG_CTL_DATA_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_data_pause_complete(aspeed_jtag);
+ }
+ } else {
+ if (type == JTAG_SIR_XFER) {
+ dev_dbg(aspeed_jtag->dev, "IR go IDLE\n");
+
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_INST,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_INST |
+ ASPEED_JTAG_CTL_INST_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_instruction_complete(aspeed_jtag);
+ } else {
+ dev_dbg(aspeed_jtag->dev, "DR go IDLE\n");
+
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_DATA,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_DATA |
+ ASPEED_JTAG_CTL_DATA_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_data_complete(aspeed_jtag);
+ }
+ }
+}
+
+static void aspeed_jtag_xfer_hw(struct aspeed_jtag *aspeed_jtag,
+ struct jtag_xfer *xfer, unsigned long *data)
+{
+ unsigned long remain_xfer = xfer->length;
+ unsigned long index = 0;
+ char shift_bits;
+ u32 data_reg;
+
+ data_reg = xfer->type == JTAG_SIR_XFER ?
+ ASPEED_JTAG_INST : ASPEED_JTAG_DATA;
+ while (remain_xfer) {
+ if (xfer->direction == JTAG_WRITE_XFER) {
+ dev_dbg(aspeed_jtag->dev, "W dr->dr_data[%lu]:%lx\n",
+ index, data[index]);
+
+ aspeed_jtag_write(aspeed_jtag, data[index], data_reg);
+ } else {
+ aspeed_jtag_write(aspeed_jtag, 0, data_reg);
+ }
+
+ if (remain_xfer > ASPEED_JTAG_DATA_CHUNK_SIZE) {
+ shift_bits = ASPEED_JTAG_DATA_CHUNK_SIZE;
+
+ /*
+ * Read bytes were not equals to column length
+ * and go to Pause-DR
+ */
+ aspeed_jtag_xfer_push_data(aspeed_jtag, xfer->type,
+ shift_bits);
+ } else {
+ /*
+ * Read bytes equals to column length =>
+ * Update-DR
+ */
+ shift_bits = remain_xfer;
+ aspeed_jtag_xfer_push_data_last(aspeed_jtag, xfer->type,
+ shift_bits,
+ xfer->endstate);
+ }
+
+ if (xfer->direction == JTAG_READ_XFER) {
+ if (shift_bits < ASPEED_JTAG_DATA_CHUNK_SIZE) {
+ data[index] = aspeed_jtag_read(aspeed_jtag,
+ data_reg);
+
+ data[index] >>= ASPEED_JTAG_DATA_CHUNK_SIZE -
+ shift_bits;
+ } else {
+ data[index] = aspeed_jtag_read(aspeed_jtag,
+ data_reg);
+ }
+ dev_dbg(aspeed_jtag->dev, "R dr->dr_data[%lu]:%lx\n",
+ index, data[index]);
+ }
+
+ remain_xfer = remain_xfer - shift_bits;
+ index++;
+ dev_dbg(aspeed_jtag->dev, "remain_xfer %lu\n", remain_xfer);
+ }
+}
+
+static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer)
+{
+ static const char sm_update_shiftir[] = {1, 1, 0, 0};
+ static const char sm_update_shiftdr[] = {1, 0, 0};
+ static const char sm_pause_idle[] = {1, 1, 0};
+ static const char sm_pause_update[] = {1, 1};
+ unsigned long *data = jtag_u64_to_ptr(xfer->tdio);
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+ unsigned long remain_xfer = xfer->length;
+ unsigned long offset;
+ char dbg_str[256];
+ int pos = 0;
+ int i;
+
+ for (offset = 0, i = 0; offset < xfer->length;
+ offset += ASPEED_JTAG_DATA_CHUNK_SIZE, i++) {
+ pos += snprintf(&dbg_str[pos], sizeof(dbg_str) - pos,
+ "0x%08lx ", data[i]);
+ }
+
+ dev_dbg(aspeed_jtag->dev, "aspeed_jtag %s %s xfer, mode:%s, END:%d, len:%lu, TDI[%s]\n",
+ xfer->type == JTAG_SIR_XFER ? "SIR" : "SDR",
+ xfer->direction == JTAG_READ_XFER ? "READ" : "WRITE",
+ xfer->mode ? "SW" : "HW",
+ xfer->endstate, remain_xfer, dbg_str);
+
+ if (xfer->mode == JTAG_XFER_SW_MODE) {
+ /* SW mode */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+ if (aspeed_jtag->status != JTAG_STATE_IDLE) {
+ /*IR/DR Pause->Exit2 IR / DR->Update IR /DR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_update,
+ sizeof(sm_pause_update));
+ }
+
+ if (xfer->type == JTAG_SIR_XFER)
+ /* ->IRSCan->CapIR->ShiftIR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_update_shiftir,
+ sizeof(sm_update_shiftir));
+ else
+ /* ->DRScan->DRCap->DRShift */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_update_shiftdr,
+ sizeof(sm_update_shiftdr));
+
+ aspeed_jtag_xfer_sw(aspeed_jtag, xfer, data);
+
+ /* DIPause/DRPause */
+ aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+
+ if (xfer->endstate == JTAG_STATE_IDLE) {
+ /* ->DRExit2->DRUpdate->IDLE */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_idle,
+ sizeof(sm_pause_idle));
+ }
+ } else {
+ /* hw mode */
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
+ aspeed_jtag_xfer_hw(aspeed_jtag, xfer, data);
+ }
+
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+ aspeed_jtag->status = xfer->endstate;
+ return 0;
+}
+
+static int aspeed_jtag_status_get(struct jtag *jtag, __u32 *status)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+ *status = aspeed_jtag->status;
+ return 0;
+}
+
+static irqreturn_t aspeed_jtag_interrupt(s32 this_irq, void *dev_id)
+{
+ struct aspeed_jtag *aspeed_jtag = dev_id;
+ irqreturn_t ret;
+ u32 status;
+
+ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
+ dev_dbg(aspeed_jtag->dev, "status %x\n", status);
+
+ if (status & ASPEED_JTAG_ISR_INT_MASK) {
+ aspeed_jtag_write(aspeed_jtag,
+ (status & ASPEED_JTAG_ISR_INT_MASK)
+ | (status & ASPEED_JTAG_ISR_INT_EN_MASK),
+ ASPEED_JTAG_ISR);
+ aspeed_jtag->flag |= status & ASPEED_JTAG_ISR_INT_MASK;
+ }
+
+ if (aspeed_jtag->flag) {
+ wake_up_interruptible(&aspeed_jtag->jtag_wq);
+ ret = IRQ_HANDLED;
+ } else {
+ dev_err(aspeed_jtag->dev, "aspeed_jtag irq status:%x\n",
+ status);
+ ret = IRQ_NONE;
+ }
+ return ret;
+}
+
+int aspeed_jtag_init(struct platform_device *pdev,
+ struct aspeed_jtag *aspeed_jtag)
+{
+ struct resource *res;
+ int err;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ aspeed_jtag->reg_base = devm_ioremap_resource(aspeed_jtag->dev, res);
+ if (IS_ERR(aspeed_jtag->reg_base)) {
+ err = -ENOMEM;
+ goto out_region;
+ }
+
+ aspeed_jtag->pclk = devm_clk_get(aspeed_jtag->dev, NULL);
+ if (IS_ERR(aspeed_jtag->pclk)) {
+ dev_err(aspeed_jtag->dev, "devm_clk_get failed\n");
+ return PTR_ERR(aspeed_jtag->pclk);
+ }
+
+ clk_prepare_enable(aspeed_jtag->pclk);
+
+ aspeed_jtag->irq = platform_get_irq(pdev, 0);
+ if (aspeed_jtag->irq < 0) {
+ dev_err(aspeed_jtag->dev, "no irq specified\n");
+ err = -ENOENT;
+ goto out_region;
+ }
+
+ /* Enable clock */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
+ ASPEED_JTAG_CTL_ENG_OUT_EN, ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+ err = devm_request_irq(aspeed_jtag->dev, aspeed_jtag->irq,
+ aspeed_jtag_interrupt, 0,
+ "aspeed-jtag", aspeed_jtag);
+ if (err) {
+ dev_err(aspeed_jtag->dev, "aspeed_jtag unable to get IRQ");
+ goto out_region;
+ }
+ dev_dbg(&pdev->dev, "aspeed_jtag:IRQ %d.\n", aspeed_jtag->irq);
+
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_PAUSE |
+ ASPEED_JTAG_ISR_INST_COMPLETE |
+ ASPEED_JTAG_ISR_DATA_PAUSE |
+ ASPEED_JTAG_ISR_DATA_COMPLETE |
+ ASPEED_JTAG_ISR_INST_PAUSE_EN |
+ ASPEED_JTAG_ISR_INST_COMPLETE_EN |
+ ASPEED_JTAG_ISR_DATA_PAUSE_EN |
+ ASPEED_JTAG_ISR_DATA_COMPLETE_EN,
+ ASPEED_JTAG_ISR);
+
+ aspeed_jtag->flag = 0;
+ init_waitqueue_head(&aspeed_jtag->jtag_wq);
+ return 0;
+
+out_region:
+ release_mem_region(res->start, resource_size(res));
+ return err;
+}
+
+int aspeed_jtag_deinit(struct platform_device *pdev,
+ struct aspeed_jtag *aspeed_jtag)
+{
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_ISR);
+ devm_free_irq(aspeed_jtag->dev, aspeed_jtag->irq, aspeed_jtag);
+ /* Disabe clock */
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL);
+ clk_disable_unprepare(aspeed_jtag->pclk);
+ return 0;
+}
+
+static const struct jtag_ops aspeed_jtag_ops = {
+ .freq_get = aspeed_jtag_freq_get,
+ .freq_set = aspeed_jtag_freq_set,
+ .status_get = aspeed_jtag_status_get,
+ .idle = aspeed_jtag_idle,
+ .xfer = aspeed_jtag_xfer
+};
+
+static int aspeed_jtag_probe(struct platform_device *pdev)
+{
+ struct aspeed_jtag *aspeed_jtag;
+ struct jtag *jtag;
+ int err;
+
+ if (!of_device_is_compatible(pdev->dev.of_node, "aspeed,aspeed-jtag"))
+ return -ENOMEM;
+
+ jtag = jtag_alloc(sizeof(*aspeed_jtag), &aspeed_jtag_ops);
+ if (!jtag)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, jtag);
+ aspeed_jtag = jtag_priv(jtag);
+ aspeed_jtag->dev = &pdev->dev;
+
+ /* Initialize device*/
+ err = aspeed_jtag_init(pdev, aspeed_jtag);
+ if (err)
+ goto err_jtag_init;
+
+ /* Initialize JTAG core structure*/
+ err = jtag_register(jtag);
+ if (err)
+ goto err_jtag_register;
+
+ return 0;
+
+err_jtag_register:
+ aspeed_jtag_deinit(pdev, aspeed_jtag);
+err_jtag_init:
+ jtag_free(jtag);
+ return err;
+}
+
+static int aspeed_jtag_remove(struct platform_device *pdev)
+{
+ struct jtag *jtag;
+
+ jtag = platform_get_drvdata(pdev);
+ aspeed_jtag_deinit(pdev, jtag_priv(jtag));
+ jtag_unregister(jtag);
+ jtag_free(jtag);
+ return 0;
+}
+
+static const struct of_device_id aspeed_jtag_of_match[] = {
+ { .compatible = "aspeed,aspeed2400-jtag", },
+ { .compatible = "aspeed,aspeed2500-jtag", },
+ {}
+};
+
+static struct platform_driver aspeed_jtag_driver = {
+ .probe = aspeed_jtag_probe,
+ .remove = aspeed_jtag_remove,
+ .driver = {
+ .name = ASPEED_JTAG_NAME,
+ .of_match_table = aspeed_jtag_of_match,
+ },
+};
+module_platform_driver(aspeed_jtag_driver);
+
+MODULE_AUTHOR("Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>");
+MODULE_DESCRIPTION("ASPEED JTAG driver");
+MODULE_LICENSE("GPL v2");
--
1.7.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [patch v4 1/3] drivers: jtag: Add JTAG core driver
From: Oleksandr Shamray @ 2017-08-18 9:49 UTC (permalink / raw)
To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, arnd-r2nGTMty4D4
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, openbmc-uLR06cmDAlY/bJ5BZ2RsiQ,
joel-U3u1mxZcP9KHXe+LvDLADg, jiri-rHqAuBHg3fBzbRFIqnYvSA,
tklauser-93Khv+1bN0NyDzI6CaY1VQ,
linux-serial-u79uwXL29TY76Z2rM5mHXA, mec-WqBc5aa1uDFeoWH0uzbU5w,
vadimp-45czdsxZ+A5DPfheJLI6IQ,
system-sw-low-level-VPRAkNaXOzVWk0Htik3J/w,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
openocd-devel-owner-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linux-api-u79uwXL29TY76Z2rM5mHXA, Oleksandr Shamray, Jiri Pirko
In-Reply-To: <1503049757-17243-1-git-send-email-oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Initial patch for JTAG friver
JTAG class driver provide infrastructure to support hardware/software
JTAG platform drivers. It provide user layer API interface for flashing
and debugging external devices which equipped with JTAG interface
using standard transactions.
Driver exposes set of IOCTL to user space for:
- XFER:
- SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
- SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
number of clocks).
- SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency.
Driver core provides set of internal APIs for allocation and
registration:
- jtag_register;
- jtag_unregister;
- jtag_alloc;
- jtag_free;
Platform driver on registration with jtag-core creates the next
entry in dev folder:
/dev/jtagX
Signed-off-by: Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Jiri Pirko <jiri-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
v3->v4
Comments pointed by Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
- change transaction pointer tdio type to __u64
- change internal status type from enum to __u32
- reorder jtag_xfer members to aviod the implied padding
- add __packed attribute to jtag_xfer and jtag_run_test_idle
v2->v3
Notifications from kbuild test robot <lkp-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
- Change include path to <linux/types.h> in jtag.h
v1->v2
Comments pointed by Greg KH <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
- Change license type from GPLv2/BSD to GPLv2
- Change type of variables which crossed user/kernel to __type
- Remove "default n" from Kconfig
Comments pointed by Andrew Lunn <andrew-g2DYL2Zd6BY@public.gmane.org>
- Change list_add_tail in jtag_unregister to list_del
Comments pointed by Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
- Add SPDX-License-Identifier instead of license text
Comments pointed by Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
- Change __copy_to_user to memdup_user
- Change __put_user to put_user
- Change type of variables to __type for compatible 32 and 64-bit systems
- Add check for maximum xfer data size
- Change lookup data mechanism to get jtag data from inode
- Add .compat_ioctl to file ops
- Add mem alignment for jtag priv data
Comments pointed by Tobias Klauser <tklauser-93Khv+1bN0NyDzI6CaY1VQ@public.gmane.org>
- Change function names to avoid match with variable types
- Fix description for jtag_ru_test_idle in uapi jtag.h
- Fix misprints IDEL/IDLE, trough/through
---
Documentation/ioctl/ioctl-number.txt | 2 +
MAINTAINERS | 8 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/jtag/Kconfig | 16 ++
drivers/jtag/Makefile | 1 +
drivers/jtag/jtag.c | 311 ++++++++++++++++++++++++++++++++++
include/linux/jtag.h | 48 ++++++
include/uapi/linux/jtag.h | 113 ++++++++++++
9 files changed, 502 insertions(+), 0 deletions(-)
create mode 100644 drivers/jtag/Kconfig
create mode 100644 drivers/jtag/Makefile
create mode 100644 drivers/jtag/jtag.c
create mode 100644 include/linux/jtag.h
create mode 100644 include/uapi/linux/jtag.h
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 3e3fdae..1af2508 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -321,6 +321,8 @@ Code Seq#(hex) Include File Comments
0xB0 all RATIO devices in development:
<mailto:vgo-/IYFIZglx74@public.gmane.org>
0xB1 00-1F PPPoX <mailto:mostrows-TTukF6hB3AoKZpuMuFhwt/d9D2ou9A/h@public.gmane.org>
+0xB2 00-0f linux/jtag.h JTAG driver
+ <mailto:oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
0xB3 00 linux/mmc/ioctl.h
0xB4 00-0F linux/gpio.h <mailto:linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
0xB5 00-0F uapi/linux/rpmsg.h <mailto:linux-remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 205d397..141aeaf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7292,6 +7292,14 @@ L: linux-serial-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
S: Maintained
F: drivers/tty/serial/jsm/
+JTAG SUBSYSTEM
+M: Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
+M: Vadim Pasternak <vadimp-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
+S: Maintained
+F: include/linux/jtag.h
+F: include/uapi/linux/jtag.h
+F: drivers/jtag/
+
K10TEMP HARDWARE MONITORING DRIVER
M: Clemens Ladisch <clemens-P6GI/4k7KOmELgA04lAiVw@public.gmane.org>
L: linux-hwmon-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 505c676..2214678 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -208,4 +208,6 @@ source "drivers/tee/Kconfig"
source "drivers/mux/Kconfig"
+source "drivers/jtag/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index dfdcda0..6a2059b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -182,3 +182,4 @@ obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_FSI) += fsi/
obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_MULTIPLEXER) += mux/
+obj-$(CONFIG_JTAG) += jtag/
diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
new file mode 100644
index 0000000..0fad1a3
--- /dev/null
+++ b/drivers/jtag/Kconfig
@@ -0,0 +1,16 @@
+menuconfig JTAG
+ tristate "JTAG support"
+ ---help---
+ This provides basic core functionality support for jtag class devices
+ Hardware equipped with JTAG microcontroller which can be built
+ on top of this drivers. Driver exposes the set of IOCTL to the
+ user space for:
+ SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
+ SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
+ RUNTEST (Forces IEEE 1149.1 bus to a run state for specified
+ number of clocks).
+
+ If you want this support, you should say Y here.
+
+ To compile this driver as a module, choose M here: the module will
+ be called jtag.
diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
new file mode 100644
index 0000000..af37493
--- /dev/null
+++ b/drivers/jtag/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_JTAG) += jtag.o
diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c
new file mode 100644
index 0000000..97b351a
--- /dev/null
+++ b/drivers/jtag/jtag.c
@@ -0,0 +1,311 @@
+/*
+ * drivers/jtag/jtag.c
+ *
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/jtag.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+#include <uapi/linux/jtag.h>
+
+struct jtag {
+ struct list_head list;
+ struct device *dev;
+ struct cdev cdev;
+ int id;
+ spinlock_t lock;
+ int open;
+ const struct jtag_ops *ops;
+ unsigned long priv[0] __aligned(ARCH_DMA_MINALIGN);
+};
+
+static dev_t jtag_devt;
+static LIST_HEAD(jtag_list);
+static DEFINE_MUTEX(jtag_mutex);
+static DEFINE_IDA(jtag_ida);
+
+void *jtag_priv(struct jtag *jtag)
+{
+ return jtag->priv;
+}
+EXPORT_SYMBOL_GPL(jtag_priv);
+
+static __u64 jtag_copy_from_user(__u64 udata, unsigned long bit_size)
+{
+ unsigned long size;
+ void *kdata;
+
+ size = DIV_ROUND_UP(bit_size, BITS_PER_BYTE);
+ kdata = memdup_user(u64_to_user_ptr(udata), size);
+
+ return (__u64)(uintptr_t)kdata;
+}
+
+static unsigned long jtag_copy_to_user(__u64 udata, __u64 kdata,
+ unsigned long bit_size)
+{
+ unsigned long size;
+
+ size = DIV_ROUND_UP(bit_size, BITS_PER_BYTE);
+
+ return copy_to_user(u64_to_user_ptr(udata), jtag_u64_to_ptr(kdata),
+ size);
+}
+
+static struct class jtag_class = {
+ .name = "jtag",
+ .owner = THIS_MODULE,
+};
+
+static int jtag_run_test_idle_op(struct jtag *jtag,
+ struct jtag_run_test_idle *idle)
+{
+ if (jtag->ops->idle)
+ return jtag->ops->idle(jtag, idle);
+ else
+ return -EOPNOTSUPP;
+}
+
+static int jtag_xfer_op(struct jtag *jtag, struct jtag_xfer *xfer)
+{
+ if (jtag->ops->xfer)
+ return jtag->ops->xfer(jtag, xfer);
+ else
+ return -EOPNOTSUPP;
+}
+
+static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct jtag *jtag = file->private_data;
+ __u32 *uarg = (__u32 __user *)arg;
+ void *varg = (void __user *)arg;
+ struct jtag_run_test_idle idle;
+ struct jtag_xfer xfer;
+ __u64 tdio_user;
+ __u32 value;
+ int err;
+
+ switch (cmd) {
+ case JTAG_GIOCFREQ:
+ if (jtag->ops->freq_get)
+ err = jtag->ops->freq_get(jtag, &value);
+ else
+ err = -EOPNOTSUPP;
+ if (err)
+ break;
+
+ err = put_user(value, uarg);
+ break;
+
+ case JTAG_SIOCFREQ:
+ err = __get_user(value, uarg);
+
+ if (value == 0)
+ err = -EINVAL;
+ if (err)
+ break;
+
+ if (jtag->ops->freq_set)
+ err = jtag->ops->freq_set(jtag, value);
+ else
+ err = -EOPNOTSUPP;
+ break;
+
+ case JTAG_IOCRUNTEST:
+ if (copy_from_user(&idle, varg,
+ sizeof(struct jtag_run_test_idle)))
+ return -ENOMEM;
+ err = jtag_run_test_idle_op(jtag, &idle);
+ break;
+
+ case JTAG_IOCXFER:
+ if (copy_from_user(&xfer, varg, sizeof(struct jtag_xfer)))
+ return -EFAULT;
+
+ if (xfer.length >= JTAG_MAX_XFER_DATA_LEN)
+ return -EFAULT;
+
+ tdio_user = xfer.tdio;
+ xfer.tdio = jtag_copy_from_user(xfer.tdio, xfer.length);
+ if (!xfer.tdio)
+ return -ENOMEM;
+
+ err = jtag_xfer_op(jtag, &xfer);
+ if (jtag_copy_to_user(tdio_user, xfer.tdio, xfer.length)) {
+ kfree(jtag_u64_to_ptr(xfer.tdio));
+ return -EFAULT;
+ }
+
+ kfree(jtag_u64_to_ptr(xfer.tdio));
+ xfer.tdio = tdio_user;
+ if (copy_to_user(varg, &xfer, sizeof(struct jtag_xfer)))
+ return -EFAULT;
+ break;
+
+ case JTAG_GIOCSTATUS:
+ if (jtag->ops->status_get)
+ err = jtag->ops->status_get(jtag, &value);
+ else
+ err = -EOPNOTSUPP;
+ if (err)
+ break;
+
+ err = put_user(value, uarg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return err;
+}
+
+#ifdef CONFIG_COMPAT
+static long jtag_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return jtag_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static int jtag_open(struct inode *inode, struct file *file)
+{
+ struct jtag *jtag = container_of(inode->i_cdev, struct jtag, cdev);
+
+ spin_lock(&jtag->lock);
+
+ if (jtag->open) {
+ dev_info(NULL, "jtag already opened\n");
+ spin_unlock(&jtag->lock);
+ return -EBUSY;
+ }
+
+ jtag->open++;
+ file->private_data = jtag;
+ spin_unlock(&jtag->lock);
+ return 0;
+}
+
+static int jtag_release(struct inode *inode, struct file *file)
+{
+ struct jtag *jtag = file->private_data;
+
+ spin_lock(&jtag->lock);
+ jtag->open--;
+ spin_unlock(&jtag->lock);
+ return 0;
+}
+
+static const struct file_operations jtag_fops = {
+ .owner = THIS_MODULE,
+ .open = jtag_open,
+ .release = jtag_release,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = jtag_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = jtag_ioctl_compat,
+#endif
+};
+
+struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops)
+{
+ struct jtag *jtag;
+
+ jtag = kzalloc(sizeof(*jtag) + round_up(priv_size, ARCH_DMA_MINALIGN),
+ GFP_KERNEL);
+ if (!jtag)
+ return NULL;
+
+ jtag->ops = ops;
+ return jtag;
+}
+EXPORT_SYMBOL_GPL(jtag_alloc);
+
+void jtag_free(struct jtag *jtag)
+{
+ kfree(jtag);
+}
+EXPORT_SYMBOL_GPL(jtag_free);
+
+int jtag_register(struct jtag *jtag)
+{
+ int id;
+ int err;
+
+ id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL);
+ if (id < 0)
+ return id;
+
+ jtag->id = id;
+ cdev_init(&jtag->cdev, &jtag_fops);
+ jtag->cdev.owner = THIS_MODULE;
+ err = cdev_add(&jtag->cdev, MKDEV(MAJOR(jtag_devt), jtag->id), 1);
+ if (err)
+ goto err_cdev;
+
+ /* Register this jtag device with the driver core */
+ jtag->dev = device_create(&jtag_class, NULL, MKDEV(MAJOR(jtag_devt),
+ jtag->id),
+ NULL, "jtag%d", jtag->id);
+ if (!jtag->dev)
+ goto err_device_create;
+
+ jtag->open = 0;
+ dev_set_drvdata(jtag->dev, jtag);
+ spin_lock_init(&jtag->lock);
+ mutex_lock(&jtag_mutex);
+ list_add_tail(&jtag->list, &jtag_list);
+ mutex_unlock(&jtag_mutex);
+ return err;
+
+err_device_create:
+ cdev_del(&jtag->cdev);
+err_cdev:
+ ida_simple_remove(&jtag_ida, id);
+ return err;
+}
+EXPORT_SYMBOL_GPL(jtag_register);
+
+void jtag_unregister(struct jtag *jtag)
+{
+ struct device *dev = jtag->dev;
+
+ mutex_lock(&jtag_mutex);
+ list_del(&jtag->list);
+ mutex_unlock(&jtag_mutex);
+ cdev_del(&jtag->cdev);
+ device_unregister(dev);
+ ida_simple_remove(&jtag_ida, jtag->id);
+}
+EXPORT_SYMBOL_GPL(jtag_unregister);
+
+static int __init jtag_init(void)
+{
+ int err;
+
+ err = alloc_chrdev_region(&jtag_devt, 0, 1, "jtag");
+ if (err)
+ return err;
+ return class_register(&jtag_class);
+}
+
+static void __exit jtag_exit(void)
+{
+ class_unregister(&jtag_class);
+}
+
+module_init(jtag_init);
+module_exit(jtag_exit);
+
+MODULE_AUTHOR("Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>");
+MODULE_DESCRIPTION("Generic jtag support");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/jtag.h b/include/linux/jtag.h
new file mode 100644
index 0000000..f48ae9d
--- /dev/null
+++ b/include/linux/jtag.h
@@ -0,0 +1,48 @@
+/*
+ * drivers/jtag/jtag.c
+ *
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef __JTAG_H
+#define __JTAG_H
+
+#include <uapi/linux/jtag.h>
+
+#ifndef ARCH_DMA_MINALIGN
+#define ARCH_DMA_MINALIGN 1
+#endif
+
+#define jtag_u64_to_ptr(arg) ((void *)(uintptr_t)arg)
+
+#define JTAG_MAX_XFER_DATA_LEN 65535
+
+struct jtag;
+/**
+ * struct jtag_ops - callbacks for jtag control functions:
+ *
+ * @freq_get: get frequency function. Filled by device driver
+ * @freq_set: set frequency function. Filled by device driver
+ * @status_get: set status function. Filled by device driver
+ * @idle: set JTAG to idle state function. Filled by device driver
+ * @xfer: send JTAG xfer function. Filled by device driver
+ */
+struct jtag_ops {
+ int (*freq_get)(struct jtag *jtag, __u32 *freq);
+ int (*freq_set)(struct jtag *jtag, __u32 freq);
+ int (*status_get)(struct jtag *jtag, __u32 *state);
+ int (*idle)(struct jtag *jtag, struct jtag_run_test_idle *idle);
+ int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer);
+};
+
+void *jtag_priv(struct jtag *jtag);
+int jtag_register(struct jtag *jtag);
+void jtag_unregister(struct jtag *jtag);
+struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops);
+void jtag_free(struct jtag *jtag);
+
+#endif /* __JTAG_H */
diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h
new file mode 100644
index 0000000..78309c3
--- /dev/null
+++ b/include/uapi/linux/jtag.h
@@ -0,0 +1,113 @@
+/*
+ * JTAG class driver
+ *
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
+ *
+ * Released under the GPLv2/BSD.
+ * SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+ */
+
+#ifndef __UAPI_LINUX_JTAG_H
+#define __UAPI_LINUX_JTAG_H
+
+#include <asm/types.h>
+
+/**
+ * enum jtag_xfer_mode:
+ *
+ * @JTAG_XFER_HW_MODE: hardware mode transfer
+ * @JTAG_XFER_SW_MODE: software mode transfer
+ */
+enum jtag_xfer_mode {
+ JTAG_XFER_HW_MODE,
+ JTAG_XFER_SW_MODE,
+};
+
+/**
+ * enum jtag_endstate:
+ *
+ * @JTAG_STATE_IDLE: JTAG state machine IDLE state
+ * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state
+ * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state
+ */
+enum jtag_endstate {
+ JTAG_STATE_IDLE,
+ JTAG_STATE_PAUSEIR,
+ JTAG_STATE_PAUSEDR,
+};
+
+/**
+ * enum jtag_xfer_type:
+ *
+ * @JTAG_SIR_XFER: SIR transfer
+ * @JTAG_SDR_XFER: SDR transfer
+ */
+enum jtag_xfer_type {
+ JTAG_SIR_XFER,
+ JTAG_SDR_XFER,
+};
+
+/**
+ * enum jtag_xfer_direction:
+ *
+ * @JTAG_READ_XFER: read transfer
+ * @JTAG_WRITE_XFER: write transfer
+ */
+enum jtag_xfer_direction {
+ JTAG_READ_XFER,
+ JTAG_WRITE_XFER,
+};
+
+/**
+ * struct jtag_run_test_idle - forces JTAG state machine to
+ * RUN_TEST/IDLE state
+ *
+ * @mode: access mode
+ * @reset: 0 - run IDLE/PAUSE from current state
+ * 1 - go through TEST_LOGIC/RESET state before IDLE/PAUSE
+ * @end: completion flag
+ * @tck: clock counter
+ *
+ * Structure represents interface to JTAG device for jtag idle
+ * execution.
+ */
+struct jtag_run_test_idle {
+ __u8 mode;
+ __u8 reset;
+ __u8 endstate;
+ __u8 tck;
+};
+
+/**
+ * struct jtag_xfer - jtag xfer:
+ *
+ * @mode: access mode
+ * @type: transfer type
+ * @direction: xfer direction
+ * @length: xfer bits len
+ * @tdio : xfer data array
+ * @endir: xfer end state
+ *
+ * Structure represents interface to Aspeed JTAG device for jtag sdr xfer
+ * execution.
+ */
+struct jtag_xfer {
+ __u8 mode;
+ __u8 type;
+ __u8 direction;
+ __u8 endstate;
+ __u32 length;
+ __u64 tdio;
+};
+
+#define __JTAG_IOCTL_MAGIC 0xb2
+
+#define JTAG_IOCRUNTEST _IOW(__JTAG_IOCTL_MAGIC, 0,\
+ struct jtag_run_test_idle)
+#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 jtag_endstate)
+
+#endif /* __UAPI_LINUX_JTAG_H */
--
1.7.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [patch v4 0/3] JTAG driver introduction
From: Oleksandr Shamray @ 2017-08-18 9:49 UTC (permalink / raw)
To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, arnd-r2nGTMty4D4
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, openbmc-uLR06cmDAlY/bJ5BZ2RsiQ,
joel-U3u1mxZcP9KHXe+LvDLADg, jiri-rHqAuBHg3fBzbRFIqnYvSA,
tklauser-93Khv+1bN0NyDzI6CaY1VQ,
linux-serial-u79uwXL29TY76Z2rM5mHXA, mec-WqBc5aa1uDFeoWH0uzbU5w,
vadimp-45czdsxZ+A5DPfheJLI6IQ,
system-sw-low-level-VPRAkNaXOzVWk0Htik3J/w,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
openocd-devel-owner-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linux-api-u79uwXL29TY76Z2rM5mHXA, Oleksandr Shamray
When a need raise up to use JTAG interface for system's devices
programming or CPU debugging, usually the user layer
application implements jtag protocol by bit-bang or using a
proprietary connection to vendor hardware.
This method can be slow and not generic.
We propose to implement general JTAG interface and infrastructure
to communicate with user layer application. In such way, we can
have the standard JTAG interface core part and separation from
specific HW implementation.
This allow new capability to debug the CPU or program system's
device via BMC without additional devices nor cost.
This patch purpose is to add JTAG master core infrastructure by
defining new JTAG class and provide generic JTAG interface
to allow hardware specific drivers to connect this interface.
This will enable all JTAG drivers to use the common interface
part and will have separate for hardware implementation.
The JTAG (Joint Test Action Group) core driver provides minimal generic
JTAG interface, which can be used by hardware specific JTAG master
controllers. By providing common interface for the JTAG controllers,
user space device programing is hardware independent.
Modern SoC which in use for embedded system' equipped with
internal JTAG master interface.
This interface is used for programming and debugging system's
hardware components, like CPLD, FPGA, CPU, voltage and
industrial controllers.
Firmware for such devices can be upgraded through JTAG interface during
Runtime. The JTAG standard support for multiple devices programming,
is in case their lines are daisy-chained together.
For example, systems which equipped with host CPU, BMC SoC or/and
number of programmable devices are capable to connect a pin and
select system components dynamically for programming and debugging,
This is using by the BMC which is equipped with internal SoC master
controller.
For example:
BMC JTAG master --> pin selected to CPLDs chain for programming (filed
upgrade, production)
BMC JTAG master --> pin selected to voltage monitors for programming
(field upgrade, production)
BMC JTAG master --> pin selected to host CPU (on-site debugging
and developers debugging)
For example, we can have application in user space which using calls
to JTAG driver executes CPLD programming directly from SVF file
The JTAG standard (IEEE 1149.1) defines the next connector pins:
- TDI (Test Data In);
- TDO (Test Data Out);
- TCK (Test Clock);
- TMS (Test Mode Select);
- TRST (Test Reset) (Optional);
The SoC equipped with JTAG master controller, performs
device programming on command or vector level. For example
a file in a standard SVF (Serial Vector Format) that contains
boundary scan vectors, can be used by sending each vector
to the JTAG interface and the JTAG controller will execute
the programming.
Initial version provides the system calls set for:
- SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
- SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
number of clocks.
SoC which are not equipped with JTAG master interface, can be built
on top of JTAG core driver infrastructure, by applying bit-banging of
TDI, TDO, TCK and TMS pins within the hardware specific driver.
Oleksandr Shamray (3):
drivers: jtag: Add JTAG core driver
drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master
driver
doccumentation: jtag: Add bindings for Aspeed SoC 24xx and 25xx
families JTAG master driver
.../devicetree/bindings/jtag/aspeed-jtag.txt | 18 +
Documentation/ioctl/ioctl-number.txt | 2 +
MAINTAINERS | 8 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/jtag/Kconfig | 29 +
drivers/jtag/Makefile | 2 +
drivers/jtag/jtag-aspeed.c | 772 ++++++++++++++++++++
drivers/jtag/jtag.c | 311 ++++++++
include/linux/jtag.h | 48 ++
include/uapi/linux/jtag.h | 113 +++
11 files changed, 1306 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
create mode 100644 drivers/jtag/Kconfig
create mode 100644 drivers/jtag/Makefile
create mode 100644 drivers/jtag/jtag-aspeed.c
create mode 100644 drivers/jtag/jtag.c
create mode 100644 include/linux/jtag.h
create mode 100644 include/uapi/linux/jtag.h
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] dt-bindings: serial: sh-sci: Add support for r8a77995 (H)SCIF
From: Simon Horman @ 2017-08-18 8:00 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Greg Kroah-Hartman, Rob Herring, Mark Rutland, linux-serial,
devicetree, linux-renesas-soc
In-Reply-To: <1502968588-7991-1-git-send-email-geert+renesas@glider.be>
On Thu, Aug 17, 2017 at 01:16:28PM +0200, Geert Uytterhoeven wrote:
> Document support for the (H)SCIF serial ports in the Renesas R-Car D3
> (r8a77995) SoC.
>
> No driver update is needed.
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Simon Horman <horms+renesas@verge.net.au>
^ permalink raw reply
* Re: [PATCH] USB: serial: ti_usb_3410_5052: Port uart_mode from io_ti.
From: Stuart Longland @ 2017-08-18 3:16 UTC (permalink / raw)
To: Stuart Longland, linux-serial-u79uwXL29TY76Z2rM5mHXA,
alborchers-2TsfIrYsZRL7O9PZeWXHPg,
linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170818025659.4402-1-stuartl-EoQOJlg3kTYQrrorzV6ljw@public.gmane.org>
[-- Attachment #1.1: Type: text/plain, Size: 961 bytes --]
Hi all,
On 18/08/17 12:56, Stuart Longland wrote:
> This introduces the `uart_mode` sysfs attribute as seen in the `io_ti`
> USB serial driver, allowing this USB serial interface to be switched
> between RS-232, 2-wire RS-485 and 4-wire RS-485.
>
> /sys/class/tty/ttyUSB${num}/device/uart takes a single integer:
… and of course, I meant 'uart_mode' here, not 'uart'.
>
> 0: RS-232 mode (default for RS-232-compatible dongles)
> 1: RS-485 2w mode (default for RS-485-only dongles)
> 2: RS-485 4w mode / RS-422 mode
This is of course, not a very elegant solution, but it gets me out of a
bind for now and at least gets the RS-485 functionality of this USB
serial dongle working.
Long term, I think the earlier ideas of expanding the RS-485 related
ioctls should be pursued. I'm open to ideas here.
Regards,
--
Stuart Longland (aka Redhatter, VK4MSL)
I haven't lost my mind...
...it's backed up on a tape somewhere.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH] USB: serial: ti_usb_3410_5052: Port uart_mode from io_ti.
From: Stuart Longland @ 2017-08-18 2:56 UTC (permalink / raw)
To: linux-serial-u79uwXL29TY76Z2rM5mHXA,
alborchers-2TsfIrYsZRL7O9PZeWXHPg,
linux-usb-u79uwXL29TY76Z2rM5mHXA
Cc: Stuart Longland
In-Reply-To: <4a469222-d48d-f498-f2db-ca582295adc3-3e+Fe6x+DsgJbe36r25VNhCuuivNXqWP@public.gmane.org>
This introduces the `uart_mode` sysfs attribute as seen in the `io_ti`
USB serial driver, allowing this USB serial interface to be switched
between RS-232, 2-wire RS-485 and 4-wire RS-485.
/sys/class/tty/ttyUSB${num}/device/uart takes a single integer:
0: RS-232 mode (default for RS-232-compatible dongles)
1: RS-485 2w mode (default for RS-485-only dongles)
2: RS-485 4w mode / RS-422 mode
Write this *before* opening your serial device.
This has been successfully tested on a Moxa UPort 1150 in 4-wire RS-485
mode.
Signed-off-by: Stuart Longland <stuartl-EoQOJlg3kTYQrrorzV6ljw@public.gmane.org>
---
drivers/usb/serial/ti_usb_3410_5052.c | 48 +++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 8fc3854e5e69..fb30d7ff32d7 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -514,6 +514,46 @@ MODULE_DEVICE_TABLE(usb, ti_id_table_combined);
module_usb_serial_driver(serial_drivers, ti_id_table_combined);
+/* Sysfs Attributes */
+
+static ssize_t uart_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_serial_port *port = to_usb_serial_port(dev);
+ struct ti_port *tport = usb_get_serial_port_data(port);
+
+ return sprintf(buf, "%d\n", tport->tp_uart_mode);
+}
+
+static ssize_t uart_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *valbuf, size_t count)
+{
+ struct usb_serial_port *port = to_usb_serial_port(dev);
+ struct ti_port *tport = usb_get_serial_port_data(port);
+ unsigned int v = simple_strtoul(valbuf, NULL, 0);
+
+ dev_dbg(dev, "%s: setting uart_mode = %d\n", __func__, v);
+
+ if (v < 256)
+ tport->tp_uart_mode = v;
+ else
+ dev_err(dev, "%s - uart_mode %d is invalid\n", __func__, v);
+
+ return count;
+}
+static DEVICE_ATTR_RW(uart_mode);
+
+static int ti_create_sysfs_attrs(struct usb_serial_port *port)
+{
+ return device_create_file(&port->dev, &dev_attr_uart_mode);
+}
+
+static int ti_remove_sysfs_attrs(struct usb_serial_port *port)
+{
+ device_remove_file(&port->dev, &dev_attr_uart_mode);
+ return 0;
+}
+
static int ti_startup(struct usb_serial *serial)
{
struct ti_device *tdev;
@@ -607,6 +647,7 @@ static void ti_release(struct usb_serial *serial)
static int ti_port_probe(struct usb_serial_port *port)
{
struct ti_port *tport;
+ int status;
tport = kzalloc(sizeof(*tport), GFP_KERNEL);
if (!tport)
@@ -628,6 +669,12 @@ static int ti_port_probe(struct usb_serial_port *port)
usb_set_serial_port_data(port, tport);
+ status = ti_create_sysfs_attrs(port);
+ if (status) {
+ kfree(tport);
+ return status;
+ }
+
port->port.drain_delay = 3;
return 0;
@@ -638,6 +685,7 @@ static int ti_port_remove(struct usb_serial_port *port)
struct ti_port *tport;
tport = usb_get_serial_port_data(port);
+ ti_remove_sysfs_attrs(port);
kfree(tport);
return 0;
--
2.13.0
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [patch v3 3/3] doccumentation: jtag: Add bindings for Aspeed SoC 24xx and 25xx families JTAG master driver
From: Rob Herring @ 2017-08-17 21:24 UTC (permalink / raw)
To: Oleksandr Shamray
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, arnd-r2nGTMty4D4,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, openbmc-uLR06cmDAlY/bJ5BZ2RsiQ,
joel-U3u1mxZcP9KHXe+LvDLADg, jiri-rHqAuBHg3fBzbRFIqnYvSA,
tklauser-93Khv+1bN0NyDzI6CaY1VQ,
linux-serial-u79uwXL29TY76Z2rM5mHXA, mec-WqBc5aa1uDFeoWH0uzbU5w,
vadimp-45czdsxZ+A5DPfheJLI6IQ,
system-sw-low-level-VPRAkNaXOzVWk0Htik3J/w,
openocd-devel-owner-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Jiri Pirko
In-Reply-To: <1502791207-26951-4-git-send-email-oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
On Tue, Aug 15, 2017 at 01:00:07PM +0300, Oleksandr Shamray wrote:
> It has been tested on Mellanox system with BMC equipped with
> Aspeed 2520 SoC for programming CPLD devices.
>
> Signed-off-by: Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
> Signed-off-by: Jiri Pirko <jiri-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
> ---
> v2->v3
> Comments pointed by Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> - split Aspeed jtag driver and binding to sepatrate patches
> - delete unnecessary "status" and "reg-shift" descriptions in
> bndings file
> ---
> .../devicetree/bindings/jtag/aspeed-jtag.txt | 19 +++++++++++++++++++
> 1 files changed, 19 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
>
> diff --git a/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
> new file mode 100644
> index 0000000..4743d6d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
> @@ -0,0 +1,19 @@
> +Aspeed JTAG driver for ast2400 and ast2500 SoC
> +
> +Required properties:
> +- compatible: Should be one of
> + - "aspeed,aspeed2400-jtag"
> + - "aspeed,aspeed2500-jtag"
> +- reg contains the offset and length of the JTAG memory
> + region
> +- clocks root clock of bus, should reference the APB clock
> +- interrupts should contain JTAG controller interrupt
> +
> +Example:
> +jtag: jtag@1e6e4000 {
> + compatible = "aspeed,aspeed2500-jtag";
> + reg = <0x1e6e4000 0x1c>;
> + reg-shift = <2>;
Still here...
Otherwise,
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> + clocks = <&clk_apb>;
> + interrupts = <43>;
> +};
> --
> 1.7.1
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2] earlycon: initialise baud field of earlycon device structure
From: Eugeniy Paltsev @ 2017-08-17 18:08 UTC (permalink / raw)
To: linux-serial
Cc: linux-snps-arc, linux-kernel, Greg Kroah-Hartman, Jiri Slaby,
Eugeniy Paltsev
For now baud field of earlycon structure device is't initialised at all
in of_setup_earlycon (in oppositе to register_earlycon).
So when I use stdout-path to point earlycon device
(like stdout-path = &serial or stdout-path = "serial:115200n8")
baud field of earlycon device structure remains uninitialised and
earlycon initialization is not performed correctly as
of_setup_earlycon is used.
When pass all arguments via bootargs
(like bootargs = "earlycon=uart8250,mmio32,0xf0005000,115200n8")
initialization is performed correctly as register_earlycon is used.
So initialise baud field of earlycon device structure by value of
"current-speed" property from device tree or from options
(if they exist) when we use of_setup_earlycon
Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
---
Changes v1 -> v2:
* Use standart property name "current-speed" instead of custom "baud"
NOTE:
I don't add parsing of the other standard options here because we don't
have any place to store them. When we parce and set options of the 'real'
uart device (using uart_parse_options + uart_set_options) we populate
ktermios structure an pass it to port->ops->set_termios callback of
uart_port structure. So it is processing by uart driver itself. But we don't
register such callbacks for earlycon. So we are only able to parse baud
value, which can be stored in baud field of earlycon_device structure.
drivers/tty/serial/earlycon.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index c365154..93f15e2 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -240,6 +240,7 @@ int __init of_setup_earlycon(const struct earlycon_id *match,
{
int err;
struct uart_port *port = &early_console_dev.port;
+ unsigned long baud;
const __be32 *val;
bool big_endian;
u64 addr;
@@ -282,7 +283,15 @@ int __init of_setup_earlycon(const struct earlycon_id *match,
}
}
+ val = of_get_flat_dt_prop(node, "current-speed", NULL);
+ if (val)
+ early_console_dev.baud = be32_to_cpu(*val);
+
if (options) {
+ err = kstrtoul(options, 10, &baud);
+ if (!err)
+ early_console_dev.baud = baud;
+
strlcpy(early_console_dev.options, options,
sizeof(early_console_dev.options));
}
--
2.9.3
^ permalink raw reply related
* Re: [PATCH repost] serial: sh-sci: use of_property_read_bool()
From: Geert Uytterhoeven @ 2017-08-17 13:00 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Greg Kroah-Hartman, Jiri Slaby, linux-serial@vger.kernel.org,
Linux-Renesas, Linux-sh list
In-Reply-To: <20170813191134.674818373@cogentembedded.com>
On Sun, Aug 13, 2017 at 9:11 PM, Sergei Shtylyov
<sergei.shtylyov@cogentembedded.com> wrote:
> Use more compact of_property_read_bool() call for a boolean property
> instead of of_find_property() call in sci_parse_dt().
>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: Race between release_tty() and vt_disallocate()
From: Alan Cox @ 2017-08-17 12:11 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Greg Kroah-Hartman, Jiri Slaby, Linux Kernel Mailing List,
Adam Borowski, linux-serial, Peter Hurley, jun.he, graeme.gregory,
gema.gomez-solano
In-Reply-To: <8400458.N1a9Uh7DF8@wuerfel>
> It seems that part of the problem is the lack of tty_port_put/tty_port_get
> calls in the VT code.
Yes
> > The only easy way I can think to keep the current semantics would instead
> > be to keep the tty port resources around and indexed somewhere but
> > blackhole input to/output from that port or switching to it and also call
> > tty_hangup if the port has a tty.
>
> What would still be missing if we just add that reference counting and
> delay the freeing of the vc_data/tty_port? I probably missed part of your
> analysis, so just throwing this out for discussion.
Is the expected behaviour that the disallocate also shuts down anything
using the port. If so I think you also need to do a hangup on it.
Otherwise, assuming the change in behaviour is ok this seems only part of
the picture. Possibly we should also hangup any proces on the now
destructed port, and right now I don't see that being done
(tty_port_tty_hangup(port, 0);)
I think the rest might also need fixing up.
con_install sets vc->port.tty rather than using tty_port_tty_set() so
looks like it doesn't end up with the needed refcount, and likewise
con_sbutdown touches it wrongly as far as I can see.
(That might actually explain a really strange tty ref counting race
bug I've seen reported very rarely for some years and never found!)
In addition there are other places that reference port->tty directly
without the right locks against hangup that probably need to use
tty_port_tty_get() instead (eg a vc_resize at the exact moment of a
hangup looks like it will crash)
>
> (not tested, probably wrong as I said)
>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>
> diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
> index 2ebaba16f785..9ab3df49d988 100644
> --- a/drivers/tty/vt/vt.c
> +++ b/drivers/tty/vt/vt.c
> @@ -750,6 +750,16 @@ static void visual_init(struct vc_data *vc, int num, int init)
> vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row;
> }
>
> +static void vt_destruct(struct tty_port *port)
> +{
> + struct vc_data *vc = container_of(port, struct vc_data, port);
> + kfree(vc);
> +}
> +
> +static const struct tty_port_operations vt_port_operations = {
> + .destruct = vt_destruct,
> +};
> +
> int vc_allocate(unsigned int currcons) /* return 0 on success */
> {
> struct vt_notifier_param param;
> @@ -775,6 +785,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
>
> vc_cons[currcons].d = vc;
> tty_port_init(&vc->port);
> + vc->port.ops = &vt_port_operations;
> INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
>
> visual_init(vc, currcons, 1);
> @@ -2880,14 +2891,16 @@ static int con_install(struct tty_driver *driver, struct tty_struct *tty)
> vc = vc_cons[currcons].d;
>
> /* Still being freed */
> - if (vc->port.tty) {
> + if (vc->port.tty || !tty_port_get(&vc->port)) {
Do we still need to check vc->port.tty as we should have a reference to
the port if the tty is open ? Also on a hangup port->tty changes under
tty_lock and port->lock not console lock.
BTW if you need an example that handles every case of hotplugging at once
the drivers/mmc/core/sdio_uart.c driver pretty much uses every API
feature to handle the sd and tty refcounting.
Alan
^ permalink raw reply
* [PATCH] dt-bindings: serial: sh-sci: Add support for r8a77995 (H)SCIF
From: Geert Uytterhoeven @ 2017-08-17 11:16 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rob Herring, Mark Rutland
Cc: linux-serial-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven
Document support for the (H)SCIF serial ports in the Renesas R-Car D3
(r8a77995) SoC.
No driver update is needed.
Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
Documentation/devicetree/bindings/serial/renesas,sci-serial.txt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
index 8d27d1a603e7bf75..4fc96946f81d6afc 100644
--- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
+++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
@@ -41,6 +41,8 @@ Required properties:
- "renesas,hscif-r8a7795" for R8A7795 (R-Car H3) HSCIF compatible UART.
- "renesas,scif-r8a7796" for R8A7796 (R-Car M3-W) SCIF compatible UART.
- "renesas,hscif-r8a7796" for R8A7796 (R-Car M3-W) HSCIF compatible UART.
+ - "renesas,scif-r8a77995" for R8A77995 (R-Car D3) SCIF compatible UART.
+ - "renesas,hscif-r8a77995" for R8A77995 (R-Car D3) HSCIF compatible UART.
- "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART.
- "renesas,scifb-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFB compatible UART.
- "renesas,rcar-gen1-scif" for R-Car Gen1 SCIF compatible UART,
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3] serial: 8250_of: Add basic PM runtime support
From: Franklin S Cooper Jr @ 2017-08-16 20:55 UTC (permalink / raw)
To: gregkh, jslaby, linux-serial, linux-kernel, vigneshr, joel,
khoroshilov, arnd, robert.jarzmik, tthayer, nsekhar
Cc: Franklin S Cooper Jr
66AK2G UART instances are not apart of the ALWAYS_ON power domain.
Therefore, pm_runtime calls must be made to properly insure the appropriate
power domains needed by UART are on. Keep legacy clk api calls since other
users of this driver may not support PM runtime.
Signed-off-by: Franklin S Cooper Jr <fcooper@ti.com>
---
Version 3 changes:
Commit message updated
Drop extra parenthesis
Version 2 changes:
Fix build error
Build tested using allmodconfig
drivers/tty/serial/8250/8250_of.c | 35 ++++++++++++++++++++++++-----------
1 file changed, 24 insertions(+), 11 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 6c5a8ca..be90c16 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -18,6 +18,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/reset.h>
@@ -65,6 +66,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
int ret;
memset(port, 0, sizeof *port);
+
+ pm_runtime_enable(&ofdev->dev);
+ pm_runtime_get_sync(&ofdev->dev);
+
if (of_property_read_u32(np, "clock-frequency", &clk)) {
/* Get clk rate through clk driver if present */
@@ -72,12 +77,13 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
if (IS_ERR(info->clk)) {
dev_warn(&ofdev->dev,
"clk or clock-frequency not defined\n");
- return PTR_ERR(info->clk);
+ ret = PTR_ERR(info->clk);
+ goto err_pmruntime;
}
ret = clk_prepare_enable(info->clk);
if (ret < 0)
- return ret;
+ goto err_pmruntime;
clk = clk_get_rate(info->clk);
}
@@ -170,8 +176,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
err_dispose:
irq_dispose_mapping(port->irq);
err_unprepare:
- if (info->clk)
- clk_disable_unprepare(info->clk);
+ clk_disable_unprepare(info->clk);
+err_pmruntime:
+ pm_runtime_put_sync(&ofdev->dev);
+ pm_runtime_disable(&ofdev->dev);
return ret;
}
@@ -227,8 +235,9 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
return 0;
err_dispose:
irq_dispose_mapping(port8250.port.irq);
- if (info->clk)
- clk_disable_unprepare(info->clk);
+ pm_runtime_put_sync(&ofdev->dev);
+ pm_runtime_disable(&ofdev->dev);
+ clk_disable_unprepare(info->clk);
err_free:
kfree(info);
return ret;
@@ -244,8 +253,9 @@ static int of_platform_serial_remove(struct platform_device *ofdev)
serial8250_unregister_port(info->line);
reset_control_assert(info->rst);
- if (info->clk)
- clk_disable_unprepare(info->clk);
+ pm_runtime_put_sync(&ofdev->dev);
+ pm_runtime_disable(&ofdev->dev);
+ clk_disable_unprepare(info->clk);
kfree(info);
return 0;
}
@@ -259,9 +269,10 @@ static int of_serial_suspend(struct device *dev)
serial8250_suspend_port(info->line);
- if (info->clk && (!uart_console(port) || console_suspend_enabled))
+ if (!uart_console(port) || console_suspend_enabled) {
+ pm_runtime_put_sync(dev);
clk_disable_unprepare(info->clk);
-
+ }
return 0;
}
@@ -271,8 +282,10 @@ static int of_serial_resume(struct device *dev)
struct uart_8250_port *port8250 = serial8250_get_port(info->line);
struct uart_port *port = &port8250->port;
- if (info->clk && (!uart_console(port) || console_suspend_enabled))
+ if (!uart_console(port) || console_suspend_enabled) {
+ pm_runtime_get_sync(dev);
clk_prepare_enable(info->clk);
+ }
serial8250_resume_port(info->line);
--
2.9.4.dirty
^ permalink raw reply related
* Re: [PATCH] earlycon: initialise baud field of earlycon device structure
From: Eugeniy Paltsev @ 2017-08-16 15:26 UTC (permalink / raw)
To: robherring2@gmail.com
Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org,
jslaby@suse.com, Eugeniy.Paltsev@synopsys.com,
gregkh@linuxfoundation.org, linux-snps-arc@lists.infradead.org
In-Reply-To: <CAL_JsqJaK=xgrzecf3PnPuNSzsnzdvuJ9NZ4uzs4u9Oc77sV2A@mail.gmail.com>
On Wed, 2017-08-16 at 08:46 -0500, Rob Herring wrote:
> On Wed, Aug 16, 2017 at 6:52 AM, Eugeniy Paltsev
> <Eugeniy.Paltsev@synopsys.com> wrote:
> > Hi Rob,
> >
> > On Tue, 2017-08-15 at 14:26 -0500, Rob Herring wrote:
> > > On Tue, Aug 15, 2017 at 12:21 PM, Eugeniy Paltsev
> > > <Eugeniy.Paltsev@synopsys.com> wrote:
> > > > [snip]
> > > > @@ -282,7 +283,15 @@ int __init of_setup_earlycon(const struct
> > > > earlycon_id *match,
> > > > }
> > > > }
> > > >
> > > > + val = of_get_flat_dt_prop(node, "baud", NULL);
> > >
> > > No, we already have a defined way to set the baud, we don't need
> > > a
> > > property in addition. Plus you didn't document it.
> >
> > I guess by defined way to set the baud you mean setting baud after
> > device alias
> > in stdout-path property (like stdout-path = "serial:115200n8"),
> > right?
> >
> > The idea was to reuse "baud" property from serial node to set the
> > earlycon baud:
> >
> > chosen {
> > ...
> > stdout-path = &serial;
> > };
> >
> > serial: uart@... {
> > ...
> > baud = <115200>; /* Get baud from here */
>
> "current-speed" is already defined for this purpose. If you want to
> add that, that's fine.
Ok, I'll add "current-speed".
> > };
> >
> > > > + if (val)
> > > > + early_console_dev.baud = be32_to_cpu(*val);
> > > > +
> > > > if (options) {
> > > > + err = kstrtoul(options, 10, &baud);
> > > > + if (!err)
> > > > + early_console_dev.baud = baud;
> > >
> > > This seems fine to do here, but then we should also parse the
> > > other
> > > standard options here too. And we should make sure we're not
> > > doing it
> > > twice.
> >
> > I added only baud parsing here because we parse only baud from
> > standard
> > options
> > when register_earlycon is used. (see parse_options function which
> > is
> > called
> > from register_earlycon)
> >
> > But I can add other standard options parsing here (probably using
> > uart_parse_options + uart_set_options).
> > What do you think?
>
> That seems fine as long as consoles can still have their own options.
Ok.
Should I also add standard options parsing to register_earlycon?
(as we parse only baud from standard options in register_earlycon)
> Rob
--
Eugeniy Paltsev
^ permalink raw reply
* RE: [patch v3 1/3] drivers: jtag: Add JTAG core driver
From: Oleksandr Shamray @ 2017-08-16 14:24 UTC (permalink / raw)
To: Arnd Bergmann
Cc: gregkh, Linux Kernel Mailing List, Linux ARM,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
OpenBMC Maillist, Joel Stanley, Jiří Pírko,
Tobias Klauser,
linux-serial-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
mec-WqBc5aa1uDFeoWH0uzbU5w@public.gmane.org,
vadimp-45czdsxZ+A5DPfheJLI6IQ@public.gmane.org,
system-sw-low-level, Rob Herring,
openocd-devel-owner-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org,
Jiri Pirko
In-Reply-To: <CAK8P3a3DutrfhGrd9MnDFbzhfvnZXSyrV2RbTgrFGgpbAs=H0w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
> -----Original Message-----
> From: arndbergmann@gmail.com [mailto:arndbergmann@gmail.com] On
> Behalf Of Arnd Bergmann
> Sent: Tuesday, August 15, 2017 2:16 PM
> To: Oleksandr Shamray <oleksandrs@mellanox.com>
> Cc: gregkh <gregkh@linuxfoundation.org>; Linux Kernel Mailing List <linux-
> kernel@vger.kernel.org>; Linux ARM <linux-arm-kernel@lists.infradead.org>;
> devicetree@vger.kernel.org; OpenBMC Maillist <openbmc@lists.ozlabs.org>;
> Joel Stanley <joel@jms.id.au>; Jiří Pírko <jiri@resnulli.us>; Tobias Klauser
> <tklauser@distanz.ch>; linux-serial@vger.kernel.org; mec@shout.net;
> vadimp@maellanox.com; system-sw-low-level <system-sw-low-
> level@mellanox.com>; Rob Herring <robh+dt@kernel.org>; openocd-devel-
> owner@lists.sourceforge.net; Jiri Pirko <jiri@mellanox.com>
> Subject: Re: [patch v3 1/3] drivers: jtag: Add JTAG core driver
>
> On Tue, Aug 15, 2017 at 12:00 PM, Oleksandr Shamray
> <oleksandrs@mellanox.com> wrote:
>
> > + case JTAG_IOCXFER:
> > + if (copy_from_user(&xfer, varg, sizeof(struct jtag_xfer)))
> > + return -EFAULT;
> > +
> > + if (xfer.length >= JTAG_MAX_XFER_DATA_LEN)
> > + return -EFAULT;
> > +
> > + user_tdio_data = xfer.tdio;
> > + xfer.tdio = jtag_copy_from_user((void __user *)user_tdio_data,
> > + xfer.length);
> > + if (!xfer.tdio)
> > + return -ENOMEM;
>
> This is not safe for 32-bit processes on 64-bit kernels, since the structure layout
> differs for the pointer member. It's better to always use either rework the
> structure to avoid the pointer, or to use a
> __u64 member to store it, and then use u64_to_user_ptr() to convert it in the
> kernel.
Thanks, I think using __u64 instead of pointer will be a good solution.
>
> > + case JTAG_GIOCSTATUS:
> > + if (jtag->ops->status_get)
> > + err = jtag->ops->status_get(jtag,
> > + (enum jtag_endstate
> > + *)&value);
>
> This pointer cast is also not safe, as an enum might have a different size than
> the 'value' variable that is not an enum. I think you need to change the
> argument type for the callback, or maybe use another intermediate.
>
> > +static int jtag_open(struct inode *inode, struct file *file) {
> > + struct jtag *jtag = container_of(inode->i_cdev, struct jtag,
> > +cdev);
> > +
> > + spin_lock(&jtag->lock);
> > +
> > + if (jtag->is_open) {
> > + dev_info(NULL, "jtag already opened\n");
> > + spin_unlock(&jtag->lock);
> > + return -EBUSY;
> > + }
> > +
> > + jtag->is_open = true;
> > + file->private_data = jtag;
> > + spin_unlock(&jtag->lock);
> > + return 0;
> > +}
>
> Does the 'is_open' flag protect you from something that doesn't also happen
> after a 'dup()' call on the file descriptor?
is_open flag protects from the opening file more than one time.
>
> > + * @mode: access mode
> > + * @type: transfer type
> > + * @direction: xfer direction
> > + * @length: xfer bits len
> > + * @tdio : xfer data array
> > + * @endir: xfer end state
> > + *
> > + * Structure represents interface to Aspeed JTAG device for jtag sdr
> > +xfer
> > + * execution.
> > + */
> > +struct jtag_xfer {
> > + __u8 mode;
> > + __u8 type;
> > + __u8 direction;
> > + __u32 length;
> > + __u8 *tdio;
> > + __u8 endstate;
> > +};
>
> As mentioned above, the pointer in here makes it incompatible. Also, you
> should reorder the members to avoid the implied padding.
> Moving the 'endstate' before 'length' is sufficient.
>
Thank. I will do it.
> Arnd
^ permalink raw reply
* Re: [PATCH] earlycon: initialise baud field of earlycon device structure
From: Rob Herring @ 2017-08-16 13:46 UTC (permalink / raw)
To: Eugeniy Paltsev
Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org,
jslaby@suse.com, gregkh@linuxfoundation.org,
linux-snps-arc@lists.infradead.org
In-Reply-To: <1502884362.2586.32.camel@synopsys.com>
On Wed, Aug 16, 2017 at 6:52 AM, Eugeniy Paltsev
<Eugeniy.Paltsev@synopsys.com> wrote:
> Hi Rob,
>
> On Tue, 2017-08-15 at 14:26 -0500, Rob Herring wrote:
>> On Tue, Aug 15, 2017 at 12:21 PM, Eugeniy Paltsev
>> <Eugeniy.Paltsev@synopsys.com> wrote:
>> > [snip]
>> > @@ -282,7 +283,15 @@ int __init of_setup_earlycon(const struct
>> > earlycon_id *match,
>> > }
>> > }
>> >
>> > + val = of_get_flat_dt_prop(node, "baud", NULL);
>>
>> No, we already have a defined way to set the baud, we don't need a
>> property in addition. Plus you didn't document it.
>
> I guess by defined way to set the baud you mean setting baud after
> device alias
> in stdout-path property (like stdout-path = "serial:115200n8"), right?
>
> The idea was to reuse "baud" property from serial node to set the
> earlycon baud:
>
> chosen {
> ...
> stdout-path = &serial;
> };
>
> serial: uart@... {
> ...
> baud = <115200>; /* Get baud from here */
"current-speed" is already defined for this purpose. If you want to
add that, that's fine.
> };
>
>> > + if (val)
>> > + early_console_dev.baud = be32_to_cpu(*val);
>> > +
>> > if (options) {
>> > + err = kstrtoul(options, 10, &baud);
>> > + if (!err)
>> > + early_console_dev.baud = baud;
>>
>> This seems fine to do here, but then we should also parse the other
>> standard options here too. And we should make sure we're not doing it
>> twice.
> I added only baud parsing here because we parse only baud from standard
> options
> when register_earlycon is used. (see parse_options function which is
> called
> from register_earlycon)
>
> But I can add other standard options parsing here (probably using
> uart_parse_options + uart_set_options).
> What do you think?
That seems fine as long as consoles can still have their own options.
Rob
^ permalink raw reply
* Re: [RFC v2 5/6] max9260: add driver for i2c over GMSL passthrough
From: Laurent Pinchart @ 2017-08-16 13:32 UTC (permalink / raw)
To: Ulrich Hecht
Cc: Wolfram Sang, linux-serial, Linux-Renesas, Magnus Damm,
Rob Herring, Peter Rosin, Geert Uytterhoeven, linux-i2c
In-Reply-To: <CAO3366yOpH3Q0J9YbCh2S_L8yLevag-FaVHNngvD3WoquXzp6w@mail.gmail.com>
Hi Ulrich,
On Wednesday 16 Aug 2017 15:23:52 Ulrich Hecht wrote:
> On Wed, Jul 19, 2017 at 5:00 PM, Wolfram Sang <wsa@the-dreams.de> wrote:
> >> +{
> >> + wait_event_interruptible_timeout(dev->rx_wq,
> >> + dev->rx_state <= RX_FRAME_ERROR,
> >> + HZ/2);
> >
> > I'd suggest to drop the interruptible. It can be done but it is usually
> > not trivial to abort the operation gracefully when a signal comes in.
> >
> > Also, timeout is superfluous since you don't get the return value?
>
> Be that as it may, I still want a timeout; wouldn't wait_event() block
> forever?
I think that Wolfram's point is that you should report the timeout error up to
the upper layers.
--
Regards,
Laurent Pinchart
^ permalink raw reply
* Re: [RFC v2 5/6] max9260: add driver for i2c over GMSL passthrough
From: Laurent Pinchart @ 2017-08-16 13:30 UTC (permalink / raw)
To: Ulrich Hecht
Cc: linux-serial, Linux-Renesas, Magnus Damm, Wolfram Sang,
Rob Herring, Peter Rosin, Geert Uytterhoeven, linux-i2c
In-Reply-To: <CAO3366zyKbiEv0vUsbXtZN6HXwzJrr1GQA_d6f=BCj=9Z+HQpQ@mail.gmail.com>
Hi Ulrich,
On Wednesday 16 Aug 2017 15:23:27 Ulrich Hecht wrote:
> On Mon, Jul 31, 2017 at 1:13 PM, Laurent Pinchart wrote:
> >> +static int max9260_probe(struct serdev_device *serdev)
> >> +{
> >> + struct max9260_device *dev;
> >> + struct i2c_adapter *adap;
> >> + int ret;
> >> +
> >> + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> >> + if (!dev)
> >> + return -ENOMEM;
> >> +
> >> + init_waitqueue_head(&dev->rx_wq);
> >> +
> >> + dev->serdev = serdev;
> >> + serdev_device_open(serdev);
> >> + serdev_device_set_drvdata(serdev, dev);
> >> +
> >> + serdev_device_set_client_ops(serdev, &max9260_serdev_client_ops);
> >> +
> >> + ret = max9260_setup(dev);
> >> + if (ret < 0)
> >> + goto err_free;
> >> +
> >> + adap = &dev->adap;
> >> + i2c_set_adapdata(adap, dev);
> >> +
> >> + adap->owner = THIS_MODULE;
> >> + adap->algo = &max9260_i2c_algorithm;
> >> + adap->dev.parent = &serdev->dev;
> >> + adap->retries = 5;
> >> + strlcpy(adap->name, dev_name(&serdev->dev), sizeof(adap->name));
> >> +
> >> + ret = i2c_add_adapter(adap);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + return 0;
> >> +
> >> +err_free:
> > No need for a serdev_device_close() ?
>
> serdev_device_{open,close}() actually operate on the controller, so
> that would pull the rug from under the other devices connected to that
> controller.
My point is that you call open in the .probe() handler and close in the
.remove() handler. If open needs to be balanced by close, it seems to me that
you should also call close in the error path.
--
Regards,
Laurent Pinchart
^ permalink raw reply
* Re: [PATCH 6/6] dt-bindings: slave-device: add reg property
From: Laurent Pinchart @ 2017-08-16 13:27 UTC (permalink / raw)
To: Ulrich Hecht
Cc: linux-serial, linux-renesas-soc, magnus.damm, wsa, robh, peda,
geert, linux-i2c
In-Reply-To: <1502889748-31499-7-git-send-email-ulrich.hecht+renesas@gmail.com>
Hi Ulrich,
Thank you for the patch.
On Wednesday 16 Aug 2017 15:22:28 Ulrich Hecht wrote:
> This adds a new DT property to define the index used by a multiplexer to
> select the device.
>
> Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
> ---
> Documentation/devicetree/bindings/serial/slave-device.txt | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/serial/slave-device.txt
> b/Documentation/devicetree/bindings/serial/slave-device.txt index
> 40110e0..f9b0783 100644
> --- a/Documentation/devicetree/bindings/serial/slave-device.txt
> +++ b/Documentation/devicetree/bindings/serial/slave-device.txt
> @@ -30,6 +30,8 @@ Optional Properties:
> way to reset the device
> * device baud rate is configured by its firmware but
> there is no way to request the actual settings
> +- reg : Index used to select the device if it is controlled
by a
> + multiplexer.
The reg property should describe the device address on the bus it is connected
to. This seems like an abuse of the property to me. The device shouldn't care
about the multiplexer, I think you should describe the hardware by adding the
serial device's DT node as a child of the multiplexer's port it is connected
to.
> Example:
--
Regards,
Laurent Pinchart
^ permalink raw reply
* Re: [RFC v2 5/6] max9260: add driver for i2c over GMSL passthrough
From: Ulrich Hecht @ 2017-08-16 13:23 UTC (permalink / raw)
To: Wolfram Sang
Cc: linux-serial, Linux-Renesas, Magnus Damm, Laurent, Rob Herring,
Peter Rosin, Geert Uytterhoeven, linux-i2c
In-Reply-To: <20170719150038.GA6172@katana>
On Wed, Jul 19, 2017 at 5:00 PM, Wolfram Sang <wsa@the-dreams.de> wrote:
>> +{
>> + wait_event_interruptible_timeout(dev->rx_wq,
>> + dev->rx_state <= RX_FRAME_ERROR,
>> + HZ/2);
>
> I'd suggest to drop the interruptible. It can be done but it is usually
> not trivial to abort the operation gracefully when a signal comes in.
>
> Also, timeout is superfluous since you don't get the return value?
Be that as it may, I still want a timeout; wouldn't wait_event() block forever?
CU
Uli
^ permalink raw reply
* Re: [RFC v2 3/6] serdev: add multiplexer support
From: Ulrich Hecht @ 2017-08-16 13:23 UTC (permalink / raw)
To: Rob Herring
Cc: linux-serial@vger.kernel.org,
open list:MEDIA DRIVERS FOR RENESAS - FCP, Magnus Damm,
Laurent Pinchart, wsa@the-dreams.de, Peter Rosin,
Geert Uytterhoeven, linux-i2c@vger.kernel.org
In-Reply-To: <CAL_JsqJ5M18+cSr0GaMHs5N5quJxLaU-fE2vDPYE1+X5pSFmuQ@mail.gmail.com>
On Wed, Jul 19, 2017 at 1:06 AM, Rob Herring <robh@kernel.org> wrote:
>> static inline struct serdev_controller *to_serdev_controller(struct device *d)
>> @@ -172,7 +180,7 @@ static inline void serdev_controller_write_wakeup(struct serdev_controller *ctrl
>> {
>> struct serdev_device *serdev = ctrl->serdev;
>>
>> - if (!serdev || !serdev->ops->write_wakeup)
>> + if (!serdev || !serdev->ops || !serdev->ops->write_wakeup)
>
> When is ops ever NULL? Should be a separate change AFIACT.
I dumped it; IIRC that happened once during development due to a bug elsewhere.
CU
Uli
^ permalink raw reply
* Re: [RFC v2 5/6] max9260: add driver for i2c over GMSL passthrough
From: Ulrich Hecht @ 2017-08-16 13:23 UTC (permalink / raw)
To: Laurent Pinchart
Cc: linux-serial, Linux-Renesas, Magnus Damm, Wolfram Sang,
Rob Herring, Peter Rosin, Geert Uytterhoeven, linux-i2c
In-Reply-To: <12735239.0iiT7hxZXA@avalon>
On Mon, Jul 31, 2017 at 1:13 PM, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>> +static int max9260_probe(struct serdev_device *serdev)
>> +{
>> + struct max9260_device *dev;
>> + struct i2c_adapter *adap;
>> + int ret;
>> +
>> + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
>> + if (!dev)
>> + return -ENOMEM;
>> +
>> + init_waitqueue_head(&dev->rx_wq);
>> +
>> + dev->serdev = serdev;
>> + serdev_device_open(serdev);
>> + serdev_device_set_drvdata(serdev, dev);
>> +
>> + serdev_device_set_client_ops(serdev, &max9260_serdev_client_ops);
>> +
>> + ret = max9260_setup(dev);
>> + if (ret < 0)
>> + goto err_free;
>> +
>> + adap = &dev->adap;
>> + i2c_set_adapdata(adap, dev);
>> +
>> + adap->owner = THIS_MODULE;
>> + adap->algo = &max9260_i2c_algorithm;
>> + adap->dev.parent = &serdev->dev;
>> + adap->retries = 5;
>> + strlcpy(adap->name, dev_name(&serdev->dev), sizeof(adap->name));
>> +
>> + ret = i2c_add_adapter(adap);
>> + if (ret < 0)
>> + return ret;
>> +
>> + return 0;
>> +
>> +err_free:
>
> No need for a serdev_device_close() ?
serdev_device_{open,close}() actually operate on the controller, so
that would pull the rug from under the other devices connected to that
controller.
CU
Uli
^ permalink raw reply
* [PATCH 6/6] dt-bindings: slave-device: add reg property
From: Ulrich Hecht @ 2017-08-16 13:22 UTC (permalink / raw)
To: linux-serial
Cc: linux-renesas-soc, magnus.damm, laurent.pinchart, wsa, robh, peda,
geert, linux-i2c, Ulrich Hecht
In-Reply-To: <1502889748-31499-1-git-send-email-ulrich.hecht+renesas@gmail.com>
This adds a new DT property to define the index used by a multiplexer to
select the device.
Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
---
Documentation/devicetree/bindings/serial/slave-device.txt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/serial/slave-device.txt b/Documentation/devicetree/bindings/serial/slave-device.txt
index 40110e0..f9b0783 100644
--- a/Documentation/devicetree/bindings/serial/slave-device.txt
+++ b/Documentation/devicetree/bindings/serial/slave-device.txt
@@ -30,6 +30,8 @@ Optional Properties:
way to reset the device
* device baud rate is configured by its firmware but
there is no way to request the actual settings
+- reg : Index used to select the device if it is controlled by a
+ multiplexer.
Example:
--
2.7.4
^ permalink raw reply related
* [PATCH 5/6] ARM: dts: blanche: add SCIF1 and MAX9260 deserializer
From: Ulrich Hecht @ 2017-08-16 13:22 UTC (permalink / raw)
To: linux-serial
Cc: linux-renesas-soc, magnus.damm, laurent.pinchart, wsa, robh, peda,
geert, linux-i2c, Ulrich Hecht
In-Reply-To: <1502889748-31499-1-git-send-email-ulrich.hecht+renesas@gmail.com>
Adds serial port SCIF1 and the MAX9260 deserializers connected to it.
Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
---
arch/arm/boot/dts/r8a7792-blanche.dts | 52 +++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/arch/arm/boot/dts/r8a7792-blanche.dts b/arch/arm/boot/dts/r8a7792-blanche.dts
index 9b67dca..0c1a6a0 100644
--- a/arch/arm/boot/dts/r8a7792-blanche.dts
+++ b/arch/arm/boot/dts/r8a7792-blanche.dts
@@ -21,6 +21,7 @@
aliases {
serial0 = &scif0;
serial1 = &scif3;
+ serial2 = &scif1;
};
chosen {
@@ -186,6 +187,16 @@
gpio = <&gpio11 12 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
+
+ mux: mux-controller {
+ compatible = "gpio-mux";
+ #mux-control-cells = <0>;
+
+ mux-gpios = <&gpio5 12 GPIO_ACTIVE_HIGH>,
+ <&gpio5 13 GPIO_ACTIVE_HIGH>,
+ <&gpio5 14 GPIO_ACTIVE_HIGH>,
+ <&gpio5 15 GPIO_ACTIVE_HIGH>;
+ };
};
&extal_clk {
@@ -202,6 +213,11 @@
function = "scif0";
};
+ scif1_pins: scif1 {
+ groups = "scif1_data";
+ function = "scif1";
+ };
+
scif3_pins: scif3 {
groups = "scif3_data";
function = "scif3";
@@ -246,6 +262,42 @@
status = "okay";
};
+&scif1 {
+ pinctrl-0 = <&scif1_pins>;
+ pinctrl-names = "default";
+
+ status = "okay";
+
+ mux-controls = <&mux>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ gmsl-deserializer@8 {
+ compatible = "maxim,max9260";
+ reg = <0x8>;
+ };
+ gmsl-deserializer@9 {
+ compatible = "maxim,max9260";
+ reg = <0x9>;
+ };
+ gmsl-deserializer@a {
+ compatible = "maxim,max9260";
+ reg = <0xa>;
+ };
+ gmsl-deserializer@b {
+ compatible = "maxim,max9260";
+ reg = <0xb>;
+ };
+ gmsl-deserializer@4 {
+ compatible = "maxim,max9260";
+ reg = <0x4>;
+ };
+ gmsl-deserializer@5 {
+ compatible = "maxim,max9260";
+ reg = <0x5>;
+ };
+};
+
&scif3 {
pinctrl-0 = <&scif3_pins>;
pinctrl-names = "default";
--
2.7.4
^ permalink raw reply related
* [PATCH 4/6] max9260: add driver for i2c over GMSL passthrough
From: Ulrich Hecht @ 2017-08-16 13:22 UTC (permalink / raw)
To: linux-serial
Cc: linux-renesas-soc, magnus.damm, laurent.pinchart, wsa, robh, peda,
geert, linux-i2c, Ulrich Hecht
In-Reply-To: <1502889748-31499-1-git-send-email-ulrich.hecht+renesas@gmail.com>
This driver implements tunnelling of i2c requests over GMSL via a
MAX9260 deserializer. It provides an i2c adapter that can be used
to reach devices on the far side of the link.
Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
---
drivers/media/i2c/Kconfig | 6 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/max9260.c | 300 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 307 insertions(+)
create mode 100644 drivers/media/i2c/max9260.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index fa2e7d8..b85ae78 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -424,6 +424,12 @@ config VIDEO_VPX3220
To compile this driver as a module, choose M here: the
module will be called vpx3220.
+config VIDEO_MAX9260
+ tristate "Maxim MAX9260 GMSL deserializer support"
+ depends on I2C
+ ---help---
+ This driver supports the Maxim MAX9260 GMSL deserializer.
+
comment "Video and audio decoders"
config VIDEO_SAA717X
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 30e856c..3b6f6f2 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
+obj-$(CONFIG_VIDEO_MAX9260) += max9260.o
obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o
obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
diff --git a/drivers/media/i2c/max9260.c b/drivers/media/i2c/max9260.c
new file mode 100644
index 0000000..9c890ab
--- /dev/null
+++ b/drivers/media/i2c/max9260.c
@@ -0,0 +1,300 @@
+/*
+ * Maxim MAX9260 GMSL Deserializer Driver
+ *
+ * Copyright (C) 2017 Ulrich Hecht
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/serdev.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+
+#define SYNC 0x79
+#define ACK 0xc3
+
+#define REG_ID 0x1e
+
+#define ID_MAX9260 0x02
+
+enum max9260_rx_state {
+ RX_FINISHED,
+ RX_FRAME_ERROR,
+ RX_EXPECT_ACK,
+ RX_EXPECT_ACK_DATA,
+ RX_EXPECT_DATA,
+};
+
+struct max9260_device {
+ struct serdev_device *serdev;
+ u8 *rx_buf;
+ size_t rx_len;
+ enum max9260_rx_state rx_state;
+ wait_queue_head_t rx_wq;
+ struct i2c_adapter adap;
+};
+
+static void max9260_wait_for_transaction(struct max9260_device *dev)
+{
+ wait_event_timeout(dev->rx_wq, dev->rx_state <= RX_FRAME_ERROR,
+ HZ/2);
+}
+
+static void max9260_transact(struct max9260_device *dev,
+ int expect,
+ const u8 *request, int len,
+ u8 *rx_buf, int rx_len)
+{
+ dev->rx_buf = rx_buf;
+ dev->rx_len = rx_len;
+
+ serdev_device_mux_select(dev->serdev);
+
+ serdev_device_set_baudrate(dev->serdev, 115200);
+ serdev_device_set_parity(dev->serdev, SERDEV_PARITY_EVEN);
+
+ dev->rx_state = expect;
+ serdev_device_write_buf(dev->serdev, request, len);
+
+ max9260_wait_for_transaction(dev);
+
+ serdev_device_mux_deselect(dev->serdev);
+}
+
+static int max9260_read_reg(struct max9260_device *dev, int reg)
+{
+ u8 request[] = { SYNC, 0x91, reg, 1 };
+ u8 rx;
+
+ dev->rx_len = 1;
+ dev->rx_buf = ℞
+
+ max9260_transact(dev, RX_EXPECT_ACK_DATA, request,
+ ARRAY_SIZE(request),
+ &rx, 1);
+
+ if (dev->rx_state == RX_FINISHED)
+ return rx;
+
+ return -EIO;
+}
+
+static int max9260_setup(struct max9260_device *dev)
+{
+ int ret;
+
+ ret = max9260_read_reg(dev, REG_ID);
+
+ if (ret != ID_MAX9260) {
+ dev_err(&dev->serdev->dev,
+ "device does not identify as MAX9260\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void max9260_uart_write_wakeup(struct serdev_device *serdev)
+{
+}
+
+static int max9260_uart_receive_buf(struct serdev_device *serdev,
+ const u8 *data, size_t count)
+{
+ struct max9260_device *dev = serdev_device_get_drvdata(serdev);
+ size_t accepted;
+
+ switch (dev->rx_state) {
+ case RX_FINISHED:
+ dev_dbg(&dev->serdev->dev, "excess data ignored\n");
+ return count;
+
+ case RX_EXPECT_ACK:
+ case RX_EXPECT_ACK_DATA:
+ if (data[0] != ACK) {
+ dev_dbg(&dev->serdev->dev, "frame error");
+ dev->rx_state = RX_FRAME_ERROR;
+ wake_up_interruptible(&dev->rx_wq);
+ return 1;
+ }
+
+ if (dev->rx_state == RX_EXPECT_ACK_DATA) {
+ dev->rx_state = RX_EXPECT_DATA;
+ } else {
+ dev->rx_state = RX_FINISHED;
+ wake_up_interruptible(&dev->rx_wq);
+ }
+ return 1;
+
+ case RX_EXPECT_DATA:
+ accepted = min(dev->rx_len, count);
+
+ memcpy(dev->rx_buf, data, accepted);
+
+ dev->rx_len -= accepted;
+ dev->rx_buf += accepted;
+
+ if (!dev->rx_len) {
+ dev->rx_state = RX_FINISHED;
+ wake_up_interruptible(&dev->rx_wq);
+ }
+
+ return accepted;
+
+ case RX_FRAME_ERROR:
+ dev_dbg(&dev->serdev->dev, "%d bytes ignored\n", count);
+ return count;
+
+ }
+ return 0;
+}
+
+static const struct serdev_device_ops max9260_serdev_client_ops = {
+ .receive_buf = max9260_uart_receive_buf,
+ .write_wakeup = max9260_uart_write_wakeup,
+};
+
+static u32 max9260_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA;
+}
+
+static s32 max9260_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write, u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ u8 request[] = { SYNC,
+ (addr << 1) + (read_write == I2C_SMBUS_READ),
+ command, 0, 0 };
+ struct max9260_device *dev = i2c_get_adapdata(adap);
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ if (read_write == I2C_SMBUS_WRITE) {
+ max9260_transact(dev, RX_EXPECT_ACK, request, 4,
+ NULL, 0);
+ dev_dbg(&adap->dev,
+ "smbus byte - addr 0x%02x, wrote 0x%02x.\n",
+ addr, command);
+ } else {
+ /* TBD */
+ return -EOPNOTSUPP;
+ }
+ break;
+
+ case I2C_SMBUS_BYTE_DATA:
+ request[3] = 1;
+ if (read_write == I2C_SMBUS_WRITE) {
+ request[4] = data->byte;
+ max9260_transact(dev, RX_EXPECT_ACK, request, 5,
+ NULL, 0);
+ dev_dbg(&adap->dev,
+ "smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x.\n",
+ addr, data->byte, command);
+ } else {
+ max9260_transact(dev, RX_EXPECT_ACK_DATA, request, 4,
+ &data->byte, 1);
+ dev_dbg(&adap->dev,
+ "smbus byte data - addr 0x%02x, read 0x%02x at 0x%02x.\n",
+ addr, data->byte, command);
+ }
+ break;
+ default:
+ dev_dbg(&adap->dev,
+ "Unsupported I2C/SMBus command %d\n", size);
+ return -EOPNOTSUPP;
+ }
+
+ if (dev->rx_state != RX_FINISHED) {
+ dev_dbg(&adap->dev, "xfer timed out\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct i2c_algorithm max9260_i2c_algorithm = {
+ .functionality = max9260_i2c_func,
+ .smbus_xfer = max9260_smbus_xfer,
+};
+
+static int max9260_probe(struct serdev_device *serdev)
+{
+ struct max9260_device *dev;
+ struct i2c_adapter *adap;
+ int ret;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ init_waitqueue_head(&dev->rx_wq);
+
+ dev->serdev = serdev;
+ serdev_device_open(serdev);
+ serdev_device_set_drvdata(serdev, dev);
+
+ serdev_device_set_client_ops(serdev, &max9260_serdev_client_ops);
+
+ ret = max9260_setup(dev);
+ if (ret < 0)
+ goto err_free;
+
+ adap = &dev->adap;
+ i2c_set_adapdata(adap, dev);
+
+ adap->owner = THIS_MODULE;
+ adap->algo = &max9260_i2c_algorithm;
+ adap->dev.parent = &serdev->dev;
+ adap->retries = 5;
+ strlcpy(adap->name, dev_name(&serdev->dev), sizeof(adap->name));
+
+ ret = i2c_add_adapter(adap);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+
+err_free:
+ kfree(dev);
+ return ret;
+}
+
+static void max9260_remove(struct serdev_device *serdev)
+{
+ struct max9260_device *dev = serdev_device_get_drvdata(serdev);
+
+ serdev_device_close(dev->serdev);
+
+ kfree(dev);
+}
+
+static const struct of_device_id max9260_dt_ids[] = {
+ { .compatible = "maxim,max9260" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, max9260_dt_ids);
+
+static struct serdev_device_driver max9260_driver = {
+ .probe = max9260_probe,
+ .remove = max9260_remove,
+ .driver = {
+ .name = "max9260",
+ .of_match_table = of_match_ptr(max9260_dt_ids),
+ },
+};
+
+module_serdev_device_driver(max9260_driver);
+
+MODULE_DESCRIPTION("Maxim MAX9260 GMSL Deserializer Driver");
+MODULE_AUTHOR("Ulrich Hecht");
+MODULE_LICENSE("GPL");
--
2.7.4
^ permalink raw reply related
* [PATCH 3/6] serial: core: support deferring serdev controller registration
From: Ulrich Hecht @ 2017-08-16 13:22 UTC (permalink / raw)
To: linux-serial
Cc: linux-renesas-soc, magnus.damm, laurent.pinchart, wsa, robh, peda,
geert, linux-i2c, Ulrich Hecht
In-Reply-To: <1502889748-31499-1-git-send-email-ulrich.hecht+renesas@gmail.com>
serdev controllers may depend on other devices (such as multiplexers)
and thus require deferred probing support.
Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
---
drivers/tty/serial/serial_core.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index f534a40..30a8997 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2785,6 +2785,10 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
uport->line, uport->dev, port, uport->tty_groups);
if (likely(!IS_ERR(tty_dev))) {
device_set_wakeup_capable(tty_dev, 1);
+ } else if (PTR_ERR(tty_dev) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ state->uart_port = NULL;
+ goto out;
} else {
dev_err(uport->dev, "Cannot register tty device on line %d\n",
uport->line);
--
2.7.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox