Linux Serial subsystem development
 help / color / mirror / Atom feed
* [serial driver PATCH] Fix typo in sanity check
From: Thomas Jarosch @ 2012-12-28 23:16 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-serial

Detected by cppcheck:
[others/linux/drivers/tty/serial/mxs-auart.c:553]: (style) Same expression on both sides of '||'.

Signed-off-by: Thomas Jarosch <thomas.jarosch@intra2net.com>
---
 drivers/tty/serial/mxs-auart.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 6db23b0..fa31bc3 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -550,7 +550,7 @@ static int mxs_auart_dma_init(struct mxs_auart_port *s)
 		return 0;
 
 	/* We do not get the right DMA channels. */
-	if (s->dma_channel_rx == -1 || s->dma_channel_rx == -1)
+	if (s->dma_channel_rx == -1 || s->dma_channel_tx == -1)
 		return -EINVAL;
 
 	/* init for RX */
-- 
1.7.11.7


^ permalink raw reply related

* [PATCH -firmware 3/3] rp2: Initial commit of Comtrol RocketPort 2 microcode
From: Kevin Cernekee @ 2012-12-30  7:23 UTC (permalink / raw)
  To: dwmw2, ben; +Cc: alan, gregkh, jslaby, linux-serial
In-Reply-To: <bd10fdeead3e321db2bb291c0b722203@localhost>

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
---
 WHENCE |   13 +++++++++++++
 rp2.fw |  Bin 0 -> 63 bytes
 2 files changed, 13 insertions(+)
 create mode 100644 rp2.fw


This is intended for the linux-firmware repo.

Also available from: git://github.com/cernekee/linux-firmware rp2


diff --git a/WHENCE b/WHENCE
index b7dcda3..3923185 100644
--- a/WHENCE
+++ b/WHENCE
@@ -2034,3 +2034,16 @@ useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 --------------------------------------------------------------------------
+
+Driver: rp2 -- Comtrol RocketPort 2 serial driver
+
+File: rp2.fw
+
+Licence: Allegedly GPLv2+, but no source visible. Marked:
+
+  // RocketPort Infinity/Express device driver for Linux
+  // Copyright (C) 2004-2011 Comtrol, Inc.
+
+Found in hex form in the manufacturer's GPL out-of-tree driver sources.
+
+--------------------------------------------------------------------------
diff --git a/rp2.fw b/rp2.fw
new file mode 100644
index 0000000000000000000000000000000000000000..a571e77925d479cbd4d17e72f49ee9b90bc8e2d7
GIT binary patch
literal 63
zcmey?J@<(45y2S>T#8)1zuFib1SPo`9E2sgel;_4F>pEl?>v;&);Q52wGYhba7b-!
NVQAvw-~s^-E&$nE6vO}k

literal 0
HcmV?d00001

-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 2/3] tty: rocket: Explicitly list supported PCI IDs
From: Kevin Cernekee @ 2012-12-30  7:23 UTC (permalink / raw)
  To: alan, gregkh, jslaby; +Cc: linux-serial, dwmw2, ben
In-Reply-To: <bd10fdeead3e321db2bb291c0b722203@localhost>

Matching PCI_ANY_ID causes conflicts with RocketPort 2 adapters, which
are supported by a different driver.

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
---
 drivers/tty/rocket.c |   31 ++++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c
index e42009a..c73ca4e 100644
--- a/drivers/tty/rocket.c
+++ b/drivers/tty/rocket.c
@@ -1758,8 +1758,32 @@ static void rp_flush_buffer(struct tty_struct *tty)
 
 #ifdef CONFIG_PCI
 
-static struct pci_device_id __used rocket_pci_ids[] = {
-	{ PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) },
+#define RP_PCI_DEVICE(id) \
+	{ PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_##id) }
+
+static DEFINE_PCI_DEVICE_TABLE(rocket_pci_ids) = {
+	RP_PCI_DEVICE(RP4QUAD),
+	RP_PCI_DEVICE(RP8OCTA),
+	RP_PCI_DEVICE(URP8OCTA),
+	RP_PCI_DEVICE(RP8INTF),
+	RP_PCI_DEVICE(URP8INTF),
+	RP_PCI_DEVICE(RP8J),
+	RP_PCI_DEVICE(RP4J),
+	RP_PCI_DEVICE(RP8SNI),
+	RP_PCI_DEVICE(RP16SNI),
+	RP_PCI_DEVICE(RP16INTF),
+	RP_PCI_DEVICE(URP16INTF),
+	RP_PCI_DEVICE(CRP16INTF),
+	RP_PCI_DEVICE(RP32INTF),
+	RP_PCI_DEVICE(URP32INTF),
+	RP_PCI_DEVICE(RPP4),
+	RP_PCI_DEVICE(RPP8),
+	RP_PCI_DEVICE(RP2_232),
+	RP_PCI_DEVICE(RP2_422),
+	RP_PCI_DEVICE(RP6M),
+	RP_PCI_DEVICE(RP4M),
+	RP_PCI_DEVICE(UPCI_RM3_8PORT),
+	RP_PCI_DEVICE(UPCI_RM3_4PORT),
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, rocket_pci_ids);
@@ -1781,7 +1805,8 @@ static __init int register_PCI(int i, struct pci_dev *dev)
 	WordIO_t ConfigIO = 0;
 	ByteIO_t UPCIRingInd = 0;
 
-	if (!dev || pci_enable_device(dev))
+	if (!dev || !pci_match_id(rocket_pci_ids, dev) ||
+	    pci_enable_device(dev))
 		return 0;
 
 	rcktpt_io_addr[i] = pci_resource_start(dev, 0);
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH V2 1/3] serial: rp2: New driver for Comtrol RocketPort 2 cards
From: Kevin Cernekee @ 2012-12-30  7:23 UTC (permalink / raw)
  To: alan, gregkh, jslaby; +Cc: linux-serial, dwmw2, ben

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
---
 drivers/tty/serial/Kconfig       |   24 ++
 drivers/tty/serial/Makefile      |    1 +
 drivers/tty/serial/rp2.c         |  882 ++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/serial_core.h |    3 +
 4 files changed, 910 insertions(+)
 create mode 100644 drivers/tty/serial/rp2.c


V1->V2 changes:

 - Fix tty locking in rp2_rx_chars()
 - Add readl() before udelay(), to make sure posted writes have cleared
 - Use udev firmware loading for the microcode blob
 - Remove deprecated __devinit and __devexit macros
 - Change device name to "ttyRP" to avoid conflicts with rocket.c
 - Add MODULE_DEVICE_TABLE

I set up a system with both old (rocket.c) and new (rp2.c) cards, and
verified that the appropriate driver(s) are loaded based on the PCI
device tables.


diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 59c23d0..555f3ca 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1447,4 +1447,28 @@ config SERIAL_ARC_NR_PORTS
 	  Set this to the number of serial ports you want the driver
 	  to support.
 
+config SERIAL_RP2
+	tristate "Comtrol RocketPort EXPRESS/INFINITY support"
+	depends on PCI
+	select SERIAL_CORE
+	help
+	  This driver supports the Comtrol RocketPort EXPRESS and
+	  RocketPort INFINITY families of PCI/PCIe multiport serial adapters.
+	  These adapters use a "RocketPort 2" ASIC that is not compatible
+	  with the original RocketPort driver (CONFIG_ROCKETPORT).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rp2.
+
+	  If you want to compile this driver into the kernel, say Y here.  If
+	  you don't have a suitable RocketPort card installed, say N.
+
+config SERIAL_RP2_NR_UARTS
+	int "Maximum number of RocketPort EXPRESS/INFINITY ports"
+	depends on SERIAL_RP2
+	default "32"
+	help
+	  If multiple cards are present, the default limit of 32 ports may
+	  need to be increased.
+
 endmenu
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index df1b998..a582ee9 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -83,3 +83,4 @@ obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o
 obj-$(CONFIG_SERIAL_AR933X)   += ar933x_uart.o
 obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
 obj-$(CONFIG_SERIAL_ARC)	+= arc_uart.o
