Linux Serial subsystem development
 help / color / mirror / Atom feed
* Re: unrecognized multi io pci card
From: Alan Cox @ 2012-08-29 13:28 UTC (permalink / raw)
  To: gianluca; +Cc: gregkh, linux-serial
In-Reply-To: <20120828135720.GA2803@server>

> I see, I missed the quirks in 8250_pci.c. I added a quirk for my card to set
> the options above and it works nicely thanks!
> 
> The new patch is attached.
> 
> Gianluca

This looks good to me - the only thing it needs to be applied is a
signed-off-by line as per Documentation/SubmittingPatches

Alan

^ permalink raw reply

* Re: [PATCH 2/2] tty: serial: imx: don't reinit clock with enabled console
From: Troy Kisky @ 2012-08-28 18:45 UTC (permalink / raw)
  To: Dirk Behme
  Cc: linux-arm-kernel@lists.infradead.org,
	linux-serial@vger.kernel.org, kernel@pengutronix.de,
	Greg Kroah-Hartman, Sascha Hauer, Xinyu Chen, Shawn Guo, Alan Cox
In-Reply-To: <503D0D7C.2070509@boundarydevices.com>

On 8/28/2012 11:27 AM, Troy Kisky wrote:
> On 8/28/2012 3:46 AM, Dirk Behme wrote:
>> On 27.08.2012 20:20, Troy Kisky wrote:
>>> On 8/27/2012 12:36 AM, Dirk Behme wrote:
>>>> From: Xinyu Chen <xinyu.chen@freescale.com>
>>>>
>>>> Remove the imx_setup_ufcr() call on startup when CONSOLE enabled,
>>>> as this will cause clock reinit, and output garbage.
>>>>
>>>> This patch is a port from Freescale's Android kernel.
>>>>
>>>> Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
>>>> Tested-by: Dirk Behme <dirk.behme@de.bosch.com>
>>>> CC: Shawn Guo <shawn.guo@linaro.org>
>>>> CC: Sascha Hauer <s.hauer@pengutronix.de>
>>>> ---
>>>>   drivers/tty/serial/imx.c |    2 ++
>>>>   1 files changed, 2 insertions(+), 0 deletions(-)
>>>>
>>>> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
>>>> index 908178f..31ce414 100644
>>>> --- a/drivers/tty/serial/imx.c
>>>> +++ b/drivers/tty/serial/imx.c
>>>> @@ -695,7 +695,9 @@ static int imx_startup(struct uart_port *port)
>>>>       int retval;
>>>>       unsigned long flags, temp;
>>>>   +#ifndef CONFIG_SERIAL_CORE_CONSOLE
>>>>       imx_setup_ufcr(sport, 0);
>>>> +#endif
>>>>         /* disable the DREN bit (Data Ready interrupt enable) before
>>>>        * requesting IRQs
>>>
>>>
>>> I'd rather do something like this
>>>
>>> static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
>>> {
>>>          unsigned int val;
>>>
>>>          /* set receiver / transmitter trigger level. */
>>>          val = readl(sport->port.membase + UFCR) & UFCR_RFDIV;
>>
>> Shouldn't it be
>>
>> ... & (UFCR_RFDIV | UFCR_DCEDTE);
>>
>> then? My i.MX6 manual has DCEDTE as bit 6, which we don't want to 
>> touch, too? We only want to touch TXTL and RXTL?
>>
>
> If you do that, you should also change imx_set_termios
> to possibly clear the bit
>
> new_ufcr = (old_ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
>
> to
>
> new_ufcr = (old_ufcr & (~(UFCR_RFDIV | UFCR_DCEDTE))) | 
> UFCR_RFDIV_REG(div);
>

Sorry, I was looking at the wrong tree. Mainline does not have

sport->use_dcedte. I'm fine either way.





^ permalink raw reply

* Re: [PATCH 2/2] tty: serial: imx: don't reinit clock with enabled console
From: Troy Kisky @ 2012-08-28 18:27 UTC (permalink / raw)
  To: Dirk Behme
  Cc: linux-arm-kernel@lists.infradead.org,
	linux-serial@vger.kernel.org, kernel@pengutronix.de,
	Greg Kroah-Hartman, Sascha Hauer, Xinyu Chen, Shawn Guo, Alan Cox
In-Reply-To: <503CA183.70704@de.bosch.com>

On 8/28/2012 3:46 AM, Dirk Behme wrote:
> On 27.08.2012 20:20, Troy Kisky wrote:
>> On 8/27/2012 12:36 AM, Dirk Behme wrote:
>>> From: Xinyu Chen <xinyu.chen@freescale.com>
>>>
>>> Remove the imx_setup_ufcr() call on startup when CONSOLE enabled,
>>> as this will cause clock reinit, and output garbage.
>>>
>>> This patch is a port from Freescale's Android kernel.
>>>
>>> Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
>>> Tested-by: Dirk Behme <dirk.behme@de.bosch.com>
>>> CC: Shawn Guo <shawn.guo@linaro.org>
>>> CC: Sascha Hauer <s.hauer@pengutronix.de>
>>> ---
>>>   drivers/tty/serial/imx.c |    2 ++
>>>   1 files changed, 2 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
>>> index 908178f..31ce414 100644
>>> --- a/drivers/tty/serial/imx.c
>>> +++ b/drivers/tty/serial/imx.c
>>> @@ -695,7 +695,9 @@ static int imx_startup(struct uart_port *port)
>>>       int retval;
>>>       unsigned long flags, temp;
>>>   +#ifndef CONFIG_SERIAL_CORE_CONSOLE
>>>       imx_setup_ufcr(sport, 0);
>>> +#endif
>>>         /* disable the DREN bit (Data Ready interrupt enable) before
>>>        * requesting IRQs
>>
>>
>> I'd rather do something like this
>>
>> static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
>> {
>>          unsigned int val;
>>
>>          /* set receiver / transmitter trigger level. */
>>          val = readl(sport->port.membase + UFCR) & UFCR_RFDIV;
>
> Shouldn't it be
>
> ... & (UFCR_RFDIV | UFCR_DCEDTE);
>
> then? My i.MX6 manual has DCEDTE as bit 6, which we don't want to 
> touch, too? We only want to touch TXTL and RXTL?
>

If you do that, you should also change imx_set_termios
to possibly clear the bit

new_ufcr = (old_ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);

to

new_ufcr = (old_ufcr & (~(UFCR_RFDIV | UFCR_DCEDTE))) | UFCR_RFDIV_REG(div);

>>          val |= TXTL << 10 | RXTL;
>>          writel(val, sport->port.membase + UFCR);
>>          return 0;
>> }
>>
>> There is no need for imx_setup_ufcr to change divisor.
>
> Ok
>
> Thanks
>
> Dirk
>


^ permalink raw reply

* [PATCH v3] serial: pl011: honour serial aliases in device tree
From: Matthew Leach @ 2012-08-28 15:41 UTC (permalink / raw)
  To: linux-arm-kernel, alan
  Cc: linux-serial, will.deacon, gregkh, devicetree-discuss,
	robherring2, Matthew Leach

If the order of UART nodes is changed in the device tree, then tty dev
devices are attached to different serial ports causing the console to
be directed to a different physical serial port. The "serial" aliases
in the device tree should prevent this.

This patch ensures that the UART driver creates tty devices that
honour these aliases if a device tree is present.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Will Deacon <will.deacon@arm.com>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Matthew Leach <matthew.leach@arm.com>
---
v3: minor consistency correction
v2: use IS_ENABLED instead of ifdefs
 drivers/tty/serial/amba-pl011.c |   36 ++++++++++++++++++++++++++++++++++++
 1 files changed, 36 insertions(+), 0 deletions(-)

diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index d3553b5..dc05dd1 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -52,6 +52,8 @@
 #include <linux/scatterlist.h>
 #include <linux/delay.h>
 #include <linux/types.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/sizes.h>
 
@@ -1869,6 +1871,38 @@ static struct uart_driver amba_reg = {
 	.cons			= AMBA_CONSOLE,
 };
 
+static int pl011_probe_dt_alias(int index, struct device *dev)
+{
+	struct device_node *np;
+	static bool seen_dev_with_alias = false;
+	static bool seen_dev_without_alias = false;
+	int ret = index;
+
+	if (!IS_ENABLED(CONFIG_OF))
+		return ret;
+
+	np = dev->of_node;
+	if (!np)
+		return ret;
+
+	ret = of_alias_get_id(np, "serial");
+	if (IS_ERR_VALUE(ret)) {
+		seen_dev_without_alias = true;
+		ret = index;
+	} else {
+		seen_dev_with_alias = true;
+		if (ret >= ARRAY_SIZE(amba_ports) || amba_ports[ret] != NULL) {
+			dev_warn(dev, "requested serial port %d  not available.\n", ret);
+			ret = index;
+		}
+	}
+
+	if (seen_dev_with_alias && seen_dev_without_alias)
+		dev_warn(dev, "aliased and non-aliased serial devices found in device tree. Serial port enumeration may be unpredictable.\n");
+
+	return ret;
+}
+
 static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
 {
 	struct uart_amba_port *uap;
@@ -1891,6 +1925,8 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
 		goto out;
 	}
 
