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

From: Martin Fuzzey
Date: Sat Nov 21 2009 - 08:40:59 EST




Signed-off-by: Martin Fuzzey <mfuzzey@xxxxxxxxx>

---

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 (tsbogend@xxxxxxxxxxxxxxxx)
+ * SC2892 support by Martin Fuzzey (mfuzzey@xxxxxxxxx)
*/

#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;

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/