+obj-$(CONFIG_SERIAL_RP2)	+= rp2.o
diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c
new file mode 100644
index 0000000..9e0e4ac
--- /dev/null
+++ b/drivers/tty/serial/rp2.c
@@ -0,0 +1,882 @@
+/*
+ * Driver for Comtrol RocketPort EXPRESS/INFINITY cards
+ *
+ * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com>
+ *
+ * Inspired by, and loosely based on:
+ *
+ *   ar933x_uart.c
+ *     Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *   rocketport_infinity_express-linux-1.20.tar.gz
+ *     Copyright (C) 2004-2011 Comtrol, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/completion.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/types.h>
+
+#define DRV_NAME			"rp2"
+
+#define RP2_FW_NAME			"rp2.fw"
+#define RP2_UCODE_BYTES			0x3f
+
+#define PORTS_PER_ASIC			16
+#define ALL_PORTS_MASK			(BIT(PORTS_PER_ASIC) - 1)
+
+#define UART_CLOCK			44236800
+#define DEFAULT_BAUD_DIV		(UART_CLOCK / (9600 * 16))
+#define FIFO_SIZE			512
+
+/* BAR0 registers */
+#define RP2_FPGA_CTL0			0x110
+#define RP2_FPGA_CTL1			0x11c
+#define RP2_IRQ_MASK			0x1ec
+#define RP2_IRQ_MASK_EN_m		BIT(0)
+#define RP2_IRQ_STATUS			0x1f0
+
+/* BAR1 registers */
+#define RP2_ASIC_SPACING		0x1000
+#define RP2_ASIC_OFFSET(i)		((i) << ilog2(RP2_ASIC_SPACING))
+
+#define RP2_PORT_BASE			0x000
+#define RP2_PORT_SPACING		0x040
+
+#define RP2_UCODE_BASE			0x400
+#define RP2_UCODE_SPACING		0x80
+
+#define RP2_CLK_PRESCALER		0xc00
+#define RP2_CH_IRQ_STAT			0xc04
+#define RP2_CH_IRQ_MASK			0xc08
+#define RP2_ASIC_IRQ			0xd00
+#define RP2_ASIC_IRQ_EN_m		BIT(20)
+#define RP2_GLOBAL_CMD			0xd0c
+#define RP2_ASIC_CFG			0xd04
+
+/* port registers */
+#define RP2_DATA_DWORD			0x000
+
+#define RP2_DATA_BYTE			0x008
+#define RP2_DATA_BYTE_ERR_PARITY_m	BIT(8)
+#define RP2_DATA_BYTE_ERR_OVERRUN_m	BIT(9)
+#define RP2_DATA_BYTE_ERR_FRAMING_m	BIT(10)
+#define RP2_DATA_BYTE_BREAK_m		BIT(11)
+
+/* This lets uart_insert_char() drop bytes received on a !CREAD port */
+#define RP2_DUMMY_READ			BIT(16)
+
+#define RP2_DATA_BYTE_EXCEPTION_MASK	(RP2_DATA_BYTE_ERR_PARITY_m | \
+					 RP2_DATA_BYTE_ERR_OVERRUN_m | \
+					 RP2_DATA_BYTE_ERR_FRAMING_m | \
+					 RP2_DATA_BYTE_BREAK_m)
+
+#define RP2_RX_FIFO_COUNT		0x00c
+#define RP2_TX_FIFO_COUNT		0x00e
+
+#define RP2_CHAN_STAT			0x010
+#define RP2_CHAN_STAT_RXDATA_m		BIT(0)
+#define RP2_CHAN_STAT_DCD_m		BIT(3)
+#define RP2_CHAN_STAT_DSR_m		BIT(4)
+#define RP2_CHAN_STAT_CTS_m		BIT(5)
+#define RP2_CHAN_STAT_RI_m		BIT(6)
+#define RP2_CHAN_STAT_OVERRUN_m		BIT(13)
+#define RP2_CHAN_STAT_DSR_CHANGED_m	BIT(16)
+#define RP2_CHAN_STAT_CTS_CHANGED_m	BIT(17)
+#define RP2_CHAN_STAT_CD_CHANGED_m	BIT(18)
+#define RP2_CHAN_STAT_RI_CHANGED_m	BIT(22)
+#define RP2_CHAN_STAT_TXEMPTY_m		BIT(25)
+
+#define RP2_CHAN_STAT_MS_CHANGED_MASK	(RP2_CHAN_STAT_DSR_CHANGED_m | \
+					 RP2_CHAN_STAT_CTS_CHANGED_m | \
+					 RP2_CHAN_STAT_CD_CHANGED_m | \
+					 RP2_CHAN_STAT_RI_CHANGED_m)
+
+#define RP2_TXRX_CTL			0x014
+#define RP2_TXRX_CTL_MSRIRQ_m		BIT(0)
+#define RP2_TXRX_CTL_RXIRQ_m		BIT(2)
+#define RP2_TXRX_CTL_RX_TRIG_s		3
+#define RP2_TXRX_CTL_RX_TRIG_m		(0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_1		(0x1 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_256	(0x2 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_448	(0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_EN_m		BIT(5)
+#define RP2_TXRX_CTL_RTSFLOW_m		BIT(6)
+#define RP2_TXRX_CTL_DTRFLOW_m		BIT(7)
+#define RP2_TXRX_CTL_TX_TRIG_s		16
+#define RP2_TXRX_CTL_TX_TRIG_m		(0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_DSRFLOW_m		BIT(18)
+#define RP2_TXRX_CTL_TXIRQ_m		BIT(19)
+#define RP2_TXRX_CTL_CTSFLOW_m		BIT(23)
+#define RP2_TXRX_CTL_TX_EN_m		BIT(24)
+#define RP2_TXRX_CTL_RTS_m		BIT(25)
+#define RP2_TXRX_CTL_DTR_m		BIT(26)
+#define RP2_TXRX_CTL_LOOP_m		BIT(27)
+#define RP2_TXRX_CTL_BREAK_m		BIT(28)
+#define RP2_TXRX_CTL_CMSPAR_m		BIT(29)
+#define RP2_TXRX_CTL_nPARODD_m		BIT(30)
+#define RP2_TXRX_CTL_PARENB_m		BIT(31)
+
+#define RP2_UART_CTL			0x018
+#define RP2_UART_CTL_MODE_s		0
+#define RP2_UART_CTL_MODE_m		(0x7 << RP2_UART_CTL_MODE_s)
+#define RP2_UART_CTL_MODE_rs232		(0x1 << RP2_UART_CTL_MODE_s)
+#define RP2_UART_CTL_FLUSH_RX_m		BIT(3)
+#define RP2_UART_CTL_FLUSH_TX_m		BIT(4)
+#define RP2_UART_CTL_RESET_CH_m		BIT(5)
+#define RP2_UART_CTL_XMIT_EN_m		BIT(6)
+#define RP2_UART_CTL_DATABITS_s		8
+#define RP2_UART_CTL_DATABITS_m		(0x3 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_8		(0x3 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_7		(0x2 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_6		(0x1 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_5		(0x0 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_STOPBITS_m		BIT(10)
+
+#define RP2_BAUD			0x01c
+
+/* ucode registers */
+#define RP2_TX_SWFLOW			0x02
+#define RP2_TX_SWFLOW_ena		0x81
+#define RP2_TX_SWFLOW_dis		0x9d
+
+#define RP2_RX_SWFLOW			0x0c
+#define RP2_RX_SWFLOW_ena		0x81
+#define RP2_RX_SWFLOW_dis		0x8d
+
+#define RP2_RX_FIFO			0x37
+#define RP2_RX_FIFO_ena			0x08
+#define RP2_RX_FIFO_dis			0x81
+
+static struct uart_driver rp2_uart_driver = {
+	.owner				= THIS_MODULE,
+	.driver_name			= DRV_NAME,
+	.dev_name			= "ttyRP",
+	.nr				= CONFIG_SERIAL_RP2_NR_UARTS,
+};
+
+struct rp2_card;
+
+struct rp2_uart_port {
+	struct uart_port		port;
+	int				idx;
+	int				ignore_rx;
+	struct rp2_card			*card;
+	void __iomem			*asic_base;
+	void __iomem			*base;
+	void __iomem			*ucode;
+};
+
+struct rp2_card {
+	struct pci_dev			*pdev;
+	struct rp2_uart_port		*ports;
+	int				n_ports;
+	int				initialized_ports;
+	int				minor_start;
+	int				smpte;
+	void __iomem			*bar0;
+	void __iomem			*bar1;
+	spinlock_t			card_lock;
+	struct completion		fw_loaded;
+};
+
+#define RP_ID(prod) PCI_VDEVICE(RP, (prod))
+#define RP_CAP(ports, smpte) (((ports) << 8) | ((smpte) << 0))
+
+static inline void rp2_decode_cap(const struct pci_device_id *id,
+				  int *ports, int *smpte)
+{
+	*ports = id->driver_data >> 8;
+	*smpte = id->driver_data & 0xff;
+}
+
+static DEFINE_SPINLOCK(rp2_minor_lock);
+static int rp2_minor_next;
+
+static int rp2_alloc_ports(int n_ports)
+{
+	int ret = -ENOSPC;
+
+	spin_lock(&rp2_minor_lock);
+	if (rp2_minor_next + n_ports <= CONFIG_SERIAL_RP2_NR_UARTS) {
+		/* sorry, no support for hot unplugging individual cards */
+		ret = rp2_minor_next;
+		rp2_minor_next += n_ports;
+	}
+	spin_unlock(&rp2_minor_lock);
+
+	return ret;
+}
+
+static inline struct rp2_uart_port *port_to_up(struct uart_port *port)
+{
+	return container_of(port, struct rp2_uart_port, port);
+}
+
+static void rp2_rmw(struct rp2_uart_port *up, int reg,
+		    u32 clr_bits, u32 set_bits)
+{
+	u32 tmp = readl(up->base + reg);
+	tmp &= ~clr_bits;
+	tmp |= set_bits;
+	writel(tmp, up->base + reg);
+}
+
+static void rp2_rmw_clr(struct rp2_uart_port *up, int reg, u32 val)
+{
+	rp2_rmw(up, reg, val, 0);
+}
+
+static void rp2_rmw_set(struct rp2_uart_port *up, int reg, u32 val)
+{
+	rp2_rmw(up, reg, 0, val);
+}
+
+static void rp2_mask_ch_irq(struct rp2_uart_port *up, int ch_num,
+			    int is_enabled)
+{
+	unsigned long flags, irq_mask;
+
+	spin_lock_irqsave(&up->card->card_lock, flags);
+
+	irq_mask = readl(up->asic_base + RP2_CH_IRQ_MASK);
+	if (is_enabled)
+		irq_mask &= ~BIT(ch_num);
+	else
+		irq_mask |= BIT(ch_num);
+	writel(irq_mask, up->asic_base + RP2_CH_IRQ_MASK);
+
+	spin_unlock_irqrestore(&up->card->card_lock, flags);
+}
+
+static unsigned int rp2_uart_tx_empty(struct uart_port *port)
+{
+	struct rp2_uart_port *up = port_to_up(port);
+	unsigned long tx_fifo_bytes, flags;
+
+	/*
+	 * This should probably check the transmitter, not the FIFO.
+	 * But the TXEMPTY bit doesn't seem to work unless the TX IRQ is
+	 * enabled.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+	tx_fifo_bytes = readw(up->base + RP2_TX_FIFO_COUNT);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return tx_fifo_bytes ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int rp2_uart_get_mctrl(struct uart_port *port)
+{
+	struct rp2_uart_port *up = port_to_up(port);
+	u32 status;
+
+	status = readl(up->base + RP2_CHAN_STAT);
+	return ((status & RP2_CHAN_STAT_DCD_m) ? TIOCM_CAR : 0) |
+	       ((status & RP2_CHAN_STAT_DSR_m) ? TIOCM_DSR : 0) |
+	       ((status & RP2_CHAN_STAT_CTS_m) ? TIOCM_CTS : 0) |
+	       ((status & RP2_CHAN_STAT_RI_m) ? TIOCM_RI : 0);
+}
+
+static void rp2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	rp2_rmw(port_to_up(port), RP2_TXRX_CTL,
+		RP2_TXRX_CTL_DTR_m | RP2_TXRX_CTL_RTS_m | RP2_TXRX_CTL_LOOP_m,
+		((mctrl & TIOCM_DTR) ? RP2_TXRX_CTL_DTR_m : 0) |
+		((mctrl & TIOCM_RTS) ? RP2_TXRX_CTL_RTS_m : 0) |
+		((mctrl & TIOCM_LOOP) ? RP2_TXRX_CTL_LOOP_m : 0));
+}
+
+static void rp2_uart_start_tx(struct uart_port *port)
+{
+	rp2_rmw_set(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_TXIRQ_m);
+}
+
+static void rp2_uart_stop_tx(struct uart_port *port)
+{
+	rp2_rmw_clr(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_TXIRQ_m);
+}
+
+static void rp2_uart_stop_rx(struct uart_port *port)
+{
+	rp2_rmw_clr(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_RXIRQ_m);
+}
+
+static void rp2_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	rp2_rmw(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_BREAK_m,
+		break_state ? RP2_TXRX_CTL_BREAK_m : 0);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void rp2_uart_enable_ms(struct uart_port *port)
+{
+	rp2_rmw_set(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_MSRIRQ_m);
+}
+
+static void __rp2_uart_set_termios(struct rp2_uart_port *up,
+				   unsigned long cfl,
+				   unsigned long ifl,
+				   unsigned int baud_div)
+{
+	/* baud rate divisor (calculated elsewhere).  0 = divide-by-1 */
+	writew(baud_div - 1, up->base + RP2_BAUD);
+
+	/* data bits and stop bits */
+	rp2_rmw(up, RP2_UART_CTL,
+		RP2_UART_CTL_STOPBITS_m | RP2_UART_CTL_DATABITS_m,
+		((cfl & CSTOPB) ? RP2_UART_CTL_STOPBITS_m : 0) |
+		(((cfl & CSIZE) == CS8) ? RP2_UART_CTL_DATABITS_8 : 0) |
+		(((cfl & CSIZE) == CS7) ? RP2_UART_CTL_DATABITS_7 : 0) |
+		(((cfl & CSIZE) == CS6) ? RP2_UART_CTL_DATABITS_6 : 0) |
+		(((cfl & CSIZE) == CS5) ? RP2_UART_CTL_DATABITS_5 : 0));
+
+	/* parity and hardware flow control */
+	rp2_rmw(up, RP2_TXRX_CTL,
+		RP2_TXRX_CTL_PARENB_m | RP2_TXRX_CTL_nPARODD_m |
+		RP2_TXRX_CTL_CMSPAR_m | RP2_TXRX_CTL_DTRFLOW_m |
+		RP2_TXRX_CTL_DSRFLOW_m | RP2_TXRX_CTL_RTSFLOW_m |
+		RP2_TXRX_CTL_CTSFLOW_m,
+		((cfl & PARENB) ? RP2_TXRX_CTL_PARENB_m : 0) |
+		((cfl & PARODD) ? 0 : RP2_TXRX_CTL_nPARODD_m) |
+		((cfl & CMSPAR) ? RP2_TXRX_CTL_CMSPAR_m : 0) |
+		((cfl & CRTSCTS) ? (RP2_TXRX_CTL_RTSFLOW_m |
+				    RP2_TXRX_CTL_CTSFLOW_m) : 0));
+
+	/* XON/XOFF software flow control */
+	writeb((ifl & IXON) ? RP2_TX_SWFLOW_ena : RP2_TX_SWFLOW_dis,
+	       up->ucode + RP2_TX_SWFLOW);
+	writeb((ifl & IXOFF) ? RP2_RX_SWFLOW_ena : RP2_RX_SWFLOW_dis,
+	       up->ucode + RP2_RX_SWFLOW);
+}
+
+static void rp2_uart_set_termios(struct uart_port *port,
+				 struct ktermios *new,
+				 struct ktermios *old)
+{
+	struct rp2_uart_port *up = port_to_up(port);
+	unsigned long flags;
+	unsigned int baud, baud_div;
+
+	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+	baud_div = uart_get_divisor(port, baud);
+
+	if (tty_termios_baud_rate(new))
+		tty_termios_encode_baud_rate(new, baud, baud);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* ignore all characters if CREAD is not set */
+	port->ignore_status_mask = (new->c_cflag & CREAD) ? 0 : RP2_DUMMY_READ;
+
+	__rp2_uart_set_termios(up, new->c_cflag, new->c_iflag, baud_div);
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void rp2_rx_chars(struct rp2_uart_port *up)
+{
+	u16 bytes = readw(up->base + RP2_RX_FIFO_COUNT);
+	struct tty_struct *tty = tty_port_tty_get(&up->port.state->port);
+
+	if (!tty) {
+		/* Discard data: no tty available */
+		for (; bytes != 0; bytes--)
+			readw(up->base + RP2_DATA_BYTE);
+		return;
+	}
+
+	for (; bytes != 0; bytes--) {
+		u32 byte = readw(up->base + RP2_DATA_BYTE) | RP2_DUMMY_READ;
+		char ch = byte & 0xff;
+
+		if (likely(!(byte & RP2_DATA_BYTE_EXCEPTION_MASK))) {
+			if (!uart_handle_sysrq_char(&up->port, ch))
+				uart_insert_char(&up->port, byte, 0, ch,
+						 TTY_NORMAL);
+		} else {
+			char flag = TTY_NORMAL;
+
+			if (byte & RP2_DATA_BYTE_BREAK_m)
+				flag = TTY_BREAK;
+			else if (byte & RP2_DATA_BYTE_ERR_FRAMING_m)
+				flag = TTY_FRAME;
+			else if (byte & RP2_DATA_BYTE_ERR_PARITY_m)
+				flag = TTY_PARITY;
+			uart_insert_char(&up->port, byte,
+					 RP2_DATA_BYTE_ERR_OVERRUN_m, ch, flag);
+		}
+		up->port.icount.rx++;
+	}
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+static void rp2_tx_chars(struct rp2_uart_port *up)
+{
+	u16 max_tx = FIFO_SIZE - readw(up->base + RP2_TX_FIFO_COUNT);
+	struct circ_buf *xmit = &up->port.state->xmit;
+
+	if (uart_tx_stopped(&up->port)) {
+		rp2_uart_stop_tx(&up->port);
+		return;
+	}
+
+	for (; max_tx != 0; max_tx--) {
+		if (up->port.x_char) {
+			writeb(up->port.x_char, up->base + RP2_DATA_BYTE);
+			up->port.x_char = 0;
+			up->port.icount.tx++;
+			continue;
+		}
+		if (uart_circ_empty(xmit)) {
+			rp2_uart_stop_tx(&up->port);
+			break;
+		}
+		writeb(xmit->buf[xmit->tail], up->base + RP2_DATA_BYTE);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+}
+
+static void rp2_ch_interrupt(struct rp2_uart_port *up)
+{
+	u32 status;
+
+	spin_lock(&up->port.lock);
+
+	/*
+	 * The IRQ status bits are clear-on-write.  Other status bits in
+	 * this register aren't, so it's harmless to write to them.
+	 */
+	status = readl(up->base + RP2_CHAN_STAT);
+	writel(status, up->base + RP2_CHAN_STAT);
+
+	if (status & RP2_CHAN_STAT_RXDATA_m)
+		rp2_rx_chars(up);
+	if (status & RP2_CHAN_STAT_TXEMPTY_m)
+		rp2_tx_chars(up);
+	if (status & RP2_CHAN_STAT_MS_CHANGED_MASK)
+		wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+
+	spin_unlock(&up->port.lock);
+}
+
+static int rp2_asic_interrupt(struct rp2_card *card, unsigned int asic_id)
+{
+	void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id);
+	int ch, handled = 0;
+	unsigned long status = readl(base + RP2_CH_IRQ_STAT) &
+			       ~readl(base + RP2_CH_IRQ_MASK);
+
+	for_each_set_bit(ch, &status, PORTS_PER_ASIC) {
+		rp2_ch_interrupt(&card->ports[ch]);
+		handled++;
+	}
+	return handled;
+}
+
+static irqreturn_t rp2_uart_interrupt(int irq, void *dev_id)
+{
+	struct rp2_card *card = dev_id;
+	int handled;
+
+	handled = rp2_asic_interrupt(card, 0);
+	if (card->n_ports >= PORTS_PER_ASIC)
+		handled += rp2_asic_interrupt(card, 1);
+
+	return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static inline void rp2_flush_fifos(struct rp2_uart_port *up)
+{
+	rp2_rmw_set(up, RP2_UART_CTL,
+		    RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m);
+	readl(up->base + RP2_UART_CTL);
+	udelay(10);
+	rp2_rmw_clr(up, RP2_UART_CTL,
+		    RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m);
+}
+
+static int rp2_uart_startup(struct uart_port *port)
+{
+	struct rp2_uart_port *up = port_to_up(port);
+
+	rp2_flush_fifos(up);
+	rp2_rmw(up, RP2_TXRX_CTL, RP2_TXRX_CTL_MSRIRQ_m, RP2_TXRX_CTL_RXIRQ_m);
+	rp2_rmw(up, RP2_TXRX_CTL, RP2_TXRX_CTL_RX_TRIG_m,
+		RP2_TXRX_CTL_RX_TRIG_1);
+	rp2_rmw(up, RP2_CHAN_STAT, 0, 0);
+	rp2_mask_ch_irq(up, up->idx, 1);
+
+	return 0;
+}
+
+static void rp2_uart_shutdown(struct uart_port *port)
+{
+	struct rp2_uart_port *up = port_to_up(port);
+	unsigned long flags;
+
+	rp2_uart_break_ctl(port, 0);
+
+	spin_lock_irqsave(&port->lock, flags);
+	rp2_mask_ch_irq(up, up->idx, 0);
+	rp2_rmw(up, RP2_CHAN_STAT, 0, 0);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *rp2_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_RP2) ? "RocketPort 2 UART" : NULL;
+}
+
+static void rp2_uart_release_port(struct uart_port *port)
+{
+	/* Nothing to release ... */
+}
+
+static int rp2_uart_request_port(struct uart_port *port)
+{
+	/* UARTs always present */
+	return 0;
+}
+
+static void rp2_uart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE)
+		port->type = PORT_RP2;
+}
+
+static int rp2_uart_verify_port(struct uart_port *port,
+				   struct serial_struct *ser)
+{
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_RP2)
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct uart_ops rp2_uart_ops = {
+	.tx_empty	= rp2_uart_tx_empty,
+	.set_mctrl	= rp2_uart_set_mctrl,
+	.get_mctrl	= rp2_uart_get_mctrl,
+	.stop_tx	= rp2_uart_stop_tx,
+	.start_tx	= rp2_uart_start_tx,
+	.stop_rx	= rp2_uart_stop_rx,
+	.enable_ms	= rp2_uart_enable_ms,
+	.break_ctl	= rp2_uart_break_ctl,
+	.startup	= rp2_uart_startup,
+	.shutdown	= rp2_uart_shutdown,
+	.set_termios	= rp2_uart_set_termios,
+	.type		= rp2_uart_type,
+	.release_port	= rp2_uart_release_port,
+	.request_port	= rp2_uart_request_port,
+	.config_port	= rp2_uart_config_port,
+	.verify_port	= rp2_uart_verify_port,
+};
+
+static void rp2_reset_asic(struct rp2_card *card, unsigned int asic_id)
+{
+	void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id);
+	u32 clk_cfg;
+
+	writew(1, base + RP2_GLOBAL_CMD);
+	readw(base + RP2_GLOBAL_CMD);
+	msleep(100);
+	writel(0, base + RP2_CLK_PRESCALER);
+
+	/* TDM clock configuration */
+	clk_cfg = readw(base + RP2_ASIC_CFG);
+	clk_cfg = (clk_cfg & ~BIT(8)) | BIT(9);
+	writew(clk_cfg, base + RP2_ASIC_CFG);
+
+	/* IRQ routing */
+	writel(ALL_PORTS_MASK, base + RP2_CH_IRQ_MASK);
+	writel(RP2_ASIC_IRQ_EN_m, base + RP2_ASIC_IRQ);
+}
+
+static void rp2_init_card(struct rp2_card *card)
+{
+	writel(4, card->bar0 + RP2_FPGA_CTL0);
+	writel(0, card->bar0 + RP2_FPGA_CTL1);
+
+	rp2_reset_asic(card, 0);
+	if (card->n_ports >= PORTS_PER_ASIC)
+		rp2_reset_asic(card, 1);
+
+	writel(RP2_IRQ_MASK_EN_m, card->bar0 + RP2_IRQ_MASK);
+}
+
+static void rp2_init_port(struct rp2_uart_port *up, const struct firmware *fw)
+{
+	int i;
+
+	writel(RP2_UART_CTL_RESET_CH_m, up->base + RP2_UART_CTL);
+	readl(up->base + RP2_UART_CTL);
+	udelay(1);
+
+	writel(0, up->base + RP2_TXRX_CTL);
+	writel(0, up->base + RP2_UART_CTL);
+	readl(up->base + RP2_UART_CTL);
+	udelay(1);
+
+	rp2_flush_fifos(up);
+
+	for (i = 0; i < min_t(int, fw->size, RP2_UCODE_BYTES); i++)
+		writeb(fw->data[i], up->ucode + i);
+
+	__rp2_uart_set_termios(up, CS8 | CREAD | CLOCAL, 0, DEFAULT_BAUD_DIV);
+	rp2_uart_set_mctrl(&up->port, 0);
+
+	writeb(RP2_RX_FIFO_ena, up->ucode + RP2_RX_FIFO);
+	rp2_rmw(up, RP2_UART_CTL, RP2_UART_CTL_MODE_m,
+		RP2_UART_CTL_XMIT_EN_m | RP2_UART_CTL_MODE_rs232);
+	rp2_rmw_set(up, RP2_TXRX_CTL,
+		    RP2_TXRX_CTL_TX_EN_m | RP2_TXRX_CTL_RX_EN_m);
+}
+
+static void rp2_fw_cb(const struct firmware *fw, void *context)
+{
+	struct rp2_card *card = context;
+	resource_size_t phys_base;
+	int i, rc = -ENOENT;
+
+	if (!fw) {
+		dev_err(&card->pdev->dev, "cannot find '%s' firmware image\n",
+			RP2_FW_NAME);
+		goto no_fw;
+	}
+
+	phys_base = pci_resource_start(card->pdev, 1);
+
+	for (i = 0; i < card->n_ports; i++) {
+		struct rp2_uart_port *rp = &card->ports[i];
+		struct uart_port *p;
+		int j = (unsigned)i % PORTS_PER_ASIC;
+
+		rp->asic_base = card->bar1;
+		rp->base = card->bar1 + RP2_PORT_BASE + j*RP2_PORT_SPACING;
+		rp->ucode = card->bar1 + RP2_UCODE_BASE + j*RP2_UCODE_SPACING;
+		rp->card = card;
+		rp->idx = j;
+
+		p = &rp->port;
+		p->line = card->minor_start + i;
+		p->dev = &card->pdev->dev;
+		p->type = PORT_RP2;
+		p->iotype = UPIO_MEM32;
+		p->uartclk = UART_CLOCK;
+		p->regshift = 2;
+		p->fifosize = FIFO_SIZE;
+		p->ops = &rp2_uart_ops;
+		p->irq = card->pdev->irq;
+		p->membase = rp->base;
+		p->mapbase = phys_base + RP2_PORT_BASE + j*RP2_PORT_SPACING;
+
+		if (i >= PORTS_PER_ASIC) {
+			rp->asic_base += RP2_ASIC_SPACING;
+			rp->base += RP2_ASIC_SPACING;
+			rp->ucode += RP2_ASIC_SPACING;
+			p->mapbase += RP2_ASIC_SPACING;
+		}
+
+		rp2_init_port(rp, fw);
+		rc = uart_add_one_port(&rp2_uart_driver, p);
+		if (rc) {
+			dev_err(&card->pdev->dev,
+				"error registering port %d: %d\n", i, rc);
+			break;
+		}
+		card->initialized_ports++;
+	}
+
+	release_firmware(fw);
+no_fw:
+	complete(&card->fw_loaded);
+
+	/*
+	 * rp2_fw_cb() is called from a workqueue long after rp2_probe()
+	 * has already returned success.  So if something failed here, we
+	 * need to manually yank the device.  This calls rp2_remove().
+	 */
+	if (rc)
+		device_release_driver(&card->pdev->dev);
+}
+
+static int rp2_probe(struct pci_dev *pdev,
+				   const struct pci_device_id *id)
+{
+	struct rp2_card *card;
+	struct rp2_uart_port *ports;
+	void __iomem * const *bars;
+	int rc;
+
+	card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+	if (!card)
+		return -ENOMEM;
+	pci_set_drvdata(pdev, card);
+	spin_lock_init(&card->card_lock);
+	init_completion(&card->fw_loaded);
+
+	rc = pcim_enable_device(pdev);
+	if (rc)
+		return rc;
+
+	rc = pcim_iomap_regions_request_all(pdev, 0x03, DRV_NAME);
+	if (rc)
+		return rc;
+
+	bars = pcim_iomap_table(pdev);
+	card->bar0 = bars[0];
+	card->bar1 = bars[1];
+	card->pdev = pdev;
+
+	rp2_decode_cap(id, &card->n_ports, &card->smpte);
+	dev_info(&pdev->dev, "found new card with %d ports\n", card->n_ports);
+
+	card->minor_start = rp2_alloc_ports(card->n_ports);
+	if (card->minor_start < 0) {
+		dev_err(&pdev->dev,
+			"too many ports (try increasing CONFIG_SERIAL_RP2_NR_UARTS)\n");
+		return -EINVAL;
+	}
+
+	rp2_init_card(card);
+
+	ports = devm_kzalloc(&pdev->dev, sizeof(*ports) * card->n_ports,
+			     GFP_KERNEL);
+	if (!ports)
+		return -ENOMEM;
+	card->ports = ports;
+
+	rc = devm_request_irq(&pdev->dev, pdev->irq, rp2_uart_interrupt,
+			      IRQF_SHARED, DRV_NAME, card);
+	if (rc)
+		return rc;
+
+	/*
+	 * Only catastrophic errors (e.g. ENOMEM) are reported here.
+	 * If the FW image is missing, we'll find out in rp2_fw_cb()
+	 * and print an error message.
+	 */
+	rc = request_firmware_nowait(THIS_MODULE, 1, RP2_FW_NAME, &pdev->dev,
+				     GFP_KERNEL, card, rp2_fw_cb);
+	if (rc)
+		return rc;
+	dev_dbg(&pdev->dev, "waiting for firmware blob...\n");
+
+	return 0;
+}
+
+static void rp2_remove(struct pci_dev *pdev)
+{
+	struct rp2_card *card = pci_get_drvdata(pdev);
+	int i;
+
+	wait_for_completion(&card->fw_loaded);
+	for (i = 0; i < card->initialized_ports; i++)
+		uart_remove_one_port(&rp2_uart_driver, &card->ports[i].port);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(rp2_pci_tbl) = {
+
+	/* RocketPort INFINITY cards */
+
+	{ RP_ID(0x0040), RP_CAP(8,  0) }, /* INF Octa, RJ45, selectable */
+	{ RP_ID(0x0041), RP_CAP(32, 0) }, /* INF 32, ext interface */
+	{ RP_ID(0x0042), RP_CAP(8,  0) }, /* INF Octa, ext interface */
+	{ RP_ID(0x0043), RP_CAP(16, 0) }, /* INF 16, ext interface */
+	{ RP_ID(0x0044), RP_CAP(4,  0) }, /* INF Quad, DB, selectable */
+	{ RP_ID(0x0045), RP_CAP(8,  0) }, /* INF Octa, DB, selectable */
+	{ RP_ID(0x0046), RP_CAP(4,  0) }, /* INF Quad, ext interface */
+	{ RP_ID(0x0047), RP_CAP(4,  0) }, /* INF Quad, RJ45 */
+	{ RP_ID(0x004a), RP_CAP(4,  0) }, /* INF Plus, Quad */
+	{ RP_ID(0x004b), RP_CAP(8,  0) }, /* INF Plus, Octa */
+	{ RP_ID(0x004c), RP_CAP(8,  0) }, /* INF III, Octa */
+	{ RP_ID(0x004d), RP_CAP(4,  0) }, /* INF III, Quad */
+	{ RP_ID(0x004e), RP_CAP(2,  0) }, /* INF Plus, 2, RS232 */
+	{ RP_ID(0x004f), RP_CAP(2,  1) }, /* INF Plus, 2, SMPTE */
+	{ RP_ID(0x0050), RP_CAP(4,  0) }, /* INF Plus, Quad, RJ45 */
+	{ RP_ID(0x0051), RP_CAP(8,  0) }, /* INF Plus, Octa, RJ45 */
+	{ RP_ID(0x0052), RP_CAP(8,  1) }, /* INF Octa, SMPTE */
+
+	/* RocketPort EXPRESS cards */
+
+	{ RP_ID(0x0060), RP_CAP(8,  0) }, /* EXP Octa, RJ45, selectable */
+	{ RP_ID(0x0061), RP_CAP(32, 0) }, /* EXP 32, ext interface */
+	{ RP_ID(0x0062), RP_CAP(8,  0) }, /* EXP Octa, ext interface */
+	{ RP_ID(0x0063), RP_CAP(16, 0) }, /* EXP 16, ext interface */
+	{ RP_ID(0x0064), RP_CAP(4,  0) }, /* EXP Quad, DB, selectable */
+	{ RP_ID(0x0065), RP_CAP(8,  0) }, /* EXP Octa, DB, selectable */
+	{ RP_ID(0x0066), RP_CAP(4,  0) }, /* EXP Quad, ext interface */
+	{ RP_ID(0x0067), RP_CAP(4,  0) }, /* EXP Quad, RJ45 */
+	{ RP_ID(0x0068), RP_CAP(8,  0) }, /* EXP Octa, RJ11 */
+	{ RP_ID(0x0072), RP_CAP(8,  1) }, /* EXP Octa, SMPTE */
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, rp2_pci_tbl);
+
+static struct pci_driver rp2_pci_driver = {
+	.name		= DRV_NAME,
+	.id_table	= rp2_pci_tbl,
+	.probe		= rp2_probe,
+	.remove		= rp2_remove,
+};
+
+static int __init rp2_uart_init(void)
+{
+	int rc;
+
+	rc = uart_register_driver(&rp2_uart_driver);
+	if (rc)
+		return rc;
+
+	rc = pci_register_driver(&rp2_pci_driver);
+	if (rc) {
+		uart_unregister_driver(&rp2_uart_driver);
+		return rc;
+	}
+
+	return 0;
+}
+
+static void __exit rp2_uart_exit(void)
+{
+	pci_unregister_driver(&rp2_pci_driver);
+	uart_unregister_driver(&rp2_uart_driver);
+}
+
+module_init(rp2_uart_init);
+module_exit(rp2_uart_exit);
+
+MODULE_DESCRIPTION("Comtrol RocketPort EXPRESS/INFINITY driver");
+MODULE_AUTHOR("Kevin Cernekee <cernekee@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 78f99d9..9dd47a5 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -219,4 +219,7 @@
 /* ARC (Synopsys) on-chip UART */
 #define PORT_ARC       101
 
+/* Rocketport EXPRESS/INFINITY */
+#define PORT_RP2	102
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
1.7.10.4


^ permalink raw reply related

* Re: [PATCH V2 1/3] serial: rp2: New driver for Comtrol RocketPort 2 cards
From: Ben Hutchings @ 2012-12-30 11:59 UTC (permalink / raw)
  To: Kevin Cernekee; +Cc: alan, gregkh, jslaby, linux-serial, dwmw2
In-Reply-To: <bd10fdeead3e321db2bb291c0b722203@localhost>

[-- Attachment #1: Type: text/plain, Size: 1130 bytes --]

On Sat, 2012-12-29 at 23:23 -0800, Kevin Cernekee wrote:
[...]
> --- /dev/null
> +++ b/drivers/tty/serial/rp2.c
[...]
> +static struct uart_ops rp2_uart_ops = {

const?

[...]
> +static void rp2_fw_cb(const struct firmware *fw, void *context)
> +{
> +	struct rp2_card *card = context;
> +	resource_size_t phys_base;
> +	int i, rc = -ENOENT;
> +
> +	if (!fw) {
> +		dev_err(&card->pdev->dev, "cannot find '%s' firmware image\n",
> +			RP2_FW_NAME);
> +		goto no_fw;
> +	}
[...]
> +no_fw:
> +	complete(&card->fw_loaded);
> +
> +	/*
> +	 * rp2_fw_cb() is called from a workqueue long after rp2_probe()
> +	 * has already returned success.  So if something failed here, we
> +	 * need to manually yank the device.  This calls rp2_remove().
> +	 */
> +	if (rc)
> +		device_release_driver(&card->pdev->dev);
[...]

The driver can be unbound and then re-bound immediately after this
function calls complete().  This will then unbind it a second time!

Is it really necessary to unbind here?

Ben.

-- 
Ben Hutchings
It is easier to change the specification to fit the program than vice versa.

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply

* Re: [PATCH -firmware 3/3] rp2: Initial commit of Comtrol RocketPort 2 microcode
From: Ben Hutchings @ 2012-12-30 12:29 UTC (permalink / raw)
  To: Kevin Cernekee; +Cc: dwmw2, alan, gregkh, jslaby, linux-serial
In-Reply-To: <4afa5e3b9acea9c3d4a9255a0f5be260@localhost>

[-- Attachment #1: Type: text/plain, Size: 1189 bytes --]

On Sat, 2012-12-29 at 23:23 -0800, Kevin Cernekee wrote:
> Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
> ---
>  WHENCE |   13 +++++++++++++
>  rp2.fw |  Bin 0 -> 63 bytes
>  2 files changed, 13 insertions(+)
>  create mode 100644 rp2.fw
> 
> 
> This is intended for the linux-firmware repo.
> 
> Also available from: git://github.com/cernekee/linux-firmware rp2
> 
> 
> diff --git a/WHENCE b/WHENCE
> index b7dcda3..3923185 100644
> --- a/WHENCE
> +++ b/WHENCE
> @@ -2034,3 +2034,16 @@ useful, but WITHOUT ANY WARRANTY; without even the implied warranty
>  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
>  
>  --------------------------------------------------------------------------
> +
> +Driver: rp2 -- Comtrol RocketPort 2 serial driver
> +
> +File: rp2.fw
> +
> +Licence: Allegedly GPLv2+, but no source visible. Marked:
[...]

The 'GPL' blobs already in the Linux source tree were grandfathered in
but I don't think we should add any more like this.  Please ask the
vendor to clarify the licence or provide source for this.

Ben.

-- 
Ben Hutchings
It is easier to change the specification to fit the program than vice versa.

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply

* [PATCH V3] serial: rp2: New driver for Comtrol RocketPort 2 cards
From: Kevin Cernekee @ 2012-12-30 22:07 UTC (permalink / raw)
  To: alan, gregkh, jslaby; +Cc: linux-serial, dwmw2, ben

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
---
 drivers/tty/serial/Kconfig       |   24 +
 drivers/tty/serial/Makefile      |    1 +
 drivers/tty/serial/rp2.c         |  892 ++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/serial_core.h |    3 +
 4 files changed, 920 insertions(+)
 create mode 100644 drivers/tty/serial/rp2.c


V2->V3 changes:

 - Don't try to unregister the device if rp2_fw_cb() fails
 - Add MODULE_FIRMWARE declaration


diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 59c23d0..555f3ca 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1447,4 +1447,28 @@ config SERIAL_ARC_NR_PORTS
 	  Set this to the number of serial ports you want the driver
 	  to support.
 
+config SERIAL_RP2
+	tristate "Comtrol RocketPort EXPRESS/INFINITY support"
+	depends on PCI
+	select SERIAL_CORE
+	help
+	  This driver supports the Comtrol RocketPort EXPRESS and
+	  RocketPort INFINITY families of PCI/PCIe multiport serial adapters.
+	  These adapters use a "RocketPort 2" ASIC that is not compatible
+	  with the original RocketPort driver (CONFIG_ROCKETPORT).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rp2.
+
+	  If you want to compile this driver into the kernel, say Y here.  If
+	  you don't have a suitable RocketPort card installed, say N.
+
+config SERIAL_RP2_NR_UARTS
+	int "Maximum number of RocketPort EXPRESS/INFINITY ports"
+	depends on SERIAL_RP2
+	default "32"
+	help
+	  If multiple cards are present, the default limit of 32 ports may
+	  need to be increased.
+
 endmenu
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index df1b998..a582ee9 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -83,3 +83,4 @@ obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o
 obj-$(CONFIG_SERIAL_AR933X)   += ar933x_uart.o
 obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
 obj-$(CONFIG_SERIAL_ARC)	+= arc_uart.o
+obj-$(CONFIG_SERIAL_RP2)	+= rp2.o
diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c
new file mode 100644
index 0000000..535dd83
--- /dev/null
+++ b/drivers/tty/serial/rp2.c
@@ -0,0 +1,892 @@
+/*
+ * Driver for Comtrol RocketPort EXPRESS/INFINITY cards
+ *
+ * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com>
+ *
+ * Inspired by, and loosely based on:
+ *
+ *   ar933x_uart.c
+ *     Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *   rocketport_infinity_express-linux-1.20.tar.gz
+ *     Copyright (C) 2004-2011 Comtrol, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/completion.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/types.h>
+
+#define DRV_NAME			"rp2"
+
+#define RP2_FW_NAME			"rp2.fw"
+#define RP2_UCODE_BYTES			0x3f
+
+#define PORTS_PER_ASIC			16
+#define ALL_PORTS_MASK			(BIT(PORTS_PER_ASIC) - 1)
+
+#define UART_CLOCK			44236800
+#define DEFAULT_BAUD_DIV		(UART_CLOCK / (9600 * 16))
+#define FIFO_SIZE			512
+
+/* BAR0 registers */
+#define RP2_FPGA_CTL0			0x110
+#define RP2_FPGA_CTL1			0x11c
+#define RP2_IRQ_MASK			0x1ec
+#define RP2_IRQ_MASK_EN_m		BIT(0)
+#define RP2_IRQ_STATUS			0x1f0
+
+/* BAR1 registers */
+#define RP2_ASIC_SPACING		0x1000
+#define RP2_ASIC_OFFSET(i)		((i) << ilog2(RP2_ASIC_SPACING))
+
+#define RP2_PORT_BASE			0x000
+#define RP2_PORT_SPACING		0x040
+
+#define RP2_UCODE_BASE			0x400
+#define RP2_UCODE_SPACING		0x80
+
+#define RP2_CLK_PRESCALER		0xc00
+#define RP2_CH_IRQ_STAT			0xc04
+#define RP2_CH_IRQ_MASK			0xc08
+#define RP2_ASIC_IRQ			0xd00
+#define RP2_ASIC_IRQ_EN_m		BIT(20)
+#define RP2_GLOBAL_CMD			0xd0c
+#define RP2_ASIC_CFG			0xd04
+
+/* port registers */
+#define RP2_DATA_DWORD			0x000
+
+#define RP2_DATA_BYTE			0x008
+#define RP2_DATA_BYTE_ERR_PARITY_m	BIT(8)
+#define RP2_DATA_BYTE_ERR_OVERRUN_m	BIT(9)
+#define RP2_DATA_BYTE_ERR_FRAMING_m	BIT(10)
+#define RP2_DATA_BYTE_BREAK_m		BIT(11)
+
+/* This lets uart_insert_char() drop bytes received on a !CREAD port */
+#define RP2_DUMMY_READ			BIT(16)
+
+#define RP2_DATA_BYTE_EXCEPTION_MASK	(RP2_DATA_BYTE_ERR_PARITY_m | \
+					 RP2_DATA_BYTE_ERR_OVERRUN_m | \
+					 RP2_DATA_BYTE_ERR_FRAMING_m | \
+					 RP2_DATA_BYTE_BREAK_m)
+
+#define RP2_RX_FIFO_COUNT		0x00c
+#define RP2_TX_FIFO_COUNT		0x00e
+
+#define RP2_CHAN_STAT			0x010
+#define RP2_CHAN_STAT_RXDATA_m		BIT(0)
+#define RP2_CHAN_STAT_DCD_m		BIT(3)
+#define RP2_CHAN_STAT_DSR_m		BIT(4)
+#define RP2_CHAN_STAT_CTS_m		BIT(5)
+#define RP2_CHAN_STAT_RI_m		BIT(6)
+#define RP2_CHAN_STAT_OVERRUN_m		BIT(13)
+#define RP2_CHAN_STAT_DSR_CHANGED_m	BIT(16)
+#define RP2_CHAN_STAT_CTS_CHANGED_m	BIT(17)
+#define RP2_CHAN_STAT_CD_CHANGED_m	BIT(18)
+#define RP2_CHAN_STAT_RI_CHANGED_m	BIT(22)
+#define RP2_CHAN_STAT_TXEMPTY_m		BIT(25)
+
+#define RP2_CHAN_STAT_MS_CHANGED_MASK	(RP2_CHAN_STAT_DSR_CHANGED_m | \
+					 RP2_CHAN_STAT_CTS_CHANGED_m | \
+					 RP2_CHAN_STAT_CD_CHANGED_m | \
+					 RP2_CHAN_STAT_RI_CHANGED_m)
+
+#define RP2_TXRX_CTL			0x014
+#define RP2_TXRX_CTL_MSRIRQ_m		BIT(0)
+#define RP2_TXRX_CTL_RXIRQ_m		BIT(2)
+#define RP2_TXRX_CTL_RX_TRIG_s		3
+#define RP2_TXRX_CTL_RX_TRIG_m		(0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_1		(0x1 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_256	(0x2 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_448	(0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_EN_m		BIT(5)
+#define RP2_TXRX_CTL_RTSFLOW_m		BIT(6)
+#define RP2_TXRX_CTL_DTRFLOW_m		BIT(7)
+#define RP2_TXRX_CTL_TX_TRIG_s		16
+#define RP2_TXRX_CTL_TX_TRIG_m		(0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_DSRFLOW_m		BIT(18)
+#define RP2_TXRX_CTL_TXIRQ_m		BIT(19)
+#define RP2_TXRX_CTL_CTSFLOW_m		BIT(23)
+#define RP2_TXRX_CTL_TX_EN_m		BIT(24)
+#define RP2_TXRX_CTL_RTS_m		BIT(25)
+#define RP2_TXRX_CTL_DTR_m		BIT(26)
+#define RP2_TXRX_CTL_LOOP_m		BIT(27)
+#define RP2_TXRX_CTL_BREAK_m		BIT(28)
+#define RP2_TXRX_CTL_CMSPAR_m		BIT(29)
+#define RP2_TXRX_CTL_nPARODD_m		BIT(30)
+#define RP2_TXRX_CTL_PARENB_m		BIT(31)
+
+#define RP2_UART_CTL			0x018
+#define RP2_UART_CTL_MODE_s		0
+#define RP2_UART_CTL_MODE_m		(0x7 << RP2_UART_CTL_MODE_s)
+#define RP2_UART_CTL_MODE_rs232		(0x1 << RP2_UART_CTL_MODE_s)
+#define RP2_UART_CTL_FLUSH_RX_m		BIT(3)
+#define RP2_UART_CTL_FLUSH_TX_m		BIT(4)
+#define RP2_UART_CTL_RESET_CH_m		BIT(5)
+#define RP2_UART_CTL_XMIT_EN_m		BIT(6)
+#define RP2_UART_CTL_DATABITS_s		8
+#define RP2_UART_CTL_DATABITS_m		(0x3 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_8		(0x3 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_7		(0x2 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_6		(0x1 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_5		(0x0 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_STOPBITS_m		BIT(10)
+
+#define RP2_BAUD			0x01c
+
+/* ucode registers */
+#define RP2_TX_SWFLOW			0x02
+#define RP2_TX_SWFLOW_ena		0x81
+#define RP2_TX_SWFLOW_dis		0x9d
+
+#define RP2_RX_SWFLOW			0x0c
+#define RP2_RX_SWFLOW_ena		0x81
+#define RP2_RX_SWFLOW_dis		0x8d
+
+#define RP2_RX_FIFO			0x37
+#define RP2_RX_FIFO_ena			0x08
+#define RP2_RX_FIFO_dis			0x81
+
+static struct uart_driver rp2_uart_driver = {
+	.owner				= THIS_MODULE,
+	.driver_name			= DRV_NAME,
+	.dev_name			= "ttyRP",
+	.nr				= CONFIG_SERIAL_RP2_NR_UARTS,
+};
+
+struct rp2_card;
+
+struct rp2_uart_port {
+	struct uart_port		port;
+	int				idx;
+	int				ignore_rx;
+	struct rp2_card			*card;
+	void __iomem			*asic_base;
+	void __iomem			*base;
+	void __iomem			*ucode;
+};
+
+struct rp2_card {
+	struct pci_dev			*pdev;
+	struct rp2_uart_port		*ports;
+	int				n_ports;
+	int				initialized_ports;
+	int				minor_start;
+	int				smpte;
+	void __iomem			*bar0;
+	void __iomem			*bar1;
+	spinlock_t			card_lock;
+	struct completion		fw_loaded;
+};
+
+#define RP_ID(prod) PCI_VDEVICE(RP, (prod))
+#define RP_CAP(ports, smpte) (((ports) << 8) | ((smpte) << 0))
+
+static inline void rp2_decode_cap(const struct pci_device_id *id,
+				  int *ports, int *smpte)
+{
+	*ports = id->driver_data >> 8;
+	*smpte = id->driver_data & 0xff;
+}
+
+static DEFINE_SPINLOCK(rp2_minor_lock);
+static int rp2_minor_next;
+
+static int rp2_alloc_ports(int n_ports)
+{
+	int ret = -ENOSPC;
+
+	spin_lock(&rp2_minor_lock);
+	if (rp2_minor_next + n_ports <= CONFIG_SERIAL_RP2_NR_UARTS) {
+		/* sorry, no support for hot unplugging individual cards */
+		ret = rp2_minor_next;
+		rp2_minor_next += n_ports;
+	}
+	spin_unlock(&rp2_minor_lock);
+
+	return ret;
+}
+
+static inline struct rp2_uart_port *port_to_up(struct uart_port *port)
+{
+	return container_of(port, struct rp2_uart_port, port);
+}
+
+static void rp2_rmw(struct rp2_uart_port *up, int reg,
+		    u32 clr_bits, u32 set_bits)
+{
+	u32 tmp = readl(up->base + reg);
+	tmp &= ~clr_bits;
+	tmp |= set_bits;
+	writel(tmp, up->base + reg);
+}
+
+static void rp2_rmw_clr(struct rp2_uart_port *up, int reg, u32 val)
+{
+	rp2_rmw(up, reg, val, 0);
+}
+
+static void rp2_rmw_set(struct rp2_uart_port *up, int reg, u32 val)
+{
+	rp2_rmw(up, reg, 0, val);
+}
+
+static void rp2_mask_ch_irq(struct rp2_uart_port *up, int ch_num,
+			    int is_enabled)
+{
+	unsigned long flags, irq_mask;
+
+	spin_lock_irqsave(&up->card->card_lock, flags);
+
+	irq_mask = readl(up->asic_base + RP2_CH_IRQ_MASK);
+	if (is_enabled)
+		irq_mask &= ~BIT(ch_num);
+	else
+		irq_mask |= BIT(ch_num);
+	writel(irq_mask, up->asic_base + RP2_CH_IRQ_MASK);
+
+	spin_unlock_irqrestore(&up->card->card_lock, flags);
+}
+
+static unsigned int rp2_uart_tx_empty(struct uart_port *port)
+{
+	struct rp2_uart_port *up = port_to_up(port);
+	unsigned long tx_fifo_bytes, flags;
+
+	/*
+	 * This should probably check the transmitter, not the FIFO.
+	 * But the TXEMPTY bit doesn't seem to work unless the TX IRQ is
+	 * enabled.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+	tx_fifo_bytes = readw(up->base + RP2_TX_FIFO_COUNT);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return tx_fifo_bytes ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int rp2_uart_get_mctrl(struct uart_port *port)
+{
+	struct rp2_uart_port *up = port_to_up(port);
+	u32 status;
+
+	status = readl(up->base + RP2_CHAN_STAT);
+	return ((status & RP2_CHAN_STAT_DCD_m) ? TIOCM_CAR : 0) |
+	       ((status & RP2_CHAN_STAT_DSR_m) ? TIOCM_DSR : 0) |
+	       ((status & RP2_CHAN_STAT_CTS_m) ? TIOCM_CTS : 0) |
+	       ((status & RP2_CHAN_STAT_RI_m) ? TIOCM_RI : 0);
+}
+
+static void rp2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	rp2_rmw(port_to_up(port), RP2_TXRX_CTL,
+		RP2_TXRX_CTL_DTR_m | RP2_TXRX_CTL_RTS_m | RP2_TXRX_CTL_LOOP_m,
+		((mctrl & TIOCM_DTR) ? RP2_TXRX_CTL_DTR_m : 0) |
+		((mctrl & TIOCM_RTS) ? RP2_TXRX_CTL_RTS_m : 0) |
+		((mctrl & TIOCM_LOOP) ? RP2_TXRX_CTL_LOOP_m : 0));
+}
+
+static void rp2_uart_start_tx(struct uart_port *port)
+{
+	rp2_rmw_set(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_TXIRQ_m);
+}
+
+static void rp2_uart_stop_tx(struct uart_port *port)
+{
+	rp2_rmw_clr(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_TXIRQ_m);
+}
+
+static void rp2_uart_stop_rx(struct uart_port *port)
+{
+	rp2_rmw_clr(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_RXIRQ_m);
+}
+
+static void rp2_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	rp2_rmw(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_BREAK_m,
+		break_state ? RP2_TXRX_CTL_BREAK_m : 0);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void rp2_uart_enable_ms(struct uart_port *port)
+{
+	rp2_rmw_set(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_MSRIRQ_m);
+}
+
+static void __rp2_uart_set_termios(struct rp2_uart_port *up,
+				   unsigned long cfl,
+				   unsigned long ifl,
+				   unsigned int baud_div)
+{
+	/* baud rate divisor (calculated elsewhere).  0 = divide-by-1 */
+	writew(baud_div - 1, up->base + RP2_BAUD);
+
+	/* data bits and stop bits */
+	rp2_rmw(up, RP2_UART_CTL,
+		RP2_UART_CTL_STOPBITS_m | RP2_UART_CTL_DATABITS_m,
+		((cfl & CSTOPB) ? RP2_UART_CTL_STOPBITS_m : 0) |
+		(((cfl & CSIZE) == CS8) ? RP2_UART_CTL_DATABITS_8 : 0) |
+		(((cfl & CSIZE) == CS7) ? RP2_UART_CTL_DATABITS_7 : 0) |
+		(((cfl & CSIZE) == CS6) ? RP2_UART_CTL_DATABITS_6 : 0) |
+		(((cfl & CSIZE) == CS5) ? RP2_UART_CTL_DATABITS_5 : 0));
+
+	/* parity and hardware flow control */
+	rp2_rmw(up, RP2_TXRX_CTL,
+		RP2_TXRX_CTL_PARENB_m | RP2_TXRX_CTL_nPARODD_m |
+		RP2_TXRX_CTL_CMSPAR_m | RP2_TXRX_CTL_DTRFLOW_m |
+		RP2_TXRX_CTL_DSRFLOW_m | RP2_TXRX_CTL_RTSFLOW_m |
+		RP2_TXRX_CTL_CTSFLOW_m,
+		((cfl & PARENB) ? RP2_TXRX_CTL_PARENB_m : 0) |
+		((cfl & PARODD) ? 0 : RP2_TXRX_CTL_nPARODD_m) |
+		((cfl & CMSPAR) ? RP2_TXRX_CTL_CMSPAR_m : 0) |
+		((cfl & CRTSCTS) ? (RP2_TXRX_CTL_RTSFLOW_m |
+				    RP2_TXRX_CTL_CTSFLOW_m) : 0));
+
+	/* XON/XOFF software flow control */
+	writeb((ifl & IXON) ? RP2_TX_SWFLOW_ena : RP2_TX_SWFLOW_dis,
+	       up->ucode + RP2_TX_SWFLOW);
+	writeb((ifl & IXOFF) ? RP2_RX_SWFLOW_ena : RP2_RX_SWFLOW_dis,
+	       up->ucode + RP2_RX_SWFLOW);
+}
+
+static void rp2_uart_set_termios(struct uart_port *port,
+				 struct ktermios *new,
+				 struct ktermios *old)
+{
+	struct rp2_uart_port *up = port_to_up(port);
+	unsigned long flags;
+	unsigned int baud, baud_div;
+
+	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+	baud_div = uart_get_divisor(port, baud);
+
+	if (tty_termios_baud_rate(new))
+		tty_termios_encode_baud_rate(new, baud, baud);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* ignore all characters if CREAD is not set */
+	port->ignore_status_mask = (new->c_cflag & CREAD) ? 0 : RP2_DUMMY_READ;
+
+	__rp2_uart_set_termios(up, new->c_cflag, new->c_iflag, baud_div);
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void rp2_rx_chars(struct rp2_uart_port *up)
+{
+	u16 bytes = readw(up->base + RP2_RX_FIFO_COUNT);
+	struct tty_struct *tty = tty_port_tty_get(&up->port.state->port);
+
+	if (!tty) {
+		/* Discard data: no tty available */
+		for (; bytes != 0; bytes--)
+			readw(up->base + RP2_DATA_BYTE);
+		return;
+	}
+
+	for (; bytes != 0; bytes--) {
+		u32 byte = readw(up->base + RP2_DATA_BYTE) | RP2_DUMMY_READ;
+		char ch = byte & 0xff;
+
+		if (likely(!(byte & RP2_DATA_BYTE_EXCEPTION_MASK))) {
+			if (!uart_handle_sysrq_char(&up->port, ch))
+				uart_insert_char(&up->port, byte, 0, ch,
+						 TTY_NORMAL);
+		} else {
+			char flag = TTY_NORMAL;
+
+			if (byte & RP2_DATA_BYTE_BREAK_m)
+				flag = TTY_BREAK;
+			else if (byte & RP2_DATA_BYTE_ERR_FRAMING_m)
+				flag = TTY_FRAME;
+			else if (byte & RP2_DATA_BYTE_ERR_PARITY_m)
+				flag = TTY_PARITY;
+			uart_insert_char(&up->port, byte,
+					 RP2_DATA_BYTE_ERR_OVERRUN_m, ch, flag);
+		}
+		up->port.icount.rx++;
+	}
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+static void rp2_tx_chars(struct rp2_uart_port *up)
+{
+	u16 max_tx = FIFO_SIZE - readw(up->base + RP2_TX_FIFO_COUNT);
+	struct circ_buf *xmit = &up->port.state->xmit;
+
+	if (uart_tx_stopped(&up->port)) {
+		rp2_uart_stop_tx(&up->port);
+		return;
+	}
+
+	for (; max_tx != 0; max_tx--) {
+		if (up->port.x_char) {
+			writeb(up->port.x_char, up->base + RP2_DATA_BYTE);
+			up->port.x_char = 0;
+			up->port.icount.tx++;
+			continue;
+		}
+		if (uart_circ_empty(xmit)) {
+			rp2_uart_stop_tx(&up->port);
+			break;
+		}
+		writeb(xmit->buf[xmit->tail], up->base + RP2_DATA_BYTE);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+}
+
+static void rp2_ch_interrupt(struct rp2_uart_port *up)
+{
+	u32 status;
+
+	spin_lock(&up->port.lock);
+
+	/*
+	 * The IRQ status bits are clear-on-write.  Other status bits in
+	 * this register aren't, so it's harmless to write to them.
+	 */
+	status = readl(up->base + RP2_CHAN_STAT);
+	writel(status, up->base + RP2_CHAN_STAT);
+
+	if (status & RP2_CHAN_STAT_RXDATA_m)
+		rp2_rx_chars(up);
+	if (status & RP2_CHAN_STAT_TXEMPTY_m)
+		rp2_tx_chars(up);
+	if (status & RP2_CHAN_STAT_MS_CHANGED_MASK)
+		wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+
+	spin_unlock(&up->port.lock);
+}
+
+static int rp2_asic_interrupt(struct rp2_card *card, unsigned int asic_id)
+{
+	void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id);
+	int ch, handled = 0;
+	unsigned long status = readl(base + RP2_CH_IRQ_STAT) &
+			       ~readl(base + RP2_CH_IRQ_MASK);
+
+	for_each_set_bit(ch, &status, PORTS_PER_ASIC) {
+		rp2_ch_interrupt(&card->ports[ch]);
+		handled++;
+	}
+	return handled;
+}
+
+static irqreturn_t rp2_uart_interrupt(int irq, void *dev_id)
+{
+	struct rp2_card *card = dev_id;
+	int handled;
+
+	handled = rp2_asic_interrupt(card, 0);
+	if (card->n_ports >= PORTS_PER_ASIC)
+		handled += rp2_asic_interrupt(card, 1);
+
+	return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static inline void rp2_flush_fifos(struct rp2_uart_port *up)
+{
+	rp2_rmw_set(up, RP2_UART_CTL,
+		    RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m);
+	readl(up->base + RP2_UART_CTL);
+	udelay(10);
+	rp2_rmw_clr(up, RP2_UART_CTL,
+		    RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m);
+}
+
+static int rp2_uart_startup(struct uart_port *port)
+{
+	struct rp2_uart_port *up = port_to_up(port);
+
+	rp2_flush_fifos(up);
+	rp2_rmw(up, RP2_TXRX_CTL, RP2_TXRX_CTL_MSRIRQ_m, RP2_TXRX_CTL_RXIRQ_m);
+	rp2_rmw(up, RP2_TXRX_CTL, RP2_TXRX_CTL_RX_TRIG_m,
+		RP2_TXRX_CTL_RX_TRIG_1);
+	rp2_rmw(up, RP2_CHAN_STAT, 0, 0);
+	rp2_mask_ch_irq(up, up->idx, 1);
+
+	return 0;
+}
+
+static void rp2_uart_shutdown(struct uart_port *port)
+{
+	struct rp2_uart_port *up = port_to_up(port);
+	unsigned long flags;
+
+	rp2_uart_break_ctl(port, 0);
+
+	spin_lock_irqsave(&port->lock, flags);
+	rp2_mask_ch_irq(up, up->idx, 0);
+	rp2_rmw(up, RP2_CHAN_STAT, 0, 0);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *rp2_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_RP2) ? "RocketPort 2 UART" : NULL;
+}
+
+static void rp2_uart_release_port(struct uart_port *port)
+{
+	/* Nothing to release ... */
+}
+
+static int rp2_uart_request_port(struct uart_port *port)
+{
+	/* UARTs always present */
+	return 0;
+}
+
+static void rp2_uart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE)
+		port->type = PORT_RP2;
+}
+
+static int rp2_uart_verify_port(struct uart_port *port,
+				   struct serial_struct *ser)
+{
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_RP2)
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct uart_ops rp2_uart_ops = {
+	.tx_empty	= rp2_uart_tx_empty,
+	.set_mctrl	= rp2_uart_set_mctrl,
+	.get_mctrl	= rp2_uart_get_mctrl,
+	.stop_tx	= rp2_uart_stop_tx,
+	.start_tx	= rp2_uart_start_tx,
+	.stop_rx	= rp2_uart_stop_rx,
+	.enable_ms	= rp2_uart_enable_ms,
+	.break_ctl	= rp2_uart_break_ctl,
+	.startup	= rp2_uart_startup,
+	.shutdown	= rp2_uart_shutdown,
+	.set_termios	= rp2_uart_set_termios,
+	.type		= rp2_uart_type,
+	.release_port	= rp2_uart_release_port,
+	.request_port	= rp2_uart_request_port,
+	.config_port	= rp2_uart_config_port,
+	.verify_port	= rp2_uart_verify_port,
+};
+
+static void rp2_reset_asic(struct rp2_card *card, unsigned int asic_id)
+{
+	void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id);
+	u32 clk_cfg;
+
+	writew(1, base + RP2_GLOBAL_CMD);
+	readw(base + RP2_GLOBAL_CMD);
+	msleep(100);
+	writel(0, base + RP2_CLK_PRESCALER);
+
+	/* TDM clock configuration */
+	clk_cfg = readw(base + RP2_ASIC_CFG);
+	clk_cfg = (clk_cfg & ~BIT(8)) | BIT(9);
+	writew(clk_cfg, base + RP2_ASIC_CFG);
+
+	/* IRQ routing */
+	writel(ALL_PORTS_MASK, base + RP2_CH_IRQ_MASK);
+	writel(RP2_ASIC_IRQ_EN_m, base + RP2_ASIC_IRQ);
+}
+
+static void rp2_init_card(struct rp2_card *card)
+{
+	writel(4, card->bar0 + RP2_FPGA_CTL0);
+	writel(0, card->bar0 + RP2_FPGA_CTL1);
+
+	rp2_reset_asic(card, 0);
+	if (card->n_ports >= PORTS_PER_ASIC)
+		rp2_reset_asic(card, 1);
+
+	writel(RP2_IRQ_MASK_EN_m, card->bar0 + RP2_IRQ_MASK);
+}
+
+static void rp2_init_port(struct rp2_uart_port *up, const struct firmware *fw)
+{
+	int i;
+
+	writel(RP2_UART_CTL_RESET_CH_m, up->base + RP2_UART_CTL);
+	readl(up->base + RP2_UART_CTL);
+	udelay(1);
+
+	writel(0, up->base + RP2_TXRX_CTL);
+	writel(0, up->base + RP2_UART_CTL);
+	readl(up->base + RP2_UART_CTL);
+	udelay(1);
+
+	rp2_flush_fifos(up);
+
+	for (i = 0; i < min_t(int, fw->size, RP2_UCODE_BYTES); i++)
+		writeb(fw->data[i], up->ucode + i);
+
+	__rp2_uart_set_termios(up, CS8 | CREAD | CLOCAL, 0, DEFAULT_BAUD_DIV);
+	rp2_uart_set_mctrl(&up->port, 0);
+
+	writeb(RP2_RX_FIFO_ena, up->ucode + RP2_RX_FIFO);
+	rp2_rmw(up, RP2_UART_CTL, RP2_UART_CTL_MODE_m,
+		RP2_UART_CTL_XMIT_EN_m | RP2_UART_CTL_MODE_rs232);
+	rp2_rmw_set(up, RP2_TXRX_CTL,
+		    RP2_TXRX_CTL_TX_EN_m | RP2_TXRX_CTL_RX_EN_m);
+}
+
+static void rp2_remove_ports(struct rp2_card *card)
+{
+	int i;
+
+	for (i = 0; i < card->initialized_ports; i++)
+		uart_remove_one_port(&rp2_uart_driver, &card->ports[i].port);
+	card->initialized_ports = 0;
+}
+
+static void rp2_fw_cb(const struct firmware *fw, void *context)
+{
+	struct rp2_card *card = context;
+	resource_size_t phys_base;
+	int i, rc = -ENOENT;
+
+	if (!fw) {
+		dev_err(&card->pdev->dev, "cannot find '%s' firmware image\n",
+			RP2_FW_NAME);
+		goto no_fw;
+	}
+
+	phys_base = pci_resource_start(card->pdev, 1);
+
+	for (i = 0; i < card->n_ports; i++) {
+		struct rp2_uart_port *rp = &card->ports[i];
+		struct uart_port *p;
+		int j = (unsigned)i % PORTS_PER_ASIC;
+
+		rp->asic_base = card->bar1;
+		rp->base = card->bar1 + RP2_PORT_BASE + j*RP2_PORT_SPACING;
+		rp->ucode = card->bar1 + RP2_UCODE_BASE + j*RP2_UCODE_SPACING;
+		rp->card = card;
+		rp->idx = j;
+
+		p = &rp->port;
+		p->line = card->minor_start + i;
+		p->dev = &card->pdev->dev;
+		p->type = PORT_RP2;
+		p->iotype = UPIO_MEM32;
+		p->uartclk = UART_CLOCK;
+		p->regshift = 2;
+		p->fifosize = FIFO_SIZE;
+		p->ops = &rp2_uart_ops;
+		p->irq = card->pdev->irq;
+		p->membase = rp->base;
+		p->mapbase = phys_base + RP2_PORT_BASE + j*RP2_PORT_SPACING;
+
+		if (i >= PORTS_PER_ASIC) {
+			rp->asic_base += RP2_ASIC_SPACING;
+			rp->base += RP2_ASIC_SPACING;
+			rp->ucode += RP2_ASIC_SPACING;
+			p->mapbase += RP2_ASIC_SPACING;
+		}
+
+		rp2_init_port(rp, fw);
+		rc = uart_add_one_port(&rp2_uart_driver, p);
+		if (rc) {
+			dev_err(&card->pdev->dev,
+				"error registering port %d: %d\n", i, rc);
+			rp2_remove_ports(card);
+			break;
+		}
+		card->initialized_ports++;
+	}
+
+	release_firmware(fw);
+no_fw:
+	/*
+	 * rp2_fw_cb() is called from a workqueue long after rp2_probe()
+	 * has already returned success.  So if something failed here,
+	 * we'll just leave the now-dormant device in place until somebody
+	 * unbinds it.
+	 */
+	if (rc)
+		dev_warn(&card->pdev->dev, "driver initialization failed\n");
+
+	complete(&card->fw_loaded);
+}
+
+static int rp2_probe(struct pci_dev *pdev,
+				   const struct pci_device_id *id)
+{
+	struct rp2_card *card;
+	struct rp2_uart_port *ports;
+	void __iomem * const *bars;
+	int rc;
+
+	card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+	if (!card)
+		return -ENOMEM;
+	pci_set_drvdata(pdev, card);
+	spin_lock_init(&card->card_lock);
+	init_completion(&card->fw_loaded);
+
+	rc = pcim_enable_device(pdev);
+	if (rc)
+		return rc;
+
+	rc = pcim_iomap_regions_request_all(pdev, 0x03, DRV_NAME);
+	if (rc)
+		return rc;
+
+	bars = pcim_iomap_table(pdev);
+	card->bar0 = bars[0];
+	card->bar1 = bars[1];
+	card->pdev = pdev;
+
+	rp2_decode_cap(id, &card->n_ports, &card->smpte);
+	dev_info(&pdev->dev, "found new card with %d ports\n", card->n_ports);
+
+	card->minor_start = rp2_alloc_ports(card->n_ports);
+	if (card->minor_start < 0) {
+		dev_err(&pdev->dev,
+			"too many ports (try increasing CONFIG_SERIAL_RP2_NR_UARTS)\n");
+		return -EINVAL;
+	}
+
+	rp2_init_card(card);
+
+	ports = devm_kzalloc(&pdev->dev, sizeof(*ports) * card->n_ports,
+			     GFP_KERNEL);
+	if (!ports)
+		return -ENOMEM;
+	card->ports = ports;
+
+	rc = devm_request_irq(&pdev->dev, pdev->irq, rp2_uart_interrupt,
+			      IRQF_SHARED, DRV_NAME, card);
+	if (rc)
+		return rc;
+
+	/*
+	 * Only catastrophic errors (e.g. ENOMEM) are reported here.
+	 * If the FW image is missing, we'll find out in rp2_fw_cb()
+	 * and print an error message.
+	 */
+	rc = request_firmware_nowait(THIS_MODULE, 1, RP2_FW_NAME, &pdev->dev,
+				     GFP_KERNEL, card, rp2_fw_cb);
+	if (rc)
+		return rc;
+	dev_dbg(&pdev->dev, "waiting for firmware blob...\n");
+
+	return 0;
+}
+
+static void rp2_remove(struct pci_dev *pdev)
+{
+	struct rp2_card *card = pci_get_drvdata(pdev);
+
+	wait_for_completion(&card->fw_loaded);
+	rp2_remove_ports(card);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(rp2_pci_tbl) = {
+
+	/* RocketPort INFINITY cards */
+
+	{ RP_ID(0x0040), RP_CAP(8,  0) }, /* INF Octa, RJ45, selectable */
+	{ RP_ID(0x0041), RP_CAP(32, 0) }, /* INF 32, ext interface */
+	{ RP_ID(0x0042), RP_CAP(8,  0) }, /* INF Octa, ext interface */
+	{ RP_ID(0x0043), RP_CAP(16, 0) }, /* INF 16, ext interface */
+	{ RP_ID(0x0044), RP_CAP(4,  0) }, /* INF Quad, DB, selectable */
+	{ RP_ID(0x0045), RP_CAP(8,  0) }, /* INF Octa, DB, selectable */
+	{ RP_ID(0x0046), RP_CAP(4,  0) }, /* INF Quad, ext interface */
+	{ RP_ID(0x0047), RP_CAP(4,  0) }, /* INF Quad, RJ45 */
+	{ RP_ID(0x004a), RP_CAP(4,  0) }, /* INF Plus, Quad */
+	{ RP_ID(0x004b), RP_CAP(8,  0) }, /* INF Plus, Octa */
+	{ RP_ID(0x004c), RP_CAP(8,  0) }, /* INF III, Octa */
+	{ RP_ID(0x004d), RP_CAP(4,  0) }, /* INF III, Quad */
+	{ RP_ID(0x004e), RP_CAP(2,  0) }, /* INF Plus, 2, RS232 */
+	{ RP_ID(0x004f), RP_CAP(2,  1) }, /* INF Plus, 2, SMPTE */
+	{ RP_ID(0x0050), RP_CAP(4,  0) }, /* INF Plus, Quad, RJ45 */
+	{ RP_ID(0x0051), RP_CAP(8,  0) }, /* INF Plus, Octa, RJ45 */
+	{ RP_ID(0x0052), RP_CAP(8,  1) }, /* INF Octa, SMPTE */
+
+	/* RocketPort EXPRESS cards */
+
+	{ RP_ID(0x0060), RP_CAP(8,  0) }, /* EXP Octa, RJ45, selectable */
+	{ RP_ID(0x0061), RP_CAP(32, 0) }, /* EXP 32, ext interface */
+	{ RP_ID(0x0062), RP_CAP(8,  0) }, /* EXP Octa, ext interface */
+	{ RP_ID(0x0063), RP_CAP(16, 0) }, /* EXP 16, ext interface */
+	{ RP_ID(0x0064), RP_CAP(4,  0) }, /* EXP Quad, DB, selectable */
+	{ RP_ID(0x0065), RP_CAP(8,  0) }, /* EXP Octa, DB, selectable */
+	{ RP_ID(0x0066), RP_CAP(4,  0) }, /* EXP Quad, ext interface */
+	{ RP_ID(0x0067), RP_CAP(4,  0) }, /* EXP Quad, RJ45 */
+	{ RP_ID(0x0068), RP_CAP(8,  0) }, /* EXP Octa, RJ11 */
+	{ RP_ID(0x0072), RP_CAP(8,  1) }, /* EXP Octa, SMPTE */
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, rp2_pci_tbl);
+
+static struct pci_driver rp2_pci_driver = {
+	.name		= DRV_NAME,
+	.id_table	= rp2_pci_tbl,
+	.probe		= rp2_probe,
+	.remove		= rp2_remove,
+};
+
+static int __init rp2_uart_init(void)
+{
+	int rc;
+
+	rc = uart_register_driver(&rp2_uart_driver);
+	if (rc)
+		return rc;
+
+	rc = pci_register_driver(&rp2_pci_driver);
+	if (rc) {
+		uart_unregister_driver(&rp2_uart_driver);
+		return rc;
+	}
+
+	return 0;
+}
+
+static void __exit rp2_uart_exit(void)
+{
+	pci_unregister_driver(&rp2_pci_driver);
+	uart_unregister_driver(&rp2_uart_driver);
+}
+
+module_init(rp2_uart_init);
+module_exit(rp2_uart_exit);
+
+MODULE_DESCRIPTION("Comtrol RocketPort EXPRESS/INFINITY driver");
+MODULE_AUTHOR("Kevin Cernekee <cernekee@gmail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE(RP2_FW_NAME);
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 78f99d9..9dd47a5 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -219,4 +219,7 @@
 /* ARC (Synopsys) on-chip UART */
 #define PORT_ARC       101
 
+/* Rocketport EXPRESS/INFINITY */
+#define PORT_RP2	102
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
1.7.10.4


^ permalink raw reply related

* Re: [PATCH] seria: sirf: only use lookup table to set baudrate when ioclk=150MHz
From: Barry Song @ 2012-12-31  2:02 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Barry Song, alan, workgroup.linux, linux-arm-kernel, linux-serial,
	Barry Song
In-Reply-To: <20121226150433.GB24604@n2100.arm.linux.org.uk>

2012/12/26, Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Tue, Dec 25, 2012 at 06:26:02PM +0800, Barry Song wrote:
>> @@ -375,7 +375,12 @@ static void sirfsoc_uart_set_termios(struct uart_port
>> *port,
>>  	int		threshold_div;
>>  	int		temp;
>>
>> -	ioclk_rate = 150000000;
>> +	struct clk *clk = clk_get_sys("io", NULL);
>> +	BUG_ON(IS_ERR(clk));
>
> No.  Really, no.  Stop using BUG_ON() as some kind of crappy assert().
> BUG_ON() takes the entire kernel out when it fails.  There's absolutely
> no need for this what so ever - especially here.
>
> Get the clock at probe or port initialization time.  Save that pointer.
> Only give it up when the port is torn down.  And treat it as any other
> clock - prepare and enable it, and disable and unprepare it when you're
> done with it.
>
> And there's no need to use this clk_get_sys() crap in drivers.  Add the
> necessary clkdev entries or deal with it in DT.  Absolutely do not use
> clk_get_sys() in drivers; it's there for *PLATFORM* code to use when
> there's no other possibility for them and NOT drivers.

Thanks, Russell.
what made this happen is that clkio is used in io bus and by all of
dmac, nand, audio, uart, i2c, spi, usp, pwm, pulse as an input, so we
took it as a global/system clock. this way should be not good for the
real users.
as i have enabled "clk: prima2: enable dt-binding clkdev mapping"
https://patchwork.kernel.org/patch/1898811/
i might simply let uart node in DT takes the uart clk output of clk
controller as your suggestion.

-barry

^ permalink raw reply

* [PATCH] serial: samsung: remove redundant setting of line config during port reset
From: Thomas Abraham @ 2012-12-31 21:42 UTC (permalink / raw)
  To: linux-serial; +Cc: gregkh, alan, kgene.kim, linux-samsung-soc

The setting of uart line control configuration in s3c24xx_serial_resetport
is can be removed since the 'set_termios' call will overwrite any ULCON
register setting which s3c24xx_serial_resetport does.

Cc: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
---
 drivers/tty/serial/samsung.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 12e5249..e514b3a 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1006,7 +1006,6 @@ static void s3c24xx_serial_resetport(struct uart_port *port,
 
 	ucon &= ucon_mask;
 	wr_regl(port, S3C2410_UCON,  ucon | cfg->ucon);
-	wr_regl(port, S3C2410_ULCON, cfg->ulcon);
 
 	/* reset both fifos */
 	wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
-- 
1.7.5.4

^ permalink raw reply related

* RE: [PATCH] serial: samsung: remove redundant setting of line config during port reset
From: Kukjin Kim @ 2012-12-31 22:03 UTC (permalink / raw)
  To: 'Thomas Abraham', linux-serial; +Cc: gregkh, alan, linux-samsung-soc
In-Reply-To: <1356990165-3905-1-git-send-email-thomas.ab@samsung.com>

Thomas Abraham wrote:
> 
> The setting of uart line control configuration in s3c24xx_serial_resetport
> is can be removed since the 'set_termios' call will overwrite any ULCON
> register setting which s3c24xx_serial_resetport does.
> 
> Cc: Kukjin Kim <kgene.kim@samsung.com>

Acked-by: Kukjin Kim <kgene.kim@samsung.com>

Greg, if you're ok on this, please pick this up for v3.8

Happy New Year!!!

- Kukjin

> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> ---
>  drivers/tty/serial/samsung.c |    1 -
>  1 files changed, 0 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
> index 12e5249..e514b3a 100644
> --- a/drivers/tty/serial/samsung.c
> +++ b/drivers/tty/serial/samsung.c
> @@ -1006,7 +1006,6 @@ static void s3c24xx_serial_resetport(struct
> uart_port *port,
> 
>  	ucon &= ucon_mask;
>  	wr_regl(port, S3C2410_UCON,  ucon | cfg->ucon);
> -	wr_regl(port, S3C2410_ULCON, cfg->ulcon);
> 
>  	/* reset both fifos */
>  	wr_regl(port, S3C2410_UFCON, cfg->ufcon |
> S3C2410_UFCON_RESETBOTH);
> --
> 1.7.5.4

^ permalink raw reply

* [PATCH] serial: samsung: remove the use of statically remapped controller address
From: Thomas Abraham @ 2013-01-01  8:21 UTC (permalink / raw)
  To: linux-serial; +Cc: gregkh, alan, kgene.kim, linux-samsung-soc

The address S3C_VA_UART is a statically ioremapped address. The driver
should not be using this. Instead, the driver should setup a mapping
during probe.

Cc: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
---
 drivers/tty/serial/samsung.c |    8 ++++++--
 1 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index e514b3a..d6c4f659 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -47,7 +47,6 @@
 #include <asm/irq.h>
 
 #include <mach/hardware.h>
-#include <mach/map.h>
 
 #include <plat/regs-serial.h>
 #include <plat/clock.h>
@@ -1143,8 +1142,13 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
 
 	dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
 
+	port->membase = devm_ioremap(port->dev, res->start, resource_size(res));
+	if (!port->membase) {
+		dev_err(port->dev, "failed to remap controller address\n");
+		return -EBUSY;
+	}
+
 	port->mapbase = res->start;
-	port->membase = S3C_VA_UART + (res->start & 0xfffff);
 	ret = platform_get_irq(platdev, 0);
 	if (ret < 0)
 		port->irq = 0;
-- 
1.7.5.4


^ permalink raw reply related

* Re: [PATCH] tty: Only wakeup the line discipline idle queue when queue is active
From: Jiri Slaby @ 2013-01-02  9:29 UTC (permalink / raw)
  To: Ivo Sieben
  Cc: linux-serial, Alan Cox, Greg KH, Oleg Nesterov, linux-kernel,
	Andi Kleen, Peter Zijlstra, Ingo Molnar
In-Reply-To: <1355842130-15482-1-git-send-email-meltedpianoman@gmail.com>

On 12/18/2012 03:48 PM, Ivo Sieben wrote:
> Before waking up the tty line discipline idle queue first check if the queue is
> active (non empty). This prevents unnecessary entering the critical section in
> the wake_up() function and therefore avoid needless scheduling overhead on a
> PREEMPT_RT system caused by two processes being in the same critical section.
> 
> Signed-off-by: Ivo Sieben <meltedpianoman@gmail.com>
> ---
> 
>  Remark:
>  This patch has kind of a long history... I first tried to prevent the critical
>  section in the wakeup() function itself by a change in the scheduler. But after
>  review remarks from Oleg Nesterov it turned out that using the
>  waitqueue_active() was a much nicer way to prevent it. See also
>  https://lkml.org/lkml/2012/10/25/159
> 
>  drivers/tty/tty_ldisc.c |    4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
> index c578229..e96d187 100644
> --- a/drivers/tty/tty_ldisc.c
> +++ b/drivers/tty/tty_ldisc.c
> @@ -64,7 +64,9 @@ static void put_ldisc(struct tty_ldisc *ld)
>  		return;
>  	}
>  	raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -	wake_up(&ld->wq_idle);
> +
> +	if (waitqueue_active(&ld->wq_idle))
> +		wake_up(&ld->wq_idle);

Looks good, but I would prefer the layer to provide us with
wake_up_if_active...

-- 
js
suse labs

^ permalink raw reply

* Re: [PATCH] tty: Only wakeup the line discipline idle queue when queue is active
From: Alan Cox @ 2013-01-02 11:43 UTC (permalink / raw)
  To: Jiri Slaby
  Cc: Ivo Sieben, linux-serial, Alan Cox, Greg KH, Oleg Nesterov,
	linux-kernel, Andi Kleen, Peter Zijlstra, Ingo Molnar
In-Reply-To: <50E3FDFA.4070203@suse.cz>


> Looks good, but I would prefer the layer to provide us with
> wake_up_if_active...

Seconded - this is a problem for the wake up logic in the RT tree. Why
would we ever do anything else ?

^ permalink raw reply

* Re: [PATCH] tty: Only wakeup the line discipline idle queue when queue is active
From: Ivo Sieben @ 2013-01-02 15:21 UTC (permalink / raw)
  To: Alan Cox
  Cc: Jiri Slaby, linux-serial, Alan Cox, Greg KH, Oleg Nesterov,
	linux-kernel, Andi Kleen, Peter Zijlstra, Ingo Molnar
In-Reply-To: <20130102114311.66215bf5@pyramind.ukuu.org.uk>

Hi Jiri & Alan,

2013/1/2 Alan Cox <alan@lxorguk.ukuu.org.uk>:
>
>> Looks good, but I would prefer the layer to provide us with
>> wake_up_if_active...
>
> Seconded - this is a problem for the wake up logic in the RT tree. Why
> would we ever do anything else ?

I don't understand your responses: do you suggest to implement this
"if active" behavior in:
* A new wake_up function called wake_up_if_active() that is part of
the waitqueue layer?
* Implement this behavior in the existing wake_up() function as part
of the RT patch?

Regards,
Ivo

^ permalink raw reply

* Re: [PATCH] tty: Only wakeup the line discipline idle queue when queue is active
From: Jiri Slaby @ 2013-01-02 19:06 UTC (permalink / raw)
  To: Ivo Sieben
  Cc: Alan Cox, linux-serial, Alan Cox, Greg KH, Oleg Nesterov,
	linux-kernel, Andi Kleen, Peter Zijlstra, Ingo Molnar
In-Reply-To: <CAMSQXEHmDb5rUQnT1JNcMCzdaN+gAr+1hR=PyPiDMu7vK5ykxA@mail.gmail.com>

On 01/02/2013 04:21 PM, Ivo Sieben wrote:
> I don't understand your responses: do you suggest to implement this
> "if active" behavior in:
> * A new wake_up function called wake_up_if_active() that is part of
> the waitqueue layer?

Sounds good.

-- 
js
suse labs

^ permalink raw reply

* Re: [PATCH] tty: Only wakeup the line discipline idle queue when queue is active
From: Ivo Sieben @ 2013-01-03  9:49 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, linux-kernel, Andi Kleen,
	Preeti U Murthy, Oleg Nesterov
  Cc: Jiri Slaby, Alan Cox, linux-serial, Alan Cox, Greg KH
In-Reply-To: <50E4854E.8060801@suse.cz>

Oleg, Peter, Ingo, Andi & Preeti,

2013/1/2 Jiri Slaby <jslaby@suse.cz>:
> On 01/02/2013 04:21 PM, Ivo Sieben wrote:
>> I don't understand your responses: do you suggest to implement this
>> "if active" behavior in:
>> * A new wake_up function called wake_up_if_active() that is part of
>> the waitqueue layer?
>
> Sounds good.
>
> --
> js
> suse labs

I want to ask you 'scheduler' people for your opinion:

Maybe you remember my previous patch where I suggested an extra
'waitqueue empty' check before entering the critical section of the
wakeup() function (If you do not remember see
https://lkml.org/lkml/2012/10/25/159)

Finally Oleg responded that a lot of callers do

	if (waitqueue_active(q))
		wake_up(...);

what made my patch pointless and adds a memory barrier. I then decided
to also implement the 'waitqueue_active' approach for my problem.

But now I get a review comment by Jiri that he would like to hide this
'if active behavior' in a wake_up_if_active() kind of function. I
think he is right that implementing this check in the wakeup function
would clean things up, right?

I would like to have your opinion on the following two suggestions:
- We still can do the original patch on the wake_up() that I
suggested. I then can do an additional code cleanup patch that removes
the double 'waitqueue_active' call (a quick grep found about 150 of
these waitqueue active calls) on several places in the code.
- Or - as an alternative - I could add extra _if_active() versions of
all wake_up() functions, that implement this extra test.

Regards,
Ivo

^ permalink raw reply

* Re: [PATCH] tty: Only wakeup the line discipline idle queue when queue is active
From: Oleg Nesterov @ 2013-01-03 18:36 UTC (permalink / raw)
  To: Ivo Sieben
  Cc: Peter Zijlstra, Ingo Molnar, linux-kernel, Andi Kleen,
	Preeti U Murthy, Jiri Slaby, Alan Cox, linux-serial, Alan Cox,
	Greg KH
In-Reply-To: <CAMSQXEGU2WmhmMqP7XMK2x39GN9iDDWpg-Wa7_cAauHWx5OWbQ@mail.gmail.com>

On 01/03, Ivo Sieben wrote:
>
> Oleg, Peter, Ingo, Andi & Preeti,
>
> 2013/1/2 Jiri Slaby <jslaby@suse.cz>:
> > On 01/02/2013 04:21 PM, Ivo Sieben wrote:
> >> I don't understand your responses: do you suggest to implement this
> >> "if active" behavior in:
> >> * A new wake_up function called wake_up_if_active() that is part of
> >> the waitqueue layer?
> >
> > Sounds good.
> >
> > --
> > js
> > suse labs
>
> I want to ask you 'scheduler' people for your opinion:
>
> Maybe you remember my previous patch where I suggested an extra
> 'waitqueue empty' check before entering the critical section of the
> wakeup() function (If you do not remember see
> https://lkml.org/lkml/2012/10/25/159)
>
> Finally Oleg responded that a lot of callers do
>
> 	if (waitqueue_active(q))
> 		wake_up(...);
>
> what made my patch pointless and adds a memory barrier.

Plus this change doesn't look 100% correct, at least in theory.

> I then decided
> to also implement the 'waitqueue_active' approach for my problem.

Well, if you ask me I think this is the best solution ;)

But I won't insist.

> But now I get a review comment by Jiri that he would like to hide this
> 'if active behavior' in a wake_up_if_active() kind of function. I
> think he is right that implementing this check in the wakeup function
> would clean things up, right?
>
> I would like to have your opinion on the following two suggestions:
> - We still can do the original patch on the wake_up() that I
> suggested. I then can do an additional code cleanup patch that removes
> the double 'waitqueue_active' call (a quick grep found about 150 of
> these waitqueue active calls) on several places in the code.

In this case we should also fix some users of add_wait_queue().

> - Or - as an alternative - I could add extra _if_active() versions of
> all wake_up() functions, that implement this extra test.

Not sure this will actually help to make the code cleaner. The last
patch you sent looks simple and clean. IMHO it doesn't make sense
to create _if_active helper for each wake_up*.

Oleg.


^ permalink raw reply

* [PATCH 1/1] serial: Samsung: Use of_match_ptr() macro
From: Sachin Kamat @ 2013-01-07  4:20 UTC (permalink / raw)
  To: linux-serial; +Cc: gregkh, alan, jslaby, sachin.kamat, patches

This eliminates having an #ifdef returning NULL for the case
when OF is disabled.

Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
---
 drivers/tty/serial/samsung.c |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 12e5249..e393b0a 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1725,8 +1725,6 @@ static const struct of_device_id s3c24xx_uart_dt_match[] = {
 	{},
 };
 MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);
-#else
-#define s3c24xx_uart_dt_match NULL
 #endif
 
 static struct platform_driver samsung_serial_driver = {
@@ -1737,7 +1735,7 @@ static struct platform_driver samsung_serial_driver = {
 		.name	= "samsung-uart",
 		.owner	= THIS_MODULE,
 		.pm	= SERIAL_SAMSUNG_PM_OPS,
-		.of_match_table	= s3c24xx_uart_dt_match,
+		.of_match_table	= of_match_ptr(s3c24xx_uart_dt_match),
 	},
 };
 
-- 
1.7.4.1


^ permalink raw reply related

* [PATCH 0/5] serial: imx: Some fixes and cleanups
From: Sachin Kamat @ 2013-01-07  4:55 UTC (permalink / raw)
  To: linux-serial
  Cc: gregkh, alan, jslaby, shawn.guo, s.hauer, sachin.kamat, patches

This series build tested using imx_v4_v5_defconfig against
linux-next tree (20130107).

Sachin Kamat (5):
  serial: imx: Fix checkpatch errors related to spacing
  serial: imx: Use <linux/io.h> instead of <asm/io.h>
  serial: imx: Fix coding style issue
  serial: imx: Use pr_info instead of printk
  serial: imx: Use devm_* APIs

 drivers/tty/serial/imx.c |  263 ++++++++++++++++++++++------------------------
 1 files changed, 125 insertions(+), 138 deletions(-)

-- 
1.7.4.1


^ permalink raw reply

* [PATCH 1/5] serial: imx: Fix checkpatch errors related to spacing
From: Sachin Kamat @ 2013-01-07  4:55 UTC (permalink / raw)
  To: linux-serial
  Cc: gregkh, alan, jslaby, shawn.guo, s.hauer, sachin.kamat, patches
In-Reply-To: <1357534506-30206-1-git-send-email-sachin.kamat@linaro.org>

Fixed checkpatch errors and warnings related to incorrect spacing.

Cc: Shawn Guo <shawn.guo@linaro.org>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
---
 drivers/tty/serial/imx.c |  222 +++++++++++++++++++++++-----------------------
 1 files changed, 111 insertions(+), 111 deletions(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 5981912..d6bce6c 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -73,102 +73,102 @@
 #define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
 
 /* UART Control Register Bit Fields.*/
-#define  URXD_CHARRDY    (1<<15)
-#define  URXD_ERR        (1<<14)
-#define  URXD_OVRRUN     (1<<13)
-#define  URXD_FRMERR     (1<<12)
-#define  URXD_BRK        (1<<11)
-#define  URXD_PRERR      (1<<10)
-#define  UCR1_ADEN       (1<<15) /* Auto detect interrupt */
-#define  UCR1_ADBR       (1<<14) /* Auto detect baud rate */
-#define  UCR1_TRDYEN     (1<<13) /* Transmitter ready interrupt enable */
-#define  UCR1_IDEN       (1<<12) /* Idle condition interrupt */
-#define  UCR1_RRDYEN     (1<<9)	 /* Recv ready interrupt enable */
-#define  UCR1_RDMAEN     (1<<8)	 /* Recv ready DMA enable */
-#define  UCR1_IREN       (1<<7)	 /* Infrared interface enable */
-#define  UCR1_TXMPTYEN   (1<<6)	 /* Transimitter empty interrupt enable */
-#define  UCR1_RTSDEN     (1<<5)	 /* RTS delta interrupt enable */
-#define  UCR1_SNDBRK     (1<<4)	 /* Send break */
-#define  UCR1_TDMAEN     (1<<3)	 /* Transmitter ready DMA enable */
-#define  IMX1_UCR1_UARTCLKEN  (1<<2)  /* UART clock enabled, i.mx1 only */
-#define  UCR1_DOZE       (1<<1)	 /* Doze */
-#define  UCR1_UARTEN     (1<<0)	 /* UART enabled */
-#define  UCR2_ESCI     	 (1<<15) /* Escape seq interrupt enable */
-#define  UCR2_IRTS  	 (1<<14) /* Ignore RTS pin */
-#define  UCR2_CTSC  	 (1<<13) /* CTS pin control */
-#define  UCR2_CTS        (1<<12) /* Clear to send */
-#define  UCR2_ESCEN      (1<<11) /* Escape enable */
-#define  UCR2_PREN       (1<<8)  /* Parity enable */
-#define  UCR2_PROE       (1<<7)  /* Parity odd/even */
-#define  UCR2_STPB       (1<<6)	 /* Stop */
-#define  UCR2_WS         (1<<5)	 /* Word size */
-#define  UCR2_RTSEN      (1<<4)	 /* Request to send interrupt enable */
-#define  UCR2_ATEN       (1<<3)  /* Aging Timer Enable */
-#define  UCR2_TXEN       (1<<2)	 /* Transmitter enabled */
-#define  UCR2_RXEN       (1<<1)	 /* Receiver enabled */
-#define  UCR2_SRST 	 (1<<0)	 /* SW reset */
-#define  UCR3_DTREN 	 (1<<13) /* DTR interrupt enable */
-#define  UCR3_PARERREN   (1<<12) /* Parity enable */
-#define  UCR3_FRAERREN   (1<<11) /* Frame error interrupt enable */
-#define  UCR3_DSR        (1<<10) /* Data set ready */
-#define  UCR3_DCD        (1<<9)  /* Data carrier detect */
-#define  UCR3_RI         (1<<8)  /* Ring indicator */
-#define  UCR3_TIMEOUTEN  (1<<7)  /* Timeout interrupt enable */
-#define  UCR3_RXDSEN	 (1<<6)  /* Receive status interrupt enable */
-#define  UCR3_AIRINTEN   (1<<5)  /* Async IR wake interrupt enable */
-#define  UCR3_AWAKEN	 (1<<4)  /* Async wake interrupt enable */
-#define  IMX21_UCR3_RXDMUXSEL	 (1<<2)  /* RXD Muxed Input Select */
-#define  UCR3_INVT  	 (1<<1)  /* Inverted Infrared transmission */
-#define  UCR3_BPEN  	 (1<<0)  /* Preset registers enable */
-#define  UCR4_CTSTL_SHF  10      /* CTS trigger level shift */
-#define  UCR4_CTSTL_MASK 0x3F    /* CTS trigger is 6 bits wide */
-#define  UCR4_INVR  	 (1<<9)  /* Inverted infrared reception */
-#define  UCR4_ENIRI 	 (1<<8)  /* Serial infrared interrupt enable */
-#define  UCR4_WKEN  	 (1<<7)  /* Wake interrupt enable */
-#define  UCR4_REF16 	 (1<<6)  /* Ref freq 16 MHz */
-#define  UCR4_IRSC  	 (1<<5)  /* IR special case */
-#define  UCR4_TCEN  	 (1<<3)  /* Transmit complete interrupt enable */
-#define  UCR4_BKEN  	 (1<<2)  /* Break condition interrupt enable */
-#define  UCR4_OREN  	 (1<<1)  /* Receiver overrun interrupt enable */
-#define  UCR4_DREN  	 (1<<0)  /* Recv data ready interrupt enable */
-#define  UFCR_RXTL_SHF   0       /* Receiver trigger level shift */
-#define  UFCR_DCEDTE	 (1<<6)  /* DCE/DTE mode select */
-#define  UFCR_RFDIV      (7<<7)  /* Reference freq divider mask */
-#define  UFCR_RFDIV_REG(x)	(((x) < 7 ? 6 - (x) : 6) << 7)
-#define  UFCR_TXTL_SHF   10      /* Transmitter trigger level shift */
-#define  USR1_PARITYERR  (1<<15) /* Parity error interrupt flag */
-#define  USR1_RTSS  	 (1<<14) /* RTS pin status */
-#define  USR1_TRDY  	 (1<<13) /* Transmitter ready interrupt/dma flag */
-#define  USR1_RTSD  	 (1<<12) /* RTS delta */
-#define  USR1_ESCF  	 (1<<11) /* Escape seq interrupt flag */
-#define  USR1_FRAMERR    (1<<10) /* Frame error interrupt flag */
-#define  USR1_RRDY       (1<<9)	 /* Receiver ready interrupt/dma flag */
-#define  USR1_TIMEOUT    (1<<7)	 /* Receive timeout interrupt status */
-#define  USR1_RXDS  	 (1<<6)	 /* Receiver idle interrupt flag */
-#define  USR1_AIRINT	 (1<<5)	 /* Async IR wake interrupt flag */
-#define  USR1_AWAKE 	 (1<<4)	 /* Aysnc wake interrupt flag */
-#define  USR2_ADET  	 (1<<15) /* Auto baud rate detect complete */
-#define  USR2_TXFE  	 (1<<14) /* Transmit buffer FIFO empty */
-#define  USR2_DTRF  	 (1<<13) /* DTR edge interrupt flag */
-#define  USR2_IDLE  	 (1<<12) /* Idle condition */
-#define  USR2_IRINT 	 (1<<8)	 /* Serial infrared interrupt flag */
-#define  USR2_WAKE  	 (1<<7)	 /* Wake */
-#define  USR2_RTSF  	 (1<<4)	 /* RTS edge interrupt flag */
-#define  USR2_TXDC  	 (1<<3)	 /* Transmitter complete */
-#define  USR2_BRCD  	 (1<<2)	 /* Break condition */
-#define  USR2_ORE        (1<<1)	 /* Overrun error */
-#define  USR2_RDR        (1<<0)	 /* Recv data ready */
-#define  UTS_FRCPERR	 (1<<13) /* Force parity error */
-#define  UTS_LOOP        (1<<12) /* Loop tx and rx */
-#define  UTS_TXEMPTY	 (1<<6)	 /* TxFIFO empty */
-#define  UTS_RXEMPTY	 (1<<5)	 /* RxFIFO empty */
-#define  UTS_TXFULL 	 (1<<4)	 /* TxFIFO full */
-#define  UTS_RXFULL 	 (1<<3)	 /* RxFIFO full */
-#define  UTS_SOFTRST	 (1<<0)	 /* Software reset */
+#define URXD_CHARRDY	(1<<15)
+#define URXD_ERR	(1<<14)
+#define URXD_OVRRUN	(1<<13)
+#define URXD_FRMERR	(1<<12)
+#define URXD_BRK	(1<<11)
+#define URXD_PRERR	(1<<10)
+#define UCR1_ADEN	(1<<15) /* Auto detect interrupt */
+#define UCR1_ADBR	(1<<14) /* Auto detect baud rate */
+#define UCR1_TRDYEN	(1<<13) /* Transmitter ready interrupt enable */
+#define UCR1_IDEN	(1<<12) /* Idle condition interrupt */
+#define UCR1_RRDYEN	(1<<9)	/* Recv ready interrupt enable */
+#define UCR1_RDMAEN	(1<<8)	/* Recv ready DMA enable */
+#define UCR1_IREN	(1<<7)	/* Infrared interface enable */
+#define UCR1_TXMPTYEN	(1<<6)	/* Transimitter empty interrupt enable */
+#define UCR1_RTSDEN	(1<<5)	/* RTS delta interrupt enable */
+#define UCR1_SNDBRK	(1<<4)	/* Send break */
+#define UCR1_TDMAEN	(1<<3)	/* Transmitter ready DMA enable */
+#define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
+#define UCR1_DOZE	(1<<1)	/* Doze */
+#define UCR1_UARTEN	(1<<0)	/* UART enabled */
+#define UCR2_ESCI	(1<<15)	/* Escape seq interrupt enable */
+#define UCR2_IRTS	(1<<14)	/* Ignore RTS pin */
+#define UCR2_CTSC	(1<<13)	/* CTS pin control */
+#define UCR2_CTS	(1<<12)	/* Clear to send */
+#define UCR2_ESCEN	(1<<11)	/* Escape enable */
+#define UCR2_PREN	(1<<8)	/* Parity enable */
+#define UCR2_PROE	(1<<7)	/* Parity odd/even */
+#define UCR2_STPB	(1<<6)	/* Stop */
+#define UCR2_WS		(1<<5)	/* Word size */
+#define UCR2_RTSEN	(1<<4)	/* Request to send interrupt enable */
+#define UCR2_ATEN	(1<<3)	/* Aging Timer Enable */
+#define UCR2_TXEN	(1<<2)	/* Transmitter enabled */
+#define UCR2_RXEN	(1<<1)	/* Receiver enabled */
+#define UCR2_SRST	(1<<0)	/* SW reset */
+#define UCR3_DTREN	(1<<13) /* DTR interrupt enable */
+#define UCR3_PARERREN	(1<<12) /* Parity enable */
+#define UCR3_FRAERREN	(1<<11) /* Frame error interrupt enable */
+#define UCR3_DSR	(1<<10) /* Data set ready */
+#define UCR3_DCD	(1<<9)	/* Data carrier detect */
+#define UCR3_RI		(1<<8)	/* Ring indicator */
+#define UCR3_TIMEOUTEN	(1<<7)	/* Timeout interrupt enable */
+#define UCR3_RXDSEN	(1<<6)	/* Receive status interrupt enable */
+#define UCR3_AIRINTEN	(1<<5)	/* Async IR wake interrupt enable */
+#define UCR3_AWAKEN	(1<<4)	/* Async wake interrupt enable */
+#define IMX21_UCR3_RXDMUXSEL	(1<<2)	/* RXD Muxed Input Select */
+#define UCR3_INVT	(1<<1)	/* Inverted Infrared transmission */
+#define UCR3_BPEN	(1<<0)	/* Preset registers enable */
+#define UCR4_CTSTL_SHF	10	/* CTS trigger level shift */
+#define UCR4_CTSTL_MASK	0x3F	/* CTS trigger is 6 bits wide */
+#define UCR4_INVR	(1<<9)	/* Inverted infrared reception */
+#define UCR4_ENIRI	(1<<8)	/* Serial infrared interrupt enable */
+#define UCR4_WKEN	(1<<7)	/* Wake interrupt enable */
+#define UCR4_REF16	(1<<6)	/* Ref freq 16 MHz */
+#define UCR4_IRSC	(1<<5)	/* IR special case */
+#define UCR4_TCEN	(1<<3)	/* Transmit complete interrupt enable */
+#define UCR4_BKEN	(1<<2)	/* Break condition interrupt enable */
+#define UCR4_OREN	(1<<1)	/* Receiver overrun interrupt enable */
+#define UCR4_DREN	(1<<0)	/* Recv data ready interrupt enable */
+#define UFCR_RXTL_SHF	0	/* Receiver trigger level shift */
+#define UFCR_DCEDTE	(1<<6)	/* DCE/DTE mode select */
+#define UFCR_RFDIV	(7<<7)	/* Reference freq divider mask */
+#define UFCR_RFDIV_REG(x)	(((x) < 7 ? 6 - (x) : 6) << 7)
+#define UFCR_TXTL_SHF	10	/* Transmitter trigger level shift */
+#define USR1_PARITYERR	(1<<15) /* Parity error interrupt flag */
+#define USR1_RTSS	(1<<14) /* RTS pin status */
+#define USR1_TRDY	(1<<13) /* Transmitter ready interrupt/dma flag */
+#define USR1_RTSD	(1<<12) /* RTS delta */
+#define USR1_ESCF	(1<<11) /* Escape seq interrupt flag */
+#define USR1_FRAMERR	(1<<10) /* Frame error interrupt flag */
+#define USR1_RRDY	(1<<9)	 /* Receiver ready interrupt/dma flag */
+#define USR1_TIMEOUT	(1<<7)	 /* Receive timeout interrupt status */
+#define USR1_RXDS	 (1<<6)	 /* Receiver idle interrupt flag */
+#define USR1_AIRINT	 (1<<5)	 /* Async IR wake interrupt flag */
+#define USR1_AWAKE	 (1<<4)	 /* Aysnc wake interrupt flag */
+#define USR2_ADET	 (1<<15) /* Auto baud rate detect complete */
+#define USR2_TXFE	 (1<<14) /* Transmit buffer FIFO empty */
+#define USR2_DTRF	 (1<<13) /* DTR edge interrupt flag */
+#define USR2_IDLE	 (1<<12) /* Idle condition */
+#define USR2_IRINT	 (1<<8)	 /* Serial infrared interrupt flag */
+#define USR2_WAKE	 (1<<7)	 /* Wake */
+#define USR2_RTSF	 (1<<4)	 /* RTS edge interrupt flag */
+#define USR2_TXDC	 (1<<3)	 /* Transmitter complete */
+#define USR2_BRCD	 (1<<2)	 /* Break condition */
+#define USR2_ORE	(1<<1)	 /* Overrun error */
+#define USR2_RDR	(1<<0)	 /* Recv data ready */
+#define UTS_FRCPERR	(1<<13) /* Force parity error */
+#define UTS_LOOP	(1<<12)	 /* Loop tx and rx */
+#define UTS_TXEMPTY	 (1<<6)	 /* TxFIFO empty */
+#define UTS_RXEMPTY	 (1<<5)	 /* RxFIFO empty */
+#define UTS_TXFULL	 (1<<4)	 /* TxFIFO full */
+#define UTS_RXFULL	 (1<<3)	 /* RxFIFO full */
+#define UTS_SOFTRST	 (1<<0)	 /* Software reset */
 
 /* We've been assigned a range on the "Low-density serial ports" major */
-#define SERIAL_IMX_MAJOR        207
-#define MINOR_START	        16
+#define SERIAL_IMX_MAJOR	207
+#define MINOR_START		16
 #define DEV_NAME		"ttymxc"
 
 /*
@@ -199,7 +199,7 @@ struct imx_port {
 	struct uart_port	port;
 	struct timer_list	timer;
 	unsigned int		old_status;
-	int			txirq,rxirq,rtsirq;
+	int			txirq, rxirq, rtsirq;
 	unsigned int		have_rtscts:1;
 	unsigned int		use_irda:1;
 	unsigned int		irda_inv_rx:1;
@@ -397,7 +397,7 @@ static void imx_stop_rx(struct uart_port *port)
 	unsigned long temp;
 
 	temp = readl(sport->port.membase + UCR2);
-	writel(temp &~ UCR2_RXEN, sport->port.membase + UCR2);
+	writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
 }
 
 /*
@@ -490,7 +490,7 @@ static irqreturn_t imx_txint(int irq, void *dev_id)
 	struct circ_buf *xmit = &sport->port.state->xmit;
 	unsigned long flags;
 
-	spin_lock_irqsave(&sport->port.lock,flags);
+	spin_lock_irqsave(&sport->port.lock, flags);
 	if (sport->port.x_char)
 	{
 		/* Send next char */
@@ -509,18 +509,18 @@ static irqreturn_t imx_txint(int irq, void *dev_id)
 		uart_write_wakeup(&sport->port);
 
 out:
-	spin_unlock_irqrestore(&sport->port.lock,flags);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
 	return IRQ_HANDLED;
 }
 
 static irqreturn_t imx_rxint(int irq, void *dev_id)
 {
 	struct imx_port *sport = dev_id;
-	unsigned int rx,flg,ignored = 0;
+	unsigned int rx, flg, ignored = 0;
 	struct tty_struct *tty = sport->port.state->port.tty;
 	unsigned long flags, temp;
 
-	spin_lock_irqsave(&sport->port.lock,flags);
+	spin_lock_irqsave(&sport->port.lock, flags);
 
 	while (readl(sport->port.membase + USR2) & USR2_RDR) {
 		flg = TTY_NORMAL;
@@ -574,7 +574,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
 	}
 
 out:
-	spin_unlock_irqrestore(&sport->port.lock,flags);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
 	tty_flip_buffer_push(tty);
 	return IRQ_HANDLED;
 }
@@ -654,7 +654,7 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
 
 	temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK;
 
-	if ( break_state != 0 )
+	if (break_state != 0)
 		temp |= UCR1_SNDBRK;
 
 	writel(temp, sport->port.membase + UCR1);
@@ -696,8 +696,8 @@ static int imx_startup(struct uart_port *port)
 		temp |= UCR4_IRSC;
 
 	/* set the trigger level for CTS */
-	temp &= ~(UCR4_CTSTL_MASK<<  UCR4_CTSTL_SHF);
-	temp |= CTSTL<<  UCR4_CTSTL_SHF;
+	temp &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF);
+	temp |= CTSTL << UCR4_CTSTL_SHF;
 
 	writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
 
@@ -799,7 +799,7 @@ static int imx_startup(struct uart_port *port)
 	 * Enable modem status interrupts
 	 */
 	imx_enable_ms(&sport->port);
-	spin_unlock_irqrestore(&sport->port.lock,flags);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
 
 	if (USE_IRDA(sport)) {
 		struct imxuart_platform_data *pdata;
@@ -909,7 +909,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 		ucr2 = UCR2_SRST | UCR2_IRTS;
 
 	if (termios->c_cflag & CRTSCTS) {
-		if( sport->have_rtscts ) {
+		if (sport->have_rtscts) {
 			ucr2 &= ~UCR2_IRTS;
 			ucr2 |= UCR2_CTSC;
 		} else {
@@ -969,12 +969,12 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
 			sport->port.membase + UCR1);
 
-	while ( !(readl(sport->port.membase + USR2) & USR2_TXDC))
+	while (!(readl(sport->port.membase + USR2) & USR2_TXDC))
 		barrier();
 
 	/* then, disable everything */
 	old_txrxen = readl(sport->port.membase + UCR2);
-	writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN),
+	writel(old_txrxen & ~(UCR2_TXEN | UCR2_RXEN),
 			sport->port.membase + UCR2);
 	old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
 
@@ -1255,7 +1255,7 @@ imx_console_get_options(struct imx_port *sport, int *baud,
 
 	if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) {
 		/* ok, the port was enabled */
-		unsigned int ucr2, ubir,ubmr, uartclk;
+		unsigned int ucr2, ubir, ubmr, uartclk;
 		unsigned int baud_raw;
 		unsigned int ucfr_rfdiv;
 
@@ -1301,7 +1301,7 @@ imx_console_get_options(struct imx_port *sport, int *baud,
 			*baud = (baud_raw + 50) / 100 * 100;
 		}
 
-		if(*baud != baud_raw)
+		if (*baud != baud_raw)
 			printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n",
 				baud_raw, *baud);
 	}
@@ -1324,7 +1324,7 @@ imx_console_setup(struct console *co, char *options)
 	if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
 		co->index = 0;
 	sport = imx_ports[co->index];
-	if(sport == NULL)
+	if (sport == NULL)
 		return -ENODEV;
 
 	if (options)
-- 
1.7.4.1


^ permalink raw reply related

* [PATCH 2/5] serial: imx: Use <linux/io.h> instead of <asm/io.h>
From: Sachin Kamat @ 2013-01-07  4:55 UTC (permalink / raw)
  To: linux-serial
  Cc: gregkh, alan, jslaby, shawn.guo, s.hauer, sachin.kamat, patches
In-Reply-To: <1357534506-30206-1-git-send-email-sachin.kamat@linaro.org>

Silences the following checkpatch warning:
WARNING: Use #include <linux/io.h> instead of <asm/io.h>

Cc: Shawn Guo <shawn.guo@linaro.org>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
---
 drivers/tty/serial/imx.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index d6bce6c..426253c 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -48,8 +48,8 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/io.h>
 
-#include <asm/io.h>
 #include <asm/irq.h>
 #include <linux/platform_data/serial-imx.h>
 
-- 
1.7.4.1


^ permalink raw reply related

* [PATCH 3/5] serial: imx: Fix coding style issue
From: Sachin Kamat @ 2013-01-07  4:55 UTC (permalink / raw)
  To: linux-serial
  Cc: gregkh, alan, jslaby, shawn.guo, s.hauer, sachin.kamat, patches
In-Reply-To: <1357534506-30206-1-git-send-email-sachin.kamat@linaro.org>

Silences the following checkpatch error:
ERROR: that open brace { should be on the previous line

Cc: Shawn Guo <shawn.guo@linaro.org>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
---
 drivers/tty/serial/imx.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 426253c..9bec8a2 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -491,8 +491,7 @@ static irqreturn_t imx_txint(int irq, void *dev_id)
 	unsigned long flags;
 
 	spin_lock_irqsave(&sport->port.lock, flags);
-	if (sport->port.x_char)
-	{
+	if (sport->port.x_char) {
 		/* Send next char */
 		writel(sport->port.x_char, sport->port.membase + URTX0);
 		goto out;
-- 
1.7.4.1


^ permalink raw reply related

* [PATCH 4/5] serial: imx: Use pr_info instead of printk
From: Sachin Kamat @ 2013-01-07  4:55 UTC (permalink / raw)
  To: linux-serial
  Cc: gregkh, alan, jslaby, shawn.guo, s.hauer, sachin.kamat, patches
In-Reply-To: <1357534506-30206-1-git-send-email-sachin.kamat@linaro.org>

Silences checkpatch warnings.

Cc: Shawn Guo <shawn.guo@linaro.org>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
---
 drivers/tty/serial/imx.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 9bec8a2..78f7936 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1301,7 +1301,7 @@ imx_console_get_options(struct imx_port *sport, int *baud,
 		}
 
 		if (*baud != baud_raw)
-			printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n",
+			pr_info("Console IMX rounded baud rate from %d to %d\n",
 				baud_raw, *baud);
 	}
 }
@@ -1595,7 +1595,7 @@ static int __init imx_serial_init(void)
 {
 	int ret;
 
-	printk(KERN_INFO "Serial: IMX driver\n");
+	pr_info("Serial: IMX driver\n");
 
 	ret = uart_register_driver(&imx_reg);
 	if (ret)
-- 
1.7.4.1


^ permalink raw reply related

* [PATCH 5/5] serial: imx: Use devm_* APIs
From: Sachin Kamat @ 2013-01-07  4:55 UTC (permalink / raw)
  To: linux-serial
  Cc: gregkh, alan, jslaby, shawn.guo, s.hauer, sachin.kamat, patches
In-Reply-To: <1357534506-30206-1-git-send-email-sachin.kamat@linaro.org>

devm_* APIs are device managed and make cleanup and exit code simpler
and easier.

Cc: Shawn Guo <shawn.guo@linaro.org>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
---
 drivers/tty/serial/imx.c |   32 ++++++++++----------------------
 1 files changed, 10 insertions(+), 22 deletions(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 78f7936..1a24884 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1461,7 +1461,7 @@ static int serial_imx_probe(struct platform_device *pdev)
 	struct resource *res;
 	struct pinctrl *pinctrl;
 
-	sport = kzalloc(sizeof(*sport), GFP_KERNEL);
+	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
 	if (!sport)
 		return -ENOMEM;
 
@@ -1469,19 +1469,15 @@ static int serial_imx_probe(struct platform_device *pdev)
 	if (ret > 0)
 		serial_imx_probe_pdata(sport, pdev);
 	else if (ret < 0)
-		goto free;
+		return ret;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res) {
-		ret = -ENODEV;
-		goto free;
-	}
+	if (!res)
+		return -ENODEV;
 
-	base = ioremap(res->start, PAGE_SIZE);
-	if (!base) {
-		ret = -ENOMEM;
-		goto free;
-	}
+	base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE);
+	if (!base)
+		return -ENOMEM;
 
 	sport->port.dev = &pdev->dev;
 	sport->port.mapbase = res->start;
@@ -1503,21 +1499,21 @@ static int serial_imx_probe(struct platform_device *pdev)
 	if (IS_ERR(pinctrl)) {
 		ret = PTR_ERR(pinctrl);
 		dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret);
-		goto unmap;
+		return ret;
 	}
 
 	sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
 	if (IS_ERR(sport->clk_ipg)) {
 		ret = PTR_ERR(sport->clk_ipg);
 		dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
-		goto unmap;
+		return ret;
 	}
 
 	sport->clk_per = devm_clk_get(&pdev->dev, "per");
 	if (IS_ERR(sport->clk_per)) {
 		ret = PTR_ERR(sport->clk_per);
 		dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);
-		goto unmap;
+		return ret;
 	}
 
 	clk_prepare_enable(sport->clk_per);
@@ -1546,11 +1542,6 @@ deinit:
 clkput:
 	clk_disable_unprepare(sport->clk_per);
 	clk_disable_unprepare(sport->clk_ipg);
-unmap:
-	iounmap(sport->port.membase);
-free:
-	kfree(sport);
-
 	return ret;
 }
 
@@ -1571,9 +1562,6 @@ static int serial_imx_remove(struct platform_device *pdev)
 	if (pdata && pdata->exit)
 		pdata->exit(pdev);
 
-	iounmap(sport->port.membase);
-	kfree(sport);
-
 	return 0;
 }
 
-- 
1.7.4.1


^ permalink raw reply related

* Re: [PATCH 0/5] serial: imx: Some fixes and cleanups
From: Sascha Hauer @ 2013-01-07  8:30 UTC (permalink / raw)
  To: Sachin Kamat; +Cc: linux-serial, gregkh, alan, jslaby, shawn.guo, patches
In-Reply-To: <1357534506-30206-1-git-send-email-sachin.kamat@linaro.org>

On Mon, Jan 07, 2013 at 10:25:01AM +0530, Sachin Kamat wrote:
> This series build tested using imx_v4_v5_defconfig against
> linux-next tree (20130107).
> 
> Sachin Kamat (5):
>   serial: imx: Fix checkpatch errors related to spacing
>   serial: imx: Use <linux/io.h> instead of <asm/io.h>
>   serial: imx: Fix coding style issue
>   serial: imx: Use pr_info instead of printk
>   serial: imx: Use devm_* APIs
> 
>  drivers/tty/serial/imx.c |  263 ++++++++++++++++++++++------------------------
>  1 files changed, 125 insertions(+), 138 deletions(-)

Looks good.

Acked-by: Sascha Hauer <s.hauer@pengutronix.de>

Sascha


-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox