2009-11-21 13:39:54

by Martin Fuzzey

[permalink] [raw]
Subject: [RFC PATCH 0/9] Serial : sc26xx - add SC2892 support

This patch series adds support for the SC2892 DUART.

This chip is quite similar to the SC2692 but has more baud rates
and bigger FIFOs.

The driver should probably be renamed (sc2xxx ??) but I haven't
done that yet to keep the patches readable.

I also intend to add support for the 28202 chip.

Unfortunately not having SC2692 hardware I haven't been able
to regression test this.


2009-11-21 13:45:20

by Martin Fuzzey

[permalink] [raw]
Subject: [RFC PATCH 1/9] Serial: Enable build of sc26xx driver for embedded platforms



Signed-off-by: Martin Fuzzey <[email protected]>

---

drivers/serial/Kconfig | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index e522572..d9a98d4 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1395,7 +1395,7 @@ config SERIAL_QE

config SERIAL_SC26XX
tristate "SC2681/SC2692 serial port support"
- depends on SNI_RM
+ depends on SNI_RM || EMBEDDED
select SERIAL_CORE
help
This is a driver for the onboard serial ports of

2009-11-21 13:40:00

by Martin Fuzzey

[permalink] [raw]
Subject: [RFC PATCH 2/9] Serial: sc26xx fix build errors.

Add missing include.
Use __devexit rather than __exit to allow building in kernel.

Signed-off-by: Martin Fuzzey <[email protected]>

---

drivers/serial/sc26xx.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c
index 75038ad..dc09682 100644
--- a/drivers/serial/sc26xx.c
+++ b/drivers/serial/sc26xx.c
@@ -20,6 +20,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
+#include <linux/io.h>

#if defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
@@ -709,7 +710,7 @@ out_free_port:
}


-static int __exit sc26xx_driver_remove(struct platform_device *dev)
+static int __devexit sc26xx_driver_remove(struct platform_device *dev)
{
struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev);

2009-11-21 13:43:11

by Martin Fuzzey

[permalink] [raw]
Subject: [RFC PATCH 3/9] Serial: sc26xx - use interrupt trigger flags from resources.

Allows platform resources to specify interrupt flags.

Signed-off-by: Martin Fuzzey <[email protected]>

---