+	i = pl011_probe_dt_alias(i, &dev->dev);
+
 	base = ioremap(dev->res.start, resource_size(&dev->res));
 	if (!base) {
 		ret = -ENOMEM;
-- 
1.7.0.4


^ permalink raw reply related

* Re: unrecognized multi io pci card
From: gianluca @ 2012-08-28 13:57 UTC (permalink / raw)
  To: Alan Cox; +Cc: gregkh, linux-serial
In-Reply-To: <20120828135529.6a826b19@pyramind.ukuu.org.uk>

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

On Tue, Aug 28, 2012 at 01:55:29PM +0100, Alan Cox wrote:
> Actually I think it is the right place to do it. Prior to that the only
> cases we've had to deal with are board specifc rules such as for the
> Timedia/Sunix card. The softmodem bit of the blacklist wants renaming and
> a comment adding to each group giving the reason.

I renamed it to "blacklist"

> Trying to get autodetection right is always a little bit exciting and one
> device tends to break another.
> 
> In this case you know the device type and have a PCI identifier you should
> be able to set
> 
> 	up->port.flags |= UPF_FIXED_TYPE;
> 	up->port.type = PORT_16550A;
> 
> before the call to serial8250_register_8250_port();
> 
> Alan

I see, I missed the quirks in 8250_pci.c. I added a quirk for my card to set
the options above and it works nicely thanks!

The new patch is attached.

Gianluca

[-- Attachment #2: wch.patch --]
[-- Type: text/plain, Size: 4206 bytes --]

diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c
index e9c3227..1631eea 100644
--- a/drivers/parport/parport_serial.c
+++ b/drivers/parport/parport_serial.c
@@ -62,6 +62,7 @@ enum parport_pc_pci_cards {
 	timedia_9079a,
 	timedia_9079b,
 	timedia_9079c,
+	wch_ch353_2s1p,
 };
 
 /* each element directly indexed from enum list, above */
@@ -145,6 +146,7 @@ static struct parport_pc_pci cards[] __devinitdata = {
 	/* timedia_9079a */             { 1, { { 2, 3 }, } },
 	/* timedia_9079b */             { 1, { { 2, 3 }, } },
 	/* timedia_9079c */             { 1, { { 2, 3 }, } },
+	/* wch_ch353_2s1p*/             { 1, { { 2, -1}, } },
 };
 
 static struct pci_device_id parport_serial_pci_tbl[] = {
@@ -243,7 +245,8 @@ static struct pci_device_id parport_serial_pci_tbl[] = {
 	{ 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a },
 	{ 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
 	{ 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
-
+	/* WCH CARDS */
+	{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
 	{ 0, } /* terminate list */
 };
 MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
@@ -460,6 +463,12 @@ static struct pciserial_board pci_parport_serial_boards[] __devinitdata = {
 		.base_baud	= 921600,
 		.uart_offset	= 8,
 	},
+	[wch_ch353_2s1p] = {
+		.flags          = FL_BASE0|FL_BASE_BARS,
+		.num_ports      = 2,
+		.base_baud      = 115200,
+		.uart_offset    = 8,
+	},
 };
 
 struct parport_serial_private {
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 28e7c7c..226b4db 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1157,6 +1157,16 @@ pci_xr17c154_setup(struct serial_private *priv,
 	return pci_default_setup(priv, board, port, idx);
 }
 
+static int
+pci_wch_ch353_setup(struct serial_private *priv,
+                    const struct pciserial_board *board,
+                    struct uart_port *port, int idx)
+{
+	port->flags |= UPF_FIXED_TYPE;
+	port->type = PORT_16550A;
+	return pci_default_setup(priv, board, port, idx);
+}
+
 #define PCI_VENDOR_ID_SBSMODULARIO	0x124B
 #define PCI_SUBVENDOR_ID_SBSMODULARIO	0x124B
 #define PCI_DEVICE_ID_OCTPRO		0x0001
@@ -1727,6 +1737,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
 		.subdevice	= PCI_ANY_ID,
 		.setup		= pci_omegapci_setup,
 	 },
+	/* WCH CH353 2S1P card (16550 clone) */
+	{
+		.vendor         = 0x4348,
+		.device         = 0x7053,
+		.subvendor      = 0x4348,
+		.subdevice      = 0x3253,
+		.setup          = pci_wch_ch353_setup,
+	},
 	/*
 	 * Default "match everything" terminator entry
 	 */
@@ -2624,10 +2642,14 @@ static struct pciserial_board pci_boards[] __devinitdata = {
 	},
 };
 
-static const struct pci_device_id softmodem_blacklist[] = {
+static const struct pci_device_id blacklist[] = {
+	/* softmodems */
 	{ PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */
 	{ PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */
 	{ PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */
+
+	/* multi-io cards handled by parport_serial */
+	{ PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */
 };
 
 /*
@@ -2638,7 +2660,7 @@ static const struct pci_device_id softmodem_blacklist[] = {
 static int __devinit
 serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
 {
-	const struct pci_device_id *blacklist;
+	const struct pci_device_id *bldev;
 	int num_iomem, num_port, first_port = -1, i;
 
 	/*
@@ -2655,13 +2677,13 @@ serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
 
 	/*
 	 * Do not access blacklisted devices that are known not to
-	 * feature serial ports.
+	 * feature serial ports or are handled by other modules.
 	 */
-	for (blacklist = softmodem_blacklist;
-	     blacklist < softmodem_blacklist + ARRAY_SIZE(softmodem_blacklist);
-	     blacklist++) {
-		if (dev->vendor == blacklist->vendor &&
-		    dev->device == blacklist->device)
+	for (bldev = blacklist;
+	     bldev < blacklist + ARRAY_SIZE(blacklist);
+	     bldev++) {
+		if (dev->vendor == bldev->vendor &&
+		    dev->device == bldev->device)
 			return -ENODEV;
 	}
 

^ permalink raw reply related

* Re: unrecognized multi io pci card
From: Alan Cox @ 2012-08-28 12:55 UTC (permalink / raw)
  To: gianluca; +Cc: gregkh, linux-serial
In-Reply-To: <20120828122458.GB1258@server>

> After adding the IDs and descriptions I found unfortunately that the module
> 8250_pci reclaims the pci device as a serial card only and this prevents the
> successful registration of the parport_serial driver (which also depends on
> 8250_pci).
> 
> So I blacklisted the card in 8250_pci.c using the sofmodem blacklist mechanism
> already in place. I know this isn't really the right place to do it but it
> works and I didn't find another way.

Actually I think it is the right place to do it. Prior to that the only
cases we've had to deal with are board specifc rules such as for the
Timedia/Sunix card. The softmodem bit of the blacklist wants renaming and
a comment adding to each group giving the reason.

> Eventually the parallel and the serial ports are detected but only the parallel
> port works because the serial ports are wrongly recognized as XScale when they
> are actually 16550 clones: if I set the port type by hand with setserial they work.
> 
> So I changed the autoconfig function in 8250.c this way: checks also the RTOIE
> bit in IER to detect the port as XSCALE. I don't think this is really correct
> but I've no knowledge on XSCALE or my serial card hardware to do it better.

Trying to get autodetection right is always a little bit exciting and one
device tends to break another.

In this case you know the device type and have a PCI identifier you should
be able to set

	up->port.flags |= UPF_FIXED_TYPE;
	up->port.type = PORT_16550A;

before the call to serial8250_register_8250_port();

Alan

^ permalink raw reply

* unrecognized multi io pci card
From: gianluca @ 2012-08-28 12:24 UTC (permalink / raw)
  To: gregkh; +Cc: linux-serial

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

Hello,

I recently bought a pci card with two serial ports and one parallel port
since my machine didn't have any, but the card isn't autodetected by linux.

Here follows a lspci -vvvxxx of the device

5:01.0 Serial controller: Device 4348:7053 (rev 10) (prog-if 02 [16550])
       Subsystem: Device 4348:3253
       Control: I/O+ Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr-
       Stepping- SERR- FastB2B- DisINTx-
       Status: Cap- 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort-
       <TAbort- <MAbort- >SERR- <PERR- INTx-
       Interrupt: pin A routed to IRQ 17
       Region 0: I/O ports at ec00 [size=8]
       Region 1: I/O ports at e880 [size=8]
       Region 2: I/O ports at e800 [size=8]
       Region 3: I/O ports at e480 [size=16]
       Kernel driver in use: parport_serial
00: 48 43 53 70 01 00 00 02 10 02 00 07 00 00 00 00
10: 01 ec 00 00 81 e8 00 00 01 e8 00 00 81 e4 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 48 43 53 32
30: 00 00 00 00 00 00 00 00 00 00 00 00 0a 01 00 00

I tried to patch parport_serial.c which, as I understand, is suited to provide
all the parallel and serial ports to userspace. That driver is unmantained
unfortunately but since the card is also a serial card I'm writing to you and
this list to get feedback on what I've done.

After adding the IDs and descriptions I found unfortunately that the module
8250_pci reclaims the pci device as a serial card only and this prevents the
successful registration of the parport_serial driver (which also depends on
8250_pci).

So I blacklisted the card in 8250_pci.c using the sofmodem blacklist mechanism
already in place. I know this isn't really the right place to do it but it
works and I didn't find another way.

Eventually the parallel and the serial ports are detected but only the parallel
port works because the serial ports are wrongly recognized as XScale when they
are actually 16550 clones: if I set the port type by hand with setserial they work.

So I changed the autoconfig function in 8250.c this way: checks also the RTOIE
bit in IER to detect the port as XSCALE. I don't think this is really correct
but I've no knowledge on XSCALE or my serial card hardware to do it better.

After this last change the ports are detected and work flawlessly.

In attachment you'll find all the patches.

Thank you.

[-- Attachment #2: parport-serial.patch --]
[-- Type: text/plain, Size: 2932 bytes --]

diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c
index e9c3227..a8d65a2 100644
--- a/drivers/parport/parport_serial.c
+++ b/drivers/parport/parport_serial.c
@@ -62,6 +62,7 @@ enum parport_pc_pci_cards {
 	timedia_9079a,
 	timedia_9079b,
 	timedia_9079c,
+	wch_ch353_2s1p,
 };
 
 /* each element directly indexed from enum list, above */
@@ -145,6 +146,7 @@ static struct parport_pc_pci cards[] __devinitdata = {
 	/* timedia_9079a */             { 1, { { 2, 3 }, } },
 	/* timedia_9079b */             { 1, { { 2, 3 }, } },
 	/* timedia_9079c */             { 1, { { 2, 3 }, } },
+	/* wch_ch353_2s1p*/             { 1, { { 2, -1}, } },
 };
 
 static struct pci_device_id parport_serial_pci_tbl[] = {
@@ -243,7 +245,7 @@ static struct pci_device_id parport_serial_pci_tbl[] = {
 	{ 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a },
 	{ 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
 	{ 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
-
+	{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
 	{ 0, } /* terminate list */
 };
 MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
@@ -460,6 +462,12 @@ static struct pciserial_board pci_parport_serial_boards[] __devinitdata = {
 		.base_baud	= 921600,
 		.uart_offset	= 8,
 	},
+	[wch_ch353_2s1p] = {
+		.flags          = FL_BASE0|FL_BASE_BARS,
+		.num_ports      = 2,
+		.base_baud      = 115200,
+		.uart_offset    = 8,
+	},
 };
 
 struct parport_serial_private {
diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c
index e9c3227..a8d65a2 100644
--- a/drivers/parport/parport_serial.c
+++ b/drivers/parport/parport_serial.c
@@ -62,6 +62,7 @@ enum parport_pc_pci_cards {
 	timedia_9079a,
 	timedia_9079b,
 	timedia_9079c,
+	wch_ch353_2s1p,
 };
 
 /* each element directly indexed from enum list, above */
@@ -145,6 +146,7 @@ static struct parport_pc_pci cards[] __devinitdata = {
 	/* timedia_9079a */             { 1, { { 2, 3 }, } },
 	/* timedia_9079b */             { 1, { { 2, 3 }, } },
 	/* timedia_9079c */             { 1, { { 2, 3 }, } },
+	/* wch_ch353_2s1p*/             { 1, { { 2, -1}, } },
 };
 
 static struct pci_device_id parport_serial_pci_tbl[] = {
@@ -243,7 +245,7 @@ static struct pci_device_id parport_serial_pci_tbl[] = {
 	{ 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a },
 	{ 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
 	{ 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
-
+	{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
 	{ 0, } /* terminate list */
 };
 MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
@@ -460,6 +462,12 @@ static struct pciserial_board pci_parport_serial_boards[] __devinitdata = {
 		.base_baud	= 921600,
 		.uart_offset	= 8,
 	},
+	[wch_ch353_2s1p] = {
+		.flags          = FL_BASE0|FL_BASE_BARS,
+		.num_ports      = 2,
+		.base_baud      = 115200,
+		.uart_offset    = 8,
+	},
 };
 
 struct parport_serial_private {

[-- Attachment #3: 8250-pci.patch --]
[-- Type: text/plain, Size: 562 bytes --]

diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 28e7c7c..61aadfc 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -2628,6 +2628,7 @@ static const struct pci_device_id softmodem_blacklist[] = {
 	{ PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */
 	{ PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */
 	{ PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */
+	{ PCI_DEVICE(0x4348, 0x7053), }, /* wch ch353 2S1P */
 };
 
 /*

[-- Attachment #4: 8250.patch --]
[-- Type: text/plain, Size: 971 bytes --]

diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index 6e1958a..9693d5a 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -980,14 +980,17 @@ static void autoconfig_16550a(struct uart_8250_port *up)
 		 */
 		serial_out(up, UART_IER, iersave | UART_IER_UUE);
 		if (serial_in(up, UART_IER) & UART_IER_UUE) {
-			/*
-			 * It's an Xscale.
-			 * We'll leave the UART_IER_UUE bit set to 1 (enabled).
-			 */
-			DEBUG_AUTOCONF("Xscale ");
-			up->port.type = PORT_XSCALE;
-			up->capabilities |= UART_CAP_UUE | UART_CAP_RTOIE;
-			return;
+			serial_out(up, UART_IER, iersave | UART_IER_RTOIE);
+			if (serial_in(up, UART_IER) & UART_IER_RTOIE) {
+				/*
+				 * It's an Xscale.
+				 * We'll leave the UART_IER_UUE bit set to 1 (enabled).
+				 */
+				DEBUG_AUTOCONF("Xscale ");
+				up->port.type = PORT_XSCALE;
+				up->capabilities |= UART_CAP_UUE | UART_CAP_RTOIE;
+				return;
+			}
 		}
 	} else {
 		/*

^ permalink raw reply related

* Re: [PATCH 2/2] tty: serial: imx: don't reinit clock with enabled console
From: Dirk Behme @ 2012-08-28 10:46 UTC (permalink / raw)
  To: Troy Kisky
  Cc: linux-arm-kernel@lists.infradead.org,
	linux-serial@vger.kernel.org, kernel@pengutronix.de,
	Greg Kroah-Hartman, Sascha Hauer, Xinyu Chen, Shawn Guo, Alan Cox
In-Reply-To: <503BBA5A.8040609@boundarydevices.com>

On 27.08.2012 20:20, Troy Kisky wrote:
> On 8/27/2012 12:36 AM, Dirk Behme wrote:
>> From: Xinyu Chen <xinyu.chen@freescale.com>
>>
>> Remove the imx_setup_ufcr() call on startup when CONSOLE enabled,
>> as this will cause clock reinit, and output garbage.
>>
>> This patch is a port from Freescale's Android kernel.
>>
>> Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
>> Tested-by: Dirk Behme <dirk.behme@de.bosch.com>
>> CC: Shawn Guo <shawn.guo@linaro.org>
>> CC: Sascha Hauer <s.hauer@pengutronix.de>
>> ---
>>   drivers/tty/serial/imx.c |    2 ++
>>   1 files changed, 2 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
>> index 908178f..31ce414 100644
>> --- a/drivers/tty/serial/imx.c
>> +++ b/drivers/tty/serial/imx.c
>> @@ -695,7 +695,9 @@ static int imx_startup(struct uart_port *port)
>>   	int retval;
>>   	unsigned long flags, temp;
>>   
>> +#ifndef CONFIG_SERIAL_CORE_CONSOLE
>>   	imx_setup_ufcr(sport, 0);
>> +#endif
>>   
>>   	/* disable the DREN bit (Data Ready interrupt enable) before
>>   	 * requesting IRQs
> 
> 
> I'd rather do something like this
> 
> static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
> {
>          unsigned int val;
> 
>          /* set receiver / transmitter trigger level. */
>          val = readl(sport->port.membase + UFCR) & UFCR_RFDIV;

Shouldn't it be

... & (UFCR_RFDIV | UFCR_DCEDTE);

then? My i.MX6 manual has DCEDTE as bit 6, which we don't want to touch, 
too? We only want to touch TXTL and RXTL?

>          val |= TXTL << 10 | RXTL;
>          writel(val, sport->port.membase + UFCR);
>          return 0;
> }
> 
> There is no need for imx_setup_ufcr to change divisor.

Ok

Thanks

Dirk

^ permalink raw reply

* Re: [PATCH 0/2] tty: serial: imx: fix lockup and garbage on SMP
From: Dirk Behme @ 2012-08-28  6:03 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Greg Kroah-Hartman, linux-arm-kernel@lists.infradead.org,
	linux-serial@vger.kernel.org, kernel@pengutronix.de, Alan Cox
In-Reply-To: <20120827233756.GH2281@r65073-Latitude-D630>

On 28.08.2012 01:37, Shawn Guo wrote:
> On Mon, Aug 27, 2012 at 03:51:42PM -0700, Greg Kroah-Hartman wrote:
>> On both of them?  I can do that if needed.  How far back should they go?
>>
> It should be okay to apply on 3.4 and 3.5. 

For patch #1/2: Yes, thanks!

> But patch #2 is still in
> question.  I do not like the #ifdef.

For patch #2/2: Yes, understood. I'll look at Troy's proposal and will 
update #2/2.

Many thanks and best regards

Dirk

^ permalink raw reply

* Re: [PATCH 0/2] tty: serial: imx: fix lockup and garbage on SMP
From: Shawn Guo @ 2012-08-27 23:37 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Dirk Behme, linux-arm-kernel, linux-serial, kernel, Alan Cox
In-Reply-To: <20120827225142.GA11302@kroah.com>

On Mon, Aug 27, 2012 at 03:51:42PM -0700, Greg Kroah-Hartman wrote:
> On both of them?  I can do that if needed.  How far back should they go?
> 
It should be okay to apply on 3.4 and 3.5.  But patch #2 is still in
question.  I do not like the #ifdef.

-- 
Regards,
Shawn

^ permalink raw reply

* Re: [PATCH 0/2] tty: serial: imx: fix lockup and garbage on SMP
From: Greg Kroah-Hartman @ 2012-08-27 22:51 UTC (permalink / raw)
  To: Shawn Guo; +Cc: Dirk Behme, linux-arm-kernel, linux-serial, kernel, Alan Cox
In-Reply-To: <20120827223134.GD2281@r65073-Latitude-D630>

On Tue, Aug 28, 2012 at 06:31:35AM +0800, Shawn Guo wrote:
> On Mon, Aug 27, 2012 at 09:36:50AM +0200, Dirk Behme wrote:
> > Both issues are gone applying these two patches.
> > 
> > Note: If these patches are fine, they should go to stable, too.
> > 
> Then you should have Cc: <stable@vger.kernel.org> tag on these patches.

On both of them?  I can do that if needed.  How far back should they go?

greg k-h

^ permalink raw reply

* Re: [PATCH 1/2] tty: serial: imx: console write routing is unsafe on SMP
From: Shawn Guo @ 2012-08-27 22:33 UTC (permalink / raw)
  To: Dirk Behme
  Cc: linux-arm-kernel, linux-serial, Alan Cox, Greg Kroah-Hartman,
	kernel, Xinyu Chen, Sascha Hauer
In-Reply-To: <1346053012-25686-2-git-send-email-dirk.behme@de.bosch.com>

On Mon, Aug 27, 2012 at 09:36:51AM +0200, Dirk Behme wrote:
> From: Xinyu Chen <xinyu.chen@freescale.com>
> 
> The console feature's write routing is unsafe on SMP with
> the startup/shutdown call.
> 
> There could be several consumers of the console
> * the kernel printk
> * the init process using /dev/kmsg to call printk to show log
> * shell, which open /dev/console and write with sys_write()
> 
> The shell goes into the normal uart open/write routing,
> but the other two go into the console operations.
> The open routing calls imx serial startup, which will write USR1/2
> register without any lock and critical with imx_console_write call.
> 
> Add a spin_lock for startup/shutdown/console_write routing.
> 
> This patch is a port from Freescale's Android kernel.
> 
> Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
> Tested-by: Dirk Behme <dirk.behme@de.bosch.com>
> CC: Shawn Guo <shawn.guo@linaro.org>
> CC: Sascha Hauer <s.hauer@pengutronix.de>

Acked-by: Shawn Guo <shawn.guo@linaro.org>

^ permalink raw reply

* Re: [PATCH 0/2] tty: serial: imx: fix lockup and garbage on SMP
From: Shawn Guo @ 2012-08-27 22:31 UTC (permalink / raw)
  To: Dirk Behme
  Cc: linux-arm-kernel, linux-serial, Greg Kroah-Hartman, kernel,
	Alan Cox
In-Reply-To: <1346053012-25686-1-git-send-email-dirk.behme@de.bosch.com>

On Mon, Aug 27, 2012 at 09:36:50AM +0200, Dirk Behme wrote:
> Both issues are gone applying these two patches.
> 
> Note: If these patches are fine, they should go to stable, too.
> 
Then you should have Cc: <stable@vger.kernel.org> tag on these patches.

-- 
Regards,
Shawn

^ permalink raw reply

* Re: [PATCH 2/2] tty: serial: imx: don't reinit clock with enabled console
From: Troy Kisky @ 2012-08-27 18:20 UTC (permalink / raw)
  To: Dirk Behme
  Cc: linux-arm-kernel, linux-serial, kernel, Greg Kroah-Hartman,
	Sascha Hauer, Xinyu Chen, Shawn Guo, Alan Cox
In-Reply-To: <1346053012-25686-3-git-send-email-dirk.behme@de.bosch.com>

On 8/27/2012 12:36 AM, Dirk Behme wrote:
> From: Xinyu Chen <xinyu.chen@freescale.com>
>
> Remove the imx_setup_ufcr() call on startup when CONSOLE enabled,
> as this will cause clock reinit, and output garbage.
>
> This patch is a port from Freescale's Android kernel.
>
> Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
> Tested-by: Dirk Behme <dirk.behme@de.bosch.com>
> CC: Shawn Guo <shawn.guo@linaro.org>
> CC: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>   drivers/tty/serial/imx.c |    2 ++
>   1 files changed, 2 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index 908178f..31ce414 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -695,7 +695,9 @@ static int imx_startup(struct uart_port *port)
>   	int retval;
>   	unsigned long flags, temp;
>   
> +#ifndef CONFIG_SERIAL_CORE_CONSOLE
>   	imx_setup_ufcr(sport, 0);
> +#endif
>   
>   	/* disable the DREN bit (Data Ready interrupt enable) before
>   	 * requesting IRQs


I'd rather do something like this

static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
{
         unsigned int val;

         /* set receiver / transmitter trigger level. */
         val = readl(sport->port.membase + UFCR) & UFCR_RFDIV;
         val |= TXTL << 10 | RXTL;
         writel(val, sport->port.membase + UFCR);
         return 0;
}

There is no need for imx_setup_ufcr to change divisor.


Troy


^ permalink raw reply

* [PATCH] tty: serial: mpc5xxx: add support for mark/space parity
From: Wolfram Sang @ 2012-08-27 14:03 UTC (permalink / raw)
  To: linux-serial; +Cc: Wolfram Sang, Anatolij Gustschin, Greg KH

Tested on a custom MPC5200B-board using some fancy industrial protocol.
Verified that MPC512x has identical bits, so should work there as well.

Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
Cc: Anatolij Gustschin <agust@denx.de>
Cc: Greg KH <gregkh@linuxfoundation.org>
---
 drivers/tty/serial/mpc52xx_uart.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index bedac0d..f19d04e 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -775,11 +775,15 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
 	}
 
 	if (new->c_cflag & PARENB) {
+		if (new->c_cflag & CMSPAR)
+			mr1 |= MPC52xx_PSC_MODE_PARFORCE;
+
+		/* With CMSPAR, PARODD also means high parity (same as termios) */
 		mr1 |= (new->c_cflag & PARODD) ?
 			MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;
-	} else
+	} else {
 		mr1 |= MPC52xx_PSC_MODE_PARNONE;
-
+	}
 
 	mr2 = 0;
 
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 1/2] tty: serial: imx: console write routing is unsafe on SMP
From: Dirk Behme @ 2012-08-27  7:36 UTC (permalink / raw)
  To: linux-arm-kernel, linux-serial
  Cc: Alan Cox, Greg Kroah-Hartman, kernel, Xinyu Chen, Shawn Guo,
	Sascha Hauer
In-Reply-To: <1346053012-25686-1-git-send-email-dirk.behme@de.bosch.com>

From: Xinyu Chen <xinyu.chen@freescale.com>

The console feature's write routing is unsafe on SMP with
the startup/shutdown call.

There could be several consumers of the console
* the kernel printk
* the init process using /dev/kmsg to call printk to show log
* shell, which open /dev/console and write with sys_write()

The shell goes into the normal uart open/write routing,
but the other two go into the console operations.
The open routing calls imx serial startup, which will write USR1/2
register without any lock and critical with imx_console_write call.

Add a spin_lock for startup/shutdown/console_write routing.

This patch is a port from Freescale's Android kernel.

Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
Tested-by: Dirk Behme <dirk.behme@de.bosch.com>
CC: Shawn Guo <shawn.guo@linaro.org>
CC: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/tty/serial/imx.c |   12 +++++++++++-
 1 files changed, 11 insertions(+), 1 deletions(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index d5c689d..908178f 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -754,6 +754,7 @@ static int imx_startup(struct uart_port *port)
 		}
 	}
 
+	spin_lock_irqsave(&sport->port.lock, flags);
 	/*
 	 * Finally, clear and enable interrupts
 	 */
@@ -807,7 +808,6 @@ static int imx_startup(struct uart_port *port)
 	/*
 	 * Enable modem status interrupts
 	 */
-	spin_lock_irqsave(&sport->port.lock,flags);
 	imx_enable_ms(&sport->port);
 	spin_unlock_irqrestore(&sport->port.lock,flags);
 
@@ -837,10 +837,13 @@ static void imx_shutdown(struct uart_port *port)
 {
 	struct imx_port *sport = (struct imx_port *)port;
 	unsigned long temp;
+	unsigned long flags;
 
+	spin_lock_irqsave(&sport->port.lock, flags);
 	temp = readl(sport->port.membase + UCR2);
 	temp &= ~(UCR2_TXEN);
 	writel(temp, sport->port.membase + UCR2);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
 
 	if (USE_IRDA(sport)) {
 		struct imxuart_platform_data *pdata;
@@ -869,12 +872,14 @@ static void imx_shutdown(struct uart_port *port)
 	 * Disable all interrupts, port and break condition.
 	 */
 
+	spin_lock_irqsave(&sport->port.lock, flags);
 	temp = readl(sport->port.membase + UCR1);
 	temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
 	if (USE_IRDA(sport))
 		temp &= ~(UCR1_IREN);
 
 	writel(temp, sport->port.membase + UCR1);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 static void
@@ -1217,6 +1222,9 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
 	struct imx_port *sport = imx_ports[co->index];
 	struct imx_port_ucrs old_ucr;
 	unsigned int ucr1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
 
 	/*
 	 *	First, save UCR1/2/3 and then disable interrupts
@@ -1242,6 +1250,8 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
 	while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
 
 	imx_port_ucrs_restore(&sport->port, &old_ucr);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 /*
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 2/2] tty: serial: imx: don't reinit clock with enabled console
From: Dirk Behme @ 2012-08-27  7:36 UTC (permalink / raw)
  To: linux-arm-kernel, linux-serial
  Cc: Alan Cox, Greg Kroah-Hartman, kernel, Xinyu Chen, Shawn Guo,
	Sascha Hauer
In-Reply-To: <1346053012-25686-1-git-send-email-dirk.behme@de.bosch.com>

From: Xinyu Chen <xinyu.chen@freescale.com>

Remove the imx_setup_ufcr() call on startup when CONSOLE enabled,
as this will cause clock reinit, and output garbage.

This patch is a port from Freescale's Android kernel.

Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
Tested-by: Dirk Behme <dirk.behme@de.bosch.com>
CC: Shawn Guo <shawn.guo@linaro.org>
CC: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/tty/serial/imx.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 908178f..31ce414 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -695,7 +695,9 @@ static int imx_startup(struct uart_port *port)
 	int retval;
 	unsigned long flags, temp;
 
+#ifndef CONFIG_SERIAL_CORE_CONSOLE
 	imx_setup_ufcr(sport, 0);
+#endif
 
 	/* disable the DREN bit (Data Ready interrupt enable) before
 	 * requesting IRQs
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 0/2] tty: serial: imx: fix lockup and garbage on SMP
From: Dirk Behme @ 2012-08-27  7:36 UTC (permalink / raw)
  To: linux-arm-kernel, linux-serial
  Cc: Greg Kroah-Hartman, Dirk Behme, kernel, Alan Cox

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

The following two patches are a port from Freescale's patch

http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/commit/drivers/tty/serial/imx.c?h=imx_3.0.15_android&id=80e525d66d1818722db18e21dc7f1dc41f314156

Using a 3.6-rc3 kernel on a i.MX6 SabreLite board and increasing the
verbosity of the kernel's serial console output with some debug printk
like [1] results in two issues:

1. After some time the kernel output hangs [2]. The debugger tells us that
it hangs in drivers/tty/serial/imx.c at

...
/*
 *	Finally, wait for transmitter to become empty
 *	and restore UCR1/2/3
 */
while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
...

with permanently reading 0x1000 from USR2 while it waits for bit USR2_TXDC
(0x8).

2. Even after applying the first patch, there happens to be some garbage in the
   output [3].

Both issues are gone applying these two patches.

Note: If these patches are fine, they should go to stable, too.

Best regards

Dirk

[1]

Index: a/mm/mmap.c
===================================================================
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1290,6 +1290,9 @@ munmap_back:
 	vma->vm_pgoff = pgoff;
 	INIT_LIST_HEAD(&vma->anon_vma_chain);
 
+	printk(" => mmap_region: start: 0x%08X end: 0x%08X len: %d\n",
+		(unsigned int)vma->vm_start, (unsigned int)vma->vm_end, (int)len);
+
 	error = -EINVAL;	/* when rejecting VM_GROWSDOWN|VM_GROWSUP */
 
 	if (file) {

[2]
...
=> mmap_region: start: 0x76DBC000 end: 0x76DBE000 len: 8192
=> mmap_region: start: 0x76DBE000 end: 0x76DC0000 len: 8192
=> mmap_region: start: 0x76D5C000 end: 0x76DA0000 ÿ
<hang, no output any more>

[3]
...
=> mmap_region: start: 0x0011C000 end: 0x0011D000 len: 4096
=> mmap_region: start: 0x00008000 end: 0x000B500fè#.Æ ÄRèérêòjµ…Á}ɝ¥½¹é start: 0x000BD000 end: 0x000BE000 len: 4096
=> mmap_region: start: 0x000BD000 end: 0x000BE000 len: 4096
...


[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH 9/13] drivers/tty/serial/amba-pl0{10,11}.c: use clk_prepare_enable and clk_disable_unprepare
From: Julia Lawall @ 2012-08-26 16:01 UTC (permalink / raw)
  To: Alan Cox; +Cc: kernel-janitors, Greg Kroah-Hartman, linux-serial, linux-kernel
In-Reply-To: <1345996865-32082-1-git-send-email-Julia.Lawall@lip6.fr>

From: Julia Lawall <Julia.Lawall@lip6.fr>

Clk_prepare_enable and clk_disable_unprepare combine clk_prepare and
clk_enable, and clk_disable and clk_unprepare.  The9 make the code more
concise, and ensure that clk_unprepare is called when clk_enable fails.

A simplified version of the semantic patch that introduces calls to these
functions is as follows: (http://coccinelle.lip6.fr/)

// <smpl>
@@
expression e;
@@

- clk_prepare(e);
- clk_enable(e);
+ clk_prepare_enable(e);

@@
expression e;
@@

- clk_disable(e);
- clk_unprepare(e);
+ clk_disable_unprepare(e);
// </smpl>

Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>

---
 drivers/tty/serial/amba-pl010.c |   15 ++++-----------
 drivers/tty/serial/amba-pl011.c |   15 ++++-----------
 2 files changed, 8 insertions(+), 22 deletions(-)

diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c
index 0d91a54..22317dd 100644
--- a/drivers/tty/serial/amba-pl010.c
+++ b/drivers/tty/serial/amba-pl010.c
@@ -312,16 +312,12 @@ static int pl010_startup(struct uart_port *port)
 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
 	int retval;
 
-	retval = clk_prepare(uap->clk);
-	if (retval)
-		goto out;
-
 	/*
 	 * Try to enable the clock producer.
 	 */
-	retval = clk_enable(uap->clk);
+	retval = clk_prepare_enable(uap->clk);
 	if (retval)
-		goto clk_unprep;
+		goto out;
 
 	uap->port.uartclk = clk_get_rate(uap->clk);
 
@@ -346,9 +342,7 @@ static int pl010_startup(struct uart_port *port)
 	return 0;
 
  clk_dis:
-	clk_disable(uap->clk);
- clk_unprep:
-	clk_unprepare(uap->clk);
+	clk_disable_unprepare(uap->clk);
  out:
 	return retval;
 }
@@ -375,8 +369,7 @@ static void pl010_shutdown(struct uart_port *port)
 	/*
 	 * Shut down the clock producer
 	 */
-	clk_disable(uap->clk);
-	clk_unprepare(uap->clk);
+	clk_disable_unprepare(uap->clk);
 }
 
 static void
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 92b1ac8..3322023 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1324,16 +1324,12 @@ static int pl011_startup(struct uart_port *port)
 				"could not set default pins\n");
 	}
 
-	retval = clk_prepare(uap->clk);
-	if (retval)
-		goto out;
-
 	/*
 	 * Try to enable the clock producer.
 	 */
-	retval = clk_enable(uap->clk);
+	retval = clk_prepare_enable(uap->clk);
 	if (retval)
-		goto clk_unprep;
+		goto out;
 
 	uap->port.uartclk = clk_get_rate(uap->clk);
 
@@ -1411,9 +1407,7 @@ static int pl011_startup(struct uart_port *port)
 	return 0;
 
  clk_dis:
-	clk_disable(uap->clk);
- clk_unprep:
-	clk_unprepare(uap->clk);
+	clk_disable_unprepare(uap->clk);
  out:
 	return retval;
 }
@@ -1473,8 +1467,7 @@ static void pl011_shutdown(struct uart_port *port)
 	/*
 	 * Shut down the clock producer
 	 */
-	clk_disable(uap->clk);
-	clk_unprepare(uap->clk);
+	clk_disable_unprepare(uap->clk);
 	/* Optionally let pins go into sleep states */
 	if (!IS_ERR(uap->pins_sleep)) {
 		retval = pinctrl_select_state(uap->pinctrl, uap->pins_sleep);

^ permalink raw reply related

* [PATCH 2/2] serial: Add note about migration to driver SCCNXP
From: Alexander Shiyan @ 2012-08-25 15:24 UTC (permalink / raw)
  To: linux-serial; +Cc: Alan Cox, Greg Kroah-Hartman, Alexander Shiyan
In-Reply-To: <1345908260-6948-1-git-send-email-shc_work@mail.ru>

This patch adds note about migration to driver SCCNXP in the code
of driver SC26XX and in MIPS SNI board initialization with example.

Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
---
 arch/mips/sni/a20r.c        |   32 ++++++++++++++++++++++++++++++++
 drivers/tty/serial/sc26xx.c |    3 +++
 2 files changed, 35 insertions(+), 0 deletions(-)

diff --git a/arch/mips/sni/a20r.c b/arch/mips/sni/a20r.c
index c48194c..b2d4f49 100644
--- a/arch/mips/sni/a20r.c
+++ b/arch/mips/sni/a20r.c
@@ -133,6 +133,38 @@ static struct platform_device sc26xx_pdev = {
 	}
 };
 
+#warning "Please try migrate to use new driver SCCNXP and report the status" \
+	 "in the linux-serial mailing list."
+
+/* The code bellow is a replacement of SC26XX to SCCNXP */
+#if 0
+#include <linux/platform_data/sccnxp.h>
+
+static struct sccnxp_pdata sccnxp_data = {
+	.reg_shift	= 2,
+	.frequency	= 3686400,
+	.mctrl_cfg[0]	= MCTRL_SIG(DTR_OP, LINE_OP7) |
+			  MCTRL_SIG(RTS_OP, LINE_OP3) |
+			  MCTRL_SIG(DSR_IP, LINE_IP5) |
+			  MCTRL_SIG(DCD_IP, LINE_IP6),
+	.mctrl_cfg[1]	= MCTRL_SIG(DTR_OP, LINE_OP2) |
+			  MCTRL_SIG(RTS_OP, LINE_OP1) |
+			  MCTRL_SIG(DSR_IP, LINE_IP0) |
+			  MCTRL_SIG(CTS_IP, LINE_IP1) |
+			  MCTRL_SIG(DCD_IP, LINE_IP2) |
+			  MCTRL_SIG(RNG_IP, LINE_IP3),
+};
+
+static struct platform_device sc2681_pdev = {
+	.name		= "sc2681",
+	.resource	= sc2xxx_rsrc,
+	.num_resources	= ARRAY_SIZE(sc2xxx_rsrc),
+	.dev	= {
+		.platform_data	= &sccnxp_data,
+	},
+};
+#endif
+
 static u32 a20r_ack_hwint(void)
 {
 	u32 status = read_c0_status();
diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c
index e0b4b0a..72589e4 100644
--- a/drivers/tty/serial/sc26xx.c
+++ b/drivers/tty/serial/sc26xx.c
@@ -21,6 +21,9 @@
 #include <linux/platform_device.h>
 #include <linux/irq.h>
 
+#warning "Please try migrate to use new driver SCCNXP and report the status" \
+	 "in the linux-serial mailing list."
+
 #if defined(CONFIG_MAGIC_SYSRQ)
 #define SUPPORT_SYSRQ
 #endif
-- 
1.7.8.6


^ permalink raw reply related

* [PATCH 1/2] serial: New serial driver SCCNXP
From: Alexander Shiyan @ 2012-08-25 15:24 UTC (permalink / raw)
  To: linux-serial; +Cc: Alan Cox, Greg Kroah-Hartman, Alexander Shiyan

This driver is a replacement for a SC26XX driver with a lot of
improvements and new features.
The main differences from the SC26XX driver:
- Removed dependency on MIPS. Driver can be used on any platform.
- Added support for SCC2681, SCC2691, SCC2692, SC28L91, SC28L92,
  SC28L202, SCC68681 and SCC68692 ICs.
- Using devm_-related functions.
- Improved error handling of serial port, improved FIFO handling.
- Ability to load multiple instances of drivers.

To avoid the possibility of regression, driver SC26XX left in the
system to confirm the stability of the driver on platforms where
it is being used.

Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
---
 drivers/tty/serial/Kconfig           |   18 +
 drivers/tty/serial/Makefile          |    1 +
 drivers/tty/serial/sccnxp.c          |  985 ++++++++++++++++++++++++++++++++++
 include/linux/platform_data/sccnxp.h |   93 ++++
 4 files changed, 1097 insertions(+), 0 deletions(-)
 create mode 100644 drivers/tty/serial/sccnxp.c
 create mode 100644 include/linux/platform_data/sccnxp.h

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 4720b4b..750671d 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1104,6 +1104,24 @@ config SERIAL_SC26XX_CONSOLE
 	help
 	  Support for Console on SC2681/SC2692 serial ports.
 
+config SERIAL_SCCNXP
+	bool "SCCNXP serial port support"
+	depends on !SERIAL_SC26XX
+	select SERIAL_CORE
+	default n
+	help
+	  This selects support for an advanced UART from NXP (Philips).
+	  Supported ICs are SCC2681, SCC2691, SCC2692, SC28L91, SC28L92,
+	  SC28L202, SCC68681 and SCC68692.
+	  Positioned as a replacement for the driver SC26XX.
+
+config SERIAL_SCCNXP_CONSOLE
+	bool "Console on SCCNXP serial port"
+	depends on SERIAL_SCCNXP
+	select SERIAL_CORE_CONSOLE
+	help
+	  Support for console on SCCNXP serial ports.
+
 config SERIAL_BFIN_SPORT
 	tristate "Blackfin SPORT emulate UART"
 	depends on BLACKFIN
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 7257c5d..320ac4b 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
 obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
 obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
 obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o
+obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
 obj-$(CONFIG_SERIAL_JSM) += jsm/
 obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
 obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
new file mode 100644
index 0000000..29dda9b
--- /dev/null
+++ b/drivers/tty/serial/sccnxp.c
@@ -0,0 +1,985 @@
+/*
+ *  NXP (Philips) SCC+++(SCN+++) serial driver
+ *
+ *  Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
+ *
+ *  Based on sc26xx.c, by Thomas Bogendörfer (tsbogend@alpha.franken.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#if defined(CONFIG_SERIAL_SCCNXP_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/io.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/console.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/sccnxp.h>
+
+#define SCCNXP_NAME			"uart-sccnxp"
+#define SCCNXP_MAJOR			204
+#define SCCNXP_MINOR			205
+
+#define SCCNXP_MR_REG			(0x00)
+#	define MR0_BAUD_NORMAL		(0 << 0)
+#	define MR0_BAUD_EXT1		(1 << 0)
+#	define MR0_BAUD_EXT2		(5 << 0)
+#	define MR0_FIFO			(1 << 3)
+#	define MR0_TXLVL		(1 << 4)
+#	define MR1_BITS_5		(0 << 0)
+#	define MR1_BITS_6		(1 << 0)
+#	define MR1_BITS_7		(2 << 0)
+#	define MR1_BITS_8		(3 << 0)
+#	define MR1_PAR_EVN		(0 << 2)
+#	define MR1_PAR_ODD		(1 << 2)
+#	define MR1_PAR_NO		(4 << 2)
+#	define MR2_STOP1		(7 << 0)
+#	define MR2_STOP2		(0xf << 0)
+#define SCCNXP_SR_REG			(0x01)
+#define SCCNXP_CSR_REG			SCCNXP_SR_REG
+#	define SR_RXRDY			(1 << 0)
+#	define SR_FULL			(1 << 1)
+#	define SR_TXRDY			(1 << 2)
+#	define SR_TXEMT			(1 << 3)
+#	define SR_OVR			(1 << 4)
+#	define SR_PE			(1 << 5)
+#	define SR_FE			(1 << 6)
+#	define SR_BRK			(1 << 7)
+#define SCCNXP_CR_REG			(0x02)
+#	define CR_RX_ENABLE		(1 << 0)
+#	define CR_RX_DISABLE		(1 << 1)
+#	define CR_TX_ENABLE		(1 << 2)
+#	define CR_TX_DISABLE		(1 << 3)
+#	define CR_CMD_MRPTR1		(0x01 << 4)
+#	define CR_CMD_RX_RESET		(0x02 << 4)
+#	define CR_CMD_TX_RESET		(0x03 << 4)
+#	define CR_CMD_STATUS_RESET	(0x04 << 4)
+#	define CR_CMD_BREAK_RESET	(0x05 << 4)
+#	define CR_CMD_START_BREAK	(0x06 << 4)
+#	define CR_CMD_STOP_BREAK	(0x07 << 4)
+#	define CR_CMD_MRPTR0		(0x0b << 4)
+#define SCCNXP_RHR_REG			(0x03)
+#define SCCNXP_THR_REG			SCCNXP_RHR_REG
+#define SCCNXP_IPCR_REG			(0x04)
+#define SCCNXP_ACR_REG			SCCNXP_IPCR_REG
+#	define ACR_BAUD0		(0 << 7)
+#	define ACR_BAUD1		(1 << 7)
+#	define ACR_TIMER_MODE		(6 << 4)
+#define SCCNXP_ISR_REG			(0x05)
+#define SCCNXP_IMR_REG			SCCNXP_ISR_REG
+#	define IMR_TXRDY		(1 << 0)
+#	define IMR_RXRDY		(1 << 1)
+#	define ISR_TXRDY(x)		(1 << ((x * 4) + 0))
+#	define ISR_RXRDY(x)		(1 << ((x * 4) + 1))
+#define SCCNXP_IPR_REG			(0x0d)
+#define SCCNXP_OPCR_REG			SCCNXP_IPR_REG
+#define SCCNXP_SOP_REG			(0x0e)
+#define SCCNXP_ROP_REG			(0x0f)
+
+/* Route helpers */
+#define MCTRL_MASK(sig)			(0xf << (sig))
+#define MCTRL_IBIT(cfg, sig)		((((cfg) >> (sig)) & 0xf) - LINE_IP0)
+#define MCTRL_OBIT(cfg, sig)		((((cfg) >> (sig)) & 0xf) - LINE_OP0)
+
+/* Supported chip types */
+enum {
+	SCCNXP_TYPE_SC2681	= 2681,
+	SCCNXP_TYPE_SC2691	= 2691,
+	SCCNXP_TYPE_SC2692	= 2692,
+	SCCNXP_TYPE_SC2891	= 2891,
+	SCCNXP_TYPE_SC2892	= 2892,
+	SCCNXP_TYPE_SC28202	= 28202,
+	SCCNXP_TYPE_SC68681	= 68681,
+	SCCNXP_TYPE_SC68692	= 68692,
+};
+
+struct sccnxp_port {
+	struct uart_driver	uart;
+	struct uart_port	port[SCCNXP_MAX_UARTS];
+
+	const char		*name;
+	int			irq;
+
+	u8			imr;
+	u8			addr_mask;
+	int			freq_std;
+
+	int			flags;
+#define SCCNXP_HAVE_IO		0x00000001
+#define SCCNXP_HAVE_MR0		0x00000002
+
+#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
+	struct console		console;
+#endif
+
+	struct mutex		sccnxp_mutex;
+
+	struct sccnxp_pdata	pdata;
+};
+
+static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift)
+{
+	return readb(base + (reg << shift));
+}
+
+static inline void sccnxp_raw_write(void __iomem *base, u8 reg, u8 shift, u8 v)
+{
+	writeb(v, base + (reg << shift));
+}
+
+static inline u8 sccnxp_read(struct uart_port *port, u8 reg)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	return sccnxp_raw_read(port->membase, reg & s->addr_mask,
+			       port->regshift);
+}
+
+static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	sccnxp_raw_write(port->membase, reg & s->addr_mask, port->regshift, v);
+}
+
+static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg)
+{
+	return sccnxp_read(port, (port->line << 3) + reg);
+}
+
+static inline void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v)
+{
+	sccnxp_write(port, (port->line << 3) + reg, v);
+}
+
+static int sccnxp_update_best_err(int a, int b, int *besterr)
+{
+	int err = abs(a - b);
+
+	if ((*besterr < 0) || (*besterr > err)) {
+		*besterr = err;
+		return 0;
+	}
+
+	return 1;
+}
+
+struct baud_table {
+	u8	csr;
+	u8	acr;
+	u8	mr0;
+	int	baud;
+};
+
+const struct baud_table baud_std[] = {
+	{ 0,	ACR_BAUD0,	MR0_BAUD_NORMAL,	50, },
+	{ 0,	ACR_BAUD1,	MR0_BAUD_NORMAL,	75, },
+	{ 1,	ACR_BAUD0,	MR0_BAUD_NORMAL,	110, },
+	{ 2,	ACR_BAUD0,	MR0_BAUD_NORMAL,	134, },
+	{ 3,	ACR_BAUD1,	MR0_BAUD_NORMAL,	150, },
+	{ 3,	ACR_BAUD0,	MR0_BAUD_NORMAL,	200, },
+	{ 4,	ACR_BAUD0,	MR0_BAUD_NORMAL,	300, },
+	{ 0,	ACR_BAUD1,	MR0_BAUD_EXT1,		450, },
+	{ 1,	ACR_BAUD0,	MR0_BAUD_EXT2,		880, },
+	{ 3,	ACR_BAUD1,	MR0_BAUD_EXT1,		900, },
+	{ 5,	ACR_BAUD0,	MR0_BAUD_NORMAL,	600, },
+	{ 7,	ACR_BAUD0,	MR0_BAUD_NORMAL,	1050, },
+	{ 2,	ACR_BAUD0,	MR0_BAUD_EXT2,		1076, },
+	{ 6,	ACR_BAUD0,	MR0_BAUD_NORMAL,	1200, },
+	{ 10,	ACR_BAUD1,	MR0_BAUD_NORMAL,	1800, },
+	{ 7,	ACR_BAUD1,	MR0_BAUD_NORMAL,	2000, },
+	{ 8,	ACR_BAUD0,	MR0_BAUD_NORMAL,	2400, },
+	{ 5,	ACR_BAUD1,	MR0_BAUD_EXT1,		3600, },
+	{ 9,	ACR_BAUD0,	MR0_BAUD_NORMAL,	4800, },
+	{ 10,	ACR_BAUD0,	MR0_BAUD_NORMAL,	7200, },
+	{ 11,	ACR_BAUD0,	MR0_BAUD_NORMAL,	9600, },
+	{ 8,	ACR_BAUD0,	MR0_BAUD_EXT1,		14400, },
+	{ 12,	ACR_BAUD1,	MR0_BAUD_NORMAL,	19200, },
+	{ 9,	ACR_BAUD0,	MR0_BAUD_EXT1,		28800, },
+	{ 12,	ACR_BAUD0,	MR0_BAUD_NORMAL,	38400, },
+	{ 11,	ACR_BAUD0,	MR0_BAUD_EXT1,		57600, },
+	{ 12,	ACR_BAUD1,	MR0_BAUD_EXT1,		115200, },
+	{ 12,	ACR_BAUD0,	MR0_BAUD_EXT1,		230400, },
+	{ 0, 0, 0, 0 }
+};
+
+static void sccnxp_set_baud(struct uart_port *port, int baud)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+	int div_std, tmp_baud, bestbaud = baud, besterr = -1;
+	u8 i, acr = 0, csr = 0, mr0 = 0;
+
+	/* Find best baud from table */
+	for (i = 0; baud_std[i].baud && besterr; i++) {
+		if (baud_std[i].mr0 && !(s->flags & SCCNXP_HAVE_MR0))
+			continue;
+		div_std = DIV_ROUND_CLOSEST(s->freq_std, baud_std[i].baud);
+		tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std);
+		if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) {
+			acr = baud_std[i].acr;
+			csr = baud_std[i].csr;
+			mr0 = baud_std[i].mr0;
+			bestbaud = tmp_baud;
+		}
+	}
+
+	if (s->flags & SCCNXP_HAVE_MR0) {
+		/* Enable FIFO, set half level for TX */
+		mr0 |= MR0_FIFO | MR0_TXLVL;
+		/* Update MR0 */
+		sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR0);
+		sccnxp_port_write(port, SCCNXP_MR_REG, mr0);
+	}
+
+	sccnxp_port_write(port, SCCNXP_ACR_REG, acr | ACR_TIMER_MODE);
+	sccnxp_port_write(port, SCCNXP_CSR_REG, (csr << 4) | csr);
+
+	dev_dbg(port->dev, "Baudrate desired: %i, calculated: %i\n",
+		baud, bestbaud);
+}
+
+static void sccnxp_enable_irq(struct uart_port *port, int mask)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	s->imr |= mask << (port->line * 4);
+	sccnxp_write(port, SCCNXP_IMR_REG, s->imr);
+}
+
+static void sccnxp_disable_irq(struct uart_port *port, int mask)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	s->imr &= ~(mask << (port->line * 4));
+	sccnxp_write(port, SCCNXP_IMR_REG, s->imr);
+}
+
+static void sccnxp_set_bit(struct uart_port *port, int sig, int state)
+{
+	u8 bitmask;
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(sig)) {
+		bitmask = 1 << MCTRL_OBIT(s->pdata.mctrl_cfg[port->line], sig);
+		if (state)
+			sccnxp_write(port, SCCNXP_SOP_REG, bitmask);
+		else
+			sccnxp_write(port, SCCNXP_ROP_REG, bitmask);
+	}
+}
+
+static void sccnxp_handle_rx(struct uart_port *port)
+{
+	u8 sr;
+	unsigned int ch, flag;
+	struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+
+	if (!tty)
+		return;
+
+	for (;;) {
+		sr = sccnxp_port_read(port, SCCNXP_SR_REG);
+		if (!(sr & SR_RXRDY))
+			break;
+		sr &= SR_PE | SR_FE | SR_OVR | SR_BRK;
+
+		ch = sccnxp_port_read(port, SCCNXP_RHR_REG);
+
+		port->icount.rx++;
+		flag = TTY_NORMAL;
+
+		if (unlikely(sr)) {
+			if (sr & SR_BRK) {
+				port->icount.brk++;
+				if (uart_handle_break(port))
+					continue;
+			} else if (sr & SR_PE)
+				port->icount.parity++;
+			else if (sr & SR_FE)
+				port->icount.frame++;
+			else if (sr & SR_OVR)
+				port->icount.overrun++;
+
+			sr &= port->read_status_mask;
+			if (sr & SR_BRK)
+				flag = TTY_BREAK;
+			else if (sr & SR_PE)
+				flag = TTY_PARITY;
+			else if (sr & SR_FE)
+				flag = TTY_FRAME;
+			else if (sr & SR_OVR)
+				flag = TTY_OVERRUN;
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			continue;
+
+		if (sr & port->ignore_status_mask)
+			continue;
+
+		uart_insert_char(port, sr, SR_OVR, ch, flag);
+	}
+
+	tty_flip_buffer_push(tty);
+
+	tty_kref_put(tty);
+}
+
+static void sccnxp_handle_tx(struct uart_port *port)
+{
+	u8 sr;
+	struct circ_buf *xmit = &port->state->xmit;
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	if (unlikely(port->x_char)) {
+		sccnxp_port_write(port, SCCNXP_THR_REG, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		return;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		/* Disable TX if FIFO is empty */
+		if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXEMT) {
+			sccnxp_disable_irq(port, IMR_TXRDY);
+
+			/* Set direction to input */
+			if (s->flags & SCCNXP_HAVE_IO)
+				sccnxp_set_bit(port, DIR_OP, 0);
+		}
+		return;
+	}
+
+	while (!uart_circ_empty(xmit)) {
+		sr = sccnxp_port_read(port, SCCNXP_SR_REG);
+		if (!(sr & SR_TXRDY))
+			break;
+
+		sccnxp_port_write(port, SCCNXP_THR_REG, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+static irqreturn_t sccnxp_ist(int irq, void *dev_id)
+{
+	int i;
+	u8 isr;
+	struct sccnxp_port *s = (struct sccnxp_port *)dev_id;
+
+	mutex_lock(&s->sccnxp_mutex);
+
+	for (;;) {
+		isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG);
+		isr &= s->imr;
+		if (!isr)
+			break;
+
+		dev_dbg(s->port[0].dev, "IRQ status: 0x%02x\n", isr);
+
+		for (i = 0; i < s->uart.nr; i++) {
+			if (isr & ISR_RXRDY(i))
+				sccnxp_handle_rx(&s->port[i]);
+			if (isr & ISR_TXRDY(i))
+				sccnxp_handle_tx(&s->port[i]);
+		}
+	}
+
+	mutex_unlock(&s->sccnxp_mutex);
+
+	return IRQ_HANDLED;
+}
+
+static void sccnxp_start_tx(struct uart_port *port)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	mutex_lock(&s->sccnxp_mutex);
+
+	/* Set direction to output */
+	if (s->flags & SCCNXP_HAVE_IO)
+		sccnxp_set_bit(port, DIR_OP, 1);
+
+	sccnxp_enable_irq(port, IMR_TXRDY);
+
+	mutex_unlock(&s->sccnxp_mutex);
+}
+
+static void sccnxp_stop_tx(struct uart_port *port)
+{
+	/* Do nothing */
+}
+
+static void sccnxp_stop_rx(struct uart_port *port)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	mutex_lock(&s->sccnxp_mutex);
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE);
+	mutex_unlock(&s->sccnxp_mutex);
+}
+
+static unsigned int sccnxp_tx_empty(struct uart_port *port)
+{
+	u8 val;
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	mutex_lock(&s->sccnxp_mutex);
+	val = sccnxp_port_read(port, SCCNXP_SR_REG);
+	mutex_unlock(&s->sccnxp_mutex);
+
+	return (val & SR_TXEMT) ? TIOCSER_TEMT : 0;
+}
+
+static void sccnxp_enable_ms(struct uart_port *port)
+{
+	/* Do nothing */
+}
+
+static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	if (!(s->flags & SCCNXP_HAVE_IO))
+		return;
+
+	mutex_lock(&s->sccnxp_mutex);
+
+	sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR);
+	sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS);
+
+	mutex_unlock(&s->sccnxp_mutex);
+}
+
+static unsigned int sccnxp_get_mctrl(struct uart_port *port)
+{
+	u8 bitmask, ipr;
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+	unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
+
+	if (!(s->flags & SCCNXP_HAVE_IO))
+		return mctrl;
+
+	mutex_lock(&s->sccnxp_mutex);
+
+	ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG);
+
+	if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DSR_IP)) {
+		bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
+					  DSR_IP);
+		mctrl &= ~TIOCM_DSR;
+		mctrl |= (ipr & bitmask) ? TIOCM_DSR : 0;
+	}
+	if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(CTS_IP)) {
+		bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
+					  CTS_IP);
+		mctrl &= ~TIOCM_CTS;
+		mctrl |= (ipr & bitmask) ? TIOCM_CTS : 0;
+	}
+	if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DCD_IP)) {
+		bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
+					  DCD_IP);
+		mctrl &= ~TIOCM_CAR;
+		mctrl |= (ipr & bitmask) ? TIOCM_CAR : 0;
+	}
+	if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(RNG_IP)) {
+		bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
+					  RNG_IP);
+		mctrl &= ~TIOCM_RNG;
+		mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0;
+	}
+
+	mutex_unlock(&s->sccnxp_mutex);
+
+	return mctrl;
+}
+
+static void sccnxp_break_ctl(struct uart_port *port, int break_state)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	mutex_lock(&s->sccnxp_mutex);
+	sccnxp_port_write(port, SCCNXP_CR_REG, break_state ?
+			  CR_CMD_START_BREAK : CR_CMD_STOP_BREAK);
+	mutex_unlock(&s->sccnxp_mutex);
+}
+
+static void sccnxp_set_termios(struct uart_port *port,
+			       struct ktermios *termios, struct ktermios *old)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+	u8 mr1, mr2;
+	int baud;
+
+	mutex_lock(&s->sccnxp_mutex);
+
+	/* Mask termios capabilities we don't support */
+	termios->c_cflag &= ~CMSPAR;
+	termios->c_iflag &= ~(IXON | IXOFF | IXANY);
+
+	/* Disable RX & TX, reset break condition, status and FIFOs */
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET |
+					       CR_RX_DISABLE | CR_TX_DISABLE);
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET);
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET);
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET);
+
+	/* Word size */
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		mr1 = MR1_BITS_5;
+		break;
+	case CS6:
+		mr1 = MR1_BITS_6;
+		break;
+	case CS7:
+		mr1 = MR1_BITS_7;
+		break;
+	default:
+	case CS8:
+		mr1 = MR1_BITS_8;
+		break;
+	}
+
+	/* Parity */
+	if (termios->c_cflag & PARENB) {
+		if (termios->c_cflag & PARODD)
+			mr1 |= MR1_PAR_ODD;
+	} else
+		mr1 |= MR1_PAR_NO;
+
+	/* Stop bits */
+	mr2 = (termios->c_cflag & CSTOPB) ? MR2_STOP2 : MR2_STOP1;
+
+	/* Update desired format */
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR1);
+	sccnxp_port_write(port, SCCNXP_MR_REG, mr1);
+	sccnxp_port_write(port, SCCNXP_MR_REG, mr2);
+
+	/* Set read status mask */
+	port->read_status_mask = SR_OVR;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= SR_PE | SR_FE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= SR_BRK;
+
+	/* Set status ignore mask */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNBRK)
+		port->ignore_status_mask |= SR_BRK;
+	if (!(termios->c_cflag & CREAD))
+		port->ignore_status_mask |= SR_PE | SR_OVR | SR_FE | SR_BRK;
+
+	/* Setup baudrate */
+	baud = uart_get_baud_rate(port, termios, old, 50,
+				  (s->flags & SCCNXP_HAVE_MR0) ?
+				  230400 : 38400);
+	sccnxp_set_baud(port, baud);
+
+	/* Update timeout according to new baud rate */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/* Enable RX & TX */
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
+
+	mutex_unlock(&s->sccnxp_mutex);
+}
+
+static int sccnxp_startup(struct uart_port *port)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	mutex_lock(&s->sccnxp_mutex);
+
+	if (s->flags & SCCNXP_HAVE_IO) {
+		/* Outputs are controlled manually */
+		sccnxp_write(port, SCCNXP_OPCR_REG, 0);
+	}
+
+	/* Reset break condition, status and FIFOs */
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET);
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET);
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET);
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET);
+
+	/* Enable RX & TX */
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
+
+	/* Enable RX interrupt */
+	sccnxp_enable_irq(port, IMR_RXRDY);
+
+	mutex_unlock(&s->sccnxp_mutex);
+
+	return 0;
+}
+
+static void sccnxp_shutdown(struct uart_port *port)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	mutex_lock(&s->sccnxp_mutex);
+
+	/* Disable interrupts */
+	sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
+
+	/* Disable TX & RX */
+	sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE);
+
+	/* Leave direction to input */
+	if (s->flags & SCCNXP_HAVE_IO)
+		sccnxp_set_bit(port, DIR_OP, 0);
+
+	mutex_unlock(&s->sccnxp_mutex);
+}
+
+static const char *sccnxp_type(struct uart_port *port)
+{
+	struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+	return (port->type == PORT_SC26XX) ? s->name : NULL;
+}
+
+static void sccnxp_release_port(struct uart_port *port)
+{
+	/* Do nothing */
+}
+
+static int sccnxp_request_port(struct uart_port *port)
+{
+	/* Do nothing */
+	return 0;
+}
+
+static void sccnxp_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE)
+		port->type = PORT_SC26XX;
+}
+
+static int sccnxp_verify_port(struct uart_port *port, struct serial_struct *s)
+{
+	if ((s->type == PORT_UNKNOWN) || (s->type == PORT_SC26XX))
+		return 0;
+	if (s->irq == port->irq)
+		return 0;
+
+	return -EINVAL;
+}
+
+static const struct uart_ops sccnxp_ops = {
+	.tx_empty	= sccnxp_tx_empty,
+	.set_mctrl	= sccnxp_set_mctrl,
+	.get_mctrl	= sccnxp_get_mctrl,
+	.stop_tx	= sccnxp_stop_tx,
+	.start_tx	= sccnxp_start_tx,
+	.stop_rx	= sccnxp_stop_rx,
+	.enable_ms	= sccnxp_enable_ms,
+	.break_ctl	= sccnxp_break_ctl,
+	.startup	= sccnxp_startup,
+	.shutdown	= sccnxp_shutdown,
+	.set_termios	= sccnxp_set_termios,
+	.type		= sccnxp_type,
+	.release_port	= sccnxp_release_port,
+	.request_port	= sccnxp_request_port,
+	.config_port	= sccnxp_config_port,
+	.verify_port	= sccnxp_verify_port,
+};
+
+#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
+static void sccnxp_console_putchar(struct uart_port *port, int c)
+{
+	int tryes = 100000;
+
+	while (tryes--) {
+		if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXRDY) {
+			sccnxp_port_write(port, SCCNXP_THR_REG, c);
+			break;
+		}
+		barrier();
+	}
+}
+
+static void sccnxp_console_write(struct console *co, const char *c, unsigned n)
+{
+	struct sccnxp_port *s = (struct sccnxp_port *)co->data;
+	struct uart_port *port = &s->port[co->index];
+
+	mutex_lock(&s->sccnxp_mutex);
+	uart_console_write(port, c, n, sccnxp_console_putchar);
+	mutex_unlock(&s->sccnxp_mutex);
+}
+
+static int sccnxp_console_setup(struct console *co, char *options)
+{
+	struct sccnxp_port *s = (struct sccnxp_port *)co->data;
+	struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0];
+	int baud = 9600, bits = 8, parity = 'n', flow = 'n';
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+#endif
+
+static int __devinit sccnxp_probe(struct platform_device *pdev)
+{
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	int chiptype = pdev->id_entry->driver_data;
+	struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev);
+	int i, ret, fifosize, freq_min, freq_max;
+	struct sccnxp_port *s;
+	void __iomem *membase;
+
+	if (!res) {
+		dev_err(&pdev->dev, "Missing memory resource data\n");
+		return -EADDRNOTAVAIL;
+	}
+
+	dev_set_name(&pdev->dev, SCCNXP_NAME);
+
+	s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL);
+	if (!s) {
+		dev_err(&pdev->dev, "Error allocating port structure\n");
+		return -ENOMEM;
+	}
+	platform_set_drvdata(pdev, s);
+
+	mutex_init(&s->sccnxp_mutex);
+
+	/* Individual chip settings */
+	switch (chiptype) {
+	case SCCNXP_TYPE_SC2681:
+		s->name		= "SC2681";
+		s->uart.nr	= 2;
+		s->freq_std	= 3686400;
+		s->addr_mask	= 0x0f;
+		s->flags	= SCCNXP_HAVE_IO;
+		fifosize	= 3;
+		freq_min	= 1000000;
+		freq_max	= 4000000;
+		break;
+	case SCCNXP_TYPE_SC2691:
+		s->name		= "SC2691";
+		s->uart.nr	= 1;
+		s->freq_std	= 3686400;
+		s->addr_mask	= 0x07;
+		s->flags	= 0;
+		fifosize	= 3;
+		freq_min	= 1000000;
+		freq_max	= 4000000;
+		break;
+	case SCCNXP_TYPE_SC2692:
+		s->name		= "SC2692";
+		s->uart.nr	= 2;
+		s->freq_std	= 3686400;
+		s->addr_mask	= 0x0f;
+		s->flags	= SCCNXP_HAVE_IO;
+		fifosize	= 3;
+		freq_min	= 1000000;
+		freq_max	= 4000000;
+		break;
+	case SCCNXP_TYPE_SC2891:
+		s->name		= "SC2891";
+		s->uart.nr	= 1;
+		s->freq_std	= 3686400;
+		s->addr_mask	= 0x0f;
+		s->flags	= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
+		fifosize	= 16;
+		freq_min	= 100000;
+		freq_max	= 8000000;
+		break;
+	case SCCNXP_TYPE_SC2892:
+		s->name		= "SC2892";
+		s->uart.nr	= 2;
+		s->freq_std	= 3686400;
+		s->addr_mask	= 0x0f;
+		s->flags	= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
+		fifosize	= 16;
+		freq_min	= 100000;
+		freq_max	= 8000000;
+		break;
+	case SCCNXP_TYPE_SC28202:
+		s->name		= "SC28202";
+		s->uart.nr	= 2;
+		s->freq_std	= 14745600;
+		s->addr_mask	= 0x7f;
+		s->flags	= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
+		fifosize	= 256;
+		freq_min	= 1000000;
+		freq_max	= 50000000;
+		break;
+	case SCCNXP_TYPE_SC68681:
+		s->name		= "SC68681";
+		s->uart.nr	= 2;
+		s->freq_std	= 3686400;
+		s->addr_mask	= 0x0f;
+		s->flags	= SCCNXP_HAVE_IO;
+		fifosize	= 3;
+		freq_min	= 1000000;
+		freq_max	= 4000000;
+		break;
+	case SCCNXP_TYPE_SC68692:
+		s->name		= "SC68692";
+		s->uart.nr	= 2;
+		s->freq_std	= 3686400;
+		s->addr_mask	= 0x0f;
+		s->flags	= SCCNXP_HAVE_IO;
+		fifosize	= 3;
+		freq_min	= 1000000;
+		freq_max	= 4000000;
+		break;
+	default:
+		dev_err(&pdev->dev, "Unsupported chip type %i\n", chiptype);
+		ret = -ENOTSUPP;
+		goto err_out;
+	}
+
+	if (!pdata) {
+		dev_warn(&pdev->dev,
+			 "No platform data supplied, using defaults\n");
+		s->pdata.frequency = s->freq_std;
+	} else
+		memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata));
+
+	s->irq = platform_get_irq(pdev, 0);
+	if (s->irq <= 0) {
+		dev_err(&pdev->dev, "Missing irq resource data\n");
+		ret = -ENXIO;
+		goto err_out;
+	}
+
+	/* Check input frequency */
+	if ((s->pdata.frequency < freq_min) ||
+	    (s->pdata.frequency > freq_max)) {
+		dev_err(&pdev->dev, "Frequency out of bounds\n");
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	membase = devm_request_and_ioremap(&pdev->dev, res);
+	if (!membase) {
+		dev_err(&pdev->dev, "Failed to ioremap\n");
+		ret = -EIO;
+		goto err_out;
+	}
+
+	s->uart.owner		= THIS_MODULE;
+	s->uart.dev_name	= "ttySC";
+	s->uart.major		= SCCNXP_MAJOR;
+	s->uart.minor		= SCCNXP_MINOR;
+#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
+	s->uart.cons		= &s->console;
+	s->uart.cons->device	= uart_console_device;
+	s->uart.cons->write	= sccnxp_console_write;
+	s->uart.cons->setup	= sccnxp_console_setup;
+	s->uart.cons->flags	= CON_PRINTBUFFER;
+	s->uart.cons->index	= -1;
+	s->uart.cons->data	= s;
+	strcpy(s->uart.cons->name, "ttySC");
+#endif
+	ret = uart_register_driver(&s->uart);
+	if (ret) {
+		dev_err(&pdev->dev, "Registering UART driver failed\n");
+		goto err_out;
+	}
+
+	for (i = 0; i < s->uart.nr; i++) {
+		s->port[i].line		= i;
+		s->port[i].dev		= &pdev->dev;
+		s->port[i].irq		= s->irq;
+		s->port[i].type		= PORT_SC26XX;
+		s->port[i].fifosize	= fifosize;
+		s->port[i].flags	= UPF_SKIP_TEST | UPF_FIXED_TYPE;
+		s->port[i].iotype	= UPIO_MEM;
+		s->port[i].mapbase	= res->start;
+		s->port[i].membase	= membase;
+		s->port[i].regshift	= s->pdata.reg_shift;
+		s->port[i].uartclk	= s->pdata.frequency;
+		s->port[i].ops		= &sccnxp_ops;
+		uart_add_one_port(&s->uart, &s->port[i]);
+		/* Set direction to input */
+		if (s->flags & SCCNXP_HAVE_IO)
+			sccnxp_set_bit(&s->port[i], DIR_OP, 0);
+	}
+
+	/* Disable interrupts */
+	s->imr = 0;
+	sccnxp_write(&s->port[0], SCCNXP_IMR_REG, 0);
+
+	/* Board specific configure */
+	if (s->pdata.init)
+		s->pdata.init();
+
+	ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, sccnxp_ist,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					dev_name(&pdev->dev), s);
+	if (!ret)
+		return 0;
+
+	dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq);
+
+err_out:
+	platform_set_drvdata(pdev, NULL);
+
+	return ret;
+}
+
+static int __devexit sccnxp_remove(struct platform_device *pdev)
+{
+	int i;
+	struct sccnxp_port *s = platform_get_drvdata(pdev);
+
+	devm_free_irq(&pdev->dev, s->irq, s);
+
+	for (i = 0; i < s->uart.nr; i++)
+		uart_remove_one_port(&s->uart, &s->port[i]);
+
+	uart_unregister_driver(&s->uart);
+	platform_set_drvdata(pdev, NULL);
+
+	if (s->pdata.exit)
+		s->pdata.exit();
+
+	return 0;
+}
+
+static const struct platform_device_id sccnxp_id_table[] = {
+	{ "sc2681",	SCCNXP_TYPE_SC2681 },
+	{ "sc2691",	SCCNXP_TYPE_SC2691 },
+	{ "sc2692",	SCCNXP_TYPE_SC2692 },
+	{ "sc2891",	SCCNXP_TYPE_SC2891 },
+	{ "sc2892",	SCCNXP_TYPE_SC2892 },
+	{ "sc28202",	SCCNXP_TYPE_SC28202 },
+	{ "sc68681",	SCCNXP_TYPE_SC68681 },
+	{ "sc68692",	SCCNXP_TYPE_SC68692 },
+};
+MODULE_DEVICE_TABLE(platform, sccnxp_id_table);
+
+static struct platform_driver sccnxp_uart_driver = {
+	.driver = {
+		.name	= SCCNXP_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= sccnxp_probe,
+	.remove		= __devexit_p(sccnxp_remove),
+	.id_table	= sccnxp_id_table,
+};
+module_platform_driver(sccnxp_uart_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("SCCNXP serial driver");
diff --git a/include/linux/platform_data/sccnxp.h b/include/linux/platform_data/sccnxp.h
new file mode 100644
index 0000000..7311ccd
--- /dev/null
+++ b/include/linux/platform_data/sccnxp.h
@@ -0,0 +1,93 @@
+/*
+ *  NXP (Philips) SCC+++(SCN+++) serial driver
+ *
+ *  Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
+ *
+ *  Based on sc26xx.c, by Thomas Bogendörfer (tsbogend@alpha.franken.de)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ */
+
+#ifndef __SCCNXP_H
+#define __SCCNXP_H
+
+#define SCCNXP_MAX_UARTS	2
+
+/* Output lines */
+#define LINE_OP0		1
+#define LINE_OP1		2
+#define LINE_OP2		3
+#define LINE_OP3		4
+#define LINE_OP4		5
+#define LINE_OP5		6
+#define LINE_OP6		7
+#define LINE_OP7		8
+
+/* Input lines */
+#define LINE_IP0		9
+#define LINE_IP1		10
+#define LINE_IP2		11
+#define LINE_IP3		12
+#define LINE_IP4		13
+#define LINE_IP5		14
+#define LINE_IP6		15
+
+/* Signals */
+#define DTR_OP			0	/* DTR */
+#define RTS_OP			4	/* RTS */
+#define DSR_IP			8	/* DSR */
+#define CTS_IP			12	/* CTS */
+#define DCD_IP			16	/* DCD */
+#define RNG_IP			20	/* RNG */
+
+#define DIR_OP			24	/* Special signal for control RS-485.
+					 * Goes high when transmit,
+					 * then goes low.
+					 */
+
+/* Routing control signal 'sig' to line 'line' */
+#define MCTRL_SIG(sig, line)	((line) << (sig))
+
+/*
+ * Example board initialization data:
+ *
+ * static struct resource sc2892_resources[] = {
+ *	DEFINE_RES_MEM(UART_PHYS_START, 0x10),
+ *	DEFINE_RES_IRQ(IRQ_EXT2),
+ * };
+ *
+ * static struct sccnxp_pdata sc2892_info = {
+ *	.frequency	= 3686400,
+ *	.mctrl_cfg[0]	= MCTRL_SIG(DIR_OP, LINE_OP0),
+ *	.mctrl_cfg[1]	= MCTRL_SIG(DIR_OP, LINE_OP1),
+ * };
+ *
+ * static struct platform_device sc2892 = {
+ *	.name		= "sc2892",
+ *	.id		= -1,
+ *	.resource	= sc2892_resources,
+ *	.num_resources	= ARRAY_SIZE(sc2892_resources),
+ *	.dev = {
+ *		.platform_data	= &sc2892_info,
+ *	},
+ * };
+ */
+
+/* SCCNXP platform data structure */
+struct sccnxp_pdata {
+	/* Frequency (extrenal clock or crystal) */
+	int			frequency;
+	/* Shift for A0 line */
+	const u8		reg_shift;
+	/* Modem control lines configuration */
+	const u32		mctrl_cfg[SCCNXP_MAX_UARTS];
+	/* Called during startup */
+	void (*init)(void);
+	/* Called before finish */
+	void (*exit)(void);
+};
+
+#endif
-- 
1.7.8.6

--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [PATCHv4 0/9] *** ARM: Update arch-vt8500 to Devicetree ***
From: Stephen Warren @ 2012-08-25  3:32 UTC (permalink / raw)
  To: Tony Prisk
  Cc: Alessandro Zummo, linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	Russell King, Linus Walleij, Florian Tobias Schandinat,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, Greg Kroah-Hartman,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	vt8500-wm8505-linux-kernel-/JYPxA39Uh5TLH3MbocFFw, Mike Turquette,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Alan Cox
In-Reply-To: <1345707346-9035-1-git-send-email-linux-ci5G2KO2hbZ+pU9mqzGVBQ@public.gmane.org>

On 08/23/2012 01:35 AM, Tony Prisk wrote:
> This patchset updates arch-vt8500 to devicetree and removes all the old-style
> code. Support for WM8650 has also been added.
> 
> Example dts/dtsi files are given for the three currently supported models.
> 
> Major changes:
> 
> GPIO code has been converted to a platform_device and rewritten as WM8505
> support was broken. Add support for WM8650 gpio controller.
> 
> UHCI support was missing. Added this as a generic non-pci uhci controller as
> it doesn't require anything special. Should be usable by any system that doesn't
> have special requirements to get the UHCI controller working.
> 
> Framebuffer code patched to support WM8650. The bindings for this are of concern
> but there doesn't seem to be a formalized binding yet. This patch is based off
> Sascha Hauer's current patch on the dri-devel mailing list and should be easily
> patched out when its finalized.
> 
> Patchset based on Arnd's arm-soc/for-next branch.

I believe all the issues I pointed out are fixed in this series. I'm not
sure I reviewed it in enough detail to ack it, but I'm fine with what I saw.

^ permalink raw reply

* Re: [PATCH v3 22/23] serial: omap: move uart_omap_port definition to C file
From: Tony Lindgren @ 2012-08-24 19:08 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Kevin Hilman, Linux Kernel Mailing List, Santosh Shilimkar,
	linux-serial, Sourav Poddar, Linux OMAP Mailing List,
	Shubhrajyoti Datta, Linux ARM Kernel Mailing List, alan
In-Reply-To: <1345717983-18179-23-git-send-email-balbi@ti.com>

* Felipe Balbi <balbi@ti.com> [120823 03:38]:
> nobody needs to access the uart_omap_port structure
> other than omap-serial.c file. Let's move that
> structure definition to the C source file in order
> to prevent anyone from accessing our structure.
> 
> Tested-by: Shubhrajyoti D <shubhrajyoti@ti.com>
> Signed-off-by: Felipe Balbi <balbi@ti.com>

Acked-by: Tony Lindgren <tony@atomide.com>

^ permalink raw reply

* Re: [PATCH v3 20/23] serial: omap: fix software flow control
From: Tony Lindgren @ 2012-08-24 19:08 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: alan, Kevin Hilman, Linux OMAP Mailing List,
	Linux ARM Kernel Mailing List, linux-serial,
	Linux Kernel Mailing List, Santosh Shilimkar, Shubhrajyoti Datta,
	Sourav Poddar, Vikram Pandita, stable
In-Reply-To: <1345717983-18179-21-git-send-email-balbi@ti.com>

* Felipe Balbi <balbi@ti.com> [120823 03:38]:
> From: Vikram Pandita <vikram.pandita@ti.com>
> 
> Software flow control register bits were not defined correctly.
> 
> Also clarify the IXON and IXOFF logic to reflect what userspace wants.
> 
> Cc: stable@vger.kernel.org
> Tested-by: Shubhrajyoti D <shubhrajyoti@ti.com>
> Signed-off-by: Vikram Pandita <vikram.pandita@ti.com>
> Signed-off-by: Shubhrajyoti D <shubhrajyoti@ti.com>
> Signed-off-by: Felipe Balbi <balbi@ti.com>

Acked-by: Tony Lindgren <tony@atomide.com>

^ permalink raw reply

* Re: [PATCH v3 03/23] serial: omap: don't access the platform_device
From: Tony Lindgren @ 2012-08-24 19:07 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Kevin Hilman, Linux Kernel Mailing List, Santosh Shilimkar,
	linux-serial, Sourav Poddar, Linux OMAP Mailing List,
	Shubhrajyoti Datta, Linux ARM Kernel Mailing List, alan
In-Reply-To: <1345717983-18179-4-git-send-email-balbi@ti.com>

* Felipe Balbi <balbi@ti.com> [120823 03:37]:
> The driver doesn't need to know about its platform_device.
> 
> Everything the driver needs can be done through the
> struct device pointer. In case we need to use the
> OMAP-specific PM function pointers, those can make
> sure to find the device's platform_device pointer
> so they can find the struct omap_device through
> pdev->archdata field.
> 
> Tested-by: Shubhrajyoti D <shubhrajyoti@ti.com>
> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> Signed-off-by: Felipe Balbi <balbi@ti.com>

Acked-by: Tony Lindgren <tony@atomide.com>

^ 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