diff options
Diffstat (limited to 'drivers/tty/serial/mfd.c')
| -rw-r--r-- | drivers/tty/serial/mfd.c | 130 |
1 files changed, 84 insertions, 46 deletions
diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index c111f36f5d2..445799dc984 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -21,6 +21,10 @@ * be triggered */ +#if defined(CONFIG_SERIAL_MFD_HSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/module.h> #include <linux/init.h> #include <linux/console.h> @@ -36,8 +40,10 @@ #include <linux/serial_mfd.h> #include <linux/dma-mapping.h> #include <linux/pci.h> +#include <linux/nmi.h> #include <linux/io.h> #include <linux/debugfs.h> +#include <linux/pm_runtime.h> #define HSU_DMA_BUF_SIZE 2048 @@ -49,8 +55,8 @@ static int hsu_dma_enable; module_param(hsu_dma_enable, int, 0); -MODULE_PARM_DESC(hsu_dma_enable, "It is a bitmap to set working mode, if \ -bit[x] is 1, then port[x] will work in DMA mode, otherwise in PIO mode."); +MODULE_PARM_DESC(hsu_dma_enable, + "It is a bitmap to set working mode, if bit[x] is 1, then port[x] will work in DMA mode, otherwise in PIO mode."); struct hsu_dma_buffer { u8 *buf; @@ -126,11 +132,6 @@ static inline void serial_out(struct uart_hsu_port *up, int offset, int value) #define HSU_REGS_BUFSIZE 1024 -static int hsu_show_regs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} static ssize_t port_show_regs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -230,14 +231,14 @@ static ssize_t dma_show_regs(struct file *file, char __user *user_buf, static const struct file_operations port_regs_ops = { .owner = THIS_MODULE, - .open = hsu_show_regs_open, + .open = simple_open, .read = port_show_regs, .llseek = default_llseek, }; static const struct file_operations dma_regs_ops = { .owner = THIS_MODULE, - .open = hsu_show_regs_open, + .open = simple_open, .read = dma_show_regs, .llseek = default_llseek, }; @@ -292,7 +293,7 @@ static void serial_hsu_enable_ms(struct uart_port *port) serial_out(up, UART_IER, up->ier); } -void hsu_dma_tx(struct uart_hsu_port *up) +static void hsu_dma_tx(struct uart_hsu_port *up) { struct circ_buf *xmit = &up->port.state->xmit; struct hsu_dma_buffer *dbuf = &up->txbuf; @@ -339,7 +340,8 @@ void hsu_dma_tx(struct uart_hsu_port *up) } /* The buffer is already cache coherent */ -void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf) +static void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, + struct hsu_dma_buffer *dbuf) { dbuf->ofs = 0; @@ -385,17 +387,15 @@ static void serial_hsu_stop_tx(struct uart_port *port) /* This is always called in spinlock protected mode, so * modify timeout timer is safe here */ -void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) +static void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts, + unsigned long *flags) { struct hsu_dma_buffer *dbuf = &up->rxbuf; struct hsu_dma_chan *chan = up->rxc; struct uart_port *port = &up->port; - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; int count; - if (!tty) - return; - /* * First need to know how many is already transferred, * then check if its a timeout DMA irq, and return @@ -426,7 +426,7 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) * explicitly set tail to 0. So head will * always be greater than tail. */ - tty_insert_flip_string(tty, dbuf->buf, count); + tty_insert_flip_string(tport, dbuf->buf, count); port->icount.rx += count; dma_sync_single_for_device(up->port.dev, dbuf->dma_addr, @@ -440,7 +440,9 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) | (0x1 << 16) | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ ); - tty_flip_buffer_push(tty); + spin_unlock_irqrestore(&up->port.lock, *flags); + tty_flip_buffer_push(tport); + spin_lock_irqsave(&up->port.lock, *flags); chan_writel(chan, HSU_CH_CR, 0x3); @@ -461,15 +463,12 @@ static void serial_hsu_stop_rx(struct uart_port *port) } } -static inline void receive_chars(struct uart_hsu_port *up, int *status) +static inline void receive_chars(struct uart_hsu_port *up, int *status, + unsigned long *flags) { - struct tty_struct *tty = up->port.state->port.tty; unsigned int ch, flag; unsigned int max_count = 256; - if (!tty) - return; - do { ch = serial_in(up, UART_RX); flag = TTY_NORMAL; @@ -525,7 +524,10 @@ static inline void receive_chars(struct uart_hsu_port *up, int *status) ignore_char: *status = serial_in(up, UART_LSR); } while ((*status & UART_LSR_DR) && max_count--); - tty_flip_buffer_push(tty); + + spin_unlock_irqrestore(&up->port.lock, *flags); + tty_flip_buffer_push(&up->port.state->port); + spin_lock_irqsave(&up->port.lock, *flags); } static void transmit_chars(struct uart_hsu_port *up) @@ -619,7 +621,7 @@ static irqreturn_t port_irq(int irq, void *dev_id) lsr = serial_in(up, UART_LSR); if (lsr & UART_LSR_DR) - receive_chars(up, &lsr); + receive_chars(up, &lsr, &flags); check_modem_status(up); /* lsr will be renewed during the receive_chars */ @@ -649,7 +651,7 @@ static inline void dma_chan_irq(struct hsu_dma_chan *chan) /* Rx channel */ if (chan->dirt == DMA_FROM_DEVICE) - hsu_dma_rx(up, int_sts); + hsu_dma_rx(up, int_sts, &flags); /* Tx channel */ if (chan->dirt == DMA_TO_DEVICE) { @@ -764,6 +766,8 @@ static int serial_hsu_startup(struct uart_port *port) container_of(port, struct uart_hsu_port, port); unsigned long flags; + pm_runtime_get_sync(up->dev); + /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) @@ -871,6 +875,8 @@ static void serial_hsu_shutdown(struct uart_port *port) UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_out(up, UART_FCR, 0); + + pm_runtime_put(up->dev); } static void @@ -879,7 +885,6 @@ serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios, { struct uart_hsu_port *up = container_of(port, struct uart_hsu_port, port); - struct tty_struct *tty = port->state->port.tty; unsigned char cval, fcr = 0; unsigned long flags; unsigned int baud, quot; @@ -902,8 +907,7 @@ serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios, } /* CMSPAR isn't supported by this driver */ - if (tty) - tty->termios->c_cflag &= ~CMSPAR; + termios->c_cflag &= ~CMSPAR; if (termios->c_cflag & CSTOPB) cval |= UART_LCR_STOP; @@ -973,7 +977,7 @@ serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios, up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (termios->c_iflag & INPCK) up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) up->port.read_status_mask |= UART_LSR_BI; /* Characters to ignore */ @@ -1115,6 +1119,8 @@ serial_hsu_console_write(struct console *co, const char *s, unsigned int count) unsigned int ier; int locked = 1; + touch_nmi_watchdog(); + local_irq_save(flags); if (up->port.sysrq) locked = 0; @@ -1151,7 +1157,6 @@ serial_hsu_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; if (co->index == -1 || co->index >= serial_hsu_reg.nr) co->index = 0; @@ -1162,9 +1167,7 @@ serial_hsu_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - ret = uart_set_options(&up->port, co, baud, parity, bits, flow); - - return ret; + return uart_set_options(&up->port, co, baud, parity, bits, flow); } static struct console serial_hsu_console = { @@ -1173,12 +1176,16 @@ static struct console serial_hsu_console = { .device = uart_console_device, .setup = serial_hsu_console_setup, .flags = CON_PRINTBUFFER, - .index = 2, + .index = -1, .data = &serial_hsu_reg, }; + +#define SERIAL_HSU_CONSOLE (&serial_hsu_console) +#else +#define SERIAL_HSU_CONSOLE NULL #endif -struct uart_ops serial_hsu_pops = { +static struct uart_ops serial_hsu_pops = { .tx_empty = serial_hsu_tx_empty, .set_mctrl = serial_hsu_set_mctrl, .get_mctrl = serial_hsu_get_mctrl, @@ -1205,6 +1212,7 @@ static struct uart_driver serial_hsu_reg = { .major = TTY_MAJOR, .minor = 128, .nr = 3, + .cons = SERIAL_HSU_CONSOLE, }; #ifdef CONFIG_PM @@ -1249,6 +1257,34 @@ static int serial_hsu_resume(struct pci_dev *pdev) #define serial_hsu_resume NULL #endif +#ifdef CONFIG_PM_RUNTIME +static int serial_hsu_runtime_idle(struct device *dev) +{ + pm_schedule_suspend(dev, 500); + return -EBUSY; +} + +static int serial_hsu_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int serial_hsu_runtime_resume(struct device *dev) +{ + return 0; +} +#else +#define serial_hsu_runtime_idle NULL +#define serial_hsu_runtime_suspend NULL +#define serial_hsu_runtime_resume NULL +#endif + +static const struct dev_pm_ops serial_hsu_pm_ops = { + .runtime_suspend = serial_hsu_runtime_suspend, + .runtime_resume = serial_hsu_runtime_resume, + .runtime_idle = serial_hsu_runtime_idle, +}; + /* temp global pointer before we settle down on using one or four PCI dev */ static struct hsu_port *phsu; @@ -1306,15 +1342,12 @@ static int serial_hsu_probe(struct pci_dev *pdev, } uart_add_one_port(&serial_hsu_reg, &uport->port); -#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE - if (index == 2) { - register_console(&serial_hsu_console); - uport->port.cons = &serial_hsu_console; - } -#endif pci_set_drvdata(pdev, uport); } + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); + return 0; err_disable: @@ -1411,19 +1444,21 @@ static void serial_hsu_remove(struct pci_dev *pdev) if (!priv) return; + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + /* For port 0/1/2, priv is the address of uart_hsu_port */ if (pdev->device != 0x081E) { up = priv; uart_remove_one_port(&serial_hsu_reg, &up->port); } - pci_set_drvdata(pdev, NULL); free_irq(pdev->irq, priv); pci_disable_device(pdev); } /* First 3 are UART ports, and the 4th is the DMA */ -static const struct pci_device_id pci_ids[] __devinitdata = { +static const struct pci_device_id pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) }, @@ -1435,9 +1470,12 @@ static struct pci_driver hsu_pci_driver = { .name = "HSU serial", .id_table = pci_ids, .probe = serial_hsu_probe, - .remove = __devexit_p(serial_hsu_remove), + .remove = serial_hsu_remove, .suspend = serial_hsu_suspend, .resume = serial_hsu_resume, + .driver = { + .pm = &serial_hsu_pm_ops, + }, }; static int __init hsu_pci_init(void) @@ -1467,4 +1505,4 @@ module_init(hsu_pci_init); module_exit(hsu_pci_exit); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:medfield-hsu"); +MODULE_DEVICE_TABLE(pci, pci_ids); |
