* [PATCH resend] Add sc16is7x2 driver
@ 2010-10-07 8:15 Manuel Stahl
2010-10-14 19:01 ` Greg KH
2010-11-08 14:01 ` Thomas Weber
0 siblings, 2 replies; 12+ messages in thread
From: Manuel Stahl @ 2010-10-07 8:15 UTC (permalink / raw)
To: linux-serial; +Cc: LKML, Greg KH, Andrew Morton
[-- Attachment #1: Type: text/plain, Size: 98 bytes --]
This patch adds support for the sc16is7x2 SPI to UART chips.
Each chip has two UARTs and 8 GPIOs.
[-- Attachment #2: sc16is7x2.patch --]
[-- Type: text/x-patch, Size: 38383 bytes --]
Signed-off-by: Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
---
drivers/serial/Kconfig | 9 +
drivers/serial/Makefile | 1 +
drivers/serial/sc16is7x2.c | 1325 +++++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 3 +-
include/linux/spi/sc16is7x2.h | 17 +
5 files changed, 1354 insertions(+), 1 deletions(-)
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 9278164..93c73ca 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 && GPIOLIB
+ 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 1ca4fd5..4c6a297 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..dbd1939
--- /dev/null
+++ b/drivers/serial/sc16is7x2.c
@@ -0,0 +1,1325 @@
+/**
+ * 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/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/spi/spi.h>
+#include <linux/freezer.h>
+#include <linux/spi/sc16is7x2.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/gpio.h>
+
+#define SC16IS7X2_MAJOR 204
+#define SC16IS7X2_MINOR 209
+#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 */
+
+/* Redefine some MCR bits */
+#ifdef UART_MCR_TCRTLR
+#undef UART_MCR_TCRTLR
+#endif
+#define UART_MCR_TCRTLR 0x04
+#define UART_MCR_IRDA 0x40
+
+/* 16bit SPI command to read or write a register */
+struct sc16is7x2_spi_reg {
+ u8 cmd;
+ u8 value;
+} __packed;
+
+struct sc16is7x2_chip;
+
+/*
+ * Some registers must be read back to modify.
+ * To save time we cache them here in memory.
+ * The @lock mutex is there to protect them.
+ */
+struct sc16is7x2_channel {
+ struct sc16is7x2_chip *chip; /* back link */
+ struct mutex lock;
+ struct uart_port uart;
+ struct spi_message fifo_message;
+ struct spi_transfer fifo_rx;
+ struct spi_transfer fifo_tx[3];
+ u8 iir;
+ u8 lsr;
+ u8 msr;
+ 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 *rx_buf;
+ u8 write_fifo_cmd;
+ u8 read_fifo_cmd;
+ bool active;
+};
+
+struct sc16is7x2_chip {
+ struct spi_device *spi;
+ struct gpio_chip gpio;
+ struct sc16is7x2_channel channel[2];
+
+ /* set to true to make the work thread exit as soon as possible */
+ bool force_end_work;
+
+ struct mutex 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 */
+};
+
+/* ******************************** SPI ********************************* */
+
+static u8 write_cmd(u8 reg, u8 ch)
+{
+ return REG_WRITE | (reg & 0xf) << 3 | (ch & 0x1) << 1;
+}
+
+static u8 read_cmd(u8 reg, u8 ch)
+{
+ return REG_READ | (reg & 0xf) << 3 | (ch & 0x1) << 1;
+}
+
+/*
+ * Reserve memory for command sequence
+ * @cnt number of commands
+ */
+static struct sc16is7x2_spi_reg *
+sc16is7x2_alloc_spi_cmds(unsigned cnt)
+{
+ return kcalloc(cnt, sizeof(struct sc16is7x2_spi_reg), GFP_KERNEL);
+}
+
+/*
+ * sc16is7x2_add_write_cmd - Add write command to sequence
+ */
+static void sc16is7x2_add_write_cmd(struct sc16is7x2_spi_reg *cmd,
+ u8 reg, u8 ch, u8 value)
+{
+ cmd->cmd = write_cmd(reg, ch);
+ cmd->value = value;
+}
+
+/*
+ * sc16is7x2_add_read_cmd - Add read command to sequence
+ */
+static void sc16is7x2_add_read_cmd(struct sc16is7x2_spi_reg *cmd,
+ u8 reg, u8 ch)
+{
+ cmd->cmd = read_cmd(reg, ch);
+ cmd->value = 0;
+}
+
+/*
+ * sc16is7x2_complete - Completion handler for async SPI transfers
+ */
+static void sc16is7x2_complete(void *context)
+{
+ struct spi_message *m = context;
+ u8 *tx_chain = m->state;
+
+ kfree(tx_chain);
+ kfree(m);
+}
+
+/*
+ * sc16is7x2_spi_async - Send command sequence
+ */
+static int sc16is7x2_spi_async(struct spi_device *spi,
+ struct sc16is7x2_spi_reg *cmds, unsigned len)
+{
+ struct spi_transfer *t;
+ struct spi_message *m;
+
+ m = spi_message_alloc(len, GFP_KERNEL);
+ if (!m)
+ return -ENOMEM;
+
+ m->complete = sc16is7x2_complete;
+ m->context = m;
+ m->state = cmds;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ t->tx_buf = (u8 *)cmds;
+ t->len = 2;
+ t->cs_change = true;
+ cmds++;
+ }
+
+ return spi_async(spi, m);
+}
+
+/*
+ * sc16is7x2_write_async - Write a new register content (async)
+ */
+static int sc16is7x2_write_async(struct spi_device *spi, u8 reg, u8 ch,
+ u8 value)
+{
+ struct sc16is7x2_spi_reg *cmd = sc16is7x2_alloc_spi_cmds(1);
+
+ if (!cmd)
+ return -ENOMEM;
+ sc16is7x2_add_write_cmd(cmd, reg, ch, value);
+ return sc16is7x2_spi_async(spi, cmd, 1);
+}
+
+/*
+ * sc16is7x2_write - Write a new register content (sync)
+ */
+static int sc16is7x2_write(struct spi_device *spi, u8 reg, u8 ch, u8 val)
+{
+ struct sc16is7x2_spi_reg out;
+
+ out.cmd = write_cmd(reg, ch);
+ out.value = val;
+ return spi_write(spi, (const u8 *)&out, sizeof(out));
+}
+
+/**
+ * sc16is7x2_read - Read back register content
+ * @spi: The SPI device
+ * @reg: Register offset
+ * @ch: Channel (0 or 1)
+ *
+ * Returns positive 8 bit value from device if successful or a
+ * negative value on error
+ */
+static int sc16is7x2_read(struct spi_device *spi, unsigned reg, unsigned ch)
+{
+ return spi_w8r8(spi, read_cmd(reg, ch));
+}
+
+/* ******************************** UART ********************************* */
+
+/* Uart divisor latch write */
+static void sc16is7x2_add_dl_write_cmd(struct sc16is7x2_spi_reg *cmd,
+ u8 ch, int value)
+{
+ sc16is7x2_add_write_cmd(&cmd[0], UART_DLL, ch, value & 0xff);
+ sc16is7x2_add_write_cmd(&cmd[1], UART_DLM, ch, value >> 8 & 0xff);
+}
+
+static unsigned int sc16is7x2_tx_empty(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned lsr;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ 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 =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned int status;
+ unsigned int ret;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ 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 unsigned int __set_mctrl(unsigned int mctrl)
+{
+ unsigned char mcr = 0;
+
+ if (mctrl & TIOCM_RTS)
+ mcr |= UART_MCR_RTS;
+ if (mctrl & TIOCM_DTR)
+ mcr |= UART_MCR_DTR;
+ if (mctrl & TIOCM_LOOP)
+ mcr |= UART_MCR_LOOP;
+
+ return mcr;
+}
+
+static void sc16is7x2_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned ch = port->line & 0x01;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+ sc16is7x2_write_async(ts->spi, UART_MCR, ch, __set_mctrl(mctrl));
+}
+
+static void __stop_tx(struct sc16is7x2_channel *chan)
+{
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned ch = chan->uart.line & 0x01;
+
+ if (chan->ier & UART_IER_THRI) {
+ chan->ier &= ~UART_IER_THRI;
+ sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier);
+ }
+}
+
+static void sc16is7x2_stop_tx(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ __stop_tx(chan);
+}
+
+static void sc16is7x2_start_tx(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned ch = port->line & 0x01;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ if (!(chan->ier & UART_IER_THRI)) {
+ chan->ier |= UART_IER_THRI;
+ sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier);
+ }
+}
+
+static void sc16is7x2_stop_rx(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned ch = port->line & 0x01;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ chan->ier &= ~UART_IER_RLSI;
+ chan->uart.read_status_mask &= ~UART_LSR_DR;
+ sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier);
+}
+
+static void sc16is7x2_enable_ms(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned ch = port->line & 0x01;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ chan->ier |= UART_IER_MSI;
+ sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier);
+}
+
+static void sc16is7x2_break_ctl(struct uart_port *port, int break_state)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned ch = port->line & 0x01;
+ unsigned long flags;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+
+ spin_lock_irqsave(&chan->uart.lock, flags);
+ if (break_state == -1)
+ chan->lcr |= UART_LCR_SBC;
+ else
+ chan->lcr &= ~UART_LCR_SBC;
+ spin_unlock_irqrestore(&chan->uart.lock, flags);
+
+ sc16is7x2_write_async(ts->spi, UART_LCR, ch, chan->lcr);
+}
+
+static int sc16is7x2_startup(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+ unsigned ch = port->line & 0x01;
+ struct sc16is7x2_spi_reg *cmds, *cmd;
+ unsigned long flags;
+
+ dev_dbg(&ts->spi->dev, "%s (line %d)\n", __func__, port->line);
+
+ spin_lock_irqsave(&chan->uart.lock, flags);
+ chan->lcr = UART_LCR_WLEN8;
+ chan->mcr = __set_mctrl(chan->uart.mctrl);
+ chan->fcr = 0;
+ chan->ier = UART_IER_RLSI | UART_IER_RDI;
+ spin_unlock_irqrestore(&chan->uart.lock, flags);
+
+ cmds = sc16is7x2_alloc_spi_cmds(8);
+ if (!cmds)
+ return -ENOMEM;
+
+ cmd = cmds;
+ /* Clear the interrupt registers. */
+ sc16is7x2_add_write_cmd(cmd, UART_IER, ch, 0);
+ sc16is7x2_add_read_cmd(++cmd, UART_IIR, ch);
+ sc16is7x2_add_read_cmd(++cmd, UART_LSR, ch);
+ sc16is7x2_add_read_cmd(++cmd, UART_MSR, ch);
+
+ sc16is7x2_add_write_cmd(++cmd, UART_FCR, ch, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ sc16is7x2_add_write_cmd(++cmd, UART_FCR, ch, chan->fcr);
+ /* Now, initialize the UART */
+ sc16is7x2_add_write_cmd(++cmd, UART_LCR, ch, chan->lcr);
+ sc16is7x2_add_write_cmd(++cmd, UART_MCR, ch, chan->mcr);
+
+ sc16is7x2_spi_async(ts->spi, cmds, 8);
+
+ chan->active = true;
+ return 0;
+}
+
+static void sc16is7x2_shutdown(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ 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);
+
+ /* Disable interrupts from this port */
+ chan->ier = 0;
+ chan->active = false;
+ sc16is7x2_write(ts->spi, UART_IER, ch, chan->ier);
+
+ /* Wait for worker of this channel to finish */
+ mutex_lock(&chan->lock);
+
+ spin_lock_irqsave(&chan->uart.lock, flags);
+ chan->mcr = __set_mctrl(chan->uart.mctrl);
+ spin_unlock_irqrestore(&chan->uart.lock, flags);
+
+ /* Disable break condition and FIFOs */
+ chan->lcr &= ~UART_LCR_SBC;
+
+ sc16is7x2_write(ts->spi, UART_MCR, ch, chan->mcr);
+ sc16is7x2_write(ts->spi, UART_LCR, ch, chan->lcr);
+
+ mutex_unlock(&chan->lock);
+}
+
+static void
+sc16is7x2_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+ struct sc16is7x2_spi_reg *cmds;
+ unsigned ch = port->line & 0x01;
+ unsigned long flags;
+ unsigned int baud, quot;
+ u8 ier, mcr, lcr, fcr = 0;
+ u8 efr = UART_EFR_ECB;
+
+ /* 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
+
+ /* Ask the core to calculate the divisor for us. */
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0xffff,
+ port->uartclk / 16);
+ quot = uart_get_divisor(port, baud);
+
+ dev_dbg(&ts->spi->dev, "%s (baud %u)\n", __func__, baud);
+
+ 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;
+
+ /*
+ * MCR-based auto flow control. When AFE is enabled, RTS will be
+ * deasserted when the receive FIFO contains more characters than
+ * the trigger, or the MCR RTS bit is cleared. In the case where
+ * the remote UART is not using CTS auto flow control, we must
+ * have sufficient FIFO entries for the latency of the remote
+ * UART to respond. IOW, at least 32 bytes of FIFO.
+ */
+ chan->mcr &= ~UART_MCR_AFE;
+ if (termios->c_cflag & CRTSCTS)
+ chan->mcr |= UART_MCR_AFE;
+
+ /*
+ * 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;
+
+ if (termios->c_cflag & CRTSCTS)
+ efr |= UART_EFR_CTS | UART_EFR_RTS;
+
+ mcr = __set_mctrl(chan->uart.mctrl);
+ ier = chan->ier;
+ chan->lcr = lcr; /* Save LCR */
+ chan->fcr = fcr; /* Save FCR */
+ chan->mcr = mcr; /* Save MCR */
+
+ fcr |= UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
+
+ spin_unlock_irqrestore(&chan->uart.lock, flags);
+
+ /* build a compound spi message to set all registers */
+ cmds = sc16is7x2_alloc_spi_cmds(9);
+ if (!cmds)
+ return;
+
+ /* set DLAB */
+ sc16is7x2_add_write_cmd(&cmds[0], UART_LCR, ch, UART_LCR_DLAB);
+ /* set divisor, must be set before UART_EFR_ECB */
+ sc16is7x2_add_dl_write_cmd(&cmds[1], ch, quot);
+ sc16is7x2_add_write_cmd(&cmds[3], UART_LCR, ch, 0xBF); /* access EFR */
+ sc16is7x2_add_write_cmd(&cmds[4], UART_EFR, ch, efr);
+ sc16is7x2_add_write_cmd(&cmds[5], UART_LCR, ch, lcr); /* reset DLAB */
+ sc16is7x2_add_write_cmd(&cmds[6], UART_FCR, ch, fcr);
+ sc16is7x2_add_write_cmd(&cmds[7], UART_MCR, ch, mcr);
+ sc16is7x2_add_write_cmd(&cmds[8], UART_IER, ch, ier);
+
+ sc16is7x2_spi_async(ts->spi, cmds, 9);
+
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+}
+
+static const char *
+sc16is7x2_type(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+ return TYPE_NAME;
+}
+
+static void sc16is7x2_release_port(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+ ts->force_end_work = true;
+}
+
+static int sc16is7x2_request_port(struct uart_port *port)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+ return 0;
+}
+
+static void sc16is7x2_config_port(struct uart_port *port, int flags)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ 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)
+{
+ struct sc16is7x2_channel *chan =
+ container_of(port, struct sc16is7x2_channel, uart);
+ struct sc16is7x2_chip *ts = chan->chip;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __func__);
+ if (ser->irq < 0 || ser->baud_base < 9600 ||
+ ser->type != PORT_SC16IS7X2)
+ return -EINVAL;
+ return 0;
+}
+
+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 ********************************* */
+
+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->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->spi, REG_IOC, 0, ts->io_control);
+ }
+
+ mutex_unlock(&ts->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->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->spi, REG_IOC, 0, ts->io_control);
+ }
+
+ mutex_unlock(&ts->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->lock);
+
+ ts->io_dir &= ~BIT(offset);
+ io_dir = ts->io_dir;
+
+ mutex_unlock(&ts->lock);
+
+ return sc16is7x2_write_async(ts->spi, 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);
+ struct sc16is7x2_spi_reg *cmds;
+
+ BUG_ON(offset > 8);
+
+ mutex_lock(&ts->lock);
+
+ if (value)
+ ts->io_state |= BIT(offset);
+ else
+ ts->io_state &= ~BIT(offset);
+
+ ts->io_dir |= BIT(offset);
+
+ cmds = sc16is7x2_alloc_spi_cmds(2);
+ if (cmds) {
+ sc16is7x2_add_write_cmd(&cmds[0], REG_IOS, 0, ts->io_state);
+ sc16is7x2_add_write_cmd(&cmds[1], REG_IOD, 0, ts->io_dir);
+ }
+
+ mutex_unlock(&ts->lock);
+
+ return sc16is7x2_spi_async(ts->spi, cmds, 2);
+}
+
+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->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->spi, REG_IOS, 0);
+ if (level >= 0) {
+ ts->io_state = level;
+ level = (ts->io_state >> offset) & 0x01;
+ }
+ }
+
+ mutex_unlock(&ts->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->lock);
+
+ if (value)
+ ts->io_state |= BIT(offset);
+ else
+ ts->io_state &= ~BIT(offset);
+ io_state = ts->io_state;
+
+ mutex_unlock(&ts->lock);
+
+ sc16is7x2_write_async(ts->spi, REG_IOS, 0, io_state);
+}
+
+/* ******************************** IRQ ********************************* */
+
+static void sc16is7x2_handle_fifo_rx(struct sc16is7x2_channel *chan)
+{
+ struct uart_port *uart = &chan->uart;
+ struct tty_struct *tty = uart->state->port.tty;
+ u8 *rxbuf = chan->fifo_rx.rx_buf;
+ u8 lsr = chan->lsr;
+ unsigned i, count = chan->fifo_rx.len;
+ unsigned long flags;
+ char flag = TTY_NORMAL;
+
+ 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++;
+
+ /*
+ * Mask off conditions which should be ignored.
+ */
+ lsr &= chan->uart.read_status_mask;
+
+ if (lsr & UART_LSR_BI)
+ flag = TTY_BREAK;
+ else if (lsr & UART_LSR_PE)
+ flag = TTY_PARITY;
+ else if (lsr & UART_LSR_FE)
+ flag = TTY_FRAME;
+ }
+
+ for (i = 1; i < count; i++) {
+ uart->icount.rx++;
+
+ if (!uart_handle_sysrq_char(uart, rxbuf[i]))
+ uart_insert_char(uart, lsr, UART_LSR_OE,
+ rxbuf[i], flag);
+ }
+
+ignore_char:
+ spin_unlock_irqrestore(&uart->lock, flags);
+
+ if (count > 1)
+ tty_flip_buffer_push(tty);
+}
+
+static void sc16is7x2_handle_fifo_tx(struct sc16is7x2_channel *chan)
+{
+ struct uart_port *uart = &chan->uart;
+ struct circ_buf *xmit = &uart->state->xmit;
+ unsigned count = chan->fifo_tx[1].len + chan->fifo_tx[2].len;
+ unsigned long flags;
+
+ BUG_ON(!uart);
+ BUG_ON(!xmit);
+
+ spin_lock_irqsave(&uart->lock, flags);
+
+ uart->icount.tx += count;
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(uart);
+
+ if (uart_circ_empty(xmit))
+ __stop_tx(chan);
+
+ spin_unlock_irqrestore(&uart->lock, flags);
+}
+
+static bool sc16is7x2_msg_add_fifo_rx(struct sc16is7x2_chip *ts, unsigned ch)
+{
+ struct spi_message *m = &(ts->channel[ch].fifo_message);
+ struct spi_transfer *t = &(ts->channel[ch].fifo_rx);
+ int rxlvl = sc16is7x2_read(ts->spi, REG_RXLVL, ch);
+
+ if (rxlvl > 0) {
+ dev_dbg(&ts->spi->dev, "%s (%i) %d bytes\n",
+ __func__, ch, rxlvl);
+
+ t->len = rxlvl + 1;
+ spi_message_add_tail(t, m);
+ return true;
+ }
+ return false;
+}
+
+static bool sc16is7x2_msg_add_fifo_tx(struct sc16is7x2_chip *ts, unsigned ch)
+{
+ struct sc16is7x2_channel * const chan = &(ts->channel[ch]);
+ struct uart_port *uart = &chan->uart;
+ struct circ_buf *xmit = &uart->state->xmit;
+ unsigned count;
+ bool split_transfer;
+ u8 txlvl;
+
+ if (chan->uart.x_char && chan->lsr & UART_LSR_THRE) {
+ dev_dbg(&ts->spi->dev, "tx: x-char\n");
+ sc16is7x2_write(ts->spi, UART_TX, ch, uart->x_char);
+ uart->icount.tx++;
+ uart->x_char = 0;
+ return false;
+ }
+ if (uart_tx_stopped(&chan->uart)) {
+ dev_dbg(&ts->spi->dev, "tx: stopped!\n");
+ sc16is7x2_stop_tx(uart);
+ return false;
+ }
+ if (uart_circ_empty(xmit)) {
+ __stop_tx(chan);
+ return false;
+ }
+
+ txlvl = sc16is7x2_read(ts->spi, REG_TXLVL, ch);
+ if (txlvl == 0) {
+ dev_dbg(&ts->spi->dev, " fifo full\n");
+ return false;
+ }
+
+ /* number of bytes to transfer to the fifo */
+ count = min(txlvl, (u8)uart_circ_chars_pending(xmit));
+ split_transfer = (UART_XMIT_SIZE - xmit->tail) <= count;
+
+ dev_dbg(&ts->spi->dev, "%s (%i) %d bytes %s\n", __func__, ch, count,
+ split_transfer ? "(split)" : "");
+
+ /* add command transfer */
+ spi_message_add_tail(&(chan->fifo_tx[0]), &(chan->fifo_message));
+ /* add first fifo transfer */
+ spi_message_add_tail(&(chan->fifo_tx[1]), &(chan->fifo_message));
+
+ chan->fifo_tx[1].tx_buf = xmit->buf + xmit->tail;
+
+ if (!split_transfer) {
+ chan->fifo_tx[1].len = count;
+ chan->fifo_tx[1].cs_change = true;
+
+ chan->fifo_tx[2].len = 0;
+ } else {
+ chan->fifo_tx[1].len = (UART_XMIT_SIZE - 1) - xmit->tail;
+ chan->fifo_tx[1].cs_change = false;
+
+ chan->fifo_tx[2].tx_buf = xmit->buf;
+ chan->fifo_tx[2].cs_change = true;
+ chan->fifo_tx[2].len = count - chan->fifo_tx[1].len;
+ /* add second fifo transfer */
+ spi_message_add_tail(&(chan->fifo_tx[2]),
+ &(chan->fifo_message));
+ }
+
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ return true;
+}
+
+static void sc16is7x2_handle_modem(struct sc16is7x2_chip *ts, unsigned ch)
+{
+ struct sc16is7x2_channel *chan = &(ts->channel[ch]);
+ struct uart_port *uart = &chan->uart;
+
+ if (chan->msr & UART_MSR_ANY_DELTA
+ && chan->ier & UART_IER_MSI
+ && uart->state != NULL) {
+ if (chan->msr & UART_MSR_TERI)
+ uart->icount.rng++;
+ if (chan->msr & UART_MSR_DDSR)
+ uart->icount.dsr++;
+ if (chan->msr & UART_MSR_DDCD)
+ uart_handle_dcd_change(uart, chan->msr & UART_MSR_DCD);
+ if (chan->msr & UART_MSR_DCTS)
+ uart_handle_cts_change(uart, chan->msr & UART_MSR_CTS);
+
+ wake_up_interruptible(&uart->state->port.delta_msr_wait);
+ }
+}
+
+static void sc16is7x2_read_status(struct sc16is7x2_chip *ts, unsigned ch)
+{
+ struct sc16is7x2_channel *chan = &(ts->channel[ch]);
+
+ chan->ier = sc16is7x2_read(ts->spi, UART_IER, ch);
+ chan->iir = sc16is7x2_read(ts->spi, UART_IIR, ch);
+ chan->msr = sc16is7x2_read(ts->spi, UART_MSR, ch);
+ chan->lsr = sc16is7x2_read(ts->spi, UART_LSR, ch);
+
+ dev_dbg(&ts->spi->dev, "%s ier = 0x%02x iir = 0x%02x\n",
+ __func__, chan->ier, chan->iir);
+}
+
+static irqreturn_t sc16is7x2_work(int irq, void *data)
+{
+ struct sc16is7x2_channel *chan = data;
+ struct sc16is7x2_chip *ts = chan->chip;
+ struct spi_message *m = &(chan->fifo_message);
+ unsigned ch = (chan == ts->channel) ? 0 : 1;
+ bool rx, tx;
+
+ dev_dbg(&ts->spi->dev, "%s (%i)\n", __func__, ch);
+
+ sc16is7x2_read_status(ts, ch);
+
+ while ((chan->iir & UART_IIR_NO_INT) == 0x00
+ && !ts->force_end_work) {
+ sc16is7x2_handle_modem(ts, ch);
+
+ spi_message_init(m);
+ rx = sc16is7x2_msg_add_fifo_rx(ts, ch);
+ tx = sc16is7x2_msg_add_fifo_tx(ts, ch);
+
+ if (rx || tx)
+ spi_sync(ts->spi, m);
+
+ if (rx)
+ sc16is7x2_handle_fifo_rx(chan);
+ if (tx)
+ sc16is7x2_handle_fifo_tx(chan);
+
+ sc16is7x2_read_status(ts, ch);
+ }
+ dev_dbg(&ts->spi->dev, "%s finished (iir = 0x%02x)\n",
+ __func__, chan->iir);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sc16is7x2_irq(int irq, void *data)
+{
+ struct sc16is7x2_channel *chan = data;
+
+ /* It takes too long to read the regs over SPI,
+ * so just wake up the thread */
+ if (chan->active)
+ return IRQ_WAKE_THREAD;
+ else
+ return IRQ_NONE;
+}
+
+/* ******************************** INIT ********************************* */
+
+static struct uart_driver sc16is7x2_uart_driver;
+
+static int sc16is7x2_register_gpio(struct sc16is7x2_chip *ts,
+ struct sc16is7x2_platform_data *pdata)
+{
+ struct sc16is7x2_spi_reg *cmds;
+
+ 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;
+
+ /* disable all GPIOs, enable on request */
+ ts->io_dir = 0x00;
+ ts->io_state = 0;
+ ts->io_gpio = 0;
+ ts->io_control = IOC_GPIO30 | IOC_GPIO74;
+
+ cmds = sc16is7x2_alloc_spi_cmds(4);
+ if (!cmds)
+ return -ENOMEM;
+
+ sc16is7x2_add_write_cmd(&cmds[0], REG_IOI, 0, 0);
+ sc16is7x2_add_write_cmd(&cmds[1], REG_IOC, 0, ts->io_control);
+ sc16is7x2_add_write_cmd(&cmds[2], REG_IOS, 0, ts->io_state);
+ sc16is7x2_add_write_cmd(&cmds[3], REG_IOD, 0, ts->io_dir);
+ sc16is7x2_spi_async(ts->spi, cmds, 4);
+
+ return gpiochip_add(&ts->gpio);
+}
+
+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;
+ int ret;
+
+ mutex_init(&chan->lock);
+ chan->active = false; /* will be set in startup */
+ chan->chip = ts;
+
+ chan->rx_buf = kzalloc(FIFO_SIZE+1, GFP_KERNEL);
+ if (chan->rx_buf == NULL)
+ return -ENOMEM;
+
+ chan->read_fifo_cmd = read_cmd(UART_RX, ch);
+ chan->fifo_rx.cs_change = true;
+ chan->fifo_rx.tx_buf = &(chan->read_fifo_cmd);
+ chan->fifo_rx.rx_buf = chan->rx_buf;
+
+
+ chan->write_fifo_cmd = write_cmd(UART_TX, ch);
+ chan->fifo_tx[0].tx_buf = &(chan->write_fifo_cmd);
+ chan->fifo_tx[0].rx_buf = NULL;
+ chan->fifo_tx[0].len = 1;
+ chan->fifo_tx[0].cs_change = false;
+ chan->fifo_tx[1].rx_buf = NULL;
+ chan->fifo_tx[2].rx_buf = NULL;
+
+ 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;
+
+ ret = request_threaded_irq(uart->irq, sc16is7x2_irq, sc16is7x2_work,
+ IRQF_TRIGGER_FALLING | IRQF_SHARED, DRIVER_NAME, chan);
+ if (ret) {
+ dev_warn(&ts->spi->dev, "cannot register interrupt\n");
+ goto exit_buf;
+ }
+
+ return uart_add_one_port(&sc16is7x2_uart_driver, uart);
+
+exit_buf:
+ kfree(chan->rx_buf);
+ return ret;
+}
+
+static int sc16is7x2_unregister_uart_port(struct sc16is7x2_chip *ts,
+ unsigned channel)
+{
+ struct sc16is7x2_channel *chan = &ts->channel[channel];
+ int ret;
+
+ free_irq(chan->uart.irq, chan);
+ kfree(&chan->rx_buf);
+ ret = uart_remove_one_port(&sc16is7x2_uart_driver, &chan->uart);
+ if (ret)
+ dev_err(&ts->spi->dev, "Failed to remove the UART port %c: %d\n",
+ 'A' + channel, ret);
+
+ return ret;
+}
+
+
+static int __devinit sc16is7x2_probe(struct spi_device *spi)
+{
+ struct sc16is7x2_chip *ts;
+ struct sc16is7x2_platform_data *pdata;
+ int ret;
+
+ pdata = spi->dev.platform_data;
+ if (!pdata || !pdata->gpio_base /* || pdata->uart_base */) {
+ dev_dbg(&spi->dev, "incorrect or missing platform data\n");
+ return -EINVAL;
+ }
+
+ ts = kzalloc(sizeof(struct sc16is7x2_chip), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ mutex_init(&ts->lock);
+ spi_set_drvdata(spi, ts);
+ ts->spi = spi;
+ ts->force_end_work = true;
+
+ /* Reset the chip TODO: and disable IRQ output */
+ sc16is7x2_write(spi, 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;
+
+ ts->force_end_work = false;
+
+ printk(KERN_INFO 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 ret;
+
+exit_uart1:
+ sc16is7x2_unregister_uart_port(ts, 1);
+
+exit_uart0:
+ sc16is7x2_unregister_uart_port(ts, 0);
+
+exit_destroy:
+ dev_set_drvdata(&spi->dev, NULL);
+ mutex_destroy(&ts->lock);
+ 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;
+
+ ts->force_end_work = true;
+
+ ret = sc16is7x2_unregister_uart_port(ts, 0);
+ if (ret)
+ goto exit_error;
+ ret = sc16is7x2_unregister_uart_port(ts, 1);
+ if (ret)
+ goto exit_error;
+ ret = gpiochip_remove(&ts->gpio);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n",
+ ret);
+ goto exit_error;
+ }
+
+ mutex_destroy(&ts->lock);
+ kfree(ts);
+
+exit_error:
+ return ret;
+}
+
+static struct uart_driver sc16is7x2_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = "eser",
+ .major = SC16IS7X2_MAJOR,
+ .minor = SC16IS7X2_MINOR,
+ .nr = MAX_SC16IS7X2,
+};
+
+static struct spi_driver sc16is7x2_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = sc16is7x2_probe,
+ .remove = __devexit_p(sc16is7x2_remove),
+};
+
+static int __init sc16is7x2_init(void)
+{
+ int ret = uart_register_driver(&sc16is7x2_uart_driver);
+
+ if (ret) {
+ printk(KERN_ERR "Couldn't register sc16is7x2 uart driver\n");
+ return ret;
+ }
+
+ return spi_register_driver(&sc16is7x2_spi_driver);
+}
+/* register after spi postcore initcall and before
+ * subsys initcalls that may rely on these GPIOs
+ */
+subsys_initcall(sc16is7x2_init);
+
+static void __exit sc16is7x2_exit(void)
+{
+ uart_unregister_driver(&sc16is7x2_uart_driver);
+ spi_unregister_driver(&sc16is7x2_spi_driver);
+}
+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 563e234..f330965 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
diff --git a/include/linux/spi/sc16is7x2.h b/include/linux/spi/sc16is7x2.h
new file mode 100755
index 0000000..931fe50
--- /dev/null
+++ b/include/linux/spi/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
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
2010-10-07 8:15 [PATCH resend] Add sc16is7x2 driver Manuel Stahl
@ 2010-10-14 19:01 ` Greg KH
2010-10-15 7:24 ` Manuel Stahl
2010-11-08 14:01 ` Thomas Weber
1 sibling, 1 reply; 12+ messages in thread
From: Greg KH @ 2010-10-14 19:01 UTC (permalink / raw)
To: Manuel Stahl; +Cc: linux-serial, LKML, Andrew Morton
On Thu, Oct 07, 2010 at 10:15:07AM +0200, Manuel Stahl wrote:
> --- /dev/null
> +++ b/include/linux/spi/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
Why is this needed in a .h file? What other .c file uses this define
and structure?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
2010-10-14 19:01 ` Greg KH
@ 2010-10-15 7:24 ` Manuel Stahl
[not found] ` <20101015142709.GA18905@kroah.com>
0 siblings, 1 reply; 12+ messages in thread
From: Manuel Stahl @ 2010-10-15 7:24 UTC (permalink / raw)
Cc: linux-serial, LKML, Andrew Morton
[-- Attachment #1: Type: text/plain, Size: 1297 bytes --]
On 14.10.2010 21:01, Greg KH wrote:
> On Thu, Oct 07, 2010 at 10:15:07AM +0200, Manuel Stahl wrote:
>> --- /dev/null
>> +++ b/include/linux/spi/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
>
> Why is this needed in a .h file? What other .c file uses this define
> and structure?
>
> thanks,
>
> greg k-h
The board setup code can pass information about the uart and gpio
enumeration. It can also define names for the GPIOs. Futhermore the base
clock of the chip must be known, as it can be clocked by an external device.
Regards,
--
Manuel Stahl
Fraunhofer-Institut IIS
Leistungsoptimierte Systeme
Nordostpark 93
D90411 Nürnberg
Telefon +49 (0)911/58061-6419
Fax +49 (0)911/58061-6398
E-Mail manuel.stahl@iis.fraunhofer.de
http://www.iis.fraunhofer.de
http://www.smart-power.fraunhofer.de
[-- Attachment #2: manuel_stahl.vcf --]
[-- Type: text/x-vcard, Size: 161 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] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
[not found] ` <20101015142709.GA18905@kroah.com>
@ 2010-10-15 15:09 ` Manuel Stahl
2010-10-15 16:49 ` Greg KH
0 siblings, 1 reply; 12+ messages in thread
From: Manuel Stahl @ 2010-10-15 15:09 UTC (permalink / raw)
To: Greg KH; +Cc: linux-serial, LKML, Andrew Morton
[-- Attachment #1: Type: text/plain, Size: 1680 bytes --]
On 15.10.2010 16:27, Greg KH wrote:
> On Fri, Oct 15, 2010 at 09:24:34AM +0200, Manuel Stahl wrote:
>> On 14.10.2010 21:01, Greg KH wrote:
>>> On Thu, Oct 07, 2010 at 10:15:07AM +0200, Manuel Stahl wrote:
>>>> --- /dev/null
>>>> +++ b/include/linux/spi/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
>>>
>>> Why is this needed in a .h file? What other .c file uses this define
>>> and structure?
>>>
>>> thanks,
>>>
>>> greg k-h
>>
>> The board setup code can pass information about the uart and gpio
>> enumeration.
>
> What "board setup code"? I don't see that here in the patch anywhere.
SPI devices are generally set up for a specific hardware board. I.e. for
some ARM boards there is setup code in
linux/arch/arm/mach-at91/board-<xyz>.c
I use the sc16is7x2 for a board that is not yet in vanilla, so the board
setup code is not included in the patch.
Regards,
--
Manuel Stahl
Fraunhofer-Institut IIS
Leistungsoptimierte Systeme
Nordostpark 93
D90411 Nürnberg
Telefon +49 (0)911/58061-6419
Fax +49 (0)911/58061-6398
E-Mail manuel.stahl@iis.fraunhofer.de
http://www.iis.fraunhofer.de
http://www.smart-power.fraunhofer.de
[-- 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] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
2010-10-15 15:09 ` Manuel Stahl
@ 2010-10-15 16:49 ` Greg KH
2010-10-18 9:46 ` Manuel Stahl
0 siblings, 1 reply; 12+ messages in thread
From: Greg KH @ 2010-10-15 16:49 UTC (permalink / raw)
To: Manuel Stahl; +Cc: linux-serial, LKML, Andrew Morton
On Fri, Oct 15, 2010 at 05:09:03PM +0200, Manuel Stahl wrote:
> On 15.10.2010 16:27, Greg KH wrote:
>> On Fri, Oct 15, 2010 at 09:24:34AM +0200, Manuel Stahl wrote:
>>> On 14.10.2010 21:01, Greg KH wrote:
>>>> On Thu, Oct 07, 2010 at 10:15:07AM +0200, Manuel Stahl wrote:
>>>>> --- /dev/null
>>>>> +++ b/include/linux/spi/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
>>>>
>>>> Why is this needed in a .h file? What other .c file uses this define
>>>> and structure?
>>>>
>>>> thanks,
>>>>
>>>> greg k-h
>>>
>>> The board setup code can pass information about the uart and gpio
>>> enumeration.
>>
>> What "board setup code"? I don't see that here in the patch anywhere.
>
> SPI devices are generally set up for a specific hardware board. I.e. for
> some ARM boards there is setup code in
> linux/arch/arm/mach-at91/board-<xyz>.c
>
> I use the sc16is7x2 for a board that is not yet in vanilla, so the board
> setup code is not included in the patch.
Then why not add this header (or move it to the header file) when your
board setup code is merged? No need to have it there now, right?
Care to resend this driver changed that way?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
2010-10-15 16:49 ` Greg KH
@ 2010-10-18 9:46 ` Manuel Stahl
2010-10-19 15:37 ` Greg KH
0 siblings, 1 reply; 12+ messages in thread
From: Manuel Stahl @ 2010-10-18 9:46 UTC (permalink / raw)
To: Greg KH; +Cc: linux-serial, LKML, Andrew Morton
[-- Attachment #1: Type: text/plain, Size: 2421 bytes --]
On 15.10.2010 18:49, Greg KH wrote:
> On Fri, Oct 15, 2010 at 05:09:03PM +0200, Manuel Stahl wrote:
>> On 15.10.2010 16:27, Greg KH wrote:
>>> On Fri, Oct 15, 2010 at 09:24:34AM +0200, Manuel Stahl wrote:
>>>> On 14.10.2010 21:01, Greg KH wrote:
>>>>> On Thu, Oct 07, 2010 at 10:15:07AM +0200, Manuel Stahl wrote:
>>>>>> --- /dev/null
>>>>>> +++ b/include/linux/spi/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
>>>>>
>>>>> Why is this needed in a .h file? What other .c file uses this define
>>>>> and structure?
>>>>>
>>>>> thanks,
>>>>>
>>>>> greg k-h
>>>>
>>>> The board setup code can pass information about the uart and gpio
>>>> enumeration.
>>>
>>> What "board setup code"? I don't see that here in the patch anywhere.
>>
>> SPI devices are generally set up for a specific hardware board. I.e. for
>> some ARM boards there is setup code in
>> linux/arch/arm/mach-at91/board-<xyz>.c
>>
>> I use the sc16is7x2 for a board that is not yet in vanilla, so the board
>> setup code is not included in the patch.
>
> Then why not add this header (or move it to the header file) when your
> board setup code is merged? No need to have it there now, right?
Cause everyone who want's to use this driver for his board needs the
header. The board I use is a prototype, nearly the same as some Atmel
Demo board, but with the sc16is752 attached to the SPI port. Therefore I
don't see any benefit in publishing my board setup code.
> Care to resend this driver changed that way?
As I said the header definitely belongs to the driver, not to the board.
Regards,
--
Manuel Stahl
Fraunhofer-Institut IIS
Leistungsoptimierte Systeme
Nordostpark 93
D90411 Nürnberg
Telefon +49 (0)911/58061-6419
Fax +49 (0)911/58061-6398
E-Mail manuel.stahl@iis.fraunhofer.de
http://www.iis.fraunhofer.de
http://www.smart-power.fraunhofer.de
[-- Attachment #2: manuel_stahl.vcf --]
[-- Type: text/x-vcard, Size: 161 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] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
2010-10-18 9:46 ` Manuel Stahl
@ 2010-10-19 15:37 ` Greg KH
2010-10-26 8:55 ` Manuel Stahl
0 siblings, 1 reply; 12+ messages in thread
From: Greg KH @ 2010-10-19 15:37 UTC (permalink / raw)
To: Manuel Stahl; +Cc: linux-serial, LKML, Andrew Morton
On Mon, Oct 18, 2010 at 11:46:38AM +0200, Manuel Stahl wrote:
> On 15.10.2010 18:49, Greg KH wrote:
> >On Fri, Oct 15, 2010 at 05:09:03PM +0200, Manuel Stahl wrote:
> >>On 15.10.2010 16:27, Greg KH wrote:
> >>>On Fri, Oct 15, 2010 at 09:24:34AM +0200, Manuel Stahl wrote:
> >>>>On 14.10.2010 21:01, Greg KH wrote:
> >>>>>On Thu, Oct 07, 2010 at 10:15:07AM +0200, Manuel Stahl wrote:
> >>>>>>--- /dev/null
> >>>>>>+++ b/include/linux/spi/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
> >>>>>
> >>>>>Why is this needed in a .h file? What other .c file uses this define
> >>>>>and structure?
> >>>>>
> >>>>>thanks,
> >>>>>
> >>>>>greg k-h
> >>>>
> >>>>The board setup code can pass information about the uart and gpio
> >>>>enumeration.
> >>>
> >>>What "board setup code"? I don't see that here in the patch anywhere.
> >>
> >>SPI devices are generally set up for a specific hardware board. I.e. for
> >>some ARM boards there is setup code in
> >>linux/arch/arm/mach-at91/board-<xyz>.c
> >>
> >>I use the sc16is7x2 for a board that is not yet in vanilla, so the board
> >>setup code is not included in the patch.
> >
> >Then why not add this header (or move it to the header file) when your
> >board setup code is merged? No need to have it there now, right?
>
> Cause everyone who want's to use this driver for his board needs the
> header. The board I use is a prototype, nearly the same as some
> Atmel Demo board, but with the sc16is752 attached to the SPI port.
> Therefore I don't see any benefit in publishing my board setup code.
How nice, you are saying that we need a .h file in the kernel for no
other reason than to support out-of-tree code? Well, feel free to
resend this driver, and watch as the .h file is removed as it's not
needed by any in-tree code...
good luck,
greg k-h
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
2010-10-19 15:37 ` Greg KH
@ 2010-10-26 8:55 ` Manuel Stahl
2010-10-26 14:51 ` Greg KH
0 siblings, 1 reply; 12+ messages in thread
From: Manuel Stahl @ 2010-10-26 8:55 UTC (permalink / raw)
To: Greg KH; +Cc: linux-serial, LKML, Andrew Morton
[-- Attachment #1: Type: text/plain, Size: 3053 bytes --]
On 19.10.2010 17:37, Greg KH wrote:
> On Mon, Oct 18, 2010 at 11:46:38AM +0200, Manuel Stahl wrote:
>> On 15.10.2010 18:49, Greg KH wrote:
>>> On Fri, Oct 15, 2010 at 05:09:03PM +0200, Manuel Stahl wrote:
>>>> On 15.10.2010 16:27, Greg KH wrote:
>>>>> On Fri, Oct 15, 2010 at 09:24:34AM +0200, Manuel Stahl wrote:
>>>>>> On 14.10.2010 21:01, Greg KH wrote:
>>>>>>> On Thu, Oct 07, 2010 at 10:15:07AM +0200, Manuel Stahl wrote:
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/include/linux/spi/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
>>>>>>>
>>>>>>> Why is this needed in a .h file? What other .c file uses this define
>>>>>>> and structure?
>>>>>>>
>>>>>>> thanks,
>>>>>>>
>>>>>>> greg k-h
>>>>>>
>>>>>> The board setup code can pass information about the uart and gpio
>>>>>> enumeration.
>>>>>
>>>>> What "board setup code"? I don't see that here in the patch anywhere.
>>>>
>>>> SPI devices are generally set up for a specific hardware board. I.e. for
>>>> some ARM boards there is setup code in
>>>> linux/arch/arm/mach-at91/board-<xyz>.c
>>>>
>>>> I use the sc16is7x2 for a board that is not yet in vanilla, so the board
>>>> setup code is not included in the patch.
>>>
>>> Then why not add this header (or move it to the header file) when your
>>> board setup code is merged? No need to have it there now, right?
>>
>> Cause everyone who want's to use this driver for his board needs the
>> header. The board I use is a prototype, nearly the same as some
>> Atmel Demo board, but with the sc16is752 attached to the SPI port.
>> Therefore I don't see any benefit in publishing my board setup code.
>
> How nice, you are saying that we need a .h file in the kernel for no
> other reason than to support out-of-tree code? Well, feel free to
> resend this driver, and watch as the .h file is removed as it's not
> needed by any in-tree code...
OK, let's take an example: max3100
This is the driver I took as a template. It consists of
drivers/serial/max3100.c and include/linux/serial_max3100.h
And huh, "grep -R serial_max3100.h *" gives what? Only max3100.c
So do we need to remove serial_max3100.h as it's not used anywhere else?
Regards,
--
Manuel Stahl
Fraunhofer-Institut IIS
Leistungsoptimierte Systeme
Nordostpark 93
D90411 Nürnberg
Telefon +49 (0)911/58061-6419
Fax +49 (0)911/58061-6398
E-Mail manuel.stahl@iis.fraunhofer.de
http://www.iis.fraunhofer.de
http://www.smart-power.fraunhofer.de
[-- 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] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
2010-10-26 8:55 ` Manuel Stahl
@ 2010-10-26 14:51 ` Greg KH
2010-10-26 14:59 ` Alan Cox
2010-10-26 19:28 ` Mark Brown
0 siblings, 2 replies; 12+ messages in thread
From: Greg KH @ 2010-10-26 14:51 UTC (permalink / raw)
To: Manuel Stahl; +Cc: linux-serial, LKML, Andrew Morton
On Tue, Oct 26, 2010 at 10:55:24AM +0200, Manuel Stahl wrote:
> On 19.10.2010 17:37, Greg KH wrote:
>> On Mon, Oct 18, 2010 at 11:46:38AM +0200, Manuel Stahl wrote:
>>> On 15.10.2010 18:49, Greg KH wrote:
>>>> On Fri, Oct 15, 2010 at 05:09:03PM +0200, Manuel Stahl wrote:
>>>>> On 15.10.2010 16:27, Greg KH wrote:
>>>>>> On Fri, Oct 15, 2010 at 09:24:34AM +0200, Manuel Stahl wrote:
>>>>>>> On 14.10.2010 21:01, Greg KH wrote:
>>>>>>>> On Thu, Oct 07, 2010 at 10:15:07AM +0200, Manuel Stahl wrote:
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/include/linux/spi/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
>>>>>>>>
>>>>>>>> Why is this needed in a .h file? What other .c file uses this define
>>>>>>>> and structure?
>>>>>>>>
>>>>>>>> thanks,
>>>>>>>>
>>>>>>>> greg k-h
>>>>>>>
>>>>>>> The board setup code can pass information about the uart and gpio
>>>>>>> enumeration.
>>>>>>
>>>>>> What "board setup code"? I don't see that here in the patch anywhere.
>>>>>
>>>>> SPI devices are generally set up for a specific hardware board. I.e. for
>>>>> some ARM boards there is setup code in
>>>>> linux/arch/arm/mach-at91/board-<xyz>.c
>>>>>
>>>>> I use the sc16is7x2 for a board that is not yet in vanilla, so the board
>>>>> setup code is not included in the patch.
>>>>
>>>> Then why not add this header (or move it to the header file) when your
>>>> board setup code is merged? No need to have it there now, right?
>>>
>>> Cause everyone who want's to use this driver for his board needs the
>>> header. The board I use is a prototype, nearly the same as some
>>> Atmel Demo board, but with the sc16is752 attached to the SPI port.
>>> Therefore I don't see any benefit in publishing my board setup code.
>>
>> How nice, you are saying that we need a .h file in the kernel for no
>> other reason than to support out-of-tree code? Well, feel free to
>> resend this driver, and watch as the .h file is removed as it's not
>> needed by any in-tree code...
>
> OK, let's take an example: max3100
> This is the driver I took as a template. It consists of
> drivers/serial/max3100.c and include/linux/serial_max3100.h
>
> And huh, "grep -R serial_max3100.h *" gives what? Only max3100.c
> So do we need to remove serial_max3100.h as it's not used anywhere else?
Yup, that sounds like a great idea, I'll go queue up that patch when I
get back from my travels at the moment.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
2010-10-26 14:51 ` Greg KH
@ 2010-10-26 14:59 ` Alan Cox
2010-10-26 19:28 ` Mark Brown
1 sibling, 0 replies; 12+ messages in thread
From: Alan Cox @ 2010-10-26 14:59 UTC (permalink / raw)
To: Greg KH; +Cc: Manuel Stahl, linux-serial, LKML, Andrew Morton
> > And huh, "grep -R serial_max3100.h *" gives what? Only max3100.c
> > So do we need to remove serial_max3100.h as it's not used anywhere else?
>
> Yup, that sounds like a great idea, I'll go queue up that patch when I
> get back from my travels at the moment.
Sounds a very bad idea to me. The point of the platform data objects
throughout the kernel is to allow other devices to use the driver without
having to hack it up. Removing these interfaces breaks that.
So NAK such a change. The existing code is right, the fact that the
interface isn't being used in that way visibly by currently merged code
doesn't mean it shouldn't be there.
Also max3100 has an active maintainer so perhaps you should Cc them on
such discussion ?
Alan
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
2010-10-26 14:51 ` Greg KH
2010-10-26 14:59 ` Alan Cox
@ 2010-10-26 19:28 ` Mark Brown
1 sibling, 0 replies; 12+ messages in thread
From: Mark Brown @ 2010-10-26 19:28 UTC (permalink / raw)
To: Greg KH; +Cc: Manuel Stahl, linux-serial, LKML, Andrew Morton
On Tue, Oct 26, 2010 at 07:51:07AM -0700, Greg KH wrote:
> On Tue, Oct 26, 2010 at 10:55:24AM +0200, Manuel Stahl wrote:
> > This is the driver I took as a template. It consists of
> > drivers/serial/max3100.c and include/linux/serial_max3100.h
> > And huh, "grep -R serial_max3100.h *" gives what? Only max3100.c
> > So do we need to remove serial_max3100.h as it's not used anywhere else?
> Yup, that sounds like a great idea, I'll go queue up that patch when I
> get back from my travels at the moment.
Requiring that we have a user for all platform data provided by drivers
would be incredibly painful for embedded development. It would mean
that we would be unable to usefully merge any drivers with platform data
unless we also merge at least one board using them, meaning that we're
dependant on getting the core board support for some public board into a
state which can be merged (which would at best be likely to end up
creating logjams on support for CPUs).
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH resend] Add sc16is7x2 driver
2010-10-07 8:15 [PATCH resend] Add sc16is7x2 driver Manuel Stahl
2010-10-14 19:01 ` Greg KH
@ 2010-11-08 14:01 ` Thomas Weber
1 sibling, 0 replies; 12+ messages in thread
From: Thomas Weber @ 2010-11-08 14:01 UTC (permalink / raw)
To: Manuel Stahl; +Cc: linux-serial, LKML, Greg KH, Andrew Morton
Am 07.10.2010 10:15, schrieb Manuel Stahl:
> This patch adds support for the sc16is7x2 SPI to UART chips.
> Each chip has two UARTs and 8 GPIOs.
Hello Manuel,
can you send the patch inline?
Why you check in your probe function the gpio_base but not the uart_base?
Thomas
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2010-11-08 14:02 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-07 8:15 [PATCH resend] Add sc16is7x2 driver Manuel Stahl
2010-10-14 19:01 ` Greg KH
2010-10-15 7:24 ` Manuel Stahl
[not found] ` <20101015142709.GA18905@kroah.com>
2010-10-15 15:09 ` Manuel Stahl
2010-10-15 16:49 ` Greg KH
2010-10-18 9:46 ` Manuel Stahl
2010-10-19 15:37 ` Greg KH
2010-10-26 8:55 ` Manuel Stahl
2010-10-26 14:51 ` Greg KH
2010-10-26 14:59 ` Alan Cox
2010-10-26 19:28 ` Mark Brown
2010-11-08 14:01 ` Thomas Weber
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).