/* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 2002 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2002, 2006 David S. Miller (davem@davemloft.net)
*
* Rewrote buffer handling to use CIRC(Circular Buffer) macros.
* Maxim Krasnyanskiy <maxk@qualcomm.com>
* Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
*
* Ported to new 2.5.x UART layer.
- * David S. Miller <davem@redhat.com>
+ * David S. Miller <davem@davemloft.net>
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/of_device.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/oplib.h>
-#include <asm/ebus.h>
+#include <asm/prom.h>
-#if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#if defined(CONFIG_SERIAL_SUNSAB_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
unsigned char interrupt_mask1;/* ISR1 masking */
unsigned char pvr_dtr_bit; /* Which PVR bit is DTR */
unsigned char pvr_dsr_bit; /* Which PVR bit is DSR */
+ unsigned int gis_shift;
int type; /* SAB82532 version */
/* Setting configuration bits while the transmitter is active
static struct tty_struct *
receive_chars(struct uart_sunsab_port *up,
- union sab82532_irq_status *stat,
- struct pt_regs *regs)
+ union sab82532_irq_status *stat)
{
struct tty_struct *tty = NULL;
unsigned char buf[32];
int count = 0;
int i;
- if (up->port.info != NULL) /* Unopened serial console */
- tty = up->port.info->tty;
+ if (up->port.state != NULL) /* Unopened serial console */
+ tty = up->port.state->port.tty;
/* Read number of BYTES (Character + Status) available. */
if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {
unsigned char ch = buf[i], flag;
if (tty == NULL) {
- uart_handle_sysrq_char(&up->port, ch, regs);
+ uart_handle_sysrq_char(&up->port, ch);
continue;
}
flag = TTY_FRAME;
}
- if (uart_handle_sysrq_char(&up->port, ch, regs))
+ if (uart_handle_sysrq_char(&up->port, ch))
continue;
if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 &&
static void transmit_chars(struct uart_sunsab_port *up,
union sab82532_irq_status *stat)
{
- struct circ_buf *xmit = &up->port.info->xmit;
+ struct circ_buf *xmit = &up->port.state->xmit;
int i;
if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {
up->port.icount.dsr++;
}
- wake_up_interruptible(&up->port.info->delta_msr_wait);
+ wake_up_interruptible(&up->port.state->port.delta_msr_wait);
}
-static irqreturn_t sunsab_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t sunsab_interrupt(int irq, void *dev_id)
{
struct uart_sunsab_port *up = dev_id;
struct tty_struct *tty;
union sab82532_irq_status status;
unsigned long flags;
+ unsigned char gis;
spin_lock_irqsave(&up->port.lock, flags);
status.stat = 0;
- if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA0)
+ gis = readb(&up->regs->r.gis) >> up->gis_shift;
+ if (gis & 1)
status.sreg.isr0 = readb(&up->regs->r.isr0);
- if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA1)
+ if (gis & 2)
status.sreg.isr1 = readb(&up->regs->r.isr1);
tty = NULL;
if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) ||
(status.sreg.isr1 & SAB82532_ISR1_BRK))
- tty = receive_chars(up, &status, regs);
+ tty = receive_chars(up, &status);
if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
(status.sreg.isr1 & SAB82532_ISR1_CSC))
check_status(up, &status);
transmit_chars(up, &status);
}
- spin_unlock(&up->port.lock);
-
- if (tty)
- tty_flip_buffer_push(tty);
-
- up++;
-
- spin_lock(&up->port.lock);
-
- status.stat = 0;
- if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB0)
- status.sreg.isr0 = readb(&up->regs->r.isr0);
- if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB1)
- status.sreg.isr1 = readb(&up->regs->r.isr1);
-
- tty = NULL;
- if (status.stat) {
- if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
- SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) ||
- (status.sreg.isr1 & SAB82532_ISR1_BRK))
-
- tty = receive_chars(up, &status, regs);
- if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
- (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
- check_status(up, &status);
- if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
- transmit_chars(up, &status);
- }
-
spin_unlock_irqrestore(&up->port.lock, flags);
if (tty)
static void sunsab_start_tx(struct uart_port *port)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
- struct circ_buf *xmit = &up->port.info->xmit;
+ struct circ_buf *xmit = &up->port.state->xmit;
int i;
up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR);
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
unsigned char tmp;
+ int err = request_irq(up->port.irq, sunsab_interrupt,
+ IRQF_SHARED, "sab", up);
+ if (err)
+ return err;
spin_lock_irqsave(&up->port.lock, flags);
#endif
spin_unlock_irqrestore(&up->port.lock, flags);
+ free_irq(up->port.irq, up);
}
/*
}
/* port->lock is not held. */
-static void sunsab_set_termios(struct uart_port *port, struct termios *termios,
- struct termios *old)
+static void sunsab_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
static struct uart_driver sunsab_reg = {
.owner = THIS_MODULE,
- .driver_name = "serial",
- .devfs_name = "tts/",
+ .driver_name = "sunsab",
.dev_name = "ttyS",
.major = TTY_MAJOR,
};
static struct uart_sunsab_port *sunsab_ports;
-static int num_channels;
#ifdef CONFIG_SERIAL_SUNSAB_CONSOLE
-static __inline__ void sunsab_console_putchar(struct uart_sunsab_port *up, char c)
+static void sunsab_console_putchar(struct uart_port *port, int c)
{
- unsigned long flags;
-
- spin_lock_irqsave(&up->port.lock, flags);
+ struct uart_sunsab_port *up = (struct uart_sunsab_port *)port;
sunsab_tec_wait(up);
writeb(c, &up->regs->w.tic);
-
- spin_unlock_irqrestore(&up->port.lock, flags);
}
static void sunsab_console_write(struct console *con, const char *s, unsigned n)
{
struct uart_sunsab_port *up = &sunsab_ports[con->index];
- int i;
+ unsigned long flags;
+ int locked = 1;
- for (i = 0; i < n; i++) {
- if (*s == '\n')
- sunsab_console_putchar(up, '\r');
- sunsab_console_putchar(up, *s++);
- }
+ local_irq_save(flags);
+ if (up->port.sysrq) {
+ locked = 0;
+ } else if (oops_in_progress) {
+ locked = spin_trylock(&up->port.lock);
+ } else
+ spin_lock(&up->port.lock);
+
+ uart_console_write(&up->port, s, n, sunsab_console_putchar);
sunsab_tec_wait(up);
+
+ if (locked)
+ spin_unlock(&up->port.lock);
+ local_irq_restore(flags);
}
static int sunsab_console_setup(struct console *con, char *options)
unsigned long flags;
unsigned int baud, quot;
+ /*
+ * The console framework calls us for each and every port
+ * registered. Defer the console setup until the requested
+ * port has been properly discovered. A bit of a hack,
+ * though...
+ */
+ if (up->port.type != PORT_SUNSAB)
+ return -1;
+
printk("Console: ttyS%d (SAB82532)\n",
(sunsab_reg.minor - 64) + con->index);
- sunserial_console_termios(con);
+ sunserial_console_termios(con, to_of_device(up->port.dev)->node);
switch (con->cflag & CBAUD) {
case B150: baud = 150; break;
.index = -1,
.data = &sunsab_reg,
};
-#define SUNSAB_CONSOLE (&sunsab_console)
-static void __init sunsab_console_init(void)
+static inline struct console *SUNSAB_CONSOLE(void)
{
- int i;
-
- if (con_is_present())
- return;
-
- for (i = 0; i < num_channels; i++) {
- int this_minor = sunsab_reg.minor + i;
-
- if ((this_minor - 64) == (serial_console - 1))
- break;
- }
- if (i == num_channels)
- return;
-
- sunsab_console.index = i;
- register_console(&sunsab_console);
+ return &sunsab_console;
}
#else
-#define SUNSAB_CONSOLE (NULL)
+#define SUNSAB_CONSOLE() (NULL)
#define sunsab_console_init() do { } while (0)
#endif
-static void __init for_each_sab_edev(void (*callback)(struct linux_ebus_device *, void *), void *arg)
+static int __devinit sunsab_init_one(struct uart_sunsab_port *up,
+ struct of_device *op,
+ unsigned long offset,
+ int line)
{
- struct linux_ebus *ebus;
- struct linux_ebus_device *edev = NULL;
-
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_name, "se")) {
- callback(edev, arg);
- continue;
- } else if (!strcmp(edev->prom_name, "serial")) {
- char compat[32];
- int clen;
-
- /* On RIO this can be an SE, check it. We could
- * just check ebus->is_rio, but this is more portable.
- */
- clen = prom_getproperty(edev->prom_node, "compatible",
- compat, sizeof(compat));
- if (clen > 0) {
- if (strncmp(compat, "sab82532", 8) == 0) {
- callback(edev, arg);
- continue;
- }
- }
- }
- }
- }
-}
+ up->port.line = line;
+ up->port.dev = &op->dev;
+
+ up->port.mapbase = op->resource[0].start + offset;
+ up->port.membase = of_ioremap(&op->resource[0], offset,
+ sizeof(union sab82532_async_regs),
+ "sab");
+ if (!up->port.membase)
+ return -ENOMEM;
+ up->regs = (union sab82532_async_regs __iomem *) up->port.membase;
-static void __init sab_count_callback(struct linux_ebus_device *edev, void *arg)
-{
- int *count_p = arg;
+ up->port.irq = op->irqs[0];
- (*count_p)++;
-}
+ up->port.fifosize = SAB82532_XMIT_FIFO_SIZE;
+ up->port.iotype = UPIO_MEM;
-static void __init sab_attach_callback(struct linux_ebus_device *edev, void *arg)
-{
- int *instance_p = arg;
- struct uart_sunsab_port *up;
- unsigned long regs, offset;
- int i;
+ writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc);
- /* Note: ports are located in reverse order */
- regs = edev->resource[0].start;
- offset = sizeof(union sab82532_async_regs);
- for (i = 0; i < 2; i++) {
- up = &sunsab_ports[(*instance_p * 2) + 1 - i];
+ up->port.ops = &sunsab_pops;
+ up->port.type = PORT_SUNSAB;
+ up->port.uartclk = SAB_BASE_BAUD;
- memset(up, 0, sizeof(*up));
- up->regs = ioremap(regs + offset, sizeof(union sab82532_async_regs));
- up->port.irq = edev->irqs[0];
- up->port.fifosize = SAB82532_XMIT_FIFO_SIZE;
- up->port.mapbase = (unsigned long)up->regs;
- up->port.iotype = UPIO_MEM;
+ up->type = readb(&up->regs->r.vstr) & 0x0f;
+ writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr);
+ writeb(0xff, &up->regs->w.pim);
+ if ((up->port.line & 0x1) == 0) {
+ up->pvr_dsr_bit = (1 << 0);
+ up->pvr_dtr_bit = (1 << 1);
+ up->gis_shift = 2;
+ } else {
+ up->pvr_dsr_bit = (1 << 3);
+ up->pvr_dtr_bit = (1 << 2);
+ up->gis_shift = 0;
+ }
+ up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4);
+ writeb(up->cached_pvr, &up->regs->w.pvr);
+ up->cached_mode = readb(&up->regs->rw.mode);
+ up->cached_mode |= SAB82532_MODE_FRTS;
+ writeb(up->cached_mode, &up->regs->rw.mode);
+ up->cached_mode |= SAB82532_MODE_RTS;
+ writeb(up->cached_mode, &up->regs->rw.mode);
- writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc);
+ up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
+ up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
- offset -= sizeof(union sab82532_async_regs);
- }
-
- (*instance_p)++;
+ return 0;
}
-static int __init probe_for_sabs(void)
+static int __devinit sab_probe(struct of_device *op, const struct of_device_id *match)
{
- int this_sab = 0;
+ static int inst;
+ struct uart_sunsab_port *up;
+ int err;
- /* Find device instances. */
- for_each_sab_edev(&sab_count_callback, &this_sab);
- if (!this_sab)
- return -ENODEV;
+ up = &sunsab_ports[inst * 2];
- /* Allocate tables. */
- sunsab_ports = kmalloc(sizeof(struct uart_sunsab_port) * this_sab * 2,
- GFP_KERNEL);
- if (!sunsab_ports)
- return -ENOMEM;
+ err = sunsab_init_one(&up[0], op,
+ 0,
+ (inst * 2) + 0);
+ if (err)
+ goto out;
- num_channels = this_sab * 2;
+ err = sunsab_init_one(&up[1], op,
+ sizeof(union sab82532_async_regs),
+ (inst * 2) + 1);
+ if (err)
+ goto out1;
- this_sab = 0;
- for_each_sab_edev(&sab_attach_callback, &this_sab);
- return 0;
-}
+ sunserial_console_match(SUNSAB_CONSOLE(), op->node,
+ &sunsab_reg, up[0].port.line,
+ false);
-static void __init sunsab_init_hw(void)
-{
- int i;
+ sunserial_console_match(SUNSAB_CONSOLE(), op->node,
+ &sunsab_reg, up[1].port.line,
+ false);
- for (i = 0; i < num_channels; i++) {
- struct uart_sunsab_port *up = &sunsab_ports[i];
-
- up->port.line = i;
- up->port.ops = &sunsab_pops;
- up->port.type = PORT_SUNSAB;
- up->port.uartclk = SAB_BASE_BAUD;
-
- up->type = readb(&up->regs->r.vstr) & 0x0f;
- writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr);
- writeb(0xff, &up->regs->w.pim);
- if (up->port.line == 0) {
- up->pvr_dsr_bit = (1 << 0);
- up->pvr_dtr_bit = (1 << 1);
- } else {
- up->pvr_dsr_bit = (1 << 3);
- up->pvr_dtr_bit = (1 << 2);
- }
- up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4);
- writeb(up->cached_pvr, &up->regs->w.pvr);
- up->cached_mode = readb(&up->regs->rw.mode);
- up->cached_mode |= SAB82532_MODE_FRTS;
- writeb(up->cached_mode, &up->regs->rw.mode);
- up->cached_mode |= SAB82532_MODE_RTS;
- writeb(up->cached_mode, &up->regs->rw.mode);
+ err = uart_add_one_port(&sunsab_reg, &up[0].port);
+ if (err)
+ goto out2;
- up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
- up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
+ err = uart_add_one_port(&sunsab_reg, &up[1].port);
+ if (err)
+ goto out3;
- if (!(up->port.line & 0x01)) {
- if (request_irq(up->port.irq, sunsab_interrupt,
- SA_SHIRQ, "serial(sab82532)", up)) {
- printk("sunsab%d: can't get IRQ %x\n",
- i, up->port.irq);
- continue;
- }
- }
- }
+ dev_set_drvdata(&op->dev, &up[0]);
+
+ inst++;
+
+ return 0;
+
+out3:
+ uart_remove_one_port(&sunsab_reg, &up[0].port);
+out2:
+ of_iounmap(&op->resource[0],
+ up[1].port.membase,
+ sizeof(union sab82532_async_regs));
+out1:
+ of_iounmap(&op->resource[0],
+ up[0].port.membase,
+ sizeof(union sab82532_async_regs));
+out:
+ return err;
}
-static int __init sunsab_init(void)
+static int __devexit sab_remove(struct of_device *op)
{
- int ret = probe_for_sabs();
- int i;
-
- if (ret < 0)
- return ret;
+ struct uart_sunsab_port *up = dev_get_drvdata(&op->dev);
- sunsab_init_hw();
+ uart_remove_one_port(&sunsab_reg, &up[1].port);
+ uart_remove_one_port(&sunsab_reg, &up[0].port);
+ of_iounmap(&op->resource[0],
+ up[1].port.membase,
+ sizeof(union sab82532_async_regs));
+ of_iounmap(&op->resource[0],
+ up[0].port.membase,
+ sizeof(union sab82532_async_regs));
- sunsab_reg.minor = sunserial_current_minor;
- sunsab_reg.nr = num_channels;
- sunsab_reg.cons = SUNSAB_CONSOLE;
+ dev_set_drvdata(&op->dev, NULL);
- ret = uart_register_driver(&sunsab_reg);
- if (ret < 0) {
- int i;
+ return 0;
+}
- for (i = 0; i < num_channels; i++) {
- struct uart_sunsab_port *up = &sunsab_ports[i];
+static const struct of_device_id sab_match[] = {
+ {
+ .name = "se",
+ },
+ {
+ .name = "serial",
+ .compatible = "sab82532",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sab_match);
- if (!(up->port.line & 0x01))
- free_irq(up->port.irq, up);
- iounmap(up->regs);
- }
- kfree(sunsab_ports);
- sunsab_ports = NULL;
+static struct of_platform_driver sab_driver = {
+ .name = "sab",
+ .match_table = sab_match,
+ .probe = sab_probe,
+ .remove = __devexit_p(sab_remove),
+};
- return ret;
+static int __init sunsab_init(void)
+{
+ struct device_node *dp;
+ int err;
+ int num_channels = 0;
+
+ for_each_node_by_name(dp, "se")
+ num_channels += 2;
+ for_each_node_by_name(dp, "serial") {
+ if (of_device_is_compatible(dp, "sab82532"))
+ num_channels += 2;
}
- sunserial_current_minor += num_channels;
-
- sunsab_console_init();
+ if (num_channels) {
+ sunsab_ports = kzalloc(sizeof(struct uart_sunsab_port) *
+ num_channels, GFP_KERNEL);
+ if (!sunsab_ports)
+ return -ENOMEM;
- for (i = 0; i < num_channels; i++) {
- struct uart_sunsab_port *up = &sunsab_ports[i];
+ err = sunserial_register_minors(&sunsab_reg, num_channels);
+ if (err) {
+ kfree(sunsab_ports);
+ sunsab_ports = NULL;
- uart_add_one_port(&sunsab_reg, &up->port);
+ return err;
+ }
}
- return 0;
+ return of_register_driver(&sab_driver, &of_bus_type);
}
static void __exit sunsab_exit(void)
{
- int i;
-
- for (i = 0; i < num_channels; i++) {
- struct uart_sunsab_port *up = &sunsab_ports[i];
-
- uart_remove_one_port(&sunsab_reg, &up->port);
-
- if (!(up->port.line & 0x01))
- free_irq(up->port.irq, up);
- iounmap(up->regs);
+ of_unregister_driver(&sab_driver);
+ if (sunsab_reg.nr) {
+ sunserial_unregister_minors(&sunsab_reg, sunsab_reg.nr);
}
- sunserial_current_minor -= num_channels;
- uart_unregister_driver(&sunsab_reg);
-
kfree(sunsab_ports);
sunsab_ports = NULL;
}