diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2010-07-26 14:44:39 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-07-26 14:44:39 -0700 |
| commit | 05eb87b12a0ff0b645dcf70ccc91942638f84802 (patch) | |
| tree | d108e0bf3f90aae0e59dd715e31636b9fbd19aba /tty | |
| parent | ba9d216e7d22ef8010eeef5deacbb37bc9e58f67 (diff) | |
| download | patches-05eb87b12a0ff0b645dcf70ccc91942638f84802.tar.gz | |
more patches
Diffstat (limited to 'tty')
| -rw-r--r-- | tty/hsu-add-a-periodic-timer-to-check-dma-rx-channel.patch | 142 | ||||
| -rw-r--r-- | tty/hsu-call-pci-pm-hooks-in-suspend-resume-function.patch | 69 | ||||
| -rw-r--r-- | tty/hsu-driver-for-medfield-high-speed-uart-device.patch | 1596 | ||||
| -rw-r--r-- | tty/hsu-some-code-cleanup.patch | 282 | ||||
| -rw-r--r-- | tty/mxser-remove-unnesesary-null-check.patch | 33 | ||||
| -rw-r--r-- | tty/serial-add-support-for-ox16pci958-card.patch | 65 |
6 files changed, 2187 insertions, 0 deletions
diff --git a/tty/hsu-add-a-periodic-timer-to-check-dma-rx-channel.patch b/tty/hsu-add-a-periodic-timer-to-check-dma-rx-channel.patch new file mode 100644 index 00000000000000..6553a6dc05b74c --- /dev/null +++ b/tty/hsu-add-a-periodic-timer-to-check-dma-rx-channel.patch @@ -0,0 +1,142 @@ +From alan@linux.intel.com Mon Jul 26 14:23:17 2010 +From: Alan Cox <alan@linux.intel.com> +Subject: hsu: add a periodic timer to check dma rx channel +To: greg@kroah.com, linux-serial@vger.kernel.org +Date: Mon, 26 Jul 2010 10:18:21 +0100 +Message-ID: <20100726091818.21579.67436.stgit@localhost.localdomain> + +From: Feng Tang <feng.tang@intel.com> + +A general problem for uart rx dma channel is you never know when +and how much data will be received, so usually preset it a DMA +descriptor with a big size, and rely on DMA RX timeout IRQ to +know there is some data in rx channel. + +For a RX data size of multiple of MOTSR, there will be no timeout +IRQ issued, thus OS will never be notified about that. + +This is a work around for that, current timer frequency is 5 times +per second, it should vary according to the baud rate + +When future silicon version fix the problem, this workaround need +be removed + +Signed-off-by: Feng Tang <feng.tang@intel.com> +Signed-off-by: Alan Cox <alan@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/mfd.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 47 insertions(+), 3 deletions(-) + +--- a/drivers/serial/mfd.c ++++ b/drivers/serial/mfd.c +@@ -64,6 +64,8 @@ + #define mfd_readl(obj, offset) readl(obj->reg + offset) + #define mfd_writel(obj, offset, val) writel(val, obj->reg + offset) + ++#define HSU_DMA_TIMEOUT_CHECK_FREQ (HZ/10) ++ + struct hsu_dma_buffer { + u8 *buf; + dma_addr_t dma_addr; +@@ -75,7 +77,8 @@ struct hsu_dma_chan { + u32 id; + u32 dirt; /* to or from device */ + struct uart_hsu_port *uport; +- void __iomem *reg; ++ void __iomem *reg; ++ struct timer_list rx_timer; /* only needed by RX channel */ + }; + + struct uart_hsu_port { +@@ -377,6 +380,8 @@ void hsu_dma_start_rx_chan(struct hsu_dm + | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ + ); + chan_writel(rxc, HSU_CH_CR, 0x3); ++ ++ mod_timer(&rxc->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); + } + + /* Protected by spin_lock_irqsave(port->lock) */ +@@ -437,8 +442,13 @@ void hsu_dma_rx(struct uart_hsu_port *up + /* We can use 2 ways to calc the actual transfer len */ + count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; + +- if (!count) ++ if (!count) { ++ /* restart the channel before we leave */ ++ chan_writel(chan, HSU_CH_CR, 0x3); + return; ++ } ++ ++ del_timer(&chan->rx_timer); + + dma_sync_single_for_cpu(port->dev, dbuf->dma_addr, + dbuf->dma_size, DMA_FROM_DEVICE); +@@ -463,9 +473,12 @@ void hsu_dma_rx(struct uart_hsu_port *up + | (0x1 << 16) + | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ + ); ++ tty_flip_buffer_push(tty); ++ + chan_writel(chan, HSU_CH_CR, 0x3); ++ chan->rx_timer.expires = jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ; ++ add_timer(&chan->rx_timer); + +- tty_flip_buffer_push(tty); + } + + static void serial_hsu_stop_rx(struct uart_port *port) +@@ -893,6 +906,8 @@ static void serial_hsu_shutdown(struct u + container_of(port, struct uart_hsu_port, port); + unsigned long flags; + ++ del_timer_sync(&up->rxc->rx_timer); ++ + /* Disable interrupts from this port */ + up->ier = 0; + serial_out(up, UART_IER, 0); +@@ -1348,6 +1363,28 @@ err_disable: + return ret; + } + ++static void hsu_dma_rx_timeout(unsigned long data) ++{ ++ struct hsu_dma_chan *chan = (void *)data; ++ struct uart_hsu_port *up = chan->uport; ++ struct hsu_dma_buffer *dbuf = &up->rxbuf; ++ int count = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ ++ count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; ++ ++ if (!count) { ++ mod_timer(&chan->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); ++ goto exit; ++ } ++ ++ hsu_dma_rx(up, 0); ++exit: ++ spin_unlock_irqrestore(&up->port.lock, flags); ++} ++ + static void hsu_global_init(void) + { + struct hsu_port *hsu; +@@ -1409,6 +1446,13 @@ static void hsu_global_init(void) + dchan->uport = &hsu->port[i/2]; + dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET + + i * HSU_DMA_CHANS_REG_LENGTH; ++ ++ /* Work around for RX */ ++ if (dchan->dirt == DMA_FROM_DEVICE) { ++ init_timer(&dchan->rx_timer); ++ dchan->rx_timer.function = hsu_dma_rx_timeout; ++ dchan->rx_timer.data = (unsigned long)dchan; ++ } + dchan++; + } + diff --git a/tty/hsu-call-pci-pm-hooks-in-suspend-resume-function.patch b/tty/hsu-call-pci-pm-hooks-in-suspend-resume-function.patch new file mode 100644 index 00000000000000..956724458e6ef7 --- /dev/null +++ b/tty/hsu-call-pci-pm-hooks-in-suspend-resume-function.patch @@ -0,0 +1,69 @@ +From alan@linux.intel.com Mon Jul 26 14:23:51 2010 +From: Alan Cox <alan@linux.intel.com> +Subject: hsu: call PCI pm hooks in suspend/resume function +To: greg@kroah.com, linux-serial@vger.kernel.org +Date: Mon, 26 Jul 2010 10:18:46 +0100 +Message-ID: <20100726091839.21579.4983.stgit@localhost.localdomain> + +From: Feng Tang <feng.tang@intel.com> + +Also add check for dma controller or the uart ports. + +Signed-off-by: Feng Tang <feng.tang@intel.com> +Signed-off-by: Alan Cox <alan@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/mfd.c | 31 ++++++++++++++++++++++--------- + 1 file changed, 22 insertions(+), 9 deletions(-) + +--- a/drivers/serial/mfd.c ++++ b/drivers/serial/mfd.c +@@ -1217,25 +1217,38 @@ static struct uart_driver serial_hsu_reg + #ifdef CONFIG_PM + static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state) + { ++ void *priv = pci_get_drvdata(pdev); + struct uart_hsu_port *up; + +- up = pci_get_drvdata(pdev); +- if (!up) +- return 0; +- +- uart_suspend_port(&serial_hsu_reg, &up->port); ++ /* Make sure this is not the internal dma controller */ ++ if (priv && (pdev->device != 0x081E)) { ++ up = priv; ++ uart_suspend_port(&serial_hsu_reg, &up->port); ++ } + ++ pci_save_state(pdev); ++ pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; + } + + static int serial_hsu_resume(struct pci_dev *pdev) + { ++ void *priv = pci_get_drvdata(pdev); + struct uart_hsu_port *up; ++ int ret; ++ ++ pci_set_power_state(pdev, PCI_D0); ++ pci_restore_state(pdev); ++ ++ ret = pci_enable_device(pdev); ++ if (ret) ++ dev_warn(&pdev->dev, ++ "HSU: can't re-enable device, try to continue\n"); + +- up = pci_get_drvdata(pdev); +- if (!up) +- return 0; +- uart_resume_port(&serial_hsu_reg, &up->port); ++ if (priv && (pdev->device != 0x081E)) { ++ up = priv; ++ uart_resume_port(&serial_hsu_reg, &up->port); ++ } + return 0; + } + #else diff --git a/tty/hsu-driver-for-medfield-high-speed-uart-device.patch b/tty/hsu-driver-for-medfield-high-speed-uart-device.patch new file mode 100644 index 00000000000000..fe3e1747abf3b7 --- /dev/null +++ b/tty/hsu-driver-for-medfield-high-speed-uart-device.patch @@ -0,0 +1,1596 @@ +From alan@linux.intel.com Mon Jul 26 14:22:51 2010 +From: Alan Cox <alan@linux.intel.com> +Subject: hsu: driver for Medfield High Speed UART device +To: greg@kroah.com, linux-serial@vger.kernel.org +Date: Mon, 26 Jul 2010 10:18:10 +0100 +Message-ID: <20100726091803.21579.750.stgit@localhost.localdomain> + +From: Feng Tang <feng.tang@intel.com> + +This is a PCI & UART driver, which suppors both PIO and DMA mode +UART operation. It has 3 identical UART ports and one internal +DMA controller. + +Current FW will export 4 pci devices for hsu: 3 uart ports and 1 +dma controller, each has one IRQ line. And we need to discuss the +device model, one PCI device covering whole HSU should be a better +model, but there is a problem of how to export the 4 IRQs info + +Current driver set the highest baud rate to 2746800bps, which is +easy to scale down to 115200/230400.... To suport higher baud rate, +we need add special process, change DLAB/DLH/PS/DIV/MUL registers +all together. + +921600 is the highest baud rate that has been tested with Bluetooth +modem connected to HSU port 0. Will test more when there is right +BT firmware. + +Current version contains several work around for A0's Silicon bugs + +Signed-off-by: Feng Tang <feng.tang@intel.com> +Signed-off-by: Alan Cox <alan@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/Kconfig | 10 + drivers/serial/Makefile | 1 + drivers/serial/mfd.c | 1488 ++++++++++++++++++++++++++++++++++++++++++++ + include/linux/serial_core.h | 2 + include/linux/serial_reg.h | 16 + 5 files changed, 1517 insertions(+) + +--- a/drivers/serial/Kconfig ++++ b/drivers/serial/Kconfig +@@ -732,6 +732,16 @@ config MRST_MAX3110_IRQ + help + This has to be enabled after Moorestown GPIO driver is loaded + ++config SERIAL_MFD_HSU ++ tristate "Medfield High Speed UART support" ++ depends on PCI ++ select SERIAL_CORE ++ ++config SERIAL_MFD_HSU_CONSOLE ++ boolean "Medfile HSU serial console support" ++ depends on SERIAL_MFD_HSU=y ++ select SERIAL_CORE_CONSOLE ++ + config SERIAL_BFIN + tristate "Blackfin serial port support" + depends on BLACKFIN +--- a/drivers/serial/Makefile ++++ b/drivers/serial/Makefile +@@ -87,3 +87,4 @@ obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUAR + obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o + obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o + obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o ++obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o +--- /dev/null ++++ b/drivers/serial/mfd.c +@@ -0,0 +1,1488 @@ ++/* ++ * mfd.c: driver for High Speed UART device of Intel Medfield platform ++ * ++ * Refer pxa.c, 8250.c and some other drivers in drivers/serial/ ++ * ++ * (C) Copyright 2009 Intel Corporation ++ * ++ * 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; version 2 ++ * of the License. ++ */ ++ ++ ++/* Notes: ++ * 1. there should be 2 types of register access method, one for ++ * UART ports, the other for the general purpose registers ++ * ++ * 2. It used to have a Irda port, but was defeatured recently ++ * ++ * 3. Based on the info from HSU MAS, 0/1 channel are assigned to ++ * port0, 2/3 chan to port 1, 4/5 chan to port 3. Even number ++ * chan will be read, odd chan for write ++ * ++ * 4. HUS supports both the 64B and 16B FIFO version, but this driver ++ * will only use 64B version ++ * ++ * 5. In A0 stepping, UART will not support TX half empty flag, thus ++ * need add a #ifdef judgement ++ * ++ * 6. One more bug for A0, the loopback mode won't support AFC ++ * auto-flow control ++ * ++ * 7. HSU has some special FCR control bits, we add it to serial_reg.h ++ * ++ * 8. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always asserted, ++ * only when the HW is reset the DDCD and DDSR will be triggered ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/console.h> ++#include <linux/sysrq.h> ++#include <linux/serial_reg.h> ++#include <linux/circ_buf.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/tty.h> ++#include <linux/tty_flip.h> ++#include <linux/serial_core.h> ++#include <linux/serial_mfd.h> ++#include <linux/dma-mapping.h> ++#include <linux/pci.h> ++#include <linux/io.h> ++#include <linux/debugfs.h> ++ ++#define MFD_HSU_A0_STEPPING 1 ++ ++#define HSU_DMA_BUF_SIZE 2048 ++ ++#define chan_readl(chan, offset) readl(chan->reg + offset) ++#define chan_writel(chan, offset, val) writel(val, chan->reg + offset) ++ ++#define mfd_readl(obj, offset) readl(obj->reg + offset) ++#define mfd_writel(obj, offset, val) writel(val, obj->reg + offset) ++ ++struct hsu_dma_buffer { ++ u8 *buf; ++ dma_addr_t dma_addr; ++ u32 dma_size; ++ u32 ofs; ++}; ++ ++struct hsu_dma_chan { ++ u32 id; ++ u32 dirt; /* to or from device */ ++ struct uart_hsu_port *uport; ++ void __iomem *reg; ++}; ++ ++struct uart_hsu_port { ++ struct uart_port port; ++ unsigned char ier; ++ unsigned char lcr; ++ unsigned char mcr; ++ unsigned int lsr_break_flag; ++ char name[12]; ++ int index; ++ struct device *dev; ++ ++ struct hsu_dma_chan *txc; ++ struct hsu_dma_chan *rxc; ++ struct hsu_dma_buffer txbuf; ++ struct hsu_dma_buffer rxbuf; ++ int use_dma; /* flag for DMA/PIO */ ++ int running; ++ int dma_tx_on; ++}; ++ ++/* Top level data structure of HSU */ ++struct hsu_port { ++ struct pci_device *pdev; ++ ++ void __iomem *reg; ++ unsigned long paddr; ++ unsigned long iolen; ++ u32 irq; ++ ++ struct uart_hsu_port port[3]; ++ struct hsu_dma_chan chans[10]; ++ ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *debugfs; ++#endif ++}; ++ ++static inline void hexdump(char *str, u8 *addr, int cnt) ++{ ++ int i; ++ ++ for (i = 0; i < cnt; i += 8) { ++ printk("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", ++ addr[i], addr[i+1], addr[i+2], addr[i+3], ++ addr[i+4], addr[i+5], addr[i+6], addr[i+7]); ++ printk("\n"); ++ } ++} ++ ++static inline unsigned int serial_in(struct uart_hsu_port *up, int offset) ++{ ++ unsigned int val; ++ ++ if (offset > UART_MSR) { ++ offset <<= 2; ++ val = readl(up->port.membase + offset); ++ } else ++ val = (unsigned int)readb(up->port.membase + offset); ++ ++ return val; ++} ++ ++static inline void serial_out(struct uart_hsu_port *up, int offset, int value) ++{ ++ if (offset > UART_MSR) { ++ offset <<= 2; ++ writel(value, up->port.membase + offset); ++ } else { ++ unsigned char val = value & 0xff; ++ writeb(val, up->port.membase + offset); ++ } ++} ++ ++#ifdef CONFIG_DEBUG_FS ++ ++#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) ++{ ++ struct uart_hsu_port *up = file->private_data; ++ char *buf; ++ u32 len = 0; ++ ssize_t ret; ++ ++ buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); ++ if (!buf) ++ return 0; ++ ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "MFD HSU port[%d] regs:\n", up->index); ++ ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "=================================\n"); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "IER: \t\t0x%08x\n", serial_in(up, UART_IER)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "IIR: \t\t0x%08x\n", serial_in(up, UART_IIR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "LCR: \t\t0x%08x\n", serial_in(up, UART_LCR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "MCR: \t\t0x%08x\n", serial_in(up, UART_MCR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "LSR: \t\t0x%08x\n", serial_in(up, UART_LSR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "MSR: \t\t0x%08x\n", serial_in(up, UART_MSR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "FOR: \t\t0x%08x\n", serial_in(up, UART_FOR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "PS: \t\t0x%08x\n", serial_in(up, UART_PS)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "MUL: \t\t0x%08x\n", serial_in(up, UART_MUL)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "DIV: \t\t0x%08x\n", serial_in(up, UART_DIV)); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); ++ kfree(buf); ++ return ret; ++} ++ ++static ssize_t dma_show_regs(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct hsu_dma_chan *chan = file->private_data; ++ char *buf; ++ u32 len = 0; ++ ssize_t ret; ++ ++ buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); ++ if (!buf) ++ return 0; ++ ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "MFD HSU DMA channel [%d] regs:\n", chan->id); ++ ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "=================================\n"); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "CR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_CR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "DCR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_DCR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "BSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_BSR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "MOTSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_MOTSR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0SAR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0TSR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1SAR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1TSR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2SAR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2TSR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3SAR)); ++ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, ++ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3TSR)); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); ++ kfree(buf); ++ return ret; ++} ++ ++static const struct file_operations port_regs_ops = { ++ .owner = THIS_MODULE, ++ .open = hsu_show_regs_open, ++ .read = port_show_regs, ++}; ++ ++static const struct file_operations dma_regs_ops = { ++ .owner = THIS_MODULE, ++ .open = hsu_show_regs_open, ++ .read = dma_show_regs, ++}; ++ ++static int hsu_debugfs_init(struct hsu_port *hsu) ++{ ++ int i; ++ char name[32]; ++ ++ hsu->debugfs = debugfs_create_dir("hsu", NULL); ++ if (!hsu->debugfs) ++ return -ENOMEM; ++ ++ for (i = 0; i < 3; i++) { ++ snprintf(name, sizeof(name), "port_%d_regs", i); ++ debugfs_create_file(name, S_IFREG | S_IRUGO, ++ hsu->debugfs, (void *)(&hsu->port[i]), &port_regs_ops); ++ } ++ ++ for (i = 0; i < 6; i++) { ++ snprintf(name, sizeof(name), "dma_chan_%d_regs", i); ++ debugfs_create_file(name, S_IFREG | S_IRUGO, ++ hsu->debugfs, (void *)&hsu->chans[i], &dma_regs_ops); ++ } ++ ++ return 0; ++} ++ ++static void hsu_debugfs_remove(struct hsu_port *hsu) ++{ ++ if (hsu->debugfs) ++ debugfs_remove_recursive(hsu->debugfs); ++} ++ ++#else ++static inline int hsu_debugfs_init(struct hsu_port *hsu) ++{ ++ return 0; ++} ++ ++static inline void hsu_debugfs_remove(struct hsu_port *hsu) ++{ ++} ++#endif /* CONFIG_DEBUG_FS */ ++ ++static void serial_hsu_enable_ms(struct uart_port *port) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ ++ up->ier |= UART_IER_MSI; ++ serial_out(up, UART_IER, up->ier); ++} ++ ++void hsu_dma_tx(struct uart_hsu_port *up) ++{ ++ struct circ_buf *xmit = &up->port.state->xmit; ++ struct hsu_dma_buffer *dbuf = &up->txbuf; ++ int count; ++ ++ /* test_and_set_bit may be better, but anyway it's in lock protected mode */ ++ if (up->dma_tx_on) ++ return; ++ ++ /* Update the circ buf info */ ++ xmit->tail += dbuf->ofs; ++ xmit->tail &= UART_XMIT_SIZE - 1; ++ ++ up->port.icount.tx += dbuf->ofs; ++ dbuf->ofs = 0; ++ ++ /* Disable the channel */ ++ chan_writel(up->txc, HSU_CH_CR, 0x0); ++ ++ if (!uart_circ_empty(xmit) && !uart_tx_stopped(&up->port)) { ++ dma_sync_single_for_device(up->port.dev, ++ dbuf->dma_addr, ++ dbuf->dma_size, ++ DMA_TO_DEVICE); ++ ++ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); ++ dbuf->ofs = count; ++ ++ /* Reprogram the channel */ ++ chan_writel(up->txc, HSU_CH_D0SAR, dbuf->dma_addr + xmit->tail); ++ chan_writel(up->txc, HSU_CH_D0TSR, count); ++ ++ /* Reenable the channel */ ++ chan_writel(up->txc, HSU_CH_DCR, 0x1 ++ | (0x1 << 8) ++ | (0x1 << 16) ++ | (0x1 << 24)); ++ ++ WARN(chan_readl(up->txc, HSU_CH_CR) & 0x1, ++ "TX channel has already be started!!\n"); ++ up->dma_tx_on = 1; ++ chan_writel(up->txc, HSU_CH_CR, 0x1); ++ } ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(&up->port); ++} ++ ++/* The buffer is already cache coherent */ ++void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf) ++{ ++ /* Need start RX dma channel here */ ++ dbuf->ofs = 0; ++ ++ chan_writel(rxc, HSU_CH_BSR, 32); ++ chan_writel(rxc, HSU_CH_MOTSR, 4); ++ ++ chan_writel(rxc, HSU_CH_D0SAR, dbuf->dma_addr); ++ chan_writel(rxc, HSU_CH_D0TSR, dbuf->dma_size); ++ chan_writel(rxc, HSU_CH_DCR, 0x1 | (0x1 << 8) ++ | (0x1 << 16) ++ | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ ++ ); ++ chan_writel(rxc, HSU_CH_CR, 0x3); ++} ++ ++/* Protected by spin_lock_irqsave(port->lock) */ ++static void serial_hsu_start_tx(struct uart_port *port) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ ++ if (up->use_dma) { ++ hsu_dma_tx(up); ++ } else if (!(up->ier & UART_IER_THRI)) { ++ up->ier |= UART_IER_THRI; ++ serial_out(up, UART_IER, up->ier); ++ } ++} ++ ++static void serial_hsu_stop_tx(struct uart_port *port) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ struct hsu_dma_chan *txc = up->txc; ++ ++ if (up->use_dma) ++ chan_writel(txc, HSU_CH_CR, 0x0); ++ else if (up->ier & UART_IER_THRI) { ++ up->ier &= ~UART_IER_THRI; ++ serial_out(up, UART_IER, up->ier); ++ } ++} ++ ++/* 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) ++{ ++ 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; ++ int count; ++ ++ if (!tty) ++ return; ++ ++ /* ++ * first need to know how many is already transferred, ++ * then check if its a timeout DMA irq, and return ++ * the trail bytes out, push them up and reenable the ++ * channel, better to use 2 descriptors at the same time ++ */ ++ ++ /* timeout IRQ, need wait some time, see Errata 2 */ ++ if (int_sts & 0xf00) ++ udelay(2); ++ ++ /* Stop the channel */ ++ chan_writel(chan, HSU_CH_CR, 0x0); ++ ++ /* We can use 2 ways to calc the actual transfer len */ ++ count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; ++ ++ if (!count) ++ return; ++ ++ dma_sync_single_for_cpu(port->dev, dbuf->dma_addr, ++ dbuf->dma_size, DMA_FROM_DEVICE); ++ ++ /* ++ * head will only wrap around when we recycle ++ * the DMA buffer, and when that happens, we ++ * explicitly set tail to 0. So head will ++ * always be greater than tail. ++ */ ++ tty_insert_flip_string(tty, dbuf->buf, count); ++ port->icount.rx += count; ++ ++ dma_sync_single_for_device(up->port.dev, dbuf->dma_addr, ++ dbuf->dma_size, DMA_FROM_DEVICE); ++ ++ /* Reprogram the channel */ ++ chan_writel(chan, HSU_CH_D0SAR, dbuf->dma_addr); ++ chan_writel(chan, HSU_CH_D0TSR, dbuf->dma_size); ++ chan_writel(chan, HSU_CH_DCR, 0x1 ++ | (0x1 << 8) ++ | (0x1 << 16) ++ | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ ++ ); ++ chan_writel(chan, HSU_CH_CR, 0x3); ++ ++ tty_flip_buffer_push(tty); ++} ++ ++static void serial_hsu_stop_rx(struct uart_port *port) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ struct hsu_dma_chan *chan = up->rxc; ++ ++ if (up->use_dma) ++ chan_writel(chan, HSU_CH_CR, 0x2); ++ else { ++ up->ier &= ~UART_IER_RLSI; ++ up->port.read_status_mask &= ~UART_LSR_DR; ++ serial_out(up, UART_IER, up->ier); ++ } ++} ++ ++/* ++ * if there is error flag, should we just reset the FIFO or keeps ++ * working on it ++ */ ++static inline void receive_chars(struct uart_hsu_port *up, int *status) ++{ ++ 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; ++ up->port.icount.rx++; ++ ++ if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | ++ UART_LSR_FE | UART_LSR_OE))) { ++ ++ dev_warn(up->dev, "We really rush into ERR/BI case" ++ "status = 0x%02x", *status); ++ /* For statistics only */ ++ if (*status & UART_LSR_BI) { ++ *status &= ~(UART_LSR_FE | UART_LSR_PE); ++ up->port.icount.brk++; ++ /* ++ * We do the SysRQ and SAK checking ++ * here because otherwise the break ++ * may get masked by ignore_status_mask ++ * or read_status_mask. ++ */ ++ if (uart_handle_break(&up->port)) ++ goto ignore_char; ++ } else if (*status & UART_LSR_PE) ++ up->port.icount.parity++; ++ else if (*status & UART_LSR_FE) ++ up->port.icount.frame++; ++ if (*status & UART_LSR_OE) ++ up->port.icount.overrun++; ++ ++ /* Mask off conditions which should be ignored. */ ++ *status &= up->port.read_status_mask; ++ ++#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE ++ if (up->port.cons && ++ up->port.cons->index == up->port.line) { ++ /* Recover the break flag from console xmit */ ++ *status |= up->lsr_break_flag; ++ up->lsr_break_flag = 0; ++ } ++#endif ++ if (*status & UART_LSR_BI) { ++ flag = TTY_BREAK; ++ } else if (*status & UART_LSR_PE) ++ flag = TTY_PARITY; ++ else if (*status & UART_LSR_FE) ++ flag = TTY_FRAME; ++ } ++ ++ if (uart_handle_sysrq_char(&up->port, ch)) ++ goto ignore_char; ++ ++ uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag); ++ ignore_char: ++ *status = serial_in(up, UART_LSR); ++ } while ((*status & UART_LSR_DR) && max_count--); ++ tty_flip_buffer_push(tty); ++} ++ ++static void transmit_chars(struct uart_hsu_port *up) ++{ ++ struct circ_buf *xmit = &up->port.state->xmit; ++ int count; ++ int i = 0; /* for debug use */ ++ ++ if (up->port.x_char) { ++ serial_out(up, UART_TX, up->port.x_char); ++ up->port.icount.tx++; ++ up->port.x_char = 0; ++ return; ++ } ++ if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { ++ serial_hsu_stop_tx(&up->port); ++ return; ++ } ++ ++#ifndef MFD_HSU_A0_STEPPING ++ count = up->port.fifosize / 2; ++#else ++ /* ++ * A0 only supports fully empty IRQ, and the first char written ++ * into it won't clear the EMPT bit, so we may need be cautious ++ * by useing a shorter buffer ++ */ ++ /* count = up->port.fifosize; */ ++ count = up->port.fifosize - 4; ++#endif ++ do { ++ serial_out(up, UART_TX, xmit->buf[xmit->tail]); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ i++; ++ ++ up->port.icount.tx++; ++ if (uart_circ_empty(xmit)) ++ break; ++ } while (--count > 0); ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(&up->port); ++ ++ if (uart_circ_empty(xmit)) ++ serial_hsu_stop_tx(&up->port); ++} ++ ++static inline void check_modem_status(struct uart_hsu_port *up) ++{ ++ int status; ++ ++ status = serial_in(up, UART_MSR); ++ ++ if ((status & UART_MSR_ANY_DELTA) == 0) ++ return; ++ ++ if (status & UART_MSR_TERI) ++ up->port.icount.rng++; ++ if (status & UART_MSR_DDSR) ++ up->port.icount.dsr++; ++ /* We may only get DDCD when HW init and reset */ ++ if (status & UART_MSR_DDCD) ++ uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); ++ /* will start/stop_tx accordingly */ ++ if (status & UART_MSR_DCTS) ++ uart_handle_cts_change(&up->port, status & UART_MSR_CTS); ++ ++ wake_up_interruptible(&up->port.state->port.delta_msr_wait); ++} ++ ++/* ++ * This handles the interrupt from one port. ++ */ ++static irqreturn_t port_irq(int irq, void *dev_id) ++{ ++ struct uart_hsu_port *up = dev_id; ++ unsigned int iir, lsr; ++ unsigned long flags; ++ ++ if (unlikely(!up->running)) ++ return IRQ_NONE; ++ ++ if (up->use_dma) { ++ lsr = serial_in(up, UART_LSR); ++ if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | ++ UART_LSR_FE | UART_LSR_OE))) ++ dev_warn(up->dev, ++ "Got lsr irq while using DMA, lsr = 0x%2x\n", ++ lsr); ++ check_modem_status(up); ++ return IRQ_HANDLED; ++ } ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ iir = serial_in(up, UART_IIR); ++ if (iir & UART_IIR_NO_INT) { ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ return IRQ_NONE; ++ } ++ ++ lsr = serial_in(up, UART_LSR); ++ ++ if (lsr & UART_LSR_DR) ++ receive_chars(up, &lsr); ++ ++ /* lsr will be renewed during the receive_chars */ ++ if (lsr & UART_LSR_THRE) ++ transmit_chars(up); ++ ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ return IRQ_HANDLED; ++} ++ ++static inline void dma_chan_irq(struct hsu_dma_chan *chan) ++{ ++ struct uart_hsu_port *up = chan->uport; ++ unsigned long flags; ++ u32 int_sts; ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ ++ if (!up->use_dma || !up->running) ++ goto exit; ++ ++ /* ++ * No matter what situation, need read clear the IRQ status ++ * There is a bug, see Errata 5, HSD 2900918 ++ */ ++ int_sts = chan_readl(chan, HSU_CH_SR); ++ ++ /* Rx channel */ ++ if (chan->dirt == DMA_FROM_DEVICE) ++ hsu_dma_rx(up, int_sts); ++ ++ /* Tx channel */ ++ if (chan->dirt == DMA_TO_DEVICE) { ++ /* dma for irq should be done */ ++ chan_writel(chan, HSU_CH_CR, 0x0); ++ up->dma_tx_on = 0; ++ hsu_dma_tx(up); ++ } ++ ++exit: ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ return; ++} ++ ++static irqreturn_t dma_irq(int irq, void *dev_id) ++{ ++ struct hsu_port *hsu = dev_id; ++ u32 int_sts, i; ++ ++ int_sts = mfd_readl(hsu, HSU_GBL_DMAISR); ++ ++ /* Currently we only have 6 channels may be used */ ++ for (i = 0; i < 6; i++) { ++ if (int_sts & 0x1) ++ dma_chan_irq(&hsu->chans[i]); ++ int_sts >>= 1; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static unsigned int serial_hsu_tx_empty(struct uart_port *port) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ unsigned long flags; ++ unsigned int ret; ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ ++ return ret; ++} ++ ++static unsigned int serial_hsu_get_mctrl(struct uart_port *port) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ unsigned char status; ++ unsigned int ret; ++ ++ status = serial_in(up, UART_MSR); ++ ++ ret = 0; ++ if (status & UART_MSR_DCD) ++ ret |= TIOCM_CAR; ++ if (status & UART_MSR_RI) ++ ret |= TIOCM_RNG; ++ if (status & UART_MSR_DSR) ++ ret |= TIOCM_DSR; ++ if (status & UART_MSR_CTS) ++ ret |= TIOCM_CTS; ++ return ret; ++} ++ ++static void serial_hsu_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ unsigned char mcr = 0; ++ ++ if (mctrl & TIOCM_RTS) ++ mcr |= UART_MCR_RTS; ++ if (mctrl & TIOCM_DTR) ++ mcr |= UART_MCR_DTR; ++ if (mctrl & TIOCM_OUT1) ++ mcr |= UART_MCR_OUT1; ++ if (mctrl & TIOCM_OUT2) ++ mcr |= UART_MCR_OUT2; ++ if (mctrl & TIOCM_LOOP) ++ mcr |= UART_MCR_LOOP; ++ ++ mcr |= up->mcr; ++ ++ serial_out(up, UART_MCR, mcr); ++} ++ ++static void serial_hsu_break_ctl(struct uart_port *port, int break_state) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ if (break_state == -1) ++ up->lcr |= UART_LCR_SBC; ++ else ++ up->lcr &= ~UART_LCR_SBC; ++ serial_out(up, UART_LCR, up->lcr); ++ spin_unlock_irqrestore(&up->port.lock, flags); ++} ++ ++/* ++ * What special to do: ++ * 1. chose the 64B fifo mode ++ * 2. make sure not to select half empty mode for A0 stepping ++ * 3. start dma or pio depends on configuration ++ * 4. we only allocate dma memory when needed ++ */ ++static int serial_hsu_startup(struct uart_port *port) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ unsigned long flags; ++ ++ /* ++ * Clear the FIFO buffers and disable them. ++ * (they will be reenabled in set_termios()) ++ */ ++ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); ++ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | ++ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); ++ serial_out(up, UART_FCR, 0); ++ ++ /* Clear the interrupt registers. */ ++ (void) serial_in(up, UART_LSR); ++ (void) serial_in(up, UART_RX); ++ (void) serial_in(up, UART_IIR); ++ (void) serial_in(up, UART_MSR); ++ ++ /* Now, initialize the UART, default is 8n1 */ ++ serial_out(up, UART_LCR, UART_LCR_WLEN8); ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ ++ up->port.mctrl |= TIOCM_OUT2; ++ serial_hsu_set_mctrl(&up->port, up->port.mctrl); ++ ++ /* ++ * Finally, enable interrupts. Note: Modem status interrupts ++ * are set via set_termios(), which will be occurring imminently ++ * anyway, so we don't enable them here. ++ */ ++ if (!up->use_dma) ++ up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE; ++ else ++ up->ier = 0; ++ serial_out(up, UART_IER, up->ier); ++ ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ ++ /* DMA init */ ++ /* When use DMA, TX/RX's FIFO and IRQ should be disabled */ ++ if (up->use_dma) { ++ struct hsu_dma_buffer *dbuf; ++ struct circ_buf *xmit = &port->state->xmit; ++ ++ up->dma_tx_on = 0; ++ ++ /* First allocate the RX buffer */ ++ dbuf = &up->rxbuf; ++ dbuf->buf = kzalloc(HSU_DMA_BUF_SIZE, GFP_KERNEL); ++ if (!dbuf->buf) { ++ up->use_dma = 0; ++ goto exit; ++ } ++ dbuf->dma_addr = dma_map_single(port->dev, ++ dbuf->buf, ++ HSU_DMA_BUF_SIZE, ++ DMA_FROM_DEVICE); ++ dbuf->dma_size = HSU_DMA_BUF_SIZE; ++ ++ /* Start the RX channel right now */ ++ hsu_dma_start_rx_chan(up->rxc, dbuf); ++ ++ /* Next init the TX DMA */ ++ dbuf = &up->txbuf; ++ dbuf->buf = xmit->buf; ++ dbuf->dma_addr = dma_map_single(port->dev, ++ dbuf->buf, ++ UART_XMIT_SIZE, ++ DMA_TO_DEVICE); ++ dbuf->dma_size = UART_XMIT_SIZE; ++ ++ /* This should not be changed all around */ ++ chan_writel(up->txc, HSU_CH_BSR, 32); ++ chan_writel(up->txc, HSU_CH_MOTSR, 4); ++ dbuf->ofs = 0; ++ } ++ ++exit: ++ /* And clear the interrupt registers again for luck. */ ++ (void) serial_in(up, UART_LSR); ++ (void) serial_in(up, UART_RX); ++ (void) serial_in(up, UART_IIR); ++ (void) serial_in(up, UART_MSR); ++ ++ up->running = 1; ++ return 0; ++} ++ ++static void serial_hsu_shutdown(struct uart_port *port) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ unsigned long flags; ++ ++ /* Disable interrupts from this port */ ++ up->ier = 0; ++ serial_out(up, UART_IER, 0); ++ up->running = 0; ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ up->port.mctrl &= ~TIOCM_OUT2; ++ serial_hsu_set_mctrl(&up->port, up->port.mctrl); ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ ++ /* Disable break condition and FIFOs */ ++ serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); ++ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | ++ UART_FCR_CLEAR_RCVR | ++ UART_FCR_CLEAR_XMIT); ++ serial_out(up, UART_FCR, 0); ++} ++ ++static void ++serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios, ++ struct ktermios *old) ++{ ++ 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; ++ u32 mul = 0x3600; ++ u32 ps = 0x10; ++ ++ switch (termios->c_cflag & CSIZE) { ++ case CS5: ++ cval = UART_LCR_WLEN5; ++ break; ++ case CS6: ++ cval = UART_LCR_WLEN6; ++ break; ++ case CS7: ++ cval = UART_LCR_WLEN7; ++ break; ++ default: ++ case CS8: ++ cval = UART_LCR_WLEN8; ++ break; ++ } ++ ++ /* CMSPAR isn't supported by this driver */ ++ if (tty) ++ tty->termios->c_cflag &= ~CMSPAR; ++ ++ if (termios->c_cflag & CSTOPB) ++ cval |= UART_LCR_STOP; ++ if (termios->c_cflag & PARENB) ++ cval |= UART_LCR_PARITY; ++ if (!(termios->c_cflag & PARODD)) ++ cval |= UART_LCR_EPAR; ++ ++ /* ++ * For those basic low baud rate we can get the direct ++ * scalar from 2746800, like 115200 = 2746800/24, for those ++ * higher baud rate, we have to handle them case by case, ++ * but DIV reg is never touched as its default value 0x3d09 ++ */ ++ baud = uart_get_baud_rate(port, termios, old, 0, 4000000); ++ quot = uart_get_divisor(port, baud); ++ ++ switch (baud) { ++ case 3500000: ++ mul = 0x3345; ++ ps = 0xC; ++ quot = 1; ++ break; ++ case 2500000: ++ mul = 0x2710; ++ ps = 0x10; ++ quot = 1; ++ break; ++ case 18432000: ++ mul = 0x2400; ++ ps = 0x10; ++ quot = 1; ++ break; ++ case 1500000: ++ mul = 0x1D4C; ++ ps = 0xc; ++ quot = 1; ++ break; ++ default: ++ ; ++ } ++ ++ if ((up->port.uartclk / quot) < (2400 * 16)) ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_1B; ++ else if ((up->port.uartclk / quot) < (230400 * 16)) ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_16B; ++ else ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_32B; ++ ++ fcr |= UART_FCR_HSU_64B_FIFO; ++#ifdef MFD_HSU_A0_STEPPING ++ /* A0 doesn't support half empty IRQ */ ++ fcr |= UART_FCR_FULL_EMPT_TXI; ++#endif ++ ++ /* ++ * Ok, we're now changing the port state. Do it with ++ * interrupts disabled. ++ */ ++ spin_lock_irqsave(&up->port.lock, flags); ++ ++ /* Update the per-port timeout */ ++ uart_update_timeout(port, termios->c_cflag, baud); ++ ++ 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)) ++ up->port.read_status_mask |= UART_LSR_BI; ++ ++ /* Characters to ignore */ ++ up->port.ignore_status_mask = 0; ++ if (termios->c_iflag & IGNPAR) ++ up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; ++ if (termios->c_iflag & IGNBRK) { ++ up->port.ignore_status_mask |= UART_LSR_BI; ++ /* ++ * If we're ignoring parity and break indicators, ++ * ignore overruns too (for real raw support). ++ */ ++ if (termios->c_iflag & IGNPAR) ++ up->port.ignore_status_mask |= UART_LSR_OE; ++ } ++ ++ /* Ignore all characters if CREAD is not set */ ++ if ((termios->c_cflag & CREAD) == 0) ++ up->port.ignore_status_mask |= UART_LSR_DR; ++ ++ /* ++ * CTS flow control flag and modem status interrupts, disable ++ * MSI by default ++ */ ++ up->ier &= ~UART_IER_MSI; ++ if (UART_ENABLE_MS(&up->port, termios->c_cflag)) ++ up->ier |= UART_IER_MSI; ++ ++ serial_out(up, UART_IER, up->ier); ++ ++ if (termios->c_cflag & CRTSCTS) ++ up->mcr |= UART_MCR_AFE | UART_MCR_RTS; ++ else ++ up->mcr &= ~UART_MCR_AFE; ++ ++ serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ ++ serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ ++ serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ ++ serial_out(up, UART_LCR, cval); /* reset DLAB */ ++ serial_out(up, UART_MUL, mul); /* set MUL */ ++ serial_out(up, UART_PS, ps); /* set PS */ ++ up->lcr = cval; /* Save LCR */ ++ serial_hsu_set_mctrl(&up->port, up->port.mctrl); ++ serial_out(up, UART_FCR, fcr); ++ spin_unlock_irqrestore(&up->port.lock, flags); ++} ++ ++static void ++serial_hsu_pm(struct uart_port *port, unsigned int state, ++ unsigned int oldstate) ++{ ++} ++ ++static void serial_hsu_release_port(struct uart_port *port) ++{ ++} ++ ++static int serial_hsu_request_port(struct uart_port *port) ++{ ++ return 0; ++} ++ ++static void serial_hsu_config_port(struct uart_port *port, int flags) ++{ ++#if 0 ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ up->port.type = PORT_MFD; ++#endif ++} ++ ++static int ++serial_hsu_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ /* We don't want the core code to modify any port params */ ++ return -EINVAL; ++} ++ ++static const char * ++serial_hsu_type(struct uart_port *port) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_port, port); ++ return up->name; ++} ++ ++/* Mainly for uart console use */ ++static struct uart_hsu_port *serial_hsu_ports[3]; ++static struct uart_driver serial_hsu_reg; ++ ++#ifdef CONFIG_SERIAL_MFD_HSU_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_hsu_port *up) ++{ ++ unsigned int status, tmout = 1000; ++ ++ /* Wait up to 1ms for the character 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 & BOTH_EMPTY)); ++ ++ /* Wait up to 1s for flow control if necessary */ ++ if (up->port.flags & UPF_CONS_FLOW) { ++ tmout = 1000000; ++ while (--tmout && ++ ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) ++ udelay(1); ++ } ++} ++ ++static void serial_hsu_console_putchar(struct uart_port *port, int ch) ++{ ++ struct uart_hsu_port *up = ++ container_of(port, struct uart_hsu_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... ++ * ++ * The console_lock must be held when we get here. ++ */ ++static void ++serial_hsu_console_write(struct console *co, const char *s, unsigned int count) ++{ ++ struct uart_hsu_port *up = serial_hsu_ports[co->index]; ++ unsigned long flags; ++ unsigned int ier; ++ int locked = 1; ++ ++ 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); ++ ++ /* First save the IER then disable the interrupts */ ++ ier = serial_in(up, UART_IER); ++ serial_out(up, UART_IER, 0); ++ ++ uart_console_write(&up->port, s, count, serial_hsu_console_putchar); ++ ++ /* ++ * Finally, wait for transmitter to become empty ++ * and restore the IER ++ */ ++ wait_for_xmitr(up); ++ serial_out(up, UART_IER, ier); ++ ++ if (locked) ++ spin_unlock(&up->port.lock); ++ local_irq_restore(flags); ++} ++ ++static struct console serial_hsu_console; ++ ++static int __init ++serial_hsu_console_setup(struct console *co, char *options) ++{ ++ struct uart_hsu_port *up; ++ int baud = 115200; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ int ret; ++ ++ if (co->index == -1 || co->index >= serial_hsu_reg.nr) ++ co->index = 0; ++ up = serial_hsu_ports[co->index]; ++ if (!up) ++ return -ENODEV; ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ ++ ret = uart_set_options(&up->port, co, baud, parity, bits, flow); ++ ++ return ret; ++} ++ ++static struct console serial_hsu_console = { ++ .name = "ttyMFD", ++ .write = serial_hsu_console_write, ++ .device = uart_console_device, ++ .setup = serial_hsu_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = 2, ++ .data = &serial_hsu_reg, ++}; ++#endif ++ ++struct uart_ops serial_hsu_pops = { ++ .tx_empty = serial_hsu_tx_empty, ++ .set_mctrl = serial_hsu_set_mctrl, ++ .get_mctrl = serial_hsu_get_mctrl, ++ .stop_tx = serial_hsu_stop_tx, ++ .start_tx = serial_hsu_start_tx, ++ .stop_rx = serial_hsu_stop_rx, ++ .enable_ms = serial_hsu_enable_ms, ++ .break_ctl = serial_hsu_break_ctl, ++ .startup = serial_hsu_startup, ++ .shutdown = serial_hsu_shutdown, ++ .set_termios = serial_hsu_set_termios, ++ .pm = serial_hsu_pm, ++ .type = serial_hsu_type, ++ .release_port = serial_hsu_release_port, ++ .request_port = serial_hsu_request_port, ++ .config_port = serial_hsu_config_port, ++ .verify_port = serial_hsu_verify_port, ++}; ++ ++static struct uart_driver serial_hsu_reg = { ++ .owner = THIS_MODULE, ++ .driver_name = "MFD serial", ++ .dev_name = "ttyMFD", ++ .major = TTY_MAJOR, ++ .minor = 128, ++ .nr = 3, ++}; ++ ++#ifdef CONFIG_PM ++static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state) ++{ ++ struct uart_hsu_port *up; ++ ++ up = pci_get_drvdata(pdev); ++ if (!up) ++ return 0; ++ ++ uart_suspend_port(&serial_hsu_reg, &up->port); ++ ++ return 0; ++} ++ ++static int serial_hsu_resume(struct pci_dev *pdev) ++{ ++ struct uart_hsu_port *up; ++ ++ up = pci_get_drvdata(pdev); ++ if (!up) ++ return 0; ++ uart_resume_port(&serial_hsu_reg, &up->port); ++ return 0; ++} ++#else ++#define serial_hsu_suspend NULL ++#define serial_hsu_resume NULL ++#endif ++ ++/* temp global pointer before we settle down on using one or four PCI dev */ ++static struct hsu_port *phsu; ++ ++static int serial_hsu_probe(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ struct uart_hsu_port *uport; ++ int index, ret; ++ ++ printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n", ++ pdev->vendor, pdev->device); ++ ++ switch (pdev->device) { ++ case 0x081B: ++ index = 0; ++ break; ++ case 0x081C: ++ index = 1; ++ break; ++ case 0x081D: ++ index = 2; ++ break; ++ case 0x081E: ++ /* internal DMA controller */ ++ index = 3; ++ break; ++ default: ++ dev_err(&pdev->dev, "HSU: out of index!"); ++ return -ENODEV; ++ } ++ ++ ret = pci_enable_device(pdev); ++ if (ret) ++ return ret; ++ ++ if (index == 3) { ++ /* DMA controller */ ++ ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu); ++ if (ret) { ++ dev_err(&pdev->dev, "can not get IRQ\n"); ++ goto err_disable; ++ } ++ pci_set_drvdata(pdev, phsu); ++ } else { ++ /* UART port 0~2 */ ++ uport = &phsu->port[index]; ++ uport->port.irq = pdev->irq; ++ uport->port.dev = &pdev->dev; ++ uport->dev = &pdev->dev; ++ ++ ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport); ++ if (ret) { ++ dev_err(&pdev->dev, "can not get IRQ\n"); ++ goto err_disable; ++ } ++ 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); ++ } ++ ++ return 0; ++ ++err_disable: ++ pci_disable_device(pdev); ++ return ret; ++} ++ ++static void hsu_global_init(void) ++{ ++ struct hsu_port *hsu; ++ struct uart_hsu_port *uport; ++ struct hsu_dma_chan *dchan; ++ int i, ret; ++ ++ hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL); ++ if (!hsu) ++ return; ++ ++ /* Get basic io resource and map it */ ++ hsu->paddr = 0xffa28000; ++ hsu->iolen = 0x1000; ++ ++ if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global"))) ++ pr_warning("HSU: error in request mem region\n"); ++ ++ hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen); ++ if (!hsu->reg) { ++ pr_err("HSU: error in ioremap\n"); ++ ret = -ENOMEM; ++ goto err_free_region; ++ } ++ ++ /* Initialise the 3 UART ports */ ++ uport = hsu->port; ++ for (i = 0; i < 3; i++) { ++ uport->port.type = PORT_MFD; ++ uport->port.iotype = UPIO_MEM; ++ uport->port.mapbase = (resource_size_t)hsu->paddr ++ + HSU_PORT_REG_OFFSET ++ + i * HSU_PORT_REG_LENGTH; ++ uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET ++ + i * HSU_PORT_REG_LENGTH; ++ ++ sprintf(uport->name, "hsu_port%d", i); ++ uport->port.fifosize = 64; ++ uport->port.ops = &serial_hsu_pops; ++ uport->port.line = i; ++ uport->port.flags = UPF_IOREMAP; ++ /* make the maxim support rate to 2746800 bps */ ++ uport->port.uartclk = 115200 * 24 * 16; ++ ++ uport->running = 0; ++ uport->txc = &hsu->chans[i * 2]; ++ uport->rxc = &hsu->chans[i * 2 + 1]; ++ ++ serial_hsu_ports[i] = uport; ++ uport->index = i; ++ uport++; ++ } ++ ++ /* Initialise 6 dma channels */ ++ dchan = hsu->chans; ++ for (i = 0; i < 6; i++) { ++ dchan->id = i; ++ dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; ++ dchan->uport = &hsu->port[i/2]; ++ dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET + ++ i * HSU_DMA_CHANS_REG_LENGTH; ++ dchan++; ++ } ++ ++ phsu = hsu; ++ ++ hsu_debugfs_init(hsu); ++ return; ++ ++err_free_region: ++ release_mem_region(hsu->paddr, hsu->iolen); ++ kfree(hsu); ++ return; ++} ++ ++static void serial_hsu_remove(struct pci_dev *pdev) ++{ ++ struct hsu_port *hsu; ++ int i; ++ ++ hsu = pci_get_drvdata(pdev); ++ if (!hsu) ++ return; ++ ++ for (i = 0; i < 3; i++) ++ uart_remove_one_port(&serial_hsu_reg, &hsu->port[i].port); ++ ++ pci_set_drvdata(pdev, NULL); ++ free_irq(hsu->irq, hsu); ++ pci_disable_device(pdev); ++} ++ ++/* First 3 are UART ports, and the 4th is the DMA */ ++static const struct pci_device_id pci_ids[] __devinitdata = { ++ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) }, ++ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) }, ++ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) }, ++ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081E) }, ++ {}, ++}; ++ ++static struct pci_driver hsu_pci_driver = { ++ .name = "HSU serial", ++ .id_table = pci_ids, ++ .probe = serial_hsu_probe, ++ .remove = __devexit_p(serial_hsu_remove), ++ .suspend = serial_hsu_suspend, ++ .resume = serial_hsu_resume, ++}; ++ ++static int __init hsu_pci_init(void) ++{ ++ int ret; ++ ++ hsu_global_init(); ++ ++ ret = uart_register_driver(&serial_hsu_reg); ++ if (ret) ++ return ret; ++ ++ return pci_register_driver(&hsu_pci_driver); ++} ++ ++static void __exit hsu_pci_exit(void) ++{ ++ pci_unregister_driver(&hsu_pci_driver); ++ uart_unregister_driver(&serial_hsu_reg); ++ ++ hsu_debugfs_remove(phsu); ++ ++ kfree(phsu); ++} ++ ++module_init(hsu_pci_init); ++module_exit(hsu_pci_exit); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:medfield-hsu"); +--- a/include/linux/serial_core.h ++++ b/include/linux/serial_core.h +@@ -189,6 +189,8 @@ + /* MAX3107 */ + #define PORT_MAX3107 94 + ++/* High Speed UART for Medfield */ ++#define PORT_MFD 95 + + #ifdef __KERNEL__ + +--- a/include/linux/serial_reg.h ++++ b/include/linux/serial_reg.h +@@ -221,8 +221,24 @@ + #define UART_FCR_PXAR16 0x80 /* receive FIFO threshold = 16 */ + #define UART_FCR_PXAR32 0xc0 /* receive FIFO threshold = 32 */ + ++/* ++ * Intel MID on-chip HSU (High Speed UART) defined bits ++ */ ++#define UART_FCR_HSU_64_1B 0x00 /* receive FIFO treshold = 1 */ ++#define UART_FCR_HSU_64_16B 0x40 /* receive FIFO treshold = 16 */ ++#define UART_FCR_HSU_64_32B 0x80 /* receive FIFO treshold = 32 */ ++#define UART_FCR_HSU_64_56B 0xc0 /* receive FIFO treshold = 56 */ ++ ++#define UART_FCR_HSU_16_1B 0x00 /* receive FIFO treshold = 1 */ ++#define UART_FCR_HSU_16_4B 0x40 /* receive FIFO treshold = 4 */ ++#define UART_FCR_HSU_16_8B 0x80 /* receive FIFO treshold = 8 */ ++#define UART_FCR_HSU_16_14B 0xc0 /* receive FIFO treshold = 14 */ + ++#define UART_FCR_HSU_64B_FIFO 0x20 /* chose 64 bytes FIFO */ ++#define UART_FCR_HSU_16B_FIFO 0x00 /* chose 16 bytes FIFO */ + ++#define UART_FCR_HALF_EMPT_TXI 0x00 /* trigger TX_EMPT IRQ for half empty */ ++#define UART_FCR_FULL_EMPT_TXI 0x08 /* trigger TX_EMPT IRQ for full empty */ + + /* + * These register definitions are for the 16C950 diff --git a/tty/hsu-some-code-cleanup.patch b/tty/hsu-some-code-cleanup.patch new file mode 100644 index 00000000000000..360a8e9170bead --- /dev/null +++ b/tty/hsu-some-code-cleanup.patch @@ -0,0 +1,282 @@ +From alan@linux.intel.com Mon Jul 26 14:23:31 2010 +From: Alan Cox <alan@linux.intel.com> +Subject: hsu: some code cleanup +To: greg@kroah.com, linux-serial@vger.kernel.org +Date: Mon, 26 Jul 2010 10:18:32 +0100 +Message-ID: <20100726091828.21579.78601.stgit@localhost.localdomain> + +From: Feng Tang <feng.tang@intel.com> + +Major changes are: +* refine the comments in the driver +* remove unused member from structure "hsu_port" +* extended spin_lock protoction for dma mode in port_irq() + +Signed-off-by: Feng Tang <feng.tang@intel.com> +Signed-off-by: Alan Cox <alan@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/mfd.c | 85 +++++++++++---------------------------------------- + 1 file changed, 19 insertions(+), 66 deletions(-) + +--- a/drivers/serial/mfd.c ++++ b/drivers/serial/mfd.c +@@ -3,7 +3,7 @@ + * + * Refer pxa.c, 8250.c and some other drivers in drivers/serial/ + * +- * (C) Copyright 2009 Intel Corporation ++ * (C) Copyright 2010 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License +@@ -11,30 +11,16 @@ + * of the License. + */ + +- + /* Notes: +- * 1. there should be 2 types of register access method, one for +- * UART ports, the other for the general purpose registers +- * +- * 2. It used to have a Irda port, but was defeatured recently +- * +- * 3. Based on the info from HSU MAS, 0/1 channel are assigned to +- * port0, 2/3 chan to port 1, 4/5 chan to port 3. Even number +- * chan will be read, odd chan for write +- * +- * 4. HUS supports both the 64B and 16B FIFO version, but this driver +- * will only use 64B version ++ * 1. DMA channel allocation: 0/1 channel are assigned to port 0, ++ * 2/3 chan to port 1, 4/5 chan to port 3. Even number chans ++ * are used for RX, odd chans for TX + * +- * 5. In A0 stepping, UART will not support TX half empty flag, thus +- * need add a #ifdef judgement ++ * 2. In A0 stepping, UART will not support TX half empty flag + * +- * 6. One more bug for A0, the loopback mode won't support AFC +- * auto-flow control +- * +- * 7. HSU has some special FCR control bits, we add it to serial_reg.h +- * +- * 8. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always asserted, +- * only when the HW is reset the DDCD and DDSR will be triggered ++ * 3. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always ++ * asserted, only when the HW is reset the DDCD and DDSR will ++ * be triggered + */ + + #include <linux/module.h> +@@ -75,7 +61,7 @@ struct hsu_dma_buffer { + + struct hsu_dma_chan { + u32 id; +- u32 dirt; /* to or from device */ ++ enum dma_data_direction dirt; + struct uart_hsu_port *uport; + void __iomem *reg; + struct timer_list rx_timer; /* only needed by RX channel */ +@@ -102,8 +88,6 @@ struct uart_hsu_port { + + /* Top level data structure of HSU */ + struct hsu_port { +- struct pci_device *pdev; +- + void __iomem *reg; + unsigned long paddr; + unsigned long iolen; +@@ -112,23 +96,9 @@ struct hsu_port { + struct uart_hsu_port port[3]; + struct hsu_dma_chan chans[10]; + +-#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs; +-#endif + }; + +-static inline void hexdump(char *str, u8 *addr, int cnt) +-{ +- int i; +- +- for (i = 0; i < cnt; i += 8) { +- printk("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", +- addr[i], addr[i+1], addr[i+2], addr[i+3], +- addr[i+4], addr[i+5], addr[i+6], addr[i+7]); +- printk("\n"); +- } +-} +- + static inline unsigned int serial_in(struct uart_hsu_port *up, int offset) + { + unsigned int val; +@@ -353,9 +323,6 @@ void hsu_dma_tx(struct uart_hsu_port *up + | (0x1 << 8) + | (0x1 << 16) + | (0x1 << 24)); +- +- WARN(chan_readl(up->txc, HSU_CH_CR) & 0x1, +- "TX channel has already be started!!\n"); + up->dma_tx_on = 1; + chan_writel(up->txc, HSU_CH_CR, 0x1); + } +@@ -367,7 +334,6 @@ 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) + { +- /* Need start RX dma channel here */ + dbuf->ofs = 0; + + chan_writel(rxc, HSU_CH_BSR, 32); +@@ -426,35 +392,32 @@ void hsu_dma_rx(struct uart_hsu_port *up + return; + + /* +- * first need to know how many is already transferred, ++ * First need to know how many is already transferred, + * then check if its a timeout DMA irq, and return + * the trail bytes out, push them up and reenable the +- * channel, better to use 2 descriptors at the same time ++ * channel + */ + +- /* timeout IRQ, need wait some time, see Errata 2 */ ++ /* Timeout IRQ, need wait some time, see Errata 2 */ + if (int_sts & 0xf00) + udelay(2); + + /* Stop the channel */ + chan_writel(chan, HSU_CH_CR, 0x0); + +- /* We can use 2 ways to calc the actual transfer len */ + count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; +- + if (!count) { +- /* restart the channel before we leave */ ++ /* Restart the channel before we leave */ + chan_writel(chan, HSU_CH_CR, 0x3); + return; + } +- + del_timer(&chan->rx_timer); + + dma_sync_single_for_cpu(port->dev, dbuf->dma_addr, + dbuf->dma_size, DMA_FROM_DEVICE); + + /* +- * head will only wrap around when we recycle ++ * Head will only wrap around when we recycle + * the DMA buffer, and when that happens, we + * explicitly set tail to 0. So head will + * always be greater than tail. +@@ -496,10 +459,6 @@ static void serial_hsu_stop_rx(struct ua + } + } + +-/* +- * if there is error flag, should we just reset the FIFO or keeps +- * working on it +- */ + static inline void receive_chars(struct uart_hsu_port *up, int *status) + { + struct tty_struct *tty = up->port.state->port.tty; +@@ -571,7 +530,6 @@ static void transmit_chars(struct uart_h + { + struct circ_buf *xmit = &up->port.state->xmit; + int count; +- int i = 0; /* for debug use */ + + if (up->port.x_char) { + serial_out(up, UART_TX, up->port.x_char); +@@ -592,13 +550,11 @@ static void transmit_chars(struct uart_h + * into it won't clear the EMPT bit, so we may need be cautious + * by useing a shorter buffer + */ +- /* count = up->port.fifosize; */ + count = up->port.fifosize - 4; + #endif + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); +- i++; + + up->port.icount.tx++; + if (uart_circ_empty(xmit)) +@@ -628,7 +584,7 @@ static inline void check_modem_status(st + /* We may only get DDCD when HW init and reset */ + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); +- /* will start/stop_tx accordingly */ ++ /* Will start/stop_tx accordingly */ + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + +@@ -647,6 +603,7 @@ static irqreturn_t port_irq(int irq, voi + if (unlikely(!up->running)) + return IRQ_NONE; + ++ spin_lock_irqsave(&up->port.lock, flags); + if (up->use_dma) { + lsr = serial_in(up, UART_LSR); + if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | +@@ -655,10 +612,10 @@ static irqreturn_t port_irq(int irq, voi + "Got lsr irq while using DMA, lsr = 0x%2x\n", + lsr); + check_modem_status(up); ++ spin_unlock_irqrestore(&up->port.lock, flags); + return IRQ_HANDLED; + } + +- spin_lock_irqsave(&up->port.lock, flags); + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) { + spin_unlock_irqrestore(&up->port.lock, flags); +@@ -666,9 +623,9 @@ static irqreturn_t port_irq(int irq, voi + } + + lsr = serial_in(up, UART_LSR); +- + if (lsr & UART_LSR_DR) + receive_chars(up, &lsr); ++ check_modem_status(up); + + /* lsr will be renewed during the receive_chars */ + if (lsr & UART_LSR_THRE) +@@ -701,7 +658,6 @@ static inline void dma_chan_irq(struct h + + /* Tx channel */ + if (chan->dirt == DMA_TO_DEVICE) { +- /* dma for irq should be done */ + chan_writel(chan, HSU_CH_CR, 0x0); + up->dma_tx_on = 0; + hsu_dma_tx(up); +@@ -851,7 +807,6 @@ static int serial_hsu_startup(struct uar + spin_unlock_irqrestore(&up->port.lock, flags); + + /* DMA init */ +- /* When use DMA, TX/RX's FIFO and IRQ should be disabled */ + if (up->use_dma) { + struct hsu_dma_buffer *dbuf; + struct circ_buf *xmit = &port->state->xmit; +@@ -1090,11 +1045,9 @@ static int serial_hsu_request_port(struc + + static void serial_hsu_config_port(struct uart_port *port, int flags) + { +-#if 0 + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + up->port.type = PORT_MFD; +-#endif + } + + static int +@@ -1426,7 +1379,7 @@ static void hsu_global_init(void) + uport->port.ops = &serial_hsu_pops; + uport->port.line = i; + uport->port.flags = UPF_IOREMAP; +- /* make the maxim support rate to 2746800 bps */ ++ /* set the scalable maxim support rate to 2746800 bps */ + uport->port.uartclk = 115200 * 24 * 16; + + uport->running = 0; diff --git a/tty/mxser-remove-unnesesary-null-check.patch b/tty/mxser-remove-unnesesary-null-check.patch new file mode 100644 index 00000000000000..ac753788456305 --- /dev/null +++ b/tty/mxser-remove-unnesesary-null-check.patch @@ -0,0 +1,33 @@ +From segooon@gmail.com Mon Jul 26 14:24:13 2010 +From: Kulikov Vasiliy <segooon@gmail.com> +To: kernel-janitors@vger.kernel.org +Cc: Jiri Slaby <jirislaby@gmail.com>, + Greg Kroah-Hartman <gregkh@suse.de>, Alan Cox <alan@linux.intel.com>, + Andrew Morton <akpm@linux-foundation.org>, Tejun Heo <tj@kernel.org>, + linux-kernel@vger.kernel.org +Subject: mxser: remove unnesesary NULL check +Date: Fri, 23 Jul 2010 20:34:53 +0400 +Message-Id: <1279902893-27006-1-git-send-email-segooon@gmail.com> + +mxser_transmit_chars(tty, port) is called only from mxser_interrupt(). +NULL check is performed in mxser_interrupt() so it is redundant here. + +Signed-off-by: Kulikov Vasiliy <segooon@gmail.com> +Acked-by: Jiri Slaby <jirislaby@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/mxser.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/char/mxser.c ++++ b/drivers/char/mxser.c +@@ -2193,7 +2193,7 @@ static void mxser_transmit_chars(struct + port->mon_data.up_txcnt += (cnt - port->xmit_cnt); + port->icount.tx += (cnt - port->xmit_cnt); + +- if (port->xmit_cnt < WAKEUP_CHARS && tty) ++ if (port->xmit_cnt < WAKEUP_CHARS) + tty_wakeup(tty); + + if (port->xmit_cnt <= 0) { diff --git a/tty/serial-add-support-for-ox16pci958-card.patch b/tty/serial-add-support-for-ox16pci958-card.patch new file mode 100644 index 00000000000000..490a2e1fc0f9c5 --- /dev/null +++ b/tty/serial-add-support-for-ox16pci958-card.patch @@ -0,0 +1,65 @@ +From a.beregalov@gmail.com Mon Jul 26 14:21:38 2010 +From: Alexander Beregalov <a.beregalov@gmail.com> +To: gregkh@suse.de +Cc: linux-kernel@vger.kernel.org, + Lytochkin Boris <lytboris@gmail.com>, + Alexander Beregalov <a.beregalov@gmail.com> +Subject: [PATCH] serial: add support for OX16PCI958 card +Date: Mon, 26 Jul 2010 10:02:26 +0400 +Message-Id: <1280124146-24735-1-git-send-email-a.beregalov@gmail.com> + +From: Lytochkin Boris <lytboris@gmail.com> + +Signed-off-by: Lytochkin Boris <lytboris@gmail.com> +Tested-by: Lytochkin Boris <lytboris@gmail.com> +Signed-off-by: Alexander Beregalov <a.beregalov@gmail.com> +Cc: stable <stable@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/8250_pci.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/drivers/serial/8250_pci.c ++++ b/drivers/serial/8250_pci.c +@@ -994,6 +994,7 @@ static int skip_tx_en_setup(struct seria + #define PCI_DEVICE_ID_TITAN_800E 0xA014 + #define PCI_DEVICE_ID_TITAN_200EI 0xA016 + #define PCI_DEVICE_ID_TITAN_200EISI 0xA017 ++#define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 + + /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ + #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 +@@ -1542,6 +1543,8 @@ enum pci_board_num_t { + pbn_b2_4_921600, + pbn_b2_8_921600, + ++ pbn_b2_8_1152000, ++ + pbn_b2_bt_1_115200, + pbn_b2_bt_2_115200, + pbn_b2_bt_4_115200, +@@ -1960,6 +1963,13 @@ static struct pciserial_board pci_boards + .uart_offset = 8, + }, + ++ [pbn_b2_8_1152000] = { ++ .flags = FL_BASE2, ++ .num_ports = 8, ++ .base_baud = 1152000, ++ .uart_offset = 8, ++ }, ++ + [pbn_b2_bt_1_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 1, +@@ -2875,6 +2885,9 @@ static struct pci_device_id serial_pci_t + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, ++ { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958, ++ PCI_ANY_ID , PCI_ANY_ID, 0, 0, ++ pbn_b2_8_1152000 }, + + /* + * Oxford Semiconductor Inc. Tornado PCI express device range. |
