* [PATCH resend 2] Add sc16is7x2 driver
@ 2011-01-07 15:14 Manuel Stahl
2011-01-07 17:05 ` Randy Dunlap
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Manuel Stahl @ 2011-01-07 15:14 UTC (permalink / raw)
To: linux-serial; +Cc: LKML, Thomas Weber, Johannes Reif, changgx, miguelangel
[-- Attachment #1.1: Type: text/plain, Size: 341 bytes --]
IRQ is now implemented with work queue (like max3100).
The problem is, that the sc16is7x2 has a low active irq. But with active
low threaded_irqs, shared irqs are not allowed.
Please try this one and report errors.
Regards,
Manuel Stahl
PS: sorry that the patch is not inline, not sure how to configure
thunderbird correctly
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: sc16is7x2_work_queue.patch --]
[-- Type: text/x-patch; name="sc16is7x2_work_queue.patch", Size: 32773 bytes --]
Add sc16is7x2 driver
This patch adds support for the sc16is7x2 SPI to UART chips.
Each chip has two UARTs and 8 GPIOs.
Signed-off-by: Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
---
drivers/serial/Kconfig | 9 +
drivers/serial/Makefile | 1 +
drivers/serial/sc16is7x2.c | 1063 ++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 6 +-
include/linux/serial_sc16is7x2.h | 17 +
5 files changed, 1095 insertions(+), 1 deletions(-)
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 188aff6..a070345 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -269,6 +269,15 @@ config SERIAL_8250_RM9K
comment "Non-8250 serial port support"
+config SERIAL_SC16IS7X2
+ tristate "SC16IS7x2 chips"
+ depends on SPI_MASTER
+ select SERIAL_CORE
+ help
+ Selecting this option will add support for SC16IS7x2 SPI UARTs.
+ The GPIOs are exported via gpiolib interface.
+ If unsure, say N.
+
config SERIAL_AMBA_PL010
tristate "ARM AMBA PL010 serial port support"
depends on ARM_AMBA && (BROKEN || !ARCH_VERSATILE)
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 8ea92e9..6ff1f0f 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o
obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o
+obj-$(CONFIG_SERIAL_SC16IS7X2) += sc16is7x2.o
obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
diff --git a/drivers/serial/sc16is7x2.c b/drivers/serial/sc16is7x2.c
new file mode 100644
index 0000000..d3156b4
--- /dev/null
+++ b/drivers/serial/sc16is7x2.c
@@ -0,0 +1,1063 @@
+/**
+ * drivers/serial/sc16is7x2.c
+ *
+ * Copyright (C) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The SC16IS7x2 device is a SPI driven dual UART with GPIOs.
+ *
+ * The driver exports two uarts and a gpiochip interface.
+ */
+
+/* #define DEBUG */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/serial_reg.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/freezer.h>
+#include <linux/serial_sc16is7x2.h>
+
+#define MAX_SC16IS7X2 8
+#define FIFO_SIZE 64
+
+#define DRIVER_NAME "sc16is7x2"
+#define TYPE_NAME "SC16IS7x2"
+
+
+#define REG_READ 0x80
+#define REG_WRITE 0x00
+
+/* Special registers */
+#define REG_TXLVL 0x08 /* Transmitter FIFO Level register */
+#define REG_RXLVL 0x09 /* Receiver FIFO Level register */
+#define REG_IOD 0x0A /* IO Direction register */
+#define REG_IOS 0x0B /* IO State register */
+#define REG_IOI 0x0C /* IO Interrupt Enable register */
+#define REG_IOC 0x0E /* IO Control register */
+
+#define IOC_SRESET 0x08 /* Software reset */
+#define IOC_GPIO30 0x04 /* GPIO 3:0 unset: as IO, set: as modem pins */
+#define IOC_GPIO74 0x02 /* GPIO 7:4 unset: as IO, set: as modem pins */
+#define IOC_IOLATCH 0x01 /* Unset: input unlatched, set: input latched */
+
+struct sc16is7x2_chip;
+
+/*
+ * Some registers must be read back to modify.
+ * To save time we cache them here in memory.
+ */
+struct sc16is7x2_channel {
+ struct sc16is7x2_chip *chip; /* back link */
+ struct uart_port uart;
+
+ /* Workqueue that does all the magic */
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ u16 quot; /* baud rate divisor */
+ u8 iir; /* state of IIR register */
+ u8 lsr; /* state of LSR register */
+ u8 msr; /* state of MSR register */
+ u8 ier; /* cache for IER register */
+ u8 fcr; /* cache for FCR register */
+ u8 lcr; /* cache for LCR register */
+ u8 mcr; /* cache for MCR register */
+ u8 efr; /* cache for EFR register */
+#ifdef DEBUG
+ bool handle_irq;
+#endif
+ bool handle_baud; /* baud rate needs update */
+ bool handle_regs; /* other regs need update */
+ u8 buf[FIFO_SIZE+1]; /* fifo transfer buffer */
+};
+
+struct sc16is7x2_chip {
+ struct spi_device *spi;
+ struct sc16is7x2_channel channel[2];
+
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio;
+ struct mutex io_lock; /* lock for GPIO functions */
+ u8 io_dir; /* cache for IODir register */
+ u8 io_state; /* cache for IOState register */
+ u8 io_gpio; /* PIN is GPIO */
+ u8 io_control; /* cache for IOControl register */
+#endif
+};
+
+/* ******************************** SPI ********************************* */
+
+static inline u8 write_cmd(u8 reg, u8 ch)
+{
+ return REG_WRITE | (reg & 0xf) << 3 | (ch & 0x1) << 1;
+}
+
+static inline u8 read_cmd(u8 reg, u8 ch)
+{
+ return REG_READ | (reg & 0xf) << 3 | (ch & 0x1) << 1;
+}
+
+/*
+ * sc16is7x2_write - Write a new register content (sync)
+ * @reg: Register offset
+ * @ch: Channel (0 or 1)
+ */
+static int sc16is7x2_write(struct sc16is7x2_chip *ts, u8 reg, u8 ch, u8 val)
+{
+ u8 out[2];
+
+ out[0] = write_cmd(reg, ch);
+ out[1] = val;
+ return spi_write(ts->spi, out, sizeof(out));
+}
+
+/**
+ * sc16is7x2_read - Read back register content
+ * @reg: Register offset
+ * @ch: Channel (0 or 1)
+ *
+ * Returns positive 8 bit value from the device if successful or a
+ * negative value on error
+ */
+static int sc16is7x2_read(struct sc16is7x2_chip *ts, unsigned reg, unsigned ch)
+{
+ return spi_w8r8(ts->spi, read_cmd(reg, ch));
+}
+
+/* ******************************** IRQ ********************************* */
+
+static void sc16is7x2_handle_rx(struct sc16is7x2_chip *ts, unsigned ch)
+{
+ struct sc16is7x2_channel *chan = &ts->channel[ch];
+ struct uart_port *uart = &chan->uart;
+ struct tty_struct *tty = uart->state->port.tty;
+ struct spi_message message;
+ struct spi_transfer t[2];
+ unsigned long flags;
+ u8 lsr = chan->lsr;
+ int rxlvl;
+
+ rxlvl = sc16is7x2_read(ts, REG_RXLVL, ch);
+ if (rxlvl <= 0) {
+ return;
+ } else if (rxlvl > FIFO_SIZE) {
+ /* Ensure sanity of RX level */
+ rxlvl = FIFO_SIZE;
+ }
+
+ dev_dbg(&ts->spi->dev, " %s (%i) %d bytes\n", __func__, ch, rxlvl);
+
+ memset(t, 0, sizeof t);
+ chan->buf[0] = read_cmd(UART_RX, ch);
+ t[0].len = 1;
+ t[0].tx_buf = &chan->buf[0];
+ t[1].len = rxlvl;
+ t[1].rx_buf = &chan->buf[1];
+
+ spi_message_init(&message);
+ spi_message_add_tail(&t[0], &message);
+ spi_message_add_tail(&t[1], &message);
+
+ if (spi_sync(ts->spi, &message)) {
+ dev_err(&ts->spi->dev, " SPI transfer RX handling failed\n");
+ return;
+ }
+ chan->buf[rxlvl + 1] = '\0';
+ dev_dbg(&ts->spi->dev, "%s\n", &chan->buf[1]);
+
+ spin_lock_irqsave(&uart->lock, flags);
+
+ if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {
+ /*
+ * For statistics only
+ */
+ if (lsr & UART_LSR_BI) {
+ lsr &= ~(UART_LSR_FE | UART_LSR_PE);
+ chan->uart.icount.brk++;
+ /*
+ * We do the SysRQ and SAK checking
+ * here because otherwise the break
+ * may get masked by ignore_status_mask
+ * or read_status_mask.
+ */
+ if (uart_handle_break(&chan->uart))
+ goto ignore_char;
+ } else if (lsr & UART_LSR_PE)
+ chan->uart.icount.parity++;
+ else if (lsr & UART_LSR_FE)
+ chan->uart.icount.frame++;
+ if (lsr & UART_LSR_OE)
+ chan->uart.icount.overrun++;
+ }
+
+ /* Insert received data */
+ tty_insert_flip_string(tty, &chan->buf[1], rxlvl);
+ /* Update RX counter */
+ uart->icount.rx += rxlvl;
+
+ignore_char:
+ spin_unlock_irqrestore(&uart->lock, flags);
+
+ /* Push the received data to receivers */
+ if (rxlvl)
+ tty_flip_buffer_push(tty);
+}
+
+static void sc16is7x2_handle_tx(struct sc16is7x2_chip *ts, unsigned ch)
+{
+ struct sc16is7x2_channel *chan = &ts->channel[ch];
+ struct uart_port *uart = &chan->uart;
+ struct circ_buf *xmit = &uart->state->xmit;
+ unsigned long flags;
+ unsigned i, len;
+ int txlvl;
+
+ if (chan->uart.x_char && chan->lsr & UART_LSR_THRE) {
+ dev_dbg(&ts->spi->dev, " tx: x-char\n");
+ sc16is7x2_write(ts, UART_TX, ch, uart->x_char);
+ uart->icount.tx++;
+ uart->x_char = 0;
+ return;
+ }
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&chan->uart))
+ /* No data to send or TX is stopped */
+ return;
+
+ txlvl = sc16is7x2_read(ts, REG_TXLVL, ch);
+ if (txlvl <= 0) {
+ dev_dbg(&ts->spi->dev, " %s (%i) fifo full\n", __func__, ch);
+ return;
+ }
+
+ /* number of bytes to transfer to the fifo */
+ len = min(txlvl, (int)uart_circ_chars_pending(xmit));
+
+ dev_dbg(&ts->spi->dev, " %s (%i) %d bytes\n", __func__, ch, len);
+
+ spin_lock_irqsave(&uart->lock, flags);
+ for (i = 1; i <= len ; i++) {
+ chan->buf[i] = xmit->buf[xmit->tail];
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ }
+ uart->icount.tx += len;
+ spin_unlock_irqrestore(&uart->lock, flags);
+
+ chan->buf[0] = write_cmd(UART_TX, ch);
+ if (spi_write(ts->spi, chan->buf, len + 1))
+ dev_err(&ts->spi->dev, " SPI transfer TX handling failed\n");
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(uart);
+}
+
+static void sc16is7x2_handle_baud(struct sc16is7x2_chip *ts, unsigned ch)
+{
+ struct sc16is7x2_channel *chan = &ts->channel[ch];
+
+ if (!chan->handle_baud)
+ return;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ sc16is7x2_write(ts, UART_IER, ch, 0);
+ sc16is7x2_write(ts, UART_LCR, ch, UART_LCR_DLAB); /* access DLL&DLM */
+ sc16is7x2_write(ts, UART_DLL, ch, chan->quot & 0xff);
+ sc16is7x2_write(ts, UART_DLM, ch, chan->quot >> 8);
+ sc16is7x2_write(ts, UART_LCR, ch, chan->lcr); /* reset DLAB */
+
+ chan->handle_baud = false;
+}
+
+static void sc16is7x2_handle_regs(struct sc16is7x2_chip *ts, unsigned ch)
+{
+ struct sc16is7x2_channel *chan = &ts->channel[ch];
+
+ if (!chan->handle_regs)
+ return;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ sc16is7x2_write(ts, UART_LCR, ch, 0xBF); /* access EFR */
+ sc16is7x2_write(ts, UART_EFR, ch, chan->efr);
+ sc16is7x2_write(ts, UART_LCR, ch, chan->lcr);
+ sc16is7x2_write(ts, UART_FCR, ch, chan->fcr);
+ sc16is7x2_write(ts, UART_MCR, ch, chan->mcr);
+ sc16is7x2_write(ts, UART_IER, ch, chan->ier);
+
+ chan->handle_regs = false;
+}
+
+static void sc16is7x2_read_status(struct sc16is7x2_chip *ts, unsigned ch)
+{
+ struct sc16is7x2_channel *chan = &(ts->channel[ch]);
+/* struct spi_message m;
+ struct spi_transfer t;
+ u8 *buf = chan->buf; */
+ u8 ier;
+
+#ifdef DEBUG
+ ier = sc16is7x2_read(ts, UART_IER, ch);
+#endif
+ chan->iir = sc16is7x2_read(ts, UART_IIR, ch);
+ chan->msr = sc16is7x2_read(ts, UART_MSR, ch);
+ chan->lsr = sc16is7x2_read(ts, UART_LSR, ch);
+/*
+ buf[0] = read_cmd(UART_IER, ch);
+ buf[1] = read_cmd(UART_IIR, ch);
+ buf[2] = read_cmd(UART_MSR, ch);
+ buf[3] = read_cmd(UART_LSR, ch);
+
+ t.tx_buf = buf;
+ t.rx_buf = &buf[16];
+ t.len = 5;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ spi_sync(ts->spi, &m); */
+
+ dev_dbg(&ts->spi->dev, " %s ier=0x%02x iir=0x%02x msr=0x%02x lsr=0x%02x\n",
+ __func__, ier, chan->iir, chan->msr, chan->lsr);
+/*
+ dev_dbg(&ts->spi->dev, " %s ier=0x%02x iir=0x%02x msr=0x%02x lsr=0x%02x\n",
+ __func__, buf[17], buf[18], buf[19], buf[20]);
+*/
+}
+
+static void sc16is7x2_handle_channel(struct work_struct *w)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(w, struct sc16is7x2_channel, work);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned ch = (chan == ts->channel) ? 0 : 1;
+
+#ifdef DEBUG
+ dev_dbg(&ts->spi->dev, "%s (%i) %s\n", __func__, ch,
+ chan->handle_irq ? "irq" : "");
+ chan->handle_irq = false;
+#endif
+
+ do {
+ sc16is7x2_handle_baud(ts, ch);
+ sc16is7x2_handle_regs(ts, ch);
+
+ sc16is7x2_read_status(ts, ch);
+ sc16is7x2_handle_tx(ts, ch);
+ sc16is7x2_handle_rx(ts, ch);
+ } while (!(chan->iir & UART_IIR_NO_INT));
+
+ dev_dbg(&ts->spi->dev, "%s finished\n", __func__);
+}
+
+/* Trigger work thread*/
+static void sc16is7x2_dowork(struct sc16is7x2_channel *chan)
+{
+ if (!freezing(current))
+ queue_work(chan->workqueue, &chan->work);
+}
+
+static irqreturn_t sc16is7x2_irq(int irq, void *data)
+{
+ struct sc16is7x2_channel *chan = data;
+
+#ifdef DEBUG
+ /* Indicate irq */
+ chan->handle_irq = true;
+#endif
+
+ /* Trigger work thread */
+ sc16is7x2_dowork(chan);
+
+ return IRQ_HANDLED;
+}
+
+/* ******************************** UART ********************************* */
+
+#define to_sc16is7x2_channel(port) \
+ container_of(port, struct sc16is7x2_channel, uart)
+
+
+static unsigned int sc16is7x2_tx_empty(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan = to_sc16is7x2_channel(port);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned lsr;
+
+ dev_dbg(&ts->spi->dev, "%s = %s\n", __func__,
+ chan->lsr & UART_LSR_TEMT ? "yes" : "no");
+
+ lsr = chan->lsr;
+ return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int sc16is7x2_get_mctrl(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan = to_sc16is7x2_channel(port);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned int status;
+ unsigned int ret;
+
+ dev_dbg(&ts->spi->dev, "%s (0x%02x)\n", __func__, chan->msr);
+
+ status = chan->msr;
+
+ ret = 0;
+ if (status & UART_MSR_DCD)
+ ret |= TIOCM_CAR;
+ if (status & UART_MSR_RI)
+ ret |= TIOCM_RNG;
+ if (status & UART_MSR_DSR)
+ ret |= TIOCM_DSR;
+ if (status & UART_MSR_CTS)
+ ret |= TIOCM_CTS;
+ return ret;
+}
+
+static void sc16is7x2_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct sc16is7x2_channel *chan = to_sc16is7x2_channel(port);
+ struct sc16is7x2_chip *ts = chan->chip;
+
+ dev_dbg(&ts->spi->dev, "%s (0x%02x)\n", __func__, mctrl);
+
+ /* TODO: set DCD and DSR
+ * CTS/RTS is handled automatically
+ */
+}
+
+static void sc16is7x2_stop_tx(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static void sc16is7x2_start_tx(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan = to_sc16is7x2_channel(port);
+ struct sc16is7x2_chip *ts = chan->chip;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ /* Trigger work thread for sending data */
+ sc16is7x2_dowork(chan);
+}
+
+static void sc16is7x2_stop_rx(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan = to_sc16is7x2_channel(port);
+ struct sc16is7x2_chip *ts = chan->chip;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ chan->ier &= ~UART_IER_RLSI;
+ chan->uart.read_status_mask &= ~UART_LSR_DR;
+ chan->handle_regs = true;
+ /* Trigger work thread for doing the actual configuration change */
+ sc16is7x2_dowork(chan);
+}
+
+static void sc16is7x2_enable_ms(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan = to_sc16is7x2_channel(port);
+ struct sc16is7x2_chip *ts = chan->chip;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ chan->ier |= UART_IER_MSI;
+ chan->handle_regs = true;
+ /* Trigger work thread for doing the actual configuration change */
+ sc16is7x2_dowork(chan);
+}
+
+static void sc16is7x2_break_ctl(struct uart_port *port, int break_state)
+{
+ /* We don't support break control yet, do nothing */
+}
+
+static int sc16is7x2_startup(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan = to_sc16is7x2_channel(port);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned ch = (&ts->channel[1] == chan) ? 1 : 0;
+ unsigned long flags;
+
+ dev_dbg(&ts->spi->dev, "\n%s (%d)\n", __func__, port->line);
+
+ /* Clear the interrupt registers. */
+ sc16is7x2_write(ts, UART_IER, ch, 0);
+ sc16is7x2_read_status(ts, ch);
+
+ /* Initialize work queue */
+ chan->workqueue = create_freezeable_workqueue("sc16is7x2");
+ if (!chan->workqueue) {
+ dev_err(&ts->spi->dev, "Workqueue creation failed\n");
+ return -EBUSY;
+ }
+ INIT_WORK(&chan->work, sc16is7x2_handle_channel);
+
+ /* Setup IRQ. Actually we have a low active IRQ, but we want
+ * one shot behaviour */
+ if (request_irq(ts->spi->irq, sc16is7x2_irq,
+ IRQF_TRIGGER_FALLING | IRQF_SHARED,
+ "sc16is7x2", chan)) {
+ dev_err(&ts->spi->dev, "IRQ request failed\n");
+ destroy_workqueue(chan->workqueue);
+ chan->workqueue = NULL;
+ return -EBUSY;
+ }
+
+
+ spin_lock_irqsave(&chan->uart.lock, flags);
+ chan->lcr = UART_LCR_WLEN8;
+ chan->mcr = 0;
+ chan->fcr = 0;
+ chan->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI;
+ spin_unlock_irqrestore(&chan->uart.lock, flags);
+
+ sc16is7x2_write(ts, UART_FCR, ch, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ sc16is7x2_write(ts, UART_FCR, ch, chan->fcr);
+ /* Now, initialize the UART */
+ sc16is7x2_write(ts, UART_LCR, ch, chan->lcr);
+ sc16is7x2_write(ts, UART_MCR, ch, chan->mcr);
+ sc16is7x2_write(ts, UART_IER, ch, chan->ier);
+
+ return 0;
+}
+
+static void sc16is7x2_shutdown(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan = to_sc16is7x2_channel(port);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned long flags;
+ unsigned ch = port->line & 0x01;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ BUG_ON(!chan);
+ BUG_ON(!ts);
+
+ /* Free the interrupt */
+ free_irq(ts->spi->irq, chan);
+
+ if (chan->workqueue) {
+ /* Flush and destroy work queue */
+ flush_workqueue(chan->workqueue);
+ destroy_workqueue(chan->workqueue);
+ chan->workqueue = NULL;
+ }
+
+ /* Suspend HW */
+ spin_lock_irqsave(&chan->uart.lock, flags);
+ chan->ier = UART_IERX_SLEEP;
+ spin_unlock_irqrestore(&chan->uart.lock, flags);
+ sc16is7x2_write(ts, UART_IER, ch, chan->ier);
+}
+
+static void
+sc16is7x2_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct sc16is7x2_channel *chan = to_sc16is7x2_channel(port);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned long flags;
+ unsigned int baud;
+ u8 lcr, fcr = 0;
+
+ /* Ask the core to calculate the divisor for us. */
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0xffff,
+ port->uartclk / 16);
+ chan->quot = uart_get_divisor(port, baud);
+ chan->handle_baud = true;
+
+ dev_dbg(&ts->spi->dev, "%s (baud %u)\n", __func__, baud);
+
+ /* set word length */
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ lcr = UART_LCR_WLEN5;
+ break;
+ case CS6:
+ lcr = UART_LCR_WLEN6;
+ break;
+ case CS7:
+ lcr = UART_LCR_WLEN7;
+ break;
+ default:
+ case CS8:
+ lcr = UART_LCR_WLEN8;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ lcr |= UART_LCR_STOP;
+ if (termios->c_cflag & PARENB)
+ lcr |= UART_LCR_PARITY;
+ if (!(termios->c_cflag & PARODD))
+ lcr |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ if (termios->c_cflag & CMSPAR)
+ lcr |= UART_LCR_SPAR;
+#endif
+
+ fcr = UART_FCR_ENABLE_FIFO;
+ /* configure the fifo */
+ if (baud < 2400)
+ fcr |= UART_FCR_TRIGGER_1;
+ else
+ fcr |= UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_10;
+
+ chan->efr = UART_EFR_ECB;
+ chan->mcr |= UART_MCR_RTS;
+ if (termios->c_cflag & CRTSCTS)
+ chan->efr |= UART_EFR_CTS | UART_EFR_RTS;
+
+ /*
+ * Ok, we're now changing the port state. Do it with
+ * interrupts disabled.
+ */
+ spin_lock_irqsave(&chan->uart.lock, flags);
+
+ /* we are sending char from a workqueue so enable */
+ chan->uart.state->port.tty->low_latency = 1;
+
+ /* Update the per-port timeout. */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ chan->uart.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+ if (termios->c_iflag & INPCK)
+ chan->uart.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ chan->uart.read_status_mask |= UART_LSR_BI;
+
+ /* Characters to ignore */
+ chan->uart.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ chan->uart.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ if (termios->c_iflag & IGNBRK) {
+ chan->uart.ignore_status_mask |= UART_LSR_BI;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ chan->uart.ignore_status_mask |= UART_LSR_OE;
+ }
+
+ /* ignore all characters if CREAD is not set */
+ if ((termios->c_cflag & CREAD) == 0)
+ chan->uart.ignore_status_mask |= UART_LSR_DR;
+
+ /* CTS flow control flag and modem status interrupts */
+ chan->ier &= ~UART_IER_MSI;
+ if (UART_ENABLE_MS(&chan->uart, termios->c_cflag))
+ chan->ier |= UART_IER_MSI;
+
+ chan->lcr = lcr; /* Save LCR */
+ chan->fcr = fcr; /* Save FCR */
+ chan->handle_regs = true;
+
+ spin_unlock_irqrestore(&chan->uart.lock, flags);
+
+ /* Trigger work thread for doing the actual configuration change */
+ sc16is7x2_dowork(chan);
+}
+
+static const char * sc16is7x2_type(struct uart_port *port)
+{
+ pr_debug("%s\n", __func__);
+ return TYPE_NAME;
+}
+
+static void sc16is7x2_release_port(struct uart_port *port)
+{
+ pr_debug("%s\n", __func__);
+}
+
+static int sc16is7x2_request_port(struct uart_port *port)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static void sc16is7x2_config_port(struct uart_port *port, int flags)
+{
+ struct sc16is7x2_channel *chan = to_sc16is7x2_channel(port);
+ struct sc16is7x2_chip *ts = chan->chip;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+ if (flags & UART_CONFIG_TYPE)
+ chan->uart.type = PORT_SC16IS7X2;
+}
+
+static int
+sc16is7x2_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ if (ser->type == PORT_UNKNOWN || ser->type == PORT_SC16IS7X2)
+ return 0;
+
+ return -EINVAL;
+}
+
+static struct uart_ops sc16is7x2_uart_ops = {
+ .tx_empty = sc16is7x2_tx_empty,
+ .set_mctrl = sc16is7x2_set_mctrl,
+ .get_mctrl = sc16is7x2_get_mctrl,
+ .stop_tx = sc16is7x2_stop_tx,
+ .start_tx = sc16is7x2_start_tx,
+ .stop_rx = sc16is7x2_stop_rx,
+ .enable_ms = sc16is7x2_enable_ms,
+ .break_ctl = sc16is7x2_break_ctl,
+ .startup = sc16is7x2_startup,
+ .shutdown = sc16is7x2_shutdown,
+ .set_termios = sc16is7x2_set_termios,
+ .type = sc16is7x2_type,
+ .release_port = sc16is7x2_release_port,
+ .request_port = sc16is7x2_request_port,
+ .config_port = sc16is7x2_config_port,
+ .verify_port = sc16is7x2_verify_port,
+};
+
+
+/* ******************************** GPIO ********************************* */
+
+#ifdef CONFIG_GPIOLIB
+
+static int sc16is7x2_gpio_request(struct gpio_chip *gpio, unsigned offset)
+{
+ struct sc16is7x2_chip *ts =
+ container_of(gpio, struct sc16is7x2_chip, gpio);
+ int control = (offset < 4) ? IOC_GPIO30 : IOC_GPIO74;
+ int ret = 0;
+
+ BUG_ON(offset > 8);
+ dev_dbg(&ts->spi->dev, "%s: offset = %d\n", __func__, offset);
+
+ mutex_lock(&ts->io_lock);
+
+ /* GPIO 0:3 and 4:7 can only be controlled as block */
+ ts->io_gpio |= BIT(offset);
+ if (ts->io_control & control) {
+ dev_dbg(&ts->spi->dev, "activate GPIOs %s\n",
+ (offset < 4) ? "0-3" : "4-7");
+ ts->io_control &= ~control;
+ ret = sc16is7x2_write(ts, REG_IOC, 0, ts->io_control);
+ }
+
+ mutex_unlock(&ts->io_lock);
+
+ return ret;
+}
+
+static void sc16is7x2_gpio_free(struct gpio_chip *gpio, unsigned offset)
+{
+ struct sc16is7x2_chip *ts =
+ container_of(gpio, struct sc16is7x2_chip, gpio);
+ int control = (offset < 4) ? IOC_GPIO30 : IOC_GPIO74;
+ int mask = (offset < 4) ? 0x0f : 0xf0;
+
+ BUG_ON(offset > 8);
+
+ mutex_lock(&ts->io_lock);
+
+ /* GPIO 0:3 and 4:7 can only be controlled as block */
+ ts->io_gpio &= ~BIT(offset);
+ dev_dbg(&ts->spi->dev, "%s: io_gpio = 0x%02X\n", __func__, ts->io_gpio);
+ if (!(ts->io_control & control) && !(ts->io_gpio & mask)) {
+ dev_dbg(&ts->spi->dev, "deactivate GPIOs %s\n",
+ (offset < 4) ? "0-3" : "4-7");
+ ts->io_control |= control;
+ sc16is7x2_write(ts, REG_IOC, 0, ts->io_control);
+ }
+
+ mutex_unlock(&ts->io_lock);
+}
+
+static int sc16is7x2_direction_input(struct gpio_chip *gpio, unsigned offset)
+{
+ struct sc16is7x2_chip *ts =
+ container_of(gpio, struct sc16is7x2_chip, gpio);
+ unsigned io_dir;
+
+ BUG_ON(offset > 8);
+
+ mutex_lock(&ts->io_lock);
+
+ ts->io_dir &= ~BIT(offset);
+ io_dir = ts->io_dir;
+
+ mutex_unlock(&ts->io_lock);
+
+ return sc16is7x2_write(ts, REG_IOD, 0, io_dir);
+}
+
+static int sc16is7x2_direction_output(struct gpio_chip *gpio, unsigned offset,
+ int value)
+{
+ struct sc16is7x2_chip *ts =
+ container_of(gpio, struct sc16is7x2_chip, gpio);
+
+ BUG_ON(offset > 8);
+
+ mutex_lock(&ts->io_lock);
+
+ if (value)
+ ts->io_state |= BIT(offset);
+ else
+ ts->io_state &= ~BIT(offset);
+
+ ts->io_dir |= BIT(offset);
+
+ mutex_unlock(&ts->io_lock);
+
+ sc16is7x2_write(ts, REG_IOS, 0, ts->io_state);
+ return sc16is7x2_write(ts, REG_IOD, 0, ts->io_dir);
+}
+
+static int sc16is7x2_get(struct gpio_chip *gpio, unsigned offset)
+{
+ struct sc16is7x2_chip *ts =
+ container_of(gpio, struct sc16is7x2_chip, gpio);
+ int level = -EINVAL;
+
+ BUG_ON(offset > 8);
+
+ mutex_lock(&ts->io_lock);
+
+ if (ts->io_dir & BIT(offset)) {
+ /* Output: return cached level */
+ level = (ts->io_state >> offset) & 0x01;
+ } else {
+ /* Input: read out all pins */
+ level = sc16is7x2_read(ts, REG_IOS, 0);
+ if (level >= 0) {
+ ts->io_state = level;
+ level = (ts->io_state >> offset) & 0x01;
+ }
+ }
+
+ mutex_unlock(&ts->io_lock);
+
+ return level;
+}
+
+static void sc16is7x2_set(struct gpio_chip *gpio, unsigned offset, int value)
+{
+ struct sc16is7x2_chip *ts =
+ container_of(gpio, struct sc16is7x2_chip, gpio);
+ unsigned io_state;
+
+ BUG_ON(offset > 8);
+
+ mutex_lock(&ts->io_lock);
+
+ if (value)
+ ts->io_state |= BIT(offset);
+ else
+ ts->io_state &= ~BIT(offset);
+ io_state = ts->io_state;
+
+ mutex_unlock(&ts->io_lock);
+
+ sc16is7x2_write(ts, REG_IOS, 0, io_state);
+}
+
+#endif /* CONFIG_GPIOLIB */
+
+/* ******************************** INIT ********************************* */
+
+static struct uart_driver sc16is7x2_uart_driver;
+
+static int sc16is7x2_register_gpio(struct sc16is7x2_chip *ts,
+ struct sc16is7x2_platform_data *pdata)
+{
+#ifdef CONFIG_GPIOLIB
+ ts->gpio.label = (pdata->label) ? pdata->label : DRIVER_NAME;
+ ts->gpio.request = sc16is7x2_gpio_request;
+ ts->gpio.free = sc16is7x2_gpio_free;
+ ts->gpio.get = sc16is7x2_get;
+ ts->gpio.set = sc16is7x2_set;
+ ts->gpio.direction_input = sc16is7x2_direction_input;
+ ts->gpio.direction_output = sc16is7x2_direction_output;
+
+ ts->gpio.base = pdata->gpio_base;
+ ts->gpio.names = pdata->names;
+ ts->gpio.ngpio = SC16IS7X2_NR_GPIOS;
+ ts->gpio.can_sleep = 1;
+ ts->gpio.dev = &ts->spi->dev;
+ ts->gpio.owner = THIS_MODULE;
+
+ mutex_init(&ts->io_lock);
+
+ /* disable all GPIOs, enable on request */
+ ts->io_gpio = 0;
+ ts->io_control = IOC_GPIO30 | IOC_GPIO74;
+ ts->io_state = 0;
+ ts->io_dir = 0;
+
+ sc16is7x2_write(ts, REG_IOI, 0, 0); /* no support for irqs yet */
+ sc16is7x2_write(ts, REG_IOC, 0, ts->io_control);
+ sc16is7x2_write(ts, REG_IOS, 0, ts->io_state);
+ sc16is7x2_write(ts, REG_IOD, 0, ts->io_dir);
+
+ return gpiochip_add(&ts->gpio);
+#else
+ return 0;
+#endif
+}
+
+static int sc16is7x2_register_uart_port(struct sc16is7x2_chip *ts,
+ struct sc16is7x2_platform_data *pdata, unsigned ch)
+{
+ struct sc16is7x2_channel *chan = &(ts->channel[ch]);
+ struct uart_port *uart = &chan->uart;
+
+ /* Disable irqs and go to sleep */
+ sc16is7x2_write(ts, UART_IER, ch, UART_IERX_SLEEP);
+
+ chan->chip = ts;
+
+ uart->irq = ts->spi->irq;
+ uart->uartclk = pdata->uartclk;
+ uart->fifosize = FIFO_SIZE;
+ uart->ops = &sc16is7x2_uart_ops;
+ uart->flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+ uart->line = pdata->uart_base + ch;
+ uart->type = PORT_SC16IS7X2;
+ uart->dev = &ts->spi->dev;
+
+ return uart_add_one_port(&sc16is7x2_uart_driver, uart);
+}
+
+static int __devinit sc16is7x2_probe(struct spi_device *spi)
+{
+ struct sc16is7x2_chip *ts;
+ struct sc16is7x2_platform_data *pdata;
+ int ret;
+
+ /* Only even uart base numbers are supported */
+ pdata = spi->dev.platform_data;
+ if (!pdata || !pdata->gpio_base || pdata->uart_base & 1) {
+ dev_dbg(&spi->dev, "incorrect or missing platform data\n");
+ return -EINVAL;
+ }
+
+ ts = kzalloc(sizeof(struct sc16is7x2_chip), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, ts);
+ ts->spi = spi;
+
+ /* Reset the chip */
+ sc16is7x2_write(ts, REG_IOC, 0, IOC_SRESET);
+
+ ret = sc16is7x2_register_uart_port(ts, pdata, 0);
+ if (ret)
+ goto exit_destroy;
+ ret = sc16is7x2_register_uart_port(ts, pdata, 1);
+ if (ret)
+ goto exit_uart0;
+
+ ret = sc16is7x2_register_gpio(ts, pdata);
+ if (ret)
+ goto exit_uart1;
+
+ dev_info(&spi->dev, DRIVER_NAME " at CS%d (irq %d), 2 UARTs, 8 GPIOs\n"
+ " eser%d, eser%d, gpiochip%d\n",
+ spi->chip_select, spi->irq,
+ pdata->uart_base, pdata->uart_base + 1,
+ pdata->gpio_base);
+
+ return 0;
+
+exit_uart1:
+ uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[1].uart);
+
+exit_uart0:
+ uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[0].uart);
+
+exit_destroy:
+ dev_set_drvdata(&spi->dev, NULL);
+ kfree(ts);
+ return ret;
+}
+
+static int __devexit sc16is7x2_remove(struct spi_device *spi)
+{
+ struct sc16is7x2_chip *ts = spi_get_drvdata(spi);
+ int ret;
+
+ if (ts == NULL)
+ return -ENODEV;
+
+ ret = uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[0].uart);
+ if (ret)
+ return ret;
+
+ ret = uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[1].uart);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_GPIOLIB
+ ret = gpiochip_remove(&ts->gpio);
+ if (ret)
+ return ret;
+#endif
+
+ kfree(ts);
+
+ return 0;
+}
+
+static struct uart_driver sc16is7x2_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = "eser",
+ .nr = MAX_SC16IS7X2,
+};
+
+/* Spi driver data */
+static struct spi_driver sc16is7x2_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = sc16is7x2_probe,
+ .remove = __devexit_p(sc16is7x2_remove),
+};
+
+/* Driver init function */
+static int __init sc16is7x2_init(void)
+{
+ int ret = uart_register_driver(&sc16is7x2_uart_driver);
+ if (ret)
+ return ret;
+
+ return spi_register_driver(&sc16is7x2_spi_driver);
+}
+
+/* Driver exit function */
+static void __exit sc16is7x2_exit(void)
+{
+ spi_unregister_driver(&sc16is7x2_spi_driver);
+ uart_unregister_driver(&sc16is7x2_uart_driver);
+}
+
+/* register after spi postcore initcall and before
+ * subsys initcalls that may rely on these GPIOs
+ */
+subsys_initcall(sc16is7x2_init);
+module_exit(sc16is7x2_exit);
+
+MODULE_AUTHOR("Manuel Stahl");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SC16IS7x2 SPI based UART chip");
+MODULE_ALIAS("spi:" DRIVER_NAME);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index a23fa29..28b9e85 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -45,7 +45,8 @@
#define PORT_OCTEON 17 /* Cavium OCTEON internal UART */
#define PORT_AR7 18 /* Texas Instruments AR7 internal UART */
#define PORT_U6_16550A 19 /* ST-Ericsson U6xxx internal UART */
-#define PORT_MAX_8250 19 /* max port ID */
+#define PORT_SC16IS7X2 20 /* SC16IS7x2 SPI UART */
+#define PORT_MAX_8250 20 /* max port ID */
/*
* ARM specific type numbers. These are not currently guaranteed
@@ -202,6 +203,9 @@
/* VIA VT8500 SoC */
#define PORT_VT8500 97
+/* SC16IS7x2 SPI UART */
+#define PORT_SC16IS7X2 98
+
#ifdef __KERNEL__
#include <linux/compiler.h>
diff --git a/include/linux/serial_sc16is7x2.h b/include/linux/serial_sc16is7x2.h
new file mode 100755
index 0000000..931fe50
--- /dev/null
+++ b/include/linux/serial_sc16is7x2.h
@@ -0,0 +1,17 @@
+#ifndef LINUX_SPI_SC16IS752_H
+#define LINUX_SPI_SC16IS752_H
+
+#define SC16IS7X2_NR_GPIOS 8
+
+struct sc16is7x2_platform_data {
+ unsigned int uartclk;
+ /* uart line number of the first channel */
+ unsigned uart_base;
+ /* number assigned to the first GPIO */
+ unsigned gpio_base;
+ char *label;
+ /* list of GPIO names (array length = SC16IS7X2_NR_GPIOS) */
+ const char *const *names;
+};
+
+#endif
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 6148 bytes --]
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH resend 2] Add sc16is7x2 driver
2011-01-07 15:14 [PATCH resend 2] Add sc16is7x2 driver Manuel Stahl
@ 2011-01-07 17:05 ` Randy Dunlap
2011-01-07 19:37 ` Greg KH
2011-01-12 11:05 ` Thomas Weber
2 siblings, 0 replies; 6+ messages in thread
From: Randy Dunlap @ 2011-01-07 17:05 UTC (permalink / raw)
To: Manuel Stahl
Cc: linux-serial, LKML, Thomas Weber, Johannes Reif, changgx, miguelangel
On Fri, 07 Jan 2011 16:14:34 +0100 Manuel Stahl wrote:
> IRQ is now implemented with work queue (like max3100).
> The problem is, that the sc16is7x2 has a low active irq. But with active
> low threaded_irqs, shared irqs are not allowed.
>
> Please try this one and report errors.
>
> Regards,
> Manuel Stahl
>
> PS: sorry that the patch is not inline, not sure how to configure
> thunderbird correctly
Please check to see if Documentation/email-clients.txt can help you,
although it may be a bit outdated.
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH resend 2] Add sc16is7x2 driver
2011-01-07 15:14 [PATCH resend 2] Add sc16is7x2 driver Manuel Stahl
2011-01-07 17:05 ` Randy Dunlap
@ 2011-01-07 19:37 ` Greg KH
2011-01-12 11:05 ` Thomas Weber
2 siblings, 0 replies; 6+ messages in thread
From: Greg KH @ 2011-01-07 19:37 UTC (permalink / raw)
To: Manuel Stahl
Cc: linux-serial, LKML, Thomas Weber, Johannes Reif, changgx, miguelangel
On Fri, Jan 07, 2011 at 04:14:34PM +0100, Manuel Stahl wrote:
> IRQ is now implemented with work queue (like max3100).
> The problem is, that the sc16is7x2 has a low active irq. But with
> active low threaded_irqs, shared irqs are not allowed.
>
> Please try this one and report errors.
>
> Regards,
> Manuel Stahl
>
> PS: sorry that the patch is not inline, not sure how to configure
> thunderbird correctly
Don't sign your emails, and that will help out in getting 'git am' to be
able to accept this.
Care to retry with the signature?
> --- /dev/null
> +++ b/include/linux/serial_sc16is7x2.h
This should go in include/linux/platform_data/ instead of the "main"
include/linux/ directory.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH resend 2] Add sc16is7x2 driver
2011-01-07 15:14 [PATCH resend 2] Add sc16is7x2 driver Manuel Stahl
2011-01-07 17:05 ` Randy Dunlap
2011-01-07 19:37 ` Greg KH
@ 2011-01-12 11:05 ` Thomas Weber
2011-01-12 12:01 ` Manuel Stahl
2 siblings, 1 reply; 6+ messages in thread
From: Thomas Weber @ 2011-01-12 11:05 UTC (permalink / raw)
To: Manuel Stahl
Cc: linux-serial, LKML, Thomas Weber, Johannes Reif, changgx, miguelangel
Am 07.01.2011 16:14, schrieb Manuel Stahl:
> IRQ is now implemented with work queue (like max3100).
> The problem is, that the sc16is7x2 has a low active irq. But with active
> low threaded_irqs, shared irqs are not allowed.
>
> Please try this one and report errors.
>
> Regards,
> Manuel Stahl
>
> PS: sorry that the patch is not inline, not sure how to configure
> thunderbird correctly
Hello Manuel,
There is a re-definition of PORT_SC16IS7X2 in serial_core.h
First time it is 20, second time it is 98.
Regards,
Thomas
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH resend 2] Add sc16is7x2 driver
2011-01-12 11:05 ` Thomas Weber
@ 2011-01-12 12:01 ` Manuel Stahl
2011-01-31 21:34 ` Thomas Weber
0 siblings, 1 reply; 6+ messages in thread
From: Manuel Stahl @ 2011-01-12 12:01 UTC (permalink / raw)
To: Thomas Weber
Cc: linux-serial, LKML, Thomas Weber, Johannes Reif, changgx, miguelangel
[-- Attachment #1: Type: text/plain, Size: 730 bytes --]
On 12.01.2011 12:05, Thomas Weber wrote:
> Am 07.01.2011 16:14, schrieb Manuel Stahl:
>> IRQ is now implemented with work queue (like max3100).
>> The problem is, that the sc16is7x2 has a low active irq. But with active
>> low threaded_irqs, shared irqs are not allowed.
>>
>> Please try this one and report errors.
>>
>> Regards,
>> Manuel Stahl
>>
>> PS: sorry that the patch is not inline, not sure how to configure
>> thunderbird correctly
>
> Hello Manuel,
>
> There is a re-definition of PORT_SC16IS7X2 in serial_core.h
> First time it is 20, second time it is 98.
A sorry, forgot to delete the line with 20.
Can someone already confirm that the this version works more reliably
than the last one?
Regards,
Manuel Stahl
[-- Attachment #2: manuel_stahl.vcf --]
[-- Type: text/x-vcard, Size: 170 bytes --]
begin:vcard
fn:Manuel Stahl
n:Stahl;Manuel
email;internet:manuel.stahl@iis.fraunhofer.de
tel;work:+49 911 58061-6419
x-mozilla-html:FALSE
version:2.1
end:vcard
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH resend 2] Add sc16is7x2 driver
2011-01-12 12:01 ` Manuel Stahl
@ 2011-01-31 21:34 ` Thomas Weber
0 siblings, 0 replies; 6+ messages in thread
From: Thomas Weber @ 2011-01-31 21:34 UTC (permalink / raw)
To: Manuel Stahl
Cc: linux-serial, LKML, Thomas Weber, Johannes Reif, changgx, miguelangel
2011/1/12 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>:
> On 12.01.2011 12:05, Thomas Weber wrote:
>>
>> Am 07.01.2011 16:14, schrieb Manuel Stahl:
>>>
>>> IRQ is now implemented with work queue (like max3100).
>>> The problem is, that the sc16is7x2 has a low active irq. But with active
>>> low threaded_irqs, shared irqs are not allowed.
>>>
>>> Please try this one and report errors.
>>>
>>> Regards,
>>> Manuel Stahl
>>>
>>> PS: sorry that the patch is not inline, not sure how to configure
>>> thunderbird correctly
>>
>> Hello Manuel,
>>
>> There is a re-definition of PORT_SC16IS7X2 in serial_core.h
>> First time it is 20, second time it is 98.
>
> A sorry, forgot to delete the line with 20.
>
> Can someone already confirm that the this version works more reliably than
> the last one?
>
> Regards,
> Manuel Stahl
>
Hello Manuel,
Johannes reworked the patch and tested it. He is at school in the
moment, but he told me that it works now. Can you send a fixed patch?
Thomas
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2011-01-31 21:34 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-07 15:14 [PATCH resend 2] Add sc16is7x2 driver Manuel Stahl
2011-01-07 17:05 ` Randy Dunlap
2011-01-07 19:37 ` Greg KH
2011-01-12 11:05 ` Thomas Weber
2011-01-12 12:01 ` Manuel Stahl
2011-01-31 21:34 ` Thomas Weber
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.