diff options
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/8250.c | 178 | ||||
-rw-r--r-- | drivers/serial/8250_acorn.c | 3 | ||||
-rw-r--r-- | drivers/serial/8250_pci.c | 6 | ||||
-rw-r--r-- | drivers/serial/8250_pnp.c | 8 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 26 | ||||
-rw-r--r-- | drivers/serial/Makefile | 2 | ||||
-rw-r--r-- | drivers/serial/icom.c | 5 | ||||
-rw-r--r-- | drivers/serial/ioc3_serial.c | 6 | ||||
-rw-r--r-- | drivers/serial/ioc4_serial.c | 9 | ||||
-rw-r--r-- | drivers/serial/ip22zilog.c | 9 | ||||
-rw-r--r-- | drivers/serial/jsm/jsm_driver.c | 6 | ||||
-rw-r--r-- | drivers/serial/jsm/jsm_tty.c | 12 | ||||
-rw-r--r-- | drivers/serial/mpc52xx_uart.c | 5 | ||||
-rw-r--r-- | drivers/serial/of_serial.c | 143 | ||||
-rw-r--r-- | drivers/serial/pnx8xxx_uart.c | 852 | ||||
-rw-r--r-- | drivers/serial/serial_core.c | 18 | ||||
-rw-r--r-- | drivers/serial/serial_cs.c | 4 | ||||
-rw-r--r-- | drivers/serial/sunsab.c | 1 | ||||
-rw-r--r-- | drivers/serial/sunsu.c | 1 | ||||
-rw-r--r-- | drivers/serial/sunzilog.c | 1 |
20 files changed, 1192 insertions, 103 deletions
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 2964ca9df5a0..98ec86185328 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -364,6 +364,23 @@ serial_out(struct uart_8250_port *up, int offset, int value) } } +static void +serial_out_sync(struct uart_8250_port *up, int offset, int value) +{ + switch (up->port.iotype) { + case UPIO_MEM: + case UPIO_MEM32: +#ifdef CONFIG_SERIAL_8250_AU1X00 + case UPIO_AU: +#endif + serial_out(up, offset, value); + serial_in(up, UART_LCR); /* safe, no side-effects */ + break; + default: + serial_out(up, offset, value); + } +} + /* * We used to support using pause I/O for certain machines. We * haven't supported this for a while, but just in case it's badly @@ -1045,7 +1062,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) #endif serial_outp(up, UART_MCR, save_mcr); serial8250_clear_fifos(up); - (void)serial_in(up, UART_RX); + serial_in(up, UART_RX); if (up->capabilities & UART_CAP_UUE) serial_outp(up, UART_IER, UART_IER_UUE); else @@ -1451,6 +1468,12 @@ static void serial_unlink_irq_chain(struct uart_8250_port *up) serial_do_unlink(i, up); } +/* Base timer interval for polling */ +static inline int poll_timeout(int timeout) +{ + return timeout > 6 ? (timeout / 2 - 2) : 1; +} + /* * This function is used to handle ports that do not have an * interrupt. This doesn't work very well for 16450's, but gives @@ -1460,16 +1483,51 @@ static void serial_unlink_irq_chain(struct uart_8250_port *up) static void serial8250_timeout(unsigned long data) { struct uart_8250_port *up = (struct uart_8250_port *)data; - unsigned int timeout; unsigned int iir; iir = serial_in(up, UART_IIR); if (!(iir & UART_IIR_NO_INT)) serial8250_handle_port(up); + mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout)); +} + +static void serial8250_backup_timeout(unsigned long data) +{ + struct uart_8250_port *up = (struct uart_8250_port *)data; + unsigned int iir, ier = 0; + + /* + * Must disable interrupts or else we risk racing with the interrupt + * based handler. + */ + if (is_real_interrupt(up->port.irq)) { + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + } - timeout = up->port.timeout; - timeout = timeout > 6 ? (timeout / 2 - 2) : 1; - mod_timer(&up->timer, jiffies + timeout); + iir = serial_in(up, UART_IIR); + + /* + * This should be a safe test for anyone who doesn't trust the + * IIR bits on their UART, but it's specifically designed for + * the "Diva" UART used on the management processor on many HP + * ia64 and parisc boxes. + */ + if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && + (!uart_circ_empty(&up->port.info->xmit) || up->port.x_char) && + (serial_in(up, UART_LSR) & UART_LSR_THRE)) { + iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); + iir |= UART_IIR_THRI; + } + + if (!(iir & UART_IIR_NO_INT)) + serial8250_handle_port(up); + + if (is_real_interrupt(up->port.irq)) + serial_out(up, UART_IER, ier); + + /* Standard timer interval plus 0.2s to keep the port running */ + mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout) + HZ/5); } static unsigned int serial8250_tx_empty(struct uart_port *port) @@ -1540,6 +1598,37 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&up->port.lock, flags); } +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_8250_port *up, int bits) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while ((status & bits) != bits); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (!(serial_in(up, UART_MSR) & UART_MSR_CTS) && --tmout) { + udelay(1); + touch_nmi_watchdog(); + } + } +} + static int serial8250_startup(struct uart_port *port) { struct uart_8250_port *up = (struct uart_8250_port *)port; @@ -1613,18 +1702,50 @@ static int serial8250_startup(struct uart_port *port) serial_outp(up, UART_LCR, 0); } + if (is_real_interrupt(up->port.irq)) { + /* + * Test for UARTs that do not reassert THRE when the + * transmitter is idle and the interrupt has already + * been cleared. Real 16550s should always reassert + * this interrupt whenever the transmitter is idle and + * the interrupt is enabled. Delays are necessary to + * allow register changes to become visible. + */ + spin_lock_irqsave(&up->port.lock, flags); + + wait_for_xmitr(up, UART_LSR_THRE); + serial_out_sync(up, UART_IER, UART_IER_THRI); + udelay(1); /* allow THRE to set */ + serial_in(up, UART_IIR); + serial_out(up, UART_IER, 0); + serial_out_sync(up, UART_IER, UART_IER_THRI); + udelay(1); /* allow a working UART time to re-assert THRE */ + iir = serial_in(up, UART_IIR); + serial_out(up, UART_IER, 0); + + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * If the interrupt is not reasserted, setup a timer to + * kick the UART on a regular basis. + */ + if (iir & UART_IIR_NO_INT) { + pr_debug("ttyS%d - using backup timer\n", port->line); + up->timer.function = serial8250_backup_timeout; + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + + poll_timeout(up->port.timeout) + HZ/5); + } + } + /* * If the "interrupt" for this port doesn't correspond with any * hardware interrupt, we use a timer-based system. The original * driver used to do this with IRQ0. */ if (!is_real_interrupt(up->port.irq)) { - unsigned int timeout = up->port.timeout; - - timeout = timeout > 6 ? (timeout / 2 - 2) : 1; - up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + timeout); + mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout)); } else { retval = serial_link_irq_chain(up); if (retval) @@ -1740,9 +1861,9 @@ static void serial8250_shutdown(struct uart_port *port) */ (void) serial_in(up, UART_RX); - if (!is_real_interrupt(up->port.irq)) - del_timer_sync(&up->timer); - else + del_timer_sync(&up->timer); + up->timer.function = serial8250_timeout; + if (is_real_interrupt(up->port.irq)) serial_unlink_irq_chain(up); } @@ -2212,37 +2333,6 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) #ifdef CONFIG_SERIAL_8250_CONSOLE -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -/* - * Wait for transmitter & holding register to empty - */ -static inline void wait_for_xmitr(struct uart_8250_port *up, int bits) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = serial_in(up, UART_LSR); - - if (status & UART_LSR_BI) - up->lsr_break_flag = UART_LSR_BI; - - if (--tmout == 0) - break; - udelay(1); - } while ((status & bits) != bits); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (!(serial_in(up, UART_MSR) & UART_MSR_CTS) && --tmout) { - udelay(1); - touch_nmi_watchdog(); - } - } -} - static void serial8250_console_putchar(struct uart_port *port, int ch) { struct uart_8250_port *up = (struct uart_8250_port *)port; diff --git a/drivers/serial/8250_acorn.c b/drivers/serial/8250_acorn.c index ef8cc8a70c60..562ba745a044 100644 --- a/drivers/serial/8250_acorn.c +++ b/drivers/serial/8250_acorn.c @@ -47,11 +47,10 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) unsigned long bus_addr; unsigned int i; - info = kmalloc(sizeof(struct serial_card_info), GFP_KERNEL); + info = kzalloc(sizeof(struct serial_card_info), GFP_KERNEL); if (!info) return -ENOMEM; - memset(info, 0, sizeof(struct serial_card_info)); info->num_ports = type->num_ports; bus_addr = ecard_resource_start(ec, type->type); diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index a2dac378bda9..6d7d616e9ccd 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -16,7 +16,6 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/pci.h> -#include <linux/sched.h> #include <linux/string.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -1628,7 +1627,7 @@ pciserial_init_ports(struct pci_dev *dev, struct pciserial_board *board) nr_ports = rc; } - priv = kmalloc(sizeof(struct serial_private) + + priv = kzalloc(sizeof(struct serial_private) + sizeof(unsigned int) * nr_ports, GFP_KERNEL); if (!priv) { @@ -1636,9 +1635,6 @@ pciserial_init_ports(struct pci_dev *dev, struct pciserial_board *board) goto err_deinit; } - memset(priv, 0, sizeof(struct serial_private) + - sizeof(unsigned int) * nr_ports); - priv->dev = dev; priv->quirk = quirk; diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c index d3d6b82706b5..cde5db44abf6 100644 --- a/drivers/serial/8250_pnp.c +++ b/drivers/serial/8250_pnp.c @@ -450,11 +450,11 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) port.dev = &dev->dev; line = serial8250_register_port(&port); + if (line < 0) + return -ENODEV; - if (line >= 0) - pnp_set_drvdata(dev, (void *)((long)line + 1)); - return line >= 0 ? 0 : -ENODEV; - + pnp_set_drvdata(dev, (void *)((long)line + 1)); + return 0; } static void __devexit serial_pnp_remove(struct pnp_dev *dev) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 5cc6b91f8408..e8dd71df9165 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -686,6 +686,22 @@ config SERIAL_SH_SCI_CONSOLE depends on SERIAL_SH_SCI=y select SERIAL_CORE_CONSOLE +config SERIAL_PNX8XXX + bool "Enable PNX8XXX SoCs' UART Support" + depends on MIPS && SOC_PNX8550 + select SERIAL_CORE + help + If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 + and you want to use serial ports, say Y. Otherwise, say N. + +config SERIAL_PNX8XXX_CONSOLE + bool "Enable PNX8XX0 serial console" + depends on SERIAL_PNX8XXX + select SERIAL_CORE_CONSOLE + help + If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 + and you want to use serial console, say Y. Otherwise, say N. + config SERIAL_CORE tristate @@ -999,4 +1015,14 @@ config SERIAL_NETX_CONSOLE If you have enabled the serial port on the Motorola IMX CPU you can make it the console by answering Y to this option. +config SERIAL_OF_PLATFORM + tristate "Serial port on Open Firmware platform bus" + depends on PPC_OF + depends on SERIAL_8250 + help + If you have a PowerPC based system that has serial ports + on a platform specific bus, you should enable this option. + Currently, only 8250 compatible ports are supported, but + others can easily be added. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index df3632cd7df9..6b3560c5749a 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o obj-$(CONFIG_SERIAL_PXA) += pxa.o +obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o obj-$(CONFIG_SERIAL_SA1100) += sa1100.o obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o @@ -58,3 +59,4 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o +obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c index 71e6a24d8c28..41431d0d5512 100644 --- a/drivers/serial/icom.c +++ b/drivers/serial/icom.c @@ -27,7 +27,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/signal.h> -#include <linux/sched.h> #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/tty.h> @@ -1417,14 +1416,12 @@ static int __devinit icom_alloc_adapter(struct icom_adapter struct list_head *tmp; icom_adapter = (struct icom_adapter *) - kmalloc(sizeof(struct icom_adapter), GFP_KERNEL); + kzalloc(sizeof(struct icom_adapter), GFP_KERNEL); if (!icom_adapter) { return -ENOMEM; } - memset(icom_adapter, 0, sizeof(struct icom_adapter)); - list_for_each(tmp, &icom_adapter_head) { cur_adapter_entry = list_entry(tmp, struct icom_adapter, diff --git a/drivers/serial/ioc3_serial.c b/drivers/serial/ioc3_serial.c index 9cc0be932316..168073f12cec 100644 --- a/drivers/serial/ioc3_serial.c +++ b/drivers/serial/ioc3_serial.c @@ -2019,13 +2019,12 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __FUNCTION__, is, idd)); - card_ptr = kmalloc(sizeof(struct ioc3_card), GFP_KERNEL); + card_ptr = kzalloc(sizeof(struct ioc3_card), GFP_KERNEL); if (!card_ptr) { printk(KERN_WARNING "ioc3_attach_one" ": unable to get memory for the IOC3\n"); return -ENOMEM; } - memset(card_ptr, 0, sizeof(struct ioc3_card)); idd->data[is->id] = card_ptr; Submodule_slot = is->id; @@ -2040,13 +2039,12 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) /* Create port structures for each port */ for (phys_port = 0; phys_port < PORTS_PER_CARD; phys_port++) { - port = kmalloc(sizeof(struct ioc3_port), GFP_KERNEL); + port = kzalloc(sizeof(struct ioc3_port), GFP_KERNEL); if (!port) { printk(KERN_WARNING "IOC3 serial memory not available for port\n"); goto out4; } - memset(port, 0, sizeof(struct ioc3_port)); spin_lock_init(&port->ip_lock); /* we need to remember the previous ones, to point back to diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c index f540212e7409..0c179384fb0c 100644 --- a/drivers/serial/ioc4_serial.c +++ b/drivers/serial/ioc4_serial.c @@ -1076,13 +1076,12 @@ static int inline ioc4_attach_local(struct ioc4_driver_data *idd) /* Create port structures for each port */ for (port_number = 0; port_number < IOC4_NUM_SERIAL_PORTS; port_number++) { - port = kmalloc(sizeof(struct ioc4_port), GFP_KERNEL); + port = kzalloc(sizeof(struct ioc4_port), GFP_KERNEL); if (!port) { printk(KERN_WARNING "IOC4 serial memory not available for port\n"); return -ENOMEM; } - memset(port, 0, sizeof(struct ioc4_port)); spin_lock_init(&port->ip_lock); /* we need to remember the previous ones, to point back to @@ -2811,7 +2810,7 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd) (void *)serial)); /* Get memory for the new card */ - control = kmalloc(sizeof(struct ioc4_control), GFP_KERNEL); + control = kzalloc(sizeof(struct ioc4_control), GFP_KERNEL); if (!control) { printk(KERN_WARNING "ioc4_attach_one" @@ -2819,11 +2818,10 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd) ret = -ENOMEM; goto out2; } - memset(control, 0, sizeof(struct ioc4_control)); idd->idd_serial_data = control; /* Allocate the soft structure */ - soft = kmalloc(sizeof(struct ioc4_soft), GFP_KERNEL); + soft = kzalloc(sizeof(struct ioc4_soft), GFP_KERNEL); if (!soft) { printk(KERN_WARNING "ioc4 (%p): unable to get memory for the soft struct\n", @@ -2831,7 +2829,6 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd) ret = -ENOMEM; goto out3; } - memset(soft, 0, sizeof(struct ioc4_soft)); spin_lock_init(&soft->is_ir_lock); soft->is_ioc4_misc_addr = idd->idd_misc_regs; diff --git a/drivers/serial/ip22zilog.c b/drivers/serial/ip22zilog.c index 0746c9446ae0..c3abfb39f316 100644 --- a/drivers/serial/ip22zilog.c +++ b/drivers/serial/ip22zilog.c @@ -14,7 +14,6 @@ */ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/sched.h> #include <linux/errno.h> #include <linux/delay.h> #include <linux/tty.h> @@ -922,13 +921,7 @@ static int zilog_irq = -1; static void * __init alloc_one_table(unsigned long size) { - void *ret; - - ret = kmalloc(size, GFP_KERNEL); - if (ret != NULL) - memset(ret, 0, size); - - return ret; + return kzalloc(size, GFP_KERNEL); } static void __init ip22zilog_alloc_tables(void) diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c index 244f63be3a03..81792e6eeb2d 100644 --- a/drivers/serial/jsm/jsm_driver.c +++ b/drivers/serial/jsm/jsm_driver.c @@ -71,14 +71,13 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_disable_device; } - brd = kmalloc(sizeof(struct jsm_board), GFP_KERNEL); + brd = kzalloc(sizeof(struct jsm_board), GFP_KERNEL); if (!brd) { dev_err(&pdev->dev, "memory allocation for board structure failed\n"); rc = -ENOMEM; goto out_release_regions; } - memset(brd, 0, sizeof(struct jsm_board)); /* store the info for the board we've found */ brd->boardnum = adapter_count++; @@ -152,7 +151,7 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) * Okay to malloc with GFP_KERNEL, we are not at interrupt * context, and there are no locks held. */ - brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL); + brd->flipbuf = kzalloc(MYFLIPLEN, GFP_KERNEL); if (!brd->flipbuf) { /* XXX: leaking all resources from jsm_tty_init and jsm_uart_port_init here! */ @@ -160,7 +159,6 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) retval = -ENOMEM; goto out_free_irq; } - memset(brd->flipbuf, 0, MYFLIPLEN); pci_set_drvdata(pdev, brd); diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c index 7cf1c60027f8..be22bbdbc8e5 100644 --- a/drivers/serial/jsm/jsm_tty.c +++ b/drivers/serial/jsm/jsm_tty.c @@ -194,31 +194,28 @@ static int jsm_tty_open(struct uart_port *port) /* Drop locks, as malloc with GFP_KERNEL can sleep */ if (!channel->ch_rqueue) { - channel->ch_rqueue = (u8 *) kmalloc(RQUEUESIZE, GFP_KERNEL); + channel->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL); if (!channel->ch_rqueue) { jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, "unable to allocate read queue buf"); return -ENOMEM; } - memset(channel->ch_rqueue, 0, RQUEUESIZE); } if (!channel->ch_equeue) { - channel->ch_equeue = (u8 *) kmalloc(EQUEUESIZE, GFP_KERNEL); + channel->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL); if (!channel->ch_equeue) { jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, "unable to allocate error queue buf"); return -ENOMEM; } - memset(channel->ch_equeue, 0, EQUEUESIZE); } if (!channel->ch_wqueue) { - channel->ch_wqueue = (u8 *) kmalloc(WQUEUESIZE, GFP_KERNEL); + channel->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL); if (!channel->ch_wqueue) { jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, "unable to allocate write queue buf"); return -ENOMEM; } - memset(channel->ch_wqueue, 0, WQUEUESIZE); } channel->ch_flags &= ~(CH_OPENING); @@ -392,13 +389,12 @@ int jsm_tty_init(struct jsm_board *brd) * Okay to malloc with GFP_KERNEL, we are not at * interrupt context, and there are no locks held. */ - brd->channels[i] = kmalloc(sizeof(struct jsm_channel), GFP_KERNEL); + brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL); if (!brd->channels[i]) { jsm_printk(CORE, ERR, &brd->pci_dev, "%s:%d Unable to allocate memory for channel struct\n", __FILE__, __LINE__); } - memset(brd->channels[i], 0, sizeof(struct jsm_channel)); } } diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 3c4b6c243712..955bbd653e22 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -127,8 +127,7 @@ static irqreturn_t mpc52xx_uart_int(int irq,void *dev_id); #if defined(CONFIG_PPC_MERGE) static struct of_device_id mpc52xx_uart_of_match[] = { - { .type = "serial", .compatible = "mpc52xx-psc-uart", }, - { .type = "serial", .compatible = "mpc5200-psc", }, /* Efika only! */ + { .type = "serial", .compatible = "mpc5200-psc-uart", }, {}, }; #endif @@ -1068,7 +1067,7 @@ mpc52xx_uart_of_enumerate(void) continue; /* Is a particular device number requested? */ - devno = get_property(np, "device_no", NULL); + devno = get_property(np, "port-number", NULL); mpc52xx_uart_of_assign(of_node_get(np), devno ? *devno : -1); } diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c new file mode 100644 index 000000000000..09b0b736a751 --- /dev/null +++ b/drivers/serial/of_serial.c @@ -0,0 +1,143 @@ +/* + * Serial Port driver for Open Firmware platform devices + * + * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. + * + * 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. + * + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/serial_core.h> +#include <linux/serial_8250.h> + +#include <asm/of_platform.h> +#include <asm/prom.h> + +/* + * Fill a struct uart_port for a given device node + */ +static int __devinit of_platform_serial_setup(struct of_device *ofdev, + int type, struct uart_port *port) +{ + struct resource resource; + struct device_node *np = ofdev->node; + const unsigned int *clk, *spd; + int ret; + + memset(port, 0, sizeof *port); + spd = get_property(np, "current-speed", NULL); + clk = get_property(np, "clock-frequency", NULL); + if (!clk) { + dev_warn(&ofdev->dev, "no clock-frequency property set\n"); + return -ENODEV; + } + + ret = of_address_to_resource(np, 0, &resource); + if (ret) { + dev_warn(&ofdev->dev, "invalid address\n"); + return ret; + } + + spin_lock_init(&port->lock); + port->mapbase = resource.start; + port->irq = irq_of_parse_and_map(np, 0); + port->iotype = UPIO_MEM; + port->type = type; + port->uartclk = *clk; + port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP; + port->dev = &ofdev->dev; + port->custom_divisor = *clk / (16 * (*spd)); + + return 0; +} + +/* + * Try to register a serial port + */ +static int __devinit of_platform_serial_probe(struct of_device *ofdev, + const struct of_device_id *id) +{ + struct uart_port port; + int port_type; + int ret; + + if (of_find_property(ofdev->node, "used-by-rtas", NULL)) + return -EBUSY; + + port_type = (unsigned long)id->data; + ret = of_platform_serial_setup(ofdev, port_type, &port); + if (ret) + goto out; + + switch (port_type) { + case PORT_UNKNOWN: + dev_info(&ofdev->dev, "Unknown serial port found, " + "attempting to use 8250 driver\n"); + /* fallthrough */ + case PORT_8250 ... PORT_MAX_8250: + ret = serial8250_register_port(&port); + break; + default: + /* need to add code for these */ + ret = -ENODEV; + break; + } + if (ret < 0) + goto out; + + ofdev->dev.driver_data = (void *)(unsigned long)ret; + return 0; +out: + irq_dispose_mapping(port.irq); + return ret; +} + +/* + * Release a line + */ +static int of_platform_serial_remove(struct of_device *ofdev) +{ + int line = (unsigned long)ofdev->dev.driver_data; + serial8250_unregister_port(line); + return 0; +} + +/* + * A few common types, add more as needed. + */ +static struct of_device_id __devinitdata of_platform_serial_table[] = { + { .type = "serial", .compatible = "ns8250", .data = (void *)PORT_8250, }, + { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, }, + { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, }, + { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, }, + { .type = "serial", .data = (void *)PORT_UNKNOWN, }, + { /* end of list */ }, +}; + +static struct of_platform_driver __devinitdata of_platform_serial_driver = { + .owner = THIS_MODULE, + .name = "of_serial", + .probe = of_platform_serial_probe, + .remove = of_platform_serial_remove, + .match_table = of_platform_serial_table, +}; + +static int __init of_platform_serial_init(void) +{ + return of_register_platform_driver(&of_platform_serial_driver); +} +module_init(of_platform_serial_init); + +static void __exit of_platform_serial_exit(void) +{ + return of_unregister_platform_driver(&of_platform_serial_driver); +}; +module_exit(of_platform_serial_exit); + +MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices"); diff --git a/drivers/serial/pnx8xxx_uart.c b/drivers/serial/pnx8xxx_uart.c new file mode 100644 index 000000000000..8d01c59e8d04 --- /dev/null +++ b/drivers/serial/pnx8xxx_uart.c @@ -0,0 +1,852 @@ +/* + * UART driver for PNX8XXX SoCs + * + * Author: Per Hallsmark per.hallsmark@mvista.com + * Ported to 2.6 kernel by EmbeddedAlley + * Reworked by Vitaly Wool <vitalywool@gmail.com> + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of + * any kind, whether express or implied. + * + */ + +#if defined(CONFIG_SERIAL_PNX8XXX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/serial_pnx8xxx.h> + +#include <asm/io.h> +#include <asm/irq.h> + +/* We'll be using StrongARM sa1100 serial port major/minor */ +#define SERIAL_PNX8XXX_MAJOR 204 +#define MINOR_START 5 + +#define NR_PORTS 2 + +#define PNX8XXX_ISR_PASS_LIMIT 256 + +/* + * Convert from ignore_status_mask or read_status_mask to FIFO + * and interrupt status bits + */ +#define SM_TO_FIFO(x) ((x) >> 10) +#define SM_TO_ISTAT(x) ((x) & 0x000001ff) +#define FIFO_TO_SM(x) ((x) << 10) +#define ISTAT_TO_SM(x) ((x) & 0x000001ff) + +/* + * This is the size of our serial port register set. + */ +#define UART_PORT_SIZE 0x1000 + +/* + * This determines how often we check the modem status signals + * for any change. They generally aren't connected to an IRQ + * so we have to poll them. We also check immediately before + * filling the TX fifo incase CTS has been dropped. + */ +#define MCTRL_TIMEOUT (250*HZ/1000) + +extern struct pnx8xxx_port pnx8xxx_ports[]; + +static inline int serial_in(struct pnx8xxx_port *sport, int offset) +{ + return (__raw_readl(sport->port.membase + offset)); +} + +static inline void serial_out(struct pnx8xxx_port *sport, int offset, int value) +{ + __raw_writel(value, sport->port.membase + offset); +} + +/* + * Handle any change of modem status signal since we were last called. + */ +static void pnx8xxx_mctrl_check(struct pnx8xxx_port *sport) +{ + unsigned int status, changed; + + status = sport->port.ops->get_mctrl(&sport->port); + changed = status ^ sport->old_status; + + if (changed == 0) + return; + + sport->old_status = status; + + if (changed & TIOCM_RI) + sport->port.icount.rng++; + if (changed & TIOCM_DSR) + sport->port.icount.dsr++; + if (changed & TIOCM_CAR) + uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); + if (changed & TIOCM_CTS) + uart_handle_cts_change(&sport->port, status & TIOCM_CTS); + + wake_up_interruptible(&sport->port.info->delta_msr_wait); +} + +/* + * This is our per-port timeout handler, for checking the + * modem status signals. + */ +static void pnx8xxx_timeout(unsigned long data) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)data; + unsigned long flags; + + if (sport->port.info) { + spin_lock_irqsave(&sport->port.lock, flags); + pnx8xxx_mctrl_check(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); + + mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); + } +} + +/* + * interrupts disabled on entry + */ +static void pnx8xxx_stop_tx(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + u32 ien; + + /* Disable TX intr */ + ien = serial_in(sport, PNX8XXX_IEN); + serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLTX); + + /* Clear all pending TX intr */ + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX); +} + +/* + * interrupts may not be disabled on entry + */ +static void pnx8xxx_start_tx(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + u32 ien; + + /* Clear all pending TX intr */ + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX); + + /* Enable TX intr */ + ien = serial_in(sport, PNX8XXX_IEN); + serial_out(sport, PNX8XXX_IEN, ien | PNX8XXX_UART_INT_ALLTX); +} + +/* + * Interrupts enabled + */ +static void pnx8xxx_stop_rx(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + u32 ien; + + /* Disable RX intr */ + ien = serial_in(sport, PNX8XXX_IEN); + serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLRX); + + /* Clear all pending RX intr */ + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX); +} + +/* + * Set the modem control timer to fire immediately. + */ +static void pnx8xxx_enable_ms(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + + mod_timer(&sport->timer, jiffies); +} + +static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport) +{ + struct tty_struct *tty = sport->port.info->tty; + unsigned int status, ch, flg; + + status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | + ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT)); + while (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFIFO)) { + ch = serial_in(sport, PNX8XXX_FIFO); + + sport->port.icount.rx++; + + flg = TTY_NORMAL; + + /* + * note that the error handling code is + * out of the main execution path + */ + if (status & (FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE | + PNX8XXX_UART_FIFO_RXPAR) | + ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN))) { + if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR)) + sport->port.icount.parity++; + else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE)) + sport->port.icount.frame++; + if (status & ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN)) + sport->port.icount.overrun++; + + status &= sport->port.read_status_mask; + + if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR)) + flg = TTY_PARITY; + else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE)) + flg = TTY_FRAME; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + if (uart_handle_sysrq_char(&sport->port, ch)) + goto ignore_char; + + uart_insert_char(&sport->port, status, + ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN), ch, flg); + + ignore_char: + serial_out(sport, PNX8XXX_LCR, serial_in(sport, PNX8XXX_LCR) | + PNX8XXX_UART_LCR_RX_NEXT); + status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | + ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT)); + } + tty_flip_buffer_push(tty); +} + +static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport) +{ + struct circ_buf *xmit = &sport->port.info->xmit; + + if (sport->port.x_char) { + serial_out(sport, PNX8XXX_FIFO, sport->port.x_char); + sport->port.icount.tx++; + sport->port.x_char = 0; + return; + } + + /* + * Check the modem control lines before + * transmitting anything. + */ + pnx8xxx_mctrl_check(sport); + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + pnx8xxx_stop_tx(&sport->port); + return; + } + + /* + * TX while bytes available + */ + while (((serial_in(sport, PNX8XXX_FIFO) & + PNX8XXX_UART_FIFO_TXFIFO) >> 16) < 16) { + serial_out(sport, PNX8XXX_FIFO, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + pnx8xxx_stop_tx(&sport->port); +} + +static irqreturn_t pnx8xxx_int(int irq, void *dev_id) +{ + struct pnx8xxx_port *sport = dev_id; + unsigned int status; + + spin_lock(&sport->port.lock); + /* Get the interrupts */ + status = serial_in(sport, PNX8XXX_ISTAT) & serial_in(sport, PNX8XXX_IEN); + + /* Break signal received */ + if (status & PNX8XXX_UART_INT_BREAK) { + sport->port.icount.brk++; + uart_handle_break(&sport->port); + } + + /* Byte received */ + if (status & PNX8XXX_UART_INT_RX) + pnx8xxx_rx_chars(sport); + + /* TX holding register empty - transmit a byte */ + if (status & PNX8XXX_UART_INT_TX) + pnx8xxx_tx_chars(sport); + + /* Clear the ISTAT register */ + serial_out(sport, PNX8XXX_ICLR, status); + + spin_unlock(&sport->port.lock); + return IRQ_HANDLED; +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int pnx8xxx_tx_empty(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + + return serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA ? 0 : TIOCSER_TEMT; +} + +static unsigned int pnx8xxx_get_mctrl(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + unsigned int mctrl = TIOCM_DSR; + unsigned int msr; + + /* REVISIT */ + + msr = serial_in(sport, PNX8XXX_MCR); + + mctrl |= msr & PNX8XXX_UART_MCR_CTS ? TIOCM_CTS : 0; + mctrl |= msr & PNX8XXX_UART_MCR_DCD ? TIOCM_CAR : 0; + + return mctrl; +} + +static void pnx8xxx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +#if 0 /* FIXME */ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + unsigned int msr; +#endif +} + +/* + * Interrupts always disabled. + */ +static void pnx8xxx_break_ctl(struct uart_port *port, int break_state) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + unsigned long flags; + unsigned int lcr; + + spin_lock_irqsave(&sport->port.lock, flags); + lcr = serial_in(sport, PNX8XXX_LCR); + if (break_state == -1) + lcr |= PNX8XXX_UART_LCR_TXBREAK; + else + lcr &= ~PNX8XXX_UART_LCR_TXBREAK; + serial_out(sport, PNX8XXX_LCR, lcr); + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static int pnx8xxx_startup(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(sport->port.irq, pnx8xxx_int, 0, + "pnx8xxx-uart", sport); + if (retval) + return retval; + + /* + * Finally, clear and enable interrupts + */ + + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX | + PNX8XXX_UART_INT_ALLTX); + + serial_out(sport, PNX8XXX_IEN, serial_in(sport, PNX8XXX_IEN) | + PNX8XXX_UART_INT_ALLRX | + PNX8XXX_UART_INT_ALLTX); + + /* + * Enable modem status interrupts + */ + spin_lock_irq(&sport->port.lock); + pnx8xxx_enable_ms(&sport->port); + spin_unlock_irq(&sport->port.lock); + + return 0; +} + +static void pnx8xxx_shutdown(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + int lcr; + + /* + * Stop our timer. + */ + del_timer_sync(&sport->timer); + + /* + * Disable all interrupts + */ + serial_out(sport, PNX8XXX_IEN, 0); + + /* + * Reset the Tx and Rx FIFOS, disable the break condition + */ + lcr = serial_in(sport, PNX8XXX_LCR); + lcr &= ~PNX8XXX_UART_LCR_TXBREAK; + lcr |= PNX8XXX_UART_LCR_TX_RST | PNX8XXX_UART_LCR_RX_RST; + serial_out(sport, PNX8XXX_LCR, lcr); + + /* + * Clear all interrupts + */ + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX | + PNX8XXX_UART_INT_ALLTX); + + /* + * Free the interrupt + */ + free_irq(sport->port.irq, sport); +} + +static void +pnx8xxx_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + unsigned long flags; + unsigned int lcr_fcr, old_ien, baud, quot; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + + /* + * We only support CS7 and CS8. + */ + while ((termios->c_cflag & CSIZE) != CS7 && + (termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8) + lcr_fcr = PNX8XXX_UART_LCR_8BIT; + else + lcr_fcr = 0; + + if (termios->c_cflag & CSTOPB) + lcr_fcr |= PNX8XXX_UART_LCR_2STOPB; + if (termios->c_cflag & PARENB) { + lcr_fcr |= PNX8XXX_UART_LCR_PAREN; + if (!(termios->c_cflag & PARODD)) + lcr_fcr |= PNX8XXX_UART_LCR_PAREVN; + } + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask = ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN) | + ISTAT_TO_SM(PNX8XXX_UART_INT_EMPTY) | + ISTAT_TO_SM(PNX8XXX_UART_INT_RX); + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= + FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) | + FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR); + if (termios->c_iflag & (BRKINT | PARMRK)) + sport->port.read_status_mask |= + ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK); + + /* + * Characters to ignore + */ + sport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= + FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) | + FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR); + if (termios->c_iflag & IGNBRK) { + sport->port.ignore_status_mask |= + ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK); + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= + ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN); + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + sport->port.ignore_status_mask |= + ISTAT_TO_SM(PNX8XXX_UART_INT_RX); + + del_timer_sync(&sport->timer); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * disable interrupts and drain transmitter + */ + old_ien = serial_in(sport, PNX8XXX_IEN); + serial_out(sport, PNX8XXX_IEN, old_ien & ~(PNX8XXX_UART_INT_ALLTX | + PNX8XXX_UART_INT_ALLRX)); + + while (serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA) + barrier(); + + /* then, disable everything */ + serial_out(sport, PNX8XXX_IEN, 0); + + /* Reset the Rx and Tx FIFOs too */ + lcr_fcr |= PNX8XXX_UART_LCR_TX_RST; + lcr_fcr |= PNX8XXX_UART_LCR_RX_RST; + + /* set the parity, stop bits and data size */ + serial_out(sport, PNX8XXX_LCR, lcr_fcr); + + /* set the baud rate */ + quot -= 1; + serial_out(sport, PNX8XXX_BAUD, quot); + + serial_out(sport, PNX8XXX_ICLR, -1); + + serial_out(sport, PNX8XXX_IEN, old_ien); + + if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) + pnx8xxx_enable_ms(&sport->port); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static const char *pnx8xxx_type(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + + return sport->port.type == PORT_PNX8XXX ? "PNX8XXX" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void pnx8xxx_release_port(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + + release_mem_region(sport->port.mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int pnx8xxx_request_port(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, + "pnx8xxx-uart") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void pnx8xxx_config_port(struct uart_port *port, int flags) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + + if (flags & UART_CONFIG_TYPE && + pnx8xxx_request_port(&sport->port) == 0) + sport->port.type = PORT_PNX8XXX; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_PNX8XXX and PORT_UNKNOWN + */ +static int +pnx8xxx_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_PNX8XXX) + ret = -EINVAL; + if (sport->port.irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + ret = -EINVAL; + if (sport->port.uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)sport->port.mapbase != ser->iomem_base) + ret = -EINVAL; + if (sport->port.iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +static struct uart_ops pnx8xxx_pops = { + .tx_empty = pnx8xxx_tx_empty, + .set_mctrl = pnx8xxx_set_mctrl, + .get_mctrl = pnx8xxx_get_mctrl, + .stop_tx = pnx8xxx_stop_tx, + .start_tx = pnx8xxx_start_tx, + .stop_rx = pnx8xxx_stop_rx, + .enable_ms = pnx8xxx_enable_ms, + .break_ctl = pnx8xxx_break_ctl, + .startup = pnx8xxx_startup, + .shutdown = pnx8xxx_shutdown, + .set_termios = pnx8xxx_set_termios, + .type = pnx8xxx_type, + .release_port = pnx8xxx_release_port, + .request_port = pnx8xxx_request_port, + .config_port = pnx8xxx_config_port, + .verify_port = pnx8xxx_verify_port, +}; + + +/* + * Setup the PNX8XXX serial ports. + * + * Note also that we support "console=ttySx" where "x" is either 0 or 1. + */ +static void __init pnx8xxx_init_ports(void) +{ + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0; i < NR_PORTS; i++) { + init_timer(&pnx8xxx_ports[i].timer); + pnx8xxx_ports[i].timer.function = pnx8xxx_timeout; + pnx8xxx_ports[i].timer.data = (unsigned long)&pnx8xxx_ports[i]; + pnx8xxx_ports[i].port.ops = &pnx8xxx_pops; + } +} + +#ifdef CONFIG_SERIAL_PNX8XXX_CONSOLE + +static void pnx8xxx_console_putchar(struct uart_port *port, int ch) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + int status; + + do { + /* Wait for UART_TX register to empty */ + status = serial_in(sport, PNX8XXX_FIFO); + } while (status & PNX8XXX_UART_FIFO_TXFIFO); + serial_out(sport, PNX8XXX_FIFO, ch); +} + +/* + * Interrupts are disabled on entering + */static void +pnx8xxx_console_write(struct console *co, const char *s, unsigned int count) +{ + struct pnx8xxx_port *sport = &pnx8xxx_ports[co->index]; + unsigned int old_ien, status; + + /* + * First, save IEN and then disable interrupts + */ + old_ien = serial_in(sport, PNX8XXX_IEN); + serial_out(sport, PNX8XXX_IEN, old_ien & ~(PNX8XXX_UART_INT_ALLTX | + PNX8XXX_UART_INT_ALLRX)); + + uart_console_write(&sport->port, s, count, pnx8xxx_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore IEN + */ + do { + /* Wait for UART_TX register to empty */ + status = serial_in(sport, PNX8XXX_FIFO); + } while (status & PNX8XXX_UART_FIFO_TXFIFO); + + /* Clear TX and EMPTY interrupt */ + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_TX | + PNX8XXX_UART_INT_EMPTY); + + serial_out(sport, PNX8XXX_IEN, old_ien); +} + +static int __init +pnx8xxx_console_setup(struct console *co, char *options) +{ + struct pnx8xxx_port *sport; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index == -1 || co->index >= NR_PORTS) + co->index = 0; + sport = &pnx8xxx_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver pnx8xxx_reg; +static struct console pnx8xxx_console = { + .name = "ttyS", + .write = pnx8xxx_console_write, + .device = uart_console_device, + .setup = pnx8xxx_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &pnx8xxx_reg, +}; + +static int __init pnx8xxx_rs_console_init(void) +{ + pnx8xxx_init_ports(); + register_console(&pnx8xxx_console); + return 0; +} +console_initcall(pnx8xxx_rs_console_init); + +#define PNX8XXX_CONSOLE &pnx8xxx_console +#else +#define PNX8XXX_CONSOLE NULL +#endif + +static struct uart_driver pnx8xxx_reg = { + .owner = THIS_MODULE, + .driver_name = "ttyS", + .dev_name = "ttyS", + .major = SERIAL_PNX8XXX_MAJOR, + .minor = MINOR_START, + .nr = NR_PORTS, + .cons = PNX8XXX_CONSOLE, +}; + +static int pnx8xxx_serial_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct pnx8xxx_port *sport = platform_get_drvdata(pdev); + + return uart_suspend_port(&pnx8xxx_reg, &sport->port); +} + +static int pnx8xxx_serial_resume(struct platform_device *pdev) +{ + struct pnx8xxx_port *sport = platform_get_drvdata(pdev); + + return uart_resume_port(&pnx8xxx_reg, &sport->port); +} + +static int pnx8xxx_serial_probe(struct platform_device *pdev) +{ + struct resource *res = pdev->resource; + int i; + + for (i = 0; i < pdev->num_resources; i++, res++) { + if (!(res->flags & IORESOURCE_MEM)) + continue; + + for (i = 0; i < NR_PORTS; i++) { + if (pnx8xxx_ports[i].port.mapbase != res->start) + continue; + + pnx8xxx_ports[i].port.dev = &pdev->dev; + uart_add_one_port(&pnx8xxx_reg, &pnx8xxx_ports[i].port); + platform_set_drvdata(pdev, &pnx8xxx_ports[i]); + break; + } + } + + return 0; +} + +static int pnx8xxx_serial_remove(struct platform_device *pdev) +{ + struct pnx8xxx_port *sport = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (sport) + uart_remove_one_port(&pnx8xxx_reg, &sport->port); + + return 0; +} + +static struct platform_driver pnx8xxx_serial_driver = { + .driver = { + .name = "pnx8xxx-uart", + .owner = THIS_MODULE, + }, + .probe = pnx8xxx_serial_probe, + .remove = pnx8xxx_serial_remove, + .suspend = pnx8xxx_serial_suspend, + .resume = pnx8xxx_serial_resume, +}; + +static int __init pnx8xxx_serial_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: PNX8XXX driver $Revision: 1.2 $\n"); + + pnx8xxx_init_ports(); + + ret = uart_register_driver(&pnx8xxx_reg); + if (ret == 0) { + ret = platform_driver_register(&pnx8xxx_serial_driver); + if (ret) + uart_unregister_driver(&pnx8xxx_reg); + } + return ret; +} + +static void __exit pnx8xxx_serial_exit(void) +{ + platform_driver_unregister(&pnx8xxx_serial_driver); + uart_unregister_driver(&pnx8xxx_reg); +} + +module_init(pnx8xxx_serial_init); +module_exit(pnx8xxx_serial_exit); + +MODULE_AUTHOR("Embedded Alley Solutions, Inc."); +MODULE_DESCRIPTION("PNX8XXX SoCs serial port driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_PNX8XXX_MAJOR); diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index f84982e508c7..0422c0f1f852 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -1523,9 +1523,8 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) } if (!state->info) { - state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); + state->info = kzalloc(sizeof(struct uart_info), GFP_KERNEL); if (state->info) { - memset(state->info, 0, sizeof(struct uart_info)); init_waitqueue_head(&state->info->open_wait); init_waitqueue_head(&state->info->delta_msr_wait); @@ -1660,6 +1659,7 @@ static const char *uart_type(struct uart_port *port) static int uart_line_info(char *buf, struct uart_driver *drv, int i) { struct uart_state *state = drv->state + i; + int pm_state; struct uart_port *port = state->port; char stat_buf[32]; unsigned int status; @@ -1682,9 +1682,16 @@ static int uart_line_info(char *buf, struct uart_driver *drv, int i) if(capable(CAP_SYS_ADMIN)) { + mutex_lock(&state->mutex); + pm_state = state->pm_state; + if (pm_state) + uart_change_pm(state, 0); spin_lock_irq(&port->lock); status = port->ops->get_mctrl(port); spin_unlock_irq(&port->lock); + if (pm_state) + uart_change_pm(state, pm_state); + mutex_unlock(&state->mutex); ret += sprintf(buf + ret, " tx:%d rx:%d", port->icount.tx, port->icount.rx); @@ -2100,6 +2107,9 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, uart_report_port(drv, port); + /* Power up port for set_mctrl() */ + uart_change_pm(state, 0); + /* * Ensure that the modem control lines are de-activated. * We probably don't need a spinlock around this, but @@ -2167,13 +2177,11 @@ int uart_register_driver(struct uart_driver *drv) * Maybe we should be using a slab cache for this, especially if * we have a large number of ports to handle. */ - drv->state = kmalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); + drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); retval = -ENOMEM; if (!drv->state) goto out; - memset(drv->state, 0, sizeof(struct uart_state) * drv->nr); - normal = alloc_tty_driver(drv->nr); if (!normal) goto out; diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index 431433f4dd6d..c2f1012449da 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -35,7 +35,6 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/sched.h> #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/string.h> @@ -334,10 +333,9 @@ static int serial_probe(struct pcmcia_device *link) DEBUG(0, "serial_attach()\n"); /* Create new serial device */ - info = kmalloc(sizeof (*info), GFP_KERNEL); + info = kzalloc(sizeof (*info), GFP_KERNEL); if (!info) return -ENOMEM; - memset(info, 0, sizeof (*info)); info->p_dev = link; link->priv = info; diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index 145d6236954b..deb9ab4b5a0b 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c @@ -17,7 +17,6 @@ #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> diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index 3ec3df21816b..96a852aa1903 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -17,7 +17,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/tty.h> diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index 244f796dc625..da73205e54cd 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/sched.h> #include <linux/errno.h> #include <linux/delay.h> #include <linux/tty.h> |