* Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
*
* Converted to new 2.5.x UART layer.
- * David S. Miller (davem@redhat.com), 2002-Jul-29
+ * David S. Miller (davem@davemloft.net), 2002-Jul-29
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/oplib.h>
-#include <asm/ebus.h>
-#ifdef CONFIG_SPARC64
-#include <asm/isa.h>
-#endif
+#include <asm/prom.h>
+#include <asm/of_device.h>
#if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
/* Probing information. */
enum su_type su_type;
unsigned int type_probed; /* XXX Stupid */
- int port_node;
- unsigned int irq;
+ unsigned long reg_size;
#ifdef CONFIG_SERIO
struct serio serio;
#endif
};
-#define _INLINE_
-
-static _INLINE_ unsigned int serial_in(struct uart_sunsu_port *up, int offset)
+static unsigned int serial_in(struct uart_sunsu_port *up, int offset)
{
offset <<= up->port.regshift;
switch (up->port.iotype) {
- case SERIAL_IO_HUB6:
+ case UPIO_HUB6:
outb(up->port.hub6 - 1 + offset, up->port.iobase);
return inb(up->port.iobase + 1);
- case SERIAL_IO_MEM:
+ case UPIO_MEM:
return readb(up->port.membase + offset);
default:
}
}
-static _INLINE_ void
-serial_out(struct uart_sunsu_port *up, int offset, int value)
+static void serial_out(struct uart_sunsu_port *up, int offset, int value)
{
#ifndef CONFIG_SPARC64
/*
offset <<= up->port.regshift;
switch (up->port.iotype) {
- case SERIAL_IO_HUB6:
+ case UPIO_HUB6:
outb(up->port.hub6 - 1 + offset, up->port.iobase);
outb(value, up->port.iobase + 1);
break;
- case SERIAL_IO_MEM:
+ case UPIO_MEM:
writeb(value, up->port.membase + offset);
break;
}
#endif /* CONFIG_SERIAL_8250_RSA */
-static void sunsu_stop_tx(struct uart_port *port, unsigned int tty_stop)
+static inline void __stop_tx(struct uart_sunsu_port *p)
+{
+ if (p->ier & UART_IER_THRI) {
+ p->ier &= ~UART_IER_THRI;
+ serial_out(p, UART_IER, p->ier);
+ }
+}
+
+static void sunsu_stop_tx(struct uart_port *port)
{
struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
- if (up->ier & UART_IER_THRI) {
- up->ier &= ~UART_IER_THRI;
- serial_out(up, UART_IER, up->ier);
- }
- if (up->port.type == PORT_16C950 && tty_stop) {
+ __stop_tx(up);
+
+ /*
+ * We really want to stop the transmitter from sending.
+ */
+ if (up->port.type == PORT_16C950) {
up->acr |= UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
}
-static void sunsu_start_tx(struct uart_port *port, unsigned int tty_start)
+static void sunsu_start_tx(struct uart_port *port)
{
struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
up->ier |= UART_IER_THRI;
serial_out(up, UART_IER, up->ier);
}
+
/*
- * We only do this from uart_start
+ * Re-enable the transmitter if we disabled it.
*/
- if (tty_start && up->port.type == PORT_16C950) {
+ if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
up->acr &= ~UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
static void sunsu_stop_rx(struct uart_port *port)
{
struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
- unsigned long flags;
- spin_lock_irqsave(&up->port.lock, flags);
up->ier &= ~UART_IER_RLSI;
up->port.read_status_mask &= ~UART_LSR_DR;
serial_out(up, UART_IER, up->ier);
- spin_unlock_irqrestore(&up->port.lock, flags);
}
static void sunsu_enable_ms(struct uart_port *port)
spin_unlock_irqrestore(&up->port.lock, flags);
}
-static _INLINE_ void
-receive_chars(struct uart_sunsu_port *up, unsigned char *status, struct pt_regs *regs)
+static struct tty_struct *
+receive_chars(struct uart_sunsu_port *up, unsigned char *status)
{
struct tty_struct *tty = up->port.info->tty;
- unsigned char ch;
+ unsigned char ch, flag;
int max_count = 256;
int saw_console_brk = 0;
do {
- if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
- tty->flip.work.func((void *)tty);
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- return; // if TTY_DONT_FLIP is set
- }
ch = serial_inp(up, UART_RX);
- *tty->flip.char_buf_ptr = ch;
- *tty->flip.flag_buf_ptr = TTY_NORMAL;
+ flag = TTY_NORMAL;
up->port.icount.rx++;
if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
}
if (*status & UART_LSR_BI) {
- *tty->flip.flag_buf_ptr = TTY_BREAK;
+ flag = TTY_BREAK;
} else if (*status & UART_LSR_PE)
- *tty->flip.flag_buf_ptr = TTY_PARITY;
+ flag = TTY_PARITY;
else if (*status & UART_LSR_FE)
- *tty->flip.flag_buf_ptr = TTY_FRAME;
+ flag = TTY_FRAME;
}
- if (uart_handle_sysrq_char(&up->port, ch, regs))
+ if (uart_handle_sysrq_char(&up->port, ch))
goto ignore_char;
- if ((*status & up->port.ignore_status_mask) == 0) {
- tty->flip.flag_buf_ptr++;
- tty->flip.char_buf_ptr++;
- tty->flip.count++;
- }
- if ((*status & UART_LSR_OE) &&
- tty->flip.count < TTY_FLIPBUF_SIZE) {
+ if ((*status & up->port.ignore_status_mask) == 0)
+ tty_insert_flip_char(tty, ch, flag);
+ if (*status & UART_LSR_OE)
/*
* Overrun is special, since it's reported
* immediately, and doesn't affect the current
* character.
*/
- *tty->flip.flag_buf_ptr = TTY_OVERRUN;
- tty->flip.flag_buf_ptr++;
- tty->flip.char_buf_ptr++;
- tty->flip.count++;
- }
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
ignore_char:
*status = serial_inp(up, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
- tty_flip_buffer_push(tty);
if (saw_console_brk)
sun_do_break();
+
+ return tty;
}
-static _INLINE_ void transmit_chars(struct uart_sunsu_port *up)
+static void transmit_chars(struct uart_sunsu_port *up)
{
struct circ_buf *xmit = &up->port.info->xmit;
int count;
up->port.x_char = 0;
return;
}
- if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
- sunsu_stop_tx(&up->port, 0);
+ if (uart_tx_stopped(&up->port)) {
+ sunsu_stop_tx(&up->port);
+ return;
+ }
+ if (uart_circ_empty(xmit)) {
+ __stop_tx(up);
return;
}
uart_write_wakeup(&up->port);
if (uart_circ_empty(xmit))
- sunsu_stop_tx(&up->port, 0);
+ __stop_tx(up);
}
-static _INLINE_ void check_modem_status(struct uart_sunsu_port *up)
+static void check_modem_status(struct uart_sunsu_port *up)
{
int status;
wake_up_interruptible(&up->port.info->delta_msr_wait);
}
-static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id)
{
struct uart_sunsu_port *up = dev_id;
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
do {
+ struct tty_struct *tty;
+
status = serial_inp(up, UART_LSR);
+ tty = NULL;
if (status & UART_LSR_DR)
- receive_chars(up, &status, regs);
+ tty = receive_chars(up, &status);
check_modem_status(up);
if (status & UART_LSR_THRE)
transmit_chars(up);
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ if (tty)
+ tty_flip_buffer_push(tty);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
} while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT));
spin_unlock_irqrestore(&up->port.lock, flags);
quot = up->port.uartclk / (16 * new_baud);
- spin_unlock(&up->port.lock);
-
sunsu_change_speed(&up->port, up->cflag, 0, quot);
-
- spin_lock(&up->port.lock);
}
-static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *regs, int is_break)
+static void receive_kbd_ms_chars(struct uart_sunsu_port *up, int is_break)
{
do {
unsigned char ch = serial_inp(up, UART_RX);
/* Stop-A is handled by drivers/char/keyboard.c now. */
if (up->su_type == SU_PORT_KBD) {
#ifdef CONFIG_SERIO
- serio_interrupt(&up->serio, ch, 0, regs);
+ serio_interrupt(&up->serio, ch, 0);
#endif
} else if (up->su_type == SU_PORT_MS) {
int ret = suncore_mouse_baud_detection(ch, is_break);
case 0:
#ifdef CONFIG_SERIO
- serio_interrupt(&up->serio, ch, 0, regs);
+ serio_interrupt(&up->serio, ch, 0);
#endif
break;
};
} while (serial_in(up, UART_LSR) & UART_LSR_DR);
}
-static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id)
{
struct uart_sunsu_port *up = dev_id;
unsigned char status = serial_inp(up, UART_LSR);
if ((status & UART_LSR_DR) || (status & UART_LSR_BI))
- receive_kbd_ms_chars(up, regs,
- (status & UART_LSR_BI) != 0);
+ receive_kbd_ms_chars(up, (status & UART_LSR_BI) != 0);
}
return IRQ_HANDLED;
static unsigned int sunsu_get_mctrl(struct uart_port *port)
{
struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
- unsigned long flags;
unsigned char status;
unsigned int ret;
- spin_lock_irqsave(&up->port.lock, flags);
status = serial_in(up, UART_MSR);
- spin_unlock_irqrestore(&up->port.lock, flags);
ret = 0;
if (status & UART_MSR_DCD)
/*
* Clear the FIFO buffers and disable them.
- * (they will be reeanbled in set_termios())
+ * (they will be reenabled in set_termios())
*/
if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) {
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
* if it is, then bail out, because there's likely no UART
* here.
*/
- if (!(up->port.flags & ASYNC_BUGGY_UART) &&
+ if (!(up->port.flags & UPF_BUGGY_UART) &&
(serial_inp(up, UART_LSR) == 0xff)) {
printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
return -ENODEV;
}
if (up->su_type != SU_PORT_PORT) {
- retval = request_irq(up->irq, sunsu_kbd_ms_interrupt,
- SA_SHIRQ, su_typev[up->su_type], up);
+ retval = request_irq(up->port.irq, sunsu_kbd_ms_interrupt,
+ IRQF_SHARED, su_typev[up->su_type], up);
} else {
- retval = request_irq(up->irq, sunsu_serial_interrupt,
- SA_SHIRQ, su_typev[up->su_type], up);
+ retval = request_irq(up->port.irq, sunsu_serial_interrupt,
+ IRQF_SHARED, su_typev[up->su_type], up);
}
if (retval) {
- printk("su: Cannot register IRQ %d\n", up->irq);
+ printk("su: Cannot register IRQ %d\n", up->port.irq);
return retval;
}
up->ier = UART_IER_RLSI | UART_IER_RDI;
serial_outp(up, UART_IER, up->ier);
- if (up->port.flags & ASYNC_FOURPORT) {
+ if (up->port.flags & UPF_FOURPORT) {
unsigned int icp;
/*
* Enable interrupts on the AST Fourport board
serial_outp(up, UART_IER, 0);
spin_lock_irqsave(&up->port.lock, flags);
- if (up->port.flags & ASYNC_FOURPORT) {
+ if (up->port.flags & UPF_FOURPORT) {
/* reset interrupts on the AST Fourport board */
inb((up->port.iobase & 0xfe0) | 0x1f);
up->port.mctrl |= TIOCM_OUT1;
*/
(void) serial_in(up, UART_RX);
- free_irq(up->irq, up);
+ free_irq(up->port.irq, up);
}
static void
}
static void
-sunsu_set_termios(struct uart_port *port, struct termios *termios,
- struct termios *old)
+sunsu_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
{
unsigned int baud, quot;
#ifdef CONFIG_SERIO
-static spinlock_t sunsu_serio_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(sunsu_serio_lock);
static int sunsu_serio_write(struct serio *serio, unsigned char ch)
{
- struct uart_sunsu_port *up = serio->driver;
+ struct uart_sunsu_port *up = serio->port_data;
unsigned long flags;
int lsr;
static int sunsu_serio_open(struct serio *serio)
{
- struct uart_sunsu_port *up = serio->driver;
+ struct uart_sunsu_port *up = serio->port_data;
unsigned long flags;
int ret;
static void sunsu_serio_close(struct serio *serio)
{
- struct uart_sunsu_port *up = serio->driver;
+ struct uart_sunsu_port *up = serio->port_data;
unsigned long flags;
spin_lock_irqsave(&sunsu_serio_lock, flags);
{
unsigned char status1, status2, scratch, scratch2, scratch3;
unsigned char save_lcr, save_mcr;
- struct linux_ebus_device *dev = 0;
- struct linux_ebus *ebus;
-#ifdef CONFIG_SPARC64
- struct sparc_isa_bridge *isa_br;
- struct sparc_isa_device *isa_dev;
-#endif
-#ifndef CONFIG_SPARC64
- struct linux_prom_registers reg0;
-#endif
unsigned long flags;
- if (!up->port_node || !up->su_type)
+ if (up->su_type == SU_PORT_NONE)
return;
up->type_probed = PORT_UNKNOWN;
- up->port.iotype = SERIAL_IO_MEM;
-
- /*
- * First we look for Ebus-bases su's
- */
- for_each_ebus(ebus) {
- for_each_ebusdev(dev, ebus) {
- if (dev->prom_node == up->port_node) {
- /*
- * The EBus is broken on sparc; it delivers
- * virtual addresses in resources. Oh well...
- * This is correct on sparc64, though.
- */
- up->port.membase = (char *) dev->resource[0].start;
- /*
- * This is correct on both architectures.
- */
- up->port.mapbase = dev->resource[0].start;
- up->irq = dev->irqs[0];
- goto ebus_done;
- }
- }
- }
-
-#ifdef CONFIG_SPARC64
- for_each_isa(isa_br) {
- for_each_isadev(isa_dev, isa_br) {
- if (isa_dev->prom_node == up->port_node) {
- /* Same on sparc64. Cool architecure... */
- up->port.membase = (char *) isa_dev->resource.start;
- up->port.mapbase = isa_dev->resource.start;
- up->irq = isa_dev->irq;
- goto ebus_done;
- }
- }
- }
-#endif
-
-#ifdef CONFIG_SPARC64
- /*
- * Not on Ebus, bailing.
- */
- return;
-#else
- /*
- * Not on Ebus, must be OBIO.
- */
- if (prom_getproperty(up->port_node, "reg",
- (char *)®0, sizeof(reg0)) == -1) {
- prom_printf("sunsu: no \"reg\" property\n");
- return;
- }
- prom_apply_obio_ranges(®0, 1);
- if (reg0.which_io != 0) { /* Just in case... */
- prom_printf("sunsu: bus number nonzero: 0x%x:%x\n",
- reg0.which_io, reg0.phys_addr);
- return;
- }
- up->port.mapbase = reg0.phys_addr;
- if ((up->port.membase = ioremap(reg0.phys_addr, reg0.reg_size)) == 0) {
- prom_printf("sunsu: Cannot map registers.\n");
- return;
- }
-
- /*
- * 0x20 is sun4m thing, Dave Redman heritage.
- * See arch/sparc/kernel/irq.c.
- */
-#define IRQ_4M(n) ((n)|0x20)
-
- /*
- * There is no intr property on MrCoffee, so hardwire it.
- */
- up->irq = IRQ_4M(13);
-#endif
-
-ebus_done:
+ up->port.iotype = UPIO_MEM;
spin_lock_irqsave(&up->port.lock, flags);
- if (!(up->port.flags & ASYNC_BUGGY_UART)) {
+ if (!(up->port.flags & UPF_BUGGY_UART)) {
/*
* Do a simple existence test first; if we fail this, there's
* no point trying anything else.
* manufacturer would be stupid enough to design a board
* that conflicts with COM 1-4 --- we hope!
*/
- if (!(up->port.flags & ASYNC_SKIP_TEST)) {
+ if (!(up->port.flags & UPF_SKIP_TEST)) {
serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
status1 = serial_inp(up, UART_MSR) & 0xF0;
serial_outp(up, UART_MCR, save_mcr);
static struct uart_driver sunsu_reg = {
.owner = THIS_MODULE,
.driver_name = "serial",
- .devfs_name = "tts/",
.dev_name = "ttyS",
.major = TTY_MAJOR,
};
-static int __init sunsu_kbd_ms_init(void)
+static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up)
{
- struct uart_sunsu_port *up;
- int i;
-
- for (i = 0, up = sunsu_ports; i < 2; i++, up++) {
- up->port.line = i;
- up->port.type = PORT_UNKNOWN;
- up->port.uartclk = (SU_BASE_BAUD * 16);
+ int quot, baud;
+#ifdef CONFIG_SERIO
+ struct serio *serio;
+#endif
- if (up->su_type == SU_PORT_KBD)
- up->cflag = B1200 | CS8 | CLOCAL | CREAD;
- else
- up->cflag = B4800 | CS8 | CLOCAL | CREAD;
+ if (up->su_type == SU_PORT_KBD) {
+ up->cflag = B1200 | CS8 | CLOCAL | CREAD;
+ baud = 1200;
+ } else {
+ up->cflag = B4800 | CS8 | CLOCAL | CREAD;
+ baud = 4800;
+ }
+ quot = up->port.uartclk / (16 * baud);
- sunsu_autoconfig(up);
- if (up->port.type == PORT_UNKNOWN)
- continue;
+ sunsu_autoconfig(up);
+ if (up->port.type == PORT_UNKNOWN)
+ return -ENODEV;
- printk(KERN_INFO "su%d at 0x%p (irq = %s) is a %s\n",
- i,
- up->port.membase, __irq_itoa(up->irq),
- sunsu_type(&up->port));
+ printk("%s: %s port at %lx, irq %u\n",
+ to_of_device(up->port.dev)->node->full_name,
+ (up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse",
+ up->port.mapbase, up->port.irq);
#ifdef CONFIG_SERIO
- memset(&up->serio, 0, sizeof(up->serio));
+ serio = &up->serio;
+ serio->port_data = up;
- up->serio.driver = up;
-
- up->serio.type = SERIO_RS232;
- if (up->su_type == SU_PORT_KBD) {
- up->serio.type |= SERIO_SUNKBD;
- up->serio.name = "sukbd";
- } else {
- up->serio.type |= (SERIO_SUN | (1 << 16));
- up->serio.name = "sums";
- }
- up->serio.phys = (i == 0 ? "su/serio0" : "su/serio1");
+ serio->id.type = SERIO_RS232;
+ if (up->su_type == SU_PORT_KBD) {
+ serio->id.proto = SERIO_SUNKBD;
+ strlcpy(serio->name, "sukbd", sizeof(serio->name));
+ } else {
+ serio->id.proto = SERIO_SUN;
+ serio->id.extra = 1;
+ strlcpy(serio->name, "sums", sizeof(serio->name));
+ }
+ strlcpy(serio->phys,
+ (!(up->port.line & 1) ? "su/serio0" : "su/serio1"),
+ sizeof(serio->phys));
- up->serio.write = sunsu_serio_write;
- up->serio.open = sunsu_serio_open;
- up->serio.close = sunsu_serio_close;
+ serio->write = sunsu_serio_write;
+ serio->open = sunsu_serio_open;
+ serio->close = sunsu_serio_close;
+ serio->dev.parent = up->port.dev;
- serio_register_port(&up->serio);
+ serio_register_port(serio);
#endif
- sunsu_startup(&up->port);
- }
+ sunsu_change_speed(&up->port, up->cflag, 0, quot);
+
+ sunsu_startup(&up->port);
return 0;
}
} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
/* Wait up to 1s for flow control if necessary */
- if (up->port.flags & ASYNC_CONS_FLOW) {
+ if (up->port.flags & UPF_CONS_FLOW) {
tmout = 1000000;
while (--tmout &&
((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
}
}
+static void sunsu_console_putchar(struct uart_port *port, int ch)
+{
+ struct uart_sunsu_port *up = (struct uart_sunsu_port *)port;
+
+ wait_for_xmitr(up);
+ serial_out(up, UART_TX, ch);
+}
+
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
{
struct uart_sunsu_port *up = &sunsu_ports[co->index];
unsigned int ier;
- int i;
/*
* First save the UER then disable the interrupts
ier = serial_in(up, UART_IER);
serial_out(up, UART_IER, 0);
- /*
- * Now, do each character
- */
- for (i = 0; i < count; i++, s++) {
- wait_for_xmitr(up);
-
- /*
- * Send the character out.
- * If a LF, also do CR...
- */
- serial_out(up, UART_TX, *s);
- if (*s == 10) {
- wait_for_xmitr(up);
- serial_out(up, UART_TX, 13);
- }
- }
+ uart_console_write(&up->port, s, count, sunsu_console_putchar);
/*
* Finally, wait for transmitter to become empty
* - initialize the serial port
* Return non-zero if we didn't find a serial port.
*/
-static int __init sunsu_console_setup(struct console *co, char *options)
+static int sunsu_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 9600;
.index = -1,
.data = &sunsu_reg,
};
-#define SUNSU_CONSOLE (&sunsu_cons)
/*
* Register console.
*/
-static int __init sunsu_serial_console_init(void)
+static inline struct console *SUNSU_CONSOLE(int num_uart)
{
int i;
if (con_is_present())
- return 0;
+ return NULL;
- for (i = 0; i < UART_NR; i++) {
+ for (i = 0; i < num_uart; i++) {
int this_minor = sunsu_reg.minor + i;
if ((this_minor - 64) == (serial_console - 1))
break;
}
- if (i == UART_NR)
- return 0;
- if (sunsu_ports[i].port_node == 0)
- return 0;
+ if (i == num_uart)
+ return NULL;
sunsu_cons.index = i;
- register_console(&sunsu_cons);
- return 0;
+
+ return &sunsu_cons;
}
#else
-#define SUNSU_CONSOLE (NULL)
+#define SUNSU_CONSOLE(num_uart) (NULL)
#define sunsu_serial_console_init() do { } while (0)
#endif
-static int __init sunsu_serial_init(void)
+static enum su_type __devinit su_get_type(struct device_node *dp)
{
- int instance, ret, i;
-
- /* How many instances do we need? */
- instance = 0;
- for (i = 0; i < UART_NR; i++) {
- struct uart_sunsu_port *up = &sunsu_ports[i];
+ struct device_node *ap = of_find_node_by_path("/aliases");
- if (up->su_type == SU_PORT_MS ||
- up->su_type == SU_PORT_KBD)
- continue;
+ if (ap) {
+ char *keyb = of_get_property(ap, "keyboard", NULL);
+ char *ms = of_get_property(ap, "mouse", NULL);
- up->port.flags |= ASYNC_BOOT_AUTOCONF;
- up->port.type = PORT_UNKNOWN;
- up->port.uartclk = (SU_BASE_BAUD * 16);
+ if (keyb) {
+ if (dp == of_find_node_by_path(keyb))
+ return SU_PORT_KBD;
+ }
+ if (ms) {
+ if (dp == of_find_node_by_path(ms))
+ return SU_PORT_MS;
+ }
+ }
- sunsu_autoconfig(up);
- if (up->port.type == PORT_UNKNOWN)
- continue;
+ return SU_PORT_PORT;
+}
- up->port.line = instance++;
- up->port.ops = &sunsu_pops;
+static int __devinit su_probe(struct of_device *op, const struct of_device_id *match)
+{
+ static int inst;
+ struct device_node *dp = op->node;
+ struct uart_sunsu_port *up;
+ struct resource *rp;
+ enum su_type type;
+ int err;
+
+ type = su_get_type(dp);
+ if (type == SU_PORT_PORT) {
+ if (inst >= UART_NR)
+ return -EINVAL;
+ up = &sunsu_ports[inst];
+ } else {
+ up = kzalloc(sizeof(*up), GFP_KERNEL);
+ if (!up)
+ return -ENOMEM;
}
- sunsu_reg.minor = sunserial_current_minor;
- sunserial_current_minor += instance;
-
- sunsu_reg.nr = instance;
- sunsu_reg.cons = SUNSU_CONSOLE;
+ up->port.line = inst;
- ret = uart_register_driver(&sunsu_reg);
- if (ret < 0)
- return ret;
+ spin_lock_init(&up->port.lock);
- for (i = 0; i < UART_NR; i++) {
- struct uart_sunsu_port *up = &sunsu_ports[i];
+ up->su_type = type;
- /* Do not register Keyboard/Mouse lines with UART
- * layer.
- */
- if (up->su_type == SU_PORT_MS ||
- up->su_type == SU_PORT_KBD)
- continue;
+ rp = &op->resource[0];
+ up->port.mapbase = rp->start;
+ up->reg_size = (rp->end - rp->start) + 1;
+ up->port.membase = of_ioremap(rp, 0, up->reg_size, "su");
+ if (!up->port.membase) {
+ if (type != SU_PORT_PORT)
+ kfree(up);
+ return -ENOMEM;
+ }
- if (up->port.type == PORT_UNKNOWN)
- continue;
+ up->port.irq = op->irqs[0];
- uart_add_one_port(&sunsu_reg, &up->port);
- }
+ up->port.dev = &op->dev;
- return 0;
-}
+ up->port.type = PORT_UNKNOWN;
+ up->port.uartclk = (SU_BASE_BAUD * 16);
-static int su_node_ok(int node, char *name, int namelen)
-{
- if (strncmp(name, "su", namelen) == 0 ||
- strncmp(name, "su_pnp", namelen) == 0)
- return 1;
-
- if (strncmp(name, "serial", namelen) == 0) {
- char compat[32];
- int clen;
-
- /* Is it _really_ a 'su' device? */
- clen = prom_getproperty(node, "compatible", compat, sizeof(compat));
- if (clen > 0) {
- if (strncmp(compat, "sab82532", 8) == 0) {
- /* Nope, Siemens serial, not for us. */
- return 0;
- }
+ err = 0;
+ if (up->su_type == SU_PORT_KBD || up->su_type == SU_PORT_MS) {
+ err = sunsu_kbd_ms_init(up);
+ if (err) {
+ kfree(up);
+ goto out_unmap;
}
- return 1;
+ dev_set_drvdata(&op->dev, up);
+
+ return 0;
}
- return 0;
-}
+ up->port.flags |= UPF_BOOT_AUTOCONF;
-#define SU_PROPSIZE 128
+ sunsu_autoconfig(up);
-/*
- * Scan status structure.
- * "prop" is a local variable but it eats stack to keep it in each
- * stack frame of a recursive procedure.
- */
-struct su_probe_scan {
- int msnode, kbnode; /* PROM nodes for mouse and keyboard */
- int msx, kbx; /* minors for mouse and keyboard */
- int devices; /* scan index */
- char prop[SU_PROPSIZE];
-};
+ err = -ENODEV;
+ if (up->port.type == PORT_UNKNOWN)
+ goto out_unmap;
-/*
- * We have several platforms which present 'su' in different parts
- * of the device tree. 'su' may be found under obio, ebus, isa and pci.
- * We walk over the tree and find them wherever PROM hides them.
- */
-static void __init su_probe_any(struct su_probe_scan *t, int sunode)
-{
- struct uart_sunsu_port *up;
- int len;
+ up->port.ops = &sunsu_pops;
- if (t->devices >= UART_NR)
- return;
+ err = uart_add_one_port(&sunsu_reg, &up->port);
+ if (err)
+ goto out_unmap;
- for (; sunode != 0; sunode = prom_getsibling(sunode)) {
- len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE);
- if (len <= 1)
- continue; /* Broken PROM node */
-
- if (su_node_ok(sunode, t->prop, len)) {
- up = &sunsu_ports[t->devices];
- if (t->kbnode != 0 && sunode == t->kbnode) {
- t->kbx = t->devices;
- up->su_type = SU_PORT_KBD;
- } else if (t->msnode != 0 && sunode == t->msnode) {
- t->msx = t->devices;
- up->su_type = SU_PORT_MS;
- } else {
-#ifdef CONFIG_SPARC64
- /*
- * Do not attempt to use the truncated
- * keyboard/mouse ports as serial ports
- * on Ultras with PC keyboard attached.
- */
- if (prom_getbool(sunode, "mouse"))
- continue;
- if (prom_getbool(sunode, "keyboard"))
- continue;
-#endif
- up->su_type = SU_PORT_PORT;
- }
- up->port_node = sunode;
- ++t->devices;
- } else {
- su_probe_any(t, prom_getchild(sunode));
- }
- }
+ dev_set_drvdata(&op->dev, up);
+
+ inst++;
+
+ return 0;
+
+out_unmap:
+ of_iounmap(&op->resource[0], up->port.membase, up->reg_size);
+ return err;
}
-static int __init sunsu_probe(void)
+static int __devexit su_remove(struct of_device *op)
{
- int node;
- int len;
- struct su_probe_scan scan;
+ struct uart_sunsu_port *up = dev_get_drvdata(&op->dev);
- /*
- * First, we scan the tree.
- */
- scan.devices = 0;
- scan.msx = -1;
- scan.kbx = -1;
- scan.kbnode = 0;
- scan.msnode = 0;
+ if (up->su_type == SU_PORT_MS ||
+ up->su_type == SU_PORT_KBD) {
+#ifdef CONFIG_SERIO
+ serio_unregister_port(&up->serio);
+#endif
+ kfree(up);
+ } else if (up->port.type != PORT_UNKNOWN) {
+ uart_remove_one_port(&sunsu_reg, &up->port);
+ }
- /*
- * Get the nodes for keyboard and mouse from 'aliases'...
- */
- node = prom_getchild(prom_root_node);
- node = prom_searchsiblings(node, "aliases");
- if (node != 0) {
- len = prom_getproperty(node, "keyboard", scan.prop, SU_PROPSIZE);
- if (len > 0) {
- scan.prop[len] = 0;
- scan.kbnode = prom_finddevice(scan.prop);
- }
+ if (up->port.membase)
+ of_iounmap(&op->resource[0], up->port.membase, up->reg_size);
- len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE);
- if (len > 0) {
- scan.prop[len] = 0;
- scan.msnode = prom_finddevice(scan.prop);
- }
- }
+ dev_set_drvdata(&op->dev, NULL);
- su_probe_any(&scan, prom_getchild(prom_root_node));
+ return 0;
+}
- /*
- * Second, we process the special case of keyboard and mouse.
- *
- * Currently if we got keyboard and mouse hooked to "su" ports
- * we do not use any possible remaining "su" as a serial port.
- * Thus, we ignore values of .msx and .kbx, then compact ports.
- */
- if (scan.msx != -1 && scan.kbx != -1) {
- sunsu_ports[0].su_type = SU_PORT_MS;
- sunsu_ports[0].port_node = scan.msnode;
- sunsu_ports[1].su_type = SU_PORT_KBD;
- sunsu_ports[1].port_node = scan.kbnode;
+static struct of_device_id su_match[] = {
+ {
+ .name = "su",
+ },
+ {
+ .name = "su_pnp",
+ },
+ {
+ .name = "serial",
+ .compatible = "su",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, su_match);
- sunsu_kbd_ms_init();
- return 0;
- }
+static struct of_platform_driver su_driver = {
+ .name = "su",
+ .match_table = su_match,
+ .probe = su_probe,
+ .remove = __devexit_p(su_remove),
+};
- if (scan.msx != -1 || scan.kbx != -1) {
- printk("sunsu_probe: cannot match keyboard and mouse, confused\n");
- return -ENODEV;
+static int num_uart;
+
+static int __init sunsu_init(void)
+{
+ struct device_node *dp;
+ int err;
+
+ num_uart = 0;
+ for_each_node_by_name(dp, "su") {
+ if (su_get_type(dp) == SU_PORT_PORT)
+ num_uart++;
+ }
+ for_each_node_by_name(dp, "su_pnp") {
+ if (su_get_type(dp) == SU_PORT_PORT)
+ num_uart++;
+ }
+ for_each_node_by_name(dp, "serial") {
+ if (of_device_is_compatible(dp, "su")) {
+ if (su_get_type(dp) == SU_PORT_PORT)
+ num_uart++;
+ }
}
- if (scan.devices == 0)
- return -ENODEV;
+ if (num_uart) {
+ sunsu_reg.minor = sunserial_current_minor;
+ sunsu_reg.nr = num_uart;
+ err = uart_register_driver(&sunsu_reg);
+ if (err)
+ return err;
+ sunsu_reg.tty_driver->name_base = sunsu_reg.minor - 64;
+ sunserial_current_minor += num_uart;
+ sunsu_reg.cons = SUNSU_CONSOLE(num_uart);
+ }
- /*
- * Console must be initiated after the generic initialization.
- */
- sunsu_serial_init();
- sunsu_serial_console_init();
+ err = of_register_driver(&su_driver, &of_bus_type);
+ if (err && num_uart)
+ uart_unregister_driver(&sunsu_reg);
- return 0;
+ return err;
}
static void __exit sunsu_exit(void)
{
- int i, saw_uart;
-
- saw_uart = 0;
- for (i = 0; i < UART_NR; i++) {
- struct uart_sunsu_port *up = &sunsu_ports[i];
-
- if (up->su_type == SU_PORT_MS ||
- up->su_type == SU_PORT_KBD) {
-#ifdef CONFIG_SERIO
- serio_unregister_port(&up->serio);
-#endif
- } else if (up->port.type != PORT_UNKNOWN) {
- uart_remove_one_port(&sunsu_reg, &up->port);
- saw_uart++;
- }
- }
-
- if (saw_uart)
+ if (num_uart)
uart_unregister_driver(&sunsu_reg);
}
-module_init(sunsu_probe);
+module_init(sunsu_init);
module_exit(sunsu_exit);
+
+MODULE_AUTHOR("Eddie C. Dost, Peter Zaitcev, and David S. Miller");
+MODULE_DESCRIPTION("Sun SU serial port driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");