drivers/serial/sc26xx.c | 11 ++++++++---
1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c
index dc09682..ca74bf6 100644
--- a/drivers/serial/sc26xx.c
+++ b/drivers/serial/sc26xx.c
@@ -631,7 +631,7 @@ static void __devinit sc26xx_init_masks(struct uart_sc26xx_port *up,

static int __devinit sc26xx_probe(struct platform_device *dev)
{
- struct resource *res;
+ struct resource *res, *irq_res;
struct uart_sc26xx_port *up;
unsigned int *sc26xx_data = dev->dev.platform_data;
int err;
@@ -640,6 +640,10 @@ static int __devinit sc26xx_probe(struct platform_device *dev)
if (!res)
return -ENODEV;

+ irq_res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+ if (!irq_res)
+ return -ENXIO;
+
up = kzalloc(sizeof *up, GFP_KERNEL);
if (unlikely(!up))
return -ENOMEM;
@@ -652,7 +656,7 @@ static int __devinit sc26xx_probe(struct platform_device *dev)
up->port[0].mapbase = res->start;
up->port[0].membase = ioremap_nocache(up->port[0].mapbase, 0x40);
up->port[0].iotype = UPIO_MEM;
- up->port[0].irq = platform_get_irq(dev, 0);
+ up->port[0].irq = irq_res->start;

up->port[0].dev = &dev->dev;

@@ -688,7 +692,8 @@ static int __devinit sc26xx_probe(struct platform_device *dev)
if (err)
goto out_remove_port0;

- err = request_irq(up->port[0].irq, sc26xx_interrupt, 0, "sc26xx", up);
+ err = request_irq(up->port[0].irq, sc26xx_interrupt,
+ irq_res->flags & IRQF_TRIGGER_MASK, "sc26xx", up);
if (err)
goto out_remove_ports;

2009-11-21 13:41:37

by Martin Fuzzey

[permalink] [raw]
Subject: [RFC PATCH 4/9] Serial: sc26xx - add accessor function for driver data.

Extract container_of and port -= line to a helper function
to avoid slightly opaque modification of port pointer.

Signed-off-by: Martin Fuzzey <[email protected]>

---

drivers/serial/sc26xx.c | 31 ++++++++++++-------------------
1 files changed, 12 insertions(+), 19 deletions(-)

diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c
index ca74bf6..dc47d4f 100644
--- a/drivers/serial/sc26xx.c
+++ b/drivers/serial/sc26xx.c
@@ -95,6 +95,12 @@ struct uart_sc26xx_port {
#define IMR_RXRDY (1 << 1)
#define IMR_TXRDY (1 << 0)

+static struct uart_sc26xx_port *driver_info(struct uart_port *port)
+{
+ port -= port->line;
+ return container_of(port, struct uart_sc26xx_port, port[0]);
+}
+
/* access port register */
static inline u8 read_sc_port(struct uart_port *p, u8 reg)
{
@@ -111,25 +117,17 @@ static inline void write_sc_port(struct uart_port *p, u8 reg, u8 val)

static void sc26xx_enable_irq(struct uart_port *port, int mask)
{
- struct uart_sc26xx_port *up;
- int line = port->line;
-
- port -= line;
- up = container_of(port, struct uart_sc26xx_port, port[0]);
+ struct uart_sc26xx_port *up = driver_info(port);

- up->imr |= mask << (line * 4);
+ up->imr |= mask << (port->line * 4);
WRITE_SC(port, IMR, up->imr);
}

static void sc26xx_disable_irq(struct uart_port *port, int mask)
{
- struct uart_sc26xx_port *up;
- int line = port->line;
-
- port -= line;
- up = container_of(port, struct uart_sc26xx_port, port[0]);
+ struct uart_sc26xx_port *up = driver_info(port);

- up->imr &= ~(mask << (line * 4));
+ up->imr &= ~(mask << (port->line * 4));
WRITE_SC(port, IMR, up->imr);
}

@@ -257,12 +255,9 @@ static unsigned int sc26xx_tx_empty(struct uart_port *port)
/* port->lock held by caller. */
static void sc26xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- struct uart_sc26xx_port *up;
+ struct uart_sc26xx_port *up = driver_info(port);
int line = port->line;

- port -= line;
- up = container_of(port, struct uart_sc26xx_port, port[0]);
-
if (up->dtr_mask[line]) {
if (mctrl & TIOCM_DTR)
WRITE_SC(port, OPR_SET, up->dtr_mask[line]);
@@ -280,13 +275,11 @@ static void sc26xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
/* port->lock is held by caller and interrupts are disabled. */
static unsigned int sc26xx_get_mctrl(struct uart_port *port)
{
- struct uart_sc26xx_port *up;
+ struct uart_sc26xx_port *up = driver_info(port);
int line = port->line;
unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
u8 ipr;

- port -= line;
- up = container_of(port, struct uart_sc26xx_port, port[0]);
ipr = READ_SC(port, IPR) ^ 0xff;

if (up->dsr_mask[line]) {

2009-11-21 13:40:15

by Martin Fuzzey

[permalink] [raw]
Subject: [RFC PATCH 5/9] Serial: sc26xx - refactor register access

Add line number based register accessor functions.
Add command function which implements the inter command
delay required by datasheet.

Signed-off-by: Martin Fuzzey <[email protected]>

---

drivers/serial/sc26xx.c | 44 +++++++++++++++++++++++++++++++++++---------
1 files changed, 35 insertions(+), 9 deletions(-)

diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c
index dc47d4f..1b47443 100644
--- a/drivers/serial/sc26xx.c
+++ b/drivers/serial/sc26xx.c
@@ -102,19 +102,45 @@ static struct uart_sc26xx_port *driver_info(struct uart_port *port)
}

/* access port register */
+static inline u8 read_sc_line(struct uart_port *p, int line, u8 reg)
+{
+ return readb(p->membase + line * 0x20 + reg);
+}
+
static inline u8 read_sc_port(struct uart_port *p, u8 reg)
{
- return readb(p->membase + p->line * 0x20 + reg);
+ return read_sc_line(p, p->line, reg);
+}
+
+static inline void write_sc_line(struct uart_port *p, int line, u8 reg, u8 val)
+{
+ writeb(val, p->membase + line * 0x20 + reg);
}

static inline void write_sc_port(struct uart_port *p, u8 reg, u8 val)
{
- writeb(val, p->membase + p->line * 0x20 + reg);
+ write_sc_line(p, p->line, reg, val);
}

#define READ_SC_PORT(p, r) read_sc_port(p, RD_PORT_##r)
#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v)

+static void write_cmd_line(struct uart_port *port, int line, u8 cmd)
+{
+ int i;
+
+ write_sc_line(port, line, WR_PORT_CR, cmd);
+
+ /* According to data sheet require 3 NOPs before next command */
+ for (i = 0; i < 3; i++)
+ write_sc_line(port, line, WR_PORT_CR, 0);
+}
+
+static inline void write_cmd_port(struct uart_port *port, u8 cmd)
+{
+ write_cmd_line(port, port->line, cmd);
+}
+
static void sc26xx_enable_irq(struct uart_port *port, int mask)
{
struct uart_sc26xx_port *up = driver_info(port);
@@ -337,9 +363,9 @@ static void sc26xx_enable_ms(struct uart_port *port)
static void sc26xx_break_ctl(struct uart_port *port, int break_state)
{
if (break_state == -1)
- WRITE_SC_PORT(port, CR, CR_STRT_BRK);
+ write_cmd_port(port, CR_STRT_BRK);
else
- WRITE_SC_PORT(port, CR, CR_STOP_BRK);
+ write_cmd_port(port, CR_STOP_BRK);
}

/* port->lock is not held. */
@@ -349,8 +375,8 @@ static int sc26xx_startup(struct uart_port *port)
WRITE_SC(port, OPCR, 0);

/* reset tx and rx */
- WRITE_SC_PORT(port, CR, CR_RES_RX);
- WRITE_SC_PORT(port, CR, CR_RES_TX);
+ write_cmd_port(port, CR_RES_RX);
+ write_cmd_port(port, CR_RES_TX);

/* start rx/tx */
WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX);
@@ -464,7 +490,7 @@ static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,
break;
}

- WRITE_SC_PORT(port, CR, CR_RES_MR);
+ write_cmd_port(port, CR_RES_MR);
WRITE_SC_PORT(port, MRx, mr1);
WRITE_SC_PORT(port, MRx, mr2);

@@ -472,8 +498,8 @@ static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,
WRITE_SC_PORT(port, CSR, csr);

/* reset tx and rx */
- WRITE_SC_PORT(port, CR, CR_RES_RX);
- WRITE_SC_PORT(port, CR, CR_RES_TX);
+ write_cmd_port(port, CR_RES_RX);
+ write_cmd_port(port, CR_RES_TX);

WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX);
while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc)

2009-11-21 13:41:14

by Martin Fuzzey

[permalink] [raw]
Subject: [RFC PATCH 6/9] Serial: sc26xx simplify timeout calculation

We already have the baud rate - no need to calculate it again.

Signed-off-by: Martin Fuzzey <[email protected]>

---

drivers/serial/sc26xx.c | 5 +----
1 files changed, 1 insertions(+), 4 deletions(-)

diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c
index 1b47443..316d110 100644
--- a/drivers/serial/sc26xx.c
+++ b/drivers/serial/sc26xx.c
@@ -401,7 +401,6 @@ static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
- unsigned int quot = uart_get_divisor(port, baud);
unsigned int iflag, cflag;
unsigned long flags;
u8 mr1, mr2, csr;
@@ -505,9 +504,7 @@ static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,
while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc)
udelay(2);

- /* XXX */
- uart_update_timeout(port, cflag,
- (port->uartclk / (16 * quot)));
+ uart_update_timeout(port, cflag, baud);

spin_unlock_irqrestore(&port->lock, flags);
}

2009-11-21 13:40:22

by Martin Fuzzey

[permalink] [raw]
Subject: [RFC PATCH 7/9] Serial: sc26xx - simplify port initialisation.

Just use a memcpy

Signed-off-by: Martin Fuzzey <[email protected]>

---

drivers/serial/sc26xx.c | 11 +----------
1 files changed, 1 insertions(+), 10 deletions(-)

diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c
index 316d110..69b7ae7 100644
--- a/drivers/serial/sc26xx.c
+++ b/drivers/serial/sc26xx.c
@@ -680,17 +680,8 @@ static int __devinit sc26xx_probe(struct platform_device *dev)

sc26xx_port = &up->port[0];

+ memcpy(&up->port[1], &up->port[0], sizeof(up->port[0]));
up->port[1].line = 1;
- up->port[1].ops = &sc26xx_ops;
- up->port[1].type = PORT_SC26XX;
- up->port[1].uartclk = (29491200 / 16); /* arbitrary */
-
- up->port[1].mapbase = up->port[0].mapbase;
- up->port[1].membase = up->port[0].membase;
- up->port[1].iotype = UPIO_MEM;
- up->port[1].irq = up->port[0].irq;
-
- up->port[1].dev = &dev->dev;

sc26xx_init_masks(up, 1, sc26xx_data[1]);

2009-11-21 13:40:26

by Martin Fuzzey

[permalink] [raw]
Subject: [RFC PATCH 8/9] Serial: sc26xx - add support for SC2892 chips



Signed-off-by: Martin Fuzzey <[email protected]>

---

drivers/serial/sc26xx.c | 238 ++++++++++++++++++++++++++++++++++++++---------
1 files changed, 193 insertions(+), 45 deletions(-)

diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c
index 69b7ae7..6764082 100644
--- a/drivers/serial/sc26xx.c
+++ b/drivers/serial/sc26xx.c
@@ -1,7 +1,8 @@
/*
- * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices.
+ * SC26xx.c: Serial driver for Philips SC2681/SC2692/SC2892 devices.
*
* Copyright (C) 2006,2007 Thomas Bogendörfer ([email protected])
+ * SC2892 support by Martin Fuzzey ([email protected])
*/

#include <linux/module.h>
@@ -32,15 +33,29 @@
#define SC26XX_MINOR_START 205
#define SC26XX_NR 2

+struct chip_def {
+ u8 has_mr0;
+ u8 init_mr0;
+ u8 init_mr1;
+ u8 max_baud_mode;
+ u8 default_baud_mode;
+ u8 default_baud_group;
+ const char *name;
+};
+
struct uart_sc26xx_port {
struct uart_port port[2];
- u8 dsr_mask[2];
- u8 cts_mask[2];
- u8 dcd_mask[2];
- u8 ri_mask[2];
- u8 dtr_mask[2];
- u8 rts_mask[2];
- u8 imr;
+ u8 dsr_mask[2];
+ u8 cts_mask[2];
+ u8 dcd_mask[2];
+ u8 ri_mask[2];
+ u8 dtr_mask[2];
+ u8 rts_mask[2];
+ u8 imr;
+ u8 acr;
+ u8 mr0_baud;
+ const unsigned int *baud_rates;
+ const struct chip_def *chip;
};

/* register common to both ports */
@@ -80,6 +95,7 @@ struct uart_sc26xx_port {
#define CR_RES_TX (3 << 4)
#define CR_STRT_BRK (6 << 4)
#define CR_STOP_BRK (7 << 4)
+#define CR_SEL_MR0 (11 << 4)
#define CR_DIS_TX (1 << 3)
#define CR_ENA_TX (1 << 2)
#define CR_DIS_RX (1 << 1)
@@ -95,6 +111,66 @@ struct uart_sc26xx_port {
#define IMR_RXRDY (1 << 1)
#define IMR_TXRDY (1 << 0)

+/* MR0 bits (>= 28L92 only */
+#define MR0_WATCHDOG (1 << 7)
+#define MR0_RXINT2 (1 << 6)
+#define MR0_TXINT(x) ((x) << 4)
+#define MR0_FIFO_SIZE (1 << 3)
+#define MR0_BAUD_MODE ((1 << 2) + (1 << 0))
+
+
+#define NR_BAUD_GROUPS 2
+#define NR_BAUD_MODES 3
+#define NR_BAUDS 13
+#define DEFAULT_BAUD_INDEX 11
+static const unsigned baud_rates[NR_BAUD_MODES][NR_BAUD_GROUPS][NR_BAUDS] = {
+ {
+ /* Normal mode ACR[7] = 0 */
+ {50, 110, 134, 200, 300, 600, 1200, 1050,
+ 2400, 4800, 7200, 9600, 38400},
+
+ /* Normal mode ACR[7] = 1 */
+ {75, 110, 134, 150, 300, 600, 1200, 2000,
+ 2400, 4800, 1800, 9600, 19200},
+ }, {
+ /* Extended mode 1 ACR[7] = 0 */
+ {300, 110, 134, 1200, 1800, 3600, 7200, 1050,
+ 14400, 28800, 7200, 57600, 230400},
+
+ /* Extended mode 1 ACR[7] = 1 */
+ {450, 110, 134, 900, 1800, 3600, 72000, 2000,
+ 14400, 28800, 1800, 57600, 115200},
+ }, {
+ /* Extended mode 2 ACR[7] = 0 */
+ {4800, 880, 1076, 19200, 28800, 57600, 115200, 1050,
+ 57600, 4800, 57600, 9600, 38400},
+
+ /* Extended mode 2 ACR[7] = 1 */
+ {7200, 880, 1076, 14400, 28800, 57600, 115200, 2000,
+ 57600, 4800, 14400, 9600, 19200},
+ }
+};
+
+static struct chip_def chip_26xx = {
+ .name = "SC26XX",
+ .max_baud_mode = 0,
+ .default_baud_mode = 0,
+ .default_baud_group = 1,
+ .has_mr0 = 0,
+ .init_mr1 = 0,
+};
+
+static struct chip_def chip_2892 = {
+ .name = "SC2892",
+ .max_baud_mode = 2,
+ .default_baud_mode = 2,
+ .default_baud_group = 0,
+ .has_mr0 = 1,
+ .init_mr0 = MR0_WATCHDOG | MR0_FIFO_SIZE | MR0_TXINT(1),
+ .init_mr1 = (1 << 6),
+};
+
+
static struct uart_sc26xx_port *driver_info(struct uart_port *port)
{
port -= port->line;
@@ -141,6 +217,19 @@ static inline void write_cmd_port(struct uart_port *port, u8 cmd)
write_cmd_line(port, port->line, cmd);
}

+
+static u8 read_mr0(struct uart_port *port, int line)
+{
+ write_cmd_line(port, line, CR_SEL_MR0);
+ return read_sc_line(port, line, RD_PORT_MRx);
+}
+
+static void write_mr0(struct uart_port *port, int line, u8 val)
+{
+ write_cmd_line(port, line, CR_SEL_MR0);
+ write_sc_line(port, line, WR_PORT_MRx, val);
+}
+
static void sc26xx_enable_irq(struct uart_port *port, int mask)
{
struct uart_sc26xx_port *up = driver_info(port);
@@ -371,6 +460,8 @@ static void sc26xx_break_ctl(struct uart_port *port, int break_state)
/* port->lock is not held. */
static int sc26xx_startup(struct uart_port *port)
{
+ struct uart_sc26xx_port *up = driver_info(port);
+
sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
WRITE_SC(port, OPCR, 0);

@@ -378,6 +469,9 @@ static int sc26xx_startup(struct uart_port *port)
write_cmd_port(port, CR_RES_RX);
write_cmd_port(port, CR_RES_TX);

+ if (up->chip->has_mr0)
+ write_mr0(port, port->line, up->chip->init_mr0);
+
/* start rx/tx */
WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX);

@@ -396,10 +490,32 @@ static void sc26xx_shutdown(struct uart_port *port)
WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX);
}

+static u8 find_csr(struct uart_sc26xx_port *up, unsigned int baud)
+{
+ int i, found = -1;
+
+ for (i = 0; i < NR_BAUDS; i++) {
+ if (up->baud_rates[i] == baud) {
+ found = i;
+ break;
+ }
+ }
+
+ if (found == -1) {
+ found = DEFAULT_BAUD_INDEX;
+ dev_warn(up->port[0].dev,
+ "%d baud not available using %d\n",
+ baud, up->baud_rates[found]);
+ }
+
+ return (u8)found | ((u8)found << 4);
+}
+
/* port->lock is not held. */
static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
+ struct uart_sc26xx_port *up = driver_info(port);
unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
unsigned int iflag, cflag;
unsigned long flags;
@@ -452,48 +568,21 @@ static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,
} else
mr1 |= (2 << 3);

- switch (baud) {
- case 50:
- csr = 0x00;
- break;
- case 110:
- csr = 0x11;
- break;
- case 134:
- csr = 0x22;
- break;
- case 200:
- csr = 0x33;
- break;
- case 300:
- csr = 0x44;
- break;
- case 600:
- csr = 0x55;
- break;
- case 1200:
- csr = 0x66;
- break;
- case 2400:
- csr = 0x88;
- break;
- case 4800:
- csr = 0x99;
- break;
- default:
- case 9600:
- csr = 0xbb;
- break;
- case 19200:
- csr = 0xcc;
- break;
+ mr1 |= up->chip->init_mr1;
+ csr = find_csr(up, baud);
+
+ if (up->chip->has_mr0) {
+ u8 mr0a = read_mr0(port, 0);
+ mr0a &= ~MR0_BAUD_MODE;
+ mr0a |= up->mr0_baud;
+ write_mr0(port, 0, mr0a);
}

write_cmd_port(port, CR_RES_MR);
WRITE_SC_PORT(port, MRx, mr1);
WRITE_SC_PORT(port, MRx, mr2);

- WRITE_SC(port, ACR, 0x80);
+ WRITE_SC(port, ACR, up->acr);
WRITE_SC_PORT(port, CSR, csr);

/* reset tx and rx */
@@ -511,7 +600,7 @@ static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,

static const char *sc26xx_type(struct uart_port *port)
{
- return "SC26XX";
+ return driver_info(port)->chip->name;
}

static void sc26xx_release_port(struct uart_port *port)
@@ -645,6 +734,60 @@ static void __devinit sc26xx_init_masks(struct uart_sc26xx_port *up,
up->ri_mask[line] = sc26xx_flags2mask(data, 20);
}

+static int __devinit init_baudgroup(struct uart_sc26xx_port *up)
+{
+ u8 baud_mode = up->chip->default_baud_mode;
+ u8 baud_group = up->chip->default_baud_group;
+
+ if (baud_mode > up->chip->max_baud_mode) {
+ printk(KERN_ERR "Baud mode %d not supported for %s\n",
+ baud_mode, up->chip->name);
+ return -EINVAL;
+ }
+
+ up->acr = baud_group ? 0x80 : 0;
+ up->baud_rates = baud_rates[baud_mode][baud_group];
+
+ switch (baud_mode) {
+ case 0:
+ up->mr0_baud = 0;
+ break;
+ case 1:
+ up->mr0_baud = 1;
+ break;
+ case 2:
+ up->mr0_baud = 4;
+ break;
+ }
+ return 0;
+}
+
+static const struct chip_def * __devinit detect_chip_type(
+ struct uart_sc26xx_port *up)
+{
+ struct uart_port *port = &up->port[0];
+ struct chip_def *chip;
+ int i;
+ u8 mrx[3];
+
+ /* Code below needs to be tested on 26xx hardware
+ * Idea is that 26xx will ignore SEL_MR0 so 2nd and 3rd reads
+ * will return same data.*/
+ write_cmd_port(port, CR_RES_MR); /* MR1 */
+ write_cmd_port(port, CR_SEL_MR0); /* Ignored on 26xx */
+
+ for (i = 0; i < 3; i++)
+ mrx[i] = READ_SC_PORT(port, MRx);
+
+ if (mrx[1] == mrx[2])
+ chip = &chip_26xx;
+ else
+ chip = &chip_2892;
+
+ dev_info(port->dev, "Autodetected %s\n", chip->name);
+ return chip;
+}
+
static int __devinit sc26xx_probe(struct platform_device *dev)
{
struct resource *res, *irq_res;
@@ -685,6 +828,11 @@ static int __devinit sc26xx_probe(struct platform_device *dev)

sc26xx_init_masks(up, 1, sc26xx_data[1]);

+ up->chip = detect_chip_type(up);
+ err = init_baudgroup(up);
+ if (err)
+ goto out_free_port;
+
err = uart_register_driver(&sc26xx_reg);
if (err)
goto out_free_port;

2009-11-21 13:40:30

by Martin Fuzzey

[permalink] [raw]
Subject: [RFC PATCH 9/9] Serial: sc26xx - introduce a structure for platform data.

Encapsulates the existing array of uint for signal mapping
and adds baudrate group selection.

Signed-off-by: Martin Fuzzey <[email protected]>

---

arch/mips/sni/a20r.c | 15 ++++++++----
drivers/serial/sc26xx.c | 49 +++++++++++++++++++++++++++++++++++----
include/linux/serial_sc2xxx.h | 52 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 107 insertions(+), 9 deletions(-)
create mode 100644 include/linux/serial_sc2xxx.h

diff --git a/arch/mips/sni/a20r.c b/arch/mips/sni/a20r.c
index 7dd76fb..6fac5b2 100644
--- a/arch/mips/sni/a20r.c
+++ b/arch/mips/sni/a20r.c
@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/serial_8250.h>
+#include <linux/serial_sc2xxx.h>

#include <asm/sni.h>
#include <asm/time.h>
@@ -117,10 +118,14 @@ static struct resource sc26xx_rsrc[] = {
}
};

-static unsigned int sc26xx_data[2] = {
- /* DTR | RTS | DSR | CTS | DCD | RI */
- (8 << 0) | (4 << 4) | (6 << 8) | (0 << 12) | (6 << 16) | (0 << 20),
- (3 << 0) | (2 << 4) | (1 << 8) | (2 << 12) | (3 << 16) | (4 << 20)
+static struct plat_serial_sc2xxx sc26xx_data = {
+ .signal_map = {
+ /* DTR | RTS | DSR | CTS | DCD | RI */
+ (8 << 0) | (4 << 4) | (6 << 8) | (0 << 12) | (6 << 16) | (0 << 20),
+ (3 << 0) | (2 << 4) | (1 << 8) | (2 << 12) | (3 << 16) | (4 << 20)
+ },
+ .baud_mode = 1,
+ .baud_group = 2,
};

static struct platform_device sc26xx_pdev = {
@@ -128,7 +133,7 @@ static struct platform_device sc26xx_pdev = {
.num_resources = ARRAY_SIZE(sc26xx_rsrc),
.resource = sc26xx_rsrc,
.dev = {
- .platform_data = sc26xx_data,
+ .platform_data = &sc26xx_data,
}
};

diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c
index 6764082..842692f 100644
--- a/drivers/serial/sc26xx.c
+++ b/drivers/serial/sc26xx.c
@@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/serial_sc2xxx.h>

#if defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
@@ -170,6 +171,10 @@ static struct chip_def chip_2892 = {
.init_mr1 = (1 << 6),
};

+static const struct chip_def *chips[] = {
+ &chip_26xx,
+ &chip_2892
+};

static struct uart_sc26xx_port *driver_info(struct uart_port *port)
{
@@ -736,9 +741,17 @@ static void __devinit sc26xx_init_masks(struct uart_sc26xx_port *up,

static int __devinit init_baudgroup(struct uart_sc26xx_port *up)
{
+ struct plat_serial_sc2xxx *pdata = up->port[0].dev->platform_data;
+
u8 baud_mode = up->chip->default_baud_mode;
u8 baud_group = up->chip->default_baud_group;

+ if (pdata->baud_mode > 0)
+ baud_mode = pdata->baud_mode - 1;
+
+ if (pdata->baud_group > 0)
+ baud_group = pdata->baud_group - 1;
+
if (baud_mode > up->chip->max_baud_mode) {
printk(KERN_ERR "Baud mode %d not supported for %s\n",
baud_mode, up->chip->name);
@@ -762,6 +775,20 @@ static int __devinit init_baudgroup(struct uart_sc26xx_port *up)
return 0;
}

+static const struct chip_def * __devinit chip_by_name(const char *name)
+{
+ int i;
+ const struct chip_def *chip;
+
+ for (i = 0; i < ARRAY_SIZE(chips); i++) {
+ chip = chips[i];
+ if (!strcmp(chip->name, name))
+ return chip;
+ }
+
+ return NULL;
+}
+
static const struct chip_def * __devinit detect_chip_type(
struct uart_sc26xx_port *up)
{
@@ -792,9 +819,14 @@ static int __devinit sc26xx_probe(struct platform_device *dev)
{
struct resource *res, *irq_res;
struct uart_sc26xx_port *up;
- unsigned int *sc26xx_data = dev->dev.platform_data;
+ struct plat_serial_sc2xxx *pdata = dev->dev.platform_data;
int err;

+ if (!pdata) {
+ dev_err(&dev->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
@@ -819,16 +851,25 @@ static int __devinit sc26xx_probe(struct platform_device *dev)

up->port[0].dev = &dev->dev;

- sc26xx_init_masks(up, 0, sc26xx_data[0]);
+ sc26xx_init_masks(up, 0, pdata->signal_map[0]);

sc26xx_port = &up->port[0];

memcpy(&up->port[1], &up->port[0], sizeof(up->port[0]));
up->port[1].line = 1;

- sc26xx_init_masks(up, 1, sc26xx_data[1]);
+ sc26xx_init_masks(up, 1, pdata->signal_map[1]);
+
+ if (pdata->chip_name)
+ up->chip = chip_by_name(pdata->chip_name);
+ if (!up->chip) {
+ if (pdata->chip_name)
+ dev_warn(&dev->dev,
+ "Unknown chip %s requested - use autodetect\n",
+ pdata->chip_name);
+ up->chip = detect_chip_type(up);
+ }

- up->chip = detect_chip_type(up);
err = init_baudgroup(up);
if (err)
goto out_free_port;
diff --git a/include/linux/serial_sc2xxx.h b/include/linux/serial_sc2xxx.h
new file mode 100644
index 0000000..d93977f
--- /dev/null
+++ b/include/linux/serial_sc2xxx.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright (C) 2009 Martin Fuzzey
+ *
+ * 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 _LINUX_SERIAL_SC26XX_H
+#define _LINUX_SERIAL_SC26XX_H 1
+
+
+/**
+ * struct plat_sc2xxx - Philips UART platform data
+ * @signal_map: Hardware signal mapping (see below)
+ *
+ * @chip_name: One of "SC26xx", "SC2892" or NULL for autodetect
+ *
+ * @baud_mode: Baudrate mode + 1 (1,2,3) must be 1 for SC26xx see
+ * datasheet or baud_rates[] in driver code for available
+ * baudrates in each mode
+ * 0 = use driver default
+ *
+ * @baud_group: Baudrate group + 1 (1, 2) within mode
+ * 0 = use driver default
+ *
+ * For each port signal_map is a bitfield defined as follows:
+ * bits 0-3 DTR
+ * bits 4-7 RTS
+ * bits 8-11 DSR
+ * bits 12-15 CTS
+ * bits 16-19 DCD
+ * bits 20-23 RI
+ *
+ * Value of each field is 1 + corresponding IP/OP pin or 0 if not connected
+ * Eg:
+ * (3 << 0) | (1 << 4) | (3 << 8) | (1 << 12) | (4 << 16) | (0 << 20),
+ * =>
+ * DTR=OP2, RTS=OP0, DSR=IP2, CTS=IP0, DCD=IP3, RI=NC
+ *
+ **/
+struct plat_serial_sc2xxx {
+ unsigned int signal_map[2];
+ const char *chip_name;
+ u8 baud_mode;
+ u8 baud_group;
+};
+
+#endif