From c7ed1ab3f7b2fe0dedebf34cbf40bf12cb5ae48b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 10 Mar 2010 18:35:14 +0900 Subject: serial: sh-sci: clkdev updates for MSTP gating. This fixes up some of the I/D/F clock ambiguity in the sh-sci driver. The interface clock in most cases just wraps back to the peripheral clock, while the function clock wraps in to the MSTP bits. As the logic was somewhat inverted, this cleans that up, and also enables all CPUs with SCI MSTP bits to match function clocks through clkdev lookup. As a result, this gets rid of the clk string abuse on the sh side, and the clock string will be killed off once the ARM code has had a chance to sync up. This also enables MSTP gating on CPUs like 7786 which had never wired it up before. Impacted CPUs are primarily all SH-Mobiles, SH7785, and SH7786. Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.c | 57 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 20 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 980f394..054a842 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -83,8 +83,8 @@ struct sci_port { /* Interface clock */ struct clk *iclk; - /* Data clock */ - struct clk *dclk; + /* Function clock */ + struct clk *fclk; struct list_head node; struct dma_chan *chan_tx; @@ -803,7 +803,7 @@ static int sci_notifier(struct notifier_block *self, (phase == CPUFREQ_RESUMECHANGE)) { spin_lock_irqsave(&priv->lock, flags); list_for_each_entry(sci_port, &priv->ports, node) - sci_port->port.uartclk = clk_get_rate(sci_port->dclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); spin_unlock_irqrestore(&priv->lock, flags); } @@ -814,21 +814,17 @@ static void sci_clk_enable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - clk_enable(sci_port->dclk); - sci_port->port.uartclk = clk_get_rate(sci_port->dclk); - - if (sci_port->iclk) - clk_enable(sci_port->iclk); + clk_enable(sci_port->iclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); + clk_enable(sci_port->fclk); } static void sci_clk_disable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - if (sci_port->iclk) - clk_disable(sci_port->iclk); - - clk_disable(sci_port->dclk); + clk_disable(sci_port->fclk); + clk_disable(sci_port->iclk); } static int sci_request_irq(struct sci_port *port) @@ -1557,10 +1553,10 @@ static struct uart_ops sci_uart_ops = { #endif }; -static void __devinit sci_init_single(struct platform_device *dev, - struct sci_port *sci_port, - unsigned int index, - struct plat_sci_port *p) +static int __devinit sci_init_single(struct platform_device *dev, + struct sci_port *sci_port, + unsigned int index, + struct plat_sci_port *p) { struct uart_port *port = &sci_port->port; @@ -1581,8 +1577,23 @@ static void __devinit sci_init_single(struct platform_device *dev, } if (dev) { - sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL; - sci_port->dclk = clk_get(&dev->dev, "peripheral_clk"); + sci_port->iclk = clk_get(&dev->dev, "sci_ick"); + if (IS_ERR(sci_port->iclk)) { + sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); + if (IS_ERR(sci_port->iclk)) { + dev_err(&dev->dev, "can't get iclk\n"); + return PTR_ERR(sci_port->iclk); + } + } + + /* + * The function clock is optional, ignore it if we can't + * find it. + */ + sci_port->fclk = clk_get(&dev->dev, "sci_fck"); + if (IS_ERR(sci_port->fclk)) + sci_port->fclk = NULL; + sci_port->enable = sci_clk_enable; sci_port->disable = sci_clk_disable; port->dev = &dev->dev; @@ -1609,6 +1620,7 @@ static void __devinit sci_init_single(struct platform_device *dev, #endif memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); + return 0; } #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE @@ -1758,8 +1770,11 @@ static int sci_remove(struct platform_device *dev) cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(p, &priv->ports, node) + list_for_each_entry(p, &priv->ports, node) { uart_remove_one_port(&sci_uart_driver, &p->port); + clk_put(p->iclk); + clk_put(p->fclk); + } spin_unlock_irqrestore(&priv->lock, flags); kfree(priv); @@ -1785,7 +1800,9 @@ static int __devinit sci_probe_single(struct platform_device *dev, return 0; } - sci_init_single(dev, sciport, index, p); + ret = sci_init_single(dev, sciport, index, p); + if (ret) + return ret; ret = uart_add_one_port(&sci_uart_driver, &sciport->port); if (ret) -- cgit v1.1 From 4bab9d426e6dbd9ea09330919a33d35d5faab400 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 19 Mar 2010 04:46:38 +0000 Subject: dmaengine: shdma: Remove sh_dmae_slave_chan_id enum This patch replaces the sh_dmae_slave_chan_id enum with an unsigned int. The purpose of this chainge is to make it possible to separate the slave id enums from the dmaengine header. The slave id enums varies with processor model, so in the future it makes sense to put these in the processor specific headers together with the pinmux enums. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index f7b9aff..2d9a06d 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -90,8 +90,8 @@ struct sci_port { struct dma_chan *chan_rx; #ifdef CONFIG_SERIAL_SH_SCI_DMA struct device *dma_dev; - enum sh_dmae_slave_chan_id slave_tx; - enum sh_dmae_slave_chan_id slave_rx; + unsigned int slave_tx; + unsigned int slave_rx; struct dma_async_tx_descriptor *desc_tx; struct dma_async_tx_descriptor *desc_rx[2]; dma_cookie_t cookie_tx; -- cgit v1.1 From f354a38175f5219146f4675dca770e114cf3c502 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 19 Mar 2010 04:47:01 +0000 Subject: serial: sh-sci: Use sg_dma_len() and sg_dma_address() Convert the dmaengine code in sh-sci.c from directly accessing the struct scatterlist members length, dma_length and dma_address to using the macros sg_dma_len() and sg_dma_address(). Makes the code build for ARM. Signed-off-by: Magnus Damm Acked-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 2d9a06d..cd52dd6 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -916,10 +916,10 @@ static void sci_dma_tx_complete(void *arg) spin_lock_irqsave(&port->lock, flags); - xmit->tail += s->sg_tx.length; + xmit->tail += sg_dma_len(&s->sg_tx); xmit->tail &= UART_XMIT_SIZE - 1; - port->icount.tx += s->sg_tx.length; + port->icount.tx += sg_dma_len(&s->sg_tx); async_tx_ack(s->desc_tx); s->cookie_tx = -EINVAL; @@ -1134,14 +1134,13 @@ static void work_fn_tx(struct work_struct *work) */ spin_lock_irq(&port->lock); sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); - sg->dma_address = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + + sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + sg->offset; - sg->length = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), + sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); - sg->dma_length = sg->length; spin_unlock_irq(&port->lock); - BUG_ON(!sg->length); + BUG_ON(!sg_dma_len(sg)); desc = chan->device->device_prep_slave_sg(chan, sg, s->sg_len_tx, DMA_TO_DEVICE, @@ -1342,8 +1341,7 @@ static void sci_request_dma(struct uart_port *port) sg_init_table(sg, 1); sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, (int)buf[i] & ~PAGE_MASK); - sg->dma_address = dma[i]; - sg->dma_length = sg->length; + sg_dma_address(sg) = dma[i]; } INIT_WORK(&s->work_rx, work_fn_rx); -- cgit v1.1 From 3089f381fbaf53560dcbcb4aef6ef17fe44e347c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 19 Mar 2010 13:53:04 +0000 Subject: SH: extend SCI DMA support to work on SCIFA ports SCIFA ports have additional bits to control DMA requests and they must have respective interrupt sources enabled, as the datasheet suggests, the only way to avoid actually taking interrupts in addition to DMA events is by masking the IRQ on the CPU. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.c | 114 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 33 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index cd52dd6..304a877 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -106,6 +106,7 @@ struct sci_port { struct work_struct work_tx; struct work_struct work_rx; struct timer_list rx_timer; + unsigned int rx_timeout; #endif }; @@ -673,22 +674,22 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) struct sci_port *s = to_sci_port(port); if (s->chan_rx) { - unsigned long tout; u16 scr = sci_in(port, SCSCR); u16 ssr = sci_in(port, SCxSR); /* Disable future Rx interrupts */ - sci_out(port, SCSCR, scr & ~SCI_CTRL_FLAGS_RIE); + if (port->type == PORT_SCIFA) { + disable_irq_nosync(irq); + scr |= 0x4000; + } else { + scr &= ~SCI_CTRL_FLAGS_RIE; + } + sci_out(port, SCSCR, scr); /* Clear current interrupt */ sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); - /* Calculate delay for 1.5 DMA buffers */ - tout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / - port->fifosize / 2; - dev_dbg(port->dev, "Rx IRQ: setup timeout in %lu ms\n", - tout * 1000 / HZ); - if (tout < 2) - tout = 2; - mod_timer(&s->rx_timer, jiffies + tout); + dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", + jiffies, s->rx_timeout); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); return IRQ_HANDLED; } @@ -925,13 +926,17 @@ static void sci_dma_tx_complete(void *arg) s->cookie_tx = -EINVAL; s->desc_tx = NULL; - spin_unlock_irqrestore(&port->lock, flags); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_chars_pending(xmit)) + if (!uart_circ_empty(xmit)) { schedule_work(&s->work_tx); + } else if (port->type == PORT_SCIFA) { + u16 ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE); + } + + spin_unlock_irqrestore(&port->lock, flags); } /* Locking: called with port lock held */ @@ -975,13 +980,13 @@ static void sci_dma_rx_complete(void *arg) unsigned long flags; int count; - dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx); spin_lock_irqsave(&port->lock, flags); count = sci_dma_rx_push(s, tty, s->buf_len_rx); - mod_timer(&s->rx_timer, jiffies + msecs_to_jiffies(5)); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); spin_unlock_irqrestore(&port->lock, flags); @@ -1053,6 +1058,8 @@ static void sci_submit_rx(struct sci_port *s) sci_rx_dma_release(s, true); return; } + dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, + s->cookie_rx[i], i); } s->active_rx = s->cookie_rx[0]; @@ -1110,10 +1117,10 @@ static void work_fn_rx(struct work_struct *work) return; } - dev_dbg(port->dev, "%s: cookie %d #%d\n", __func__, - s->cookie_rx[new], new); - s->active_rx = s->cookie_rx[!new]; + + dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, + s->cookie_rx[new], new, s->active_rx); } static void work_fn_tx(struct work_struct *work) @@ -1175,23 +1182,28 @@ static void work_fn_tx(struct work_struct *work) static void sci_start_tx(struct uart_port *port) { + struct sci_port *s = to_sci_port(port); unsigned short ctrl; #ifdef CONFIG_SERIAL_SH_SCI_DMA - struct sci_port *s = to_sci_port(port); - - if (s->chan_tx) { - if (!uart_circ_empty(&s->port.state->xmit) && s->cookie_tx < 0) - schedule_work(&s->work_tx); - - return; + if (port->type == PORT_SCIFA) { + u16 new, scr = sci_in(port, SCSCR); + if (s->chan_tx) + new = scr | 0x8000; + else + new = scr & ~0x8000; + if (new != scr) + sci_out(port, SCSCR, new); } + if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + s->cookie_tx < 0) + schedule_work(&s->work_tx); #endif - - /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - ctrl |= SCI_CTRL_FLAGS_TIE; - sci_out(port, SCSCR, ctrl); + if (!s->chan_tx || port->type == PORT_SCIFA) { + /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE); + } } static void sci_stop_tx(struct uart_port *port) @@ -1200,6 +1212,8 @@ static void sci_stop_tx(struct uart_port *port) /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x8000; ctrl &= ~SCI_CTRL_FLAGS_TIE; sci_out(port, SCSCR, ctrl); } @@ -1210,6 +1224,8 @@ static void sci_start_rx(struct uart_port *port) /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl |= sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x4000; sci_out(port, SCSCR, ctrl); } @@ -1219,6 +1235,8 @@ static void sci_stop_rx(struct uart_port *port) /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x4000; ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); sci_out(port, SCSCR, ctrl); } @@ -1253,8 +1271,12 @@ static void rx_timer_fn(unsigned long arg) { struct sci_port *s = (struct sci_port *)arg; struct uart_port *port = &s->port; - u16 scr = sci_in(port, SCSCR); + + if (port->type == PORT_SCIFA) { + scr &= ~0x4000; + enable_irq(s->irqs[1]); + } sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE); dev_dbg(port->dev, "DMA Rx timed out\n"); schedule_work(&s->work_rx); @@ -1404,8 +1426,12 @@ static void sci_shutdown(struct uart_port *port) static void sci_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct sci_port *s = to_sci_port(port); +#endif unsigned int status, baud, smr_val, max_baud; int t = -1; + u16 scfcr = 0; /* * earlyprintk comes here early on with port->uartclk set to zero. @@ -1428,7 +1454,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ if (port->type != PORT_SCI) - sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); + sci_out(port, SCFCR, scfcr | SCFCR_RFRST | SCFCR_TFRST); smr_val = sci_in(port, SCSMR) & 3; if ((termios->c_cflag & CSIZE) == CS7) @@ -1459,10 +1485,32 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, } sci_init_pins(port, termios->c_cflag); - sci_out(port, SCFCR, (termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0); + sci_out(port, SCFCR, scfcr | ((termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0)); sci_out(port, SCSCR, SCSCR_INIT(port)); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + /* + * Calculate delay for 1.5 DMA buffers: see + * drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits + * (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function + * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." + * Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO + * sizes), but it has been found out experimentally, that this is not + * enough: the driver too often needlessly runs on a DMA timeout. 20ms + * as a minimum seem to work perfectly. + */ + if (s->chan_rx) { + s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / + port->fifosize / 2; + dev_dbg(port->dev, + "DMA Rx t-out %ums, tty t-out %u jiffies\n", + s->rx_timeout * 1000 / HZ, port->timeout); + if (s->rx_timeout < msecs_to_jiffies(20)) + s->rx_timeout = msecs_to_jiffies(20); + } +#endif + if ((termios->c_cflag & CREAD) != 0) sci_start_rx(port); } -- cgit v1.1 From c3635c78e500a52c9fcd55de381a72928d9e054d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 26 Mar 2010 16:44:01 -0700 Subject: DMAENGINE: generic slave control v2 Convert the device_terminate_all() operation on the DMA engine to a generic device_control() operation which can now optionally support also pausing and resuming DMA on a certain channel. Implemented for the COH 901 318 DMAC as an example. [dan.j.williams@intel.com: update for timberdale] Signed-off-by: Linus Walleij Acked-by: Mark Brown Cc: Maciej Sosnowski Cc: Nicolas Ferre Cc: Pavel Machek Cc: Li Yang Cc: Guennadi Liakhovetski Cc: Paul Mundt Cc: Ralf Baechle Cc: Haavard Skinnemoen Cc: Magnus Damm Cc: Liam Girdwood Cc: Joe Perches Cc: Roland Dreier Signed-off-by: Dan Williams --- drivers/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index f7b9aff..6909882 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -1087,7 +1087,7 @@ static void work_fn_rx(struct work_struct *work) unsigned long flags; int count; - chan->device->device_terminate_all(chan); + chan->device->device_control(chan, DMA_TERMINATE_ALL); dev_dbg(port->dev, "Read %u bytes with cookie %d\n", sh_desc->partial, sh_desc->cookie); -- cgit v1.1 From f467bc148d05a1465211102804858df9c667f8b9 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 1 Feb 2010 09:37:46 +0000 Subject: powerpc/mpsc: Set the port device in the mpsc serial driver The mpsc serial driver needx to set the port's device tree element to register properly. Signed-off-by: Corey Minyard Signed-off-by: Benjamin Herrenschmidt --- drivers/serial/mpsc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/serial') diff --git a/drivers/serial/mpsc.c b/drivers/serial/mpsc.c index 55e113a..6a9c660 100644 --- a/drivers/serial/mpsc.c +++ b/drivers/serial/mpsc.c @@ -2071,6 +2071,7 @@ static int mpsc_drv_probe(struct platform_device *dev) if (!(rc = mpsc_drv_map_regs(pi, dev))) { mpsc_drv_get_platform_data(pi, dev, dev->id); + pi->port.dev = &dev->dev; if (!(rc = mpsc_make_ready(pi))) { spin_lock_init(&pi->tx_lock); -- cgit v1.1 From 6f4567c8cf64d1887c8e993bbf066465262b392f Mon Sep 17 00:00:00 2001 From: Timur Maximov Date: Wed, 14 Apr 2010 19:06:57 +0400 Subject: serial_cs: MD55x support (PCMCIA GPRS/EDGE modem) (kernel 2.6.33) Many PCMCIA GPRS modems like: Onda Edge N100E, Novaway PC98 (OEM SPC98Z), Rovermate Edgus Adaptmate-039 and others have same construction and identification: lspcmcia -vvv Product Name: Generic Modem: MD55x 1.00 Serial number: xxxxx-xxx Identification: manf_id: 0x015d card_id: 0x4c45 function: 2 (serial) prod_id(1): "Generic" (0xc49e4731) prod_id(2): "Modem: MD55x" (0x8913b110) prod_id(3): "1.00" (0x83dbf271) prod_id(4): "Serial number: xxxxx-xxx" (0x73ee9514) Serial connection to GSM module based on Elan VPU16551 PCMCIA UART with datasheet recommeded 14.7456MHz crystal oscillator. By default serial_cs set UART clock == 1843200 Hz For correct work need set clock 14745600 Hz. This quirk already present in driver, only need add device in quirk list. Signed-off-by: Timur Maximov Acked-by: Alan Cox Signed-off-by: Dominik Brodowski --- drivers/serial/serial_cs.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/serial') diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index 175d202..8cfa5b1 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -105,6 +105,10 @@ struct serial_cfg_mem { * manfid 0x0160, 0x0104 * This card appears to have a 14.7456MHz clock. */ +/* Generic Modem: MD55x (GPRS/EDGE) have + * Elan VPU16551 UART with 14.7456MHz oscillator + * manfid 0x015D, 0x4C45 + */ static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port) { port->uartclk = 14745600; @@ -196,6 +200,11 @@ static const struct serial_quirk quirks[] = { .multi = -1, .setup = quirk_setup_brainboxes_0104, }, { + .manfid = 0x015D, + .prodid = 0x4C45, + .multi = -1, + .setup = quirk_setup_brainboxes_0104, + }, { .manfid = MANFID_IBM, .prodid = ~0, .multi = -1, -- cgit v1.1 From 3732b68f22857201fa09cb82b128f295096a2375 Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Fri, 2 Apr 2010 17:56:08 +0200 Subject: m68knommu: fix coldfire tcdrain Fix tcdrain on coldfire uarts. Currently with coldfire uarts tcdrain returns without waiting for txempty, because (tx)fifosize is 0. Fix that and call uart_update_timeout when setting the baud rate, otherwise tcdrain will wait for an half our :) Also constify mcf_uart_ops. Signed-off-by: Philippe De Muyter Signed-off-by: Greg Ungerer --- drivers/serial/mcf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/mcf.c b/drivers/serial/mcf.c index 7bb5fee..b9f7dfe 100644 --- a/drivers/serial/mcf.c +++ b/drivers/serial/mcf.c @@ -263,6 +263,7 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, } spin_lock_irqsave(&port->lock, flags); + uart_update_timeout(port, termios->c_cflag, baud); writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); writeb(MCFUART_UCR_CMDRESETMRPTR, port->membase + MCFUART_UCR); @@ -379,6 +380,7 @@ static irqreturn_t mcf_interrupt(int irq, void *data) static void mcf_config_port(struct uart_port *port, int flags) { port->type = PORT_MCF; + port->fifosize = MCFUART_TXFIFOSIZE; /* Clear mask, so no surprise interrupts. */ writeb(0, port->membase + MCFUART_UIMR); @@ -424,7 +426,7 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) /* * Define the basic serial functions we support. */ -static struct uart_ops mcf_uart_ops = { +static const struct uart_ops mcf_uart_ops = { .tx_empty = mcf_tx_empty, .get_mctrl = mcf_get_mctrl, .set_mctrl = mcf_set_mctrl, -- cgit v1.1 From 2545cf6e94b4eb5a2c48dd55751aa9a70ff1ff9d Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Thu, 18 Mar 2010 11:37:13 +0100 Subject: m68knommu: allow 4 coldfire serial ports Fix driver/serial/mcf.c for 4-ports coldfire's (e.g. MCF5484). Signed-off-by: Philippe De Muyter Signed-off-by: Greg Ungerer --- drivers/serial/mcf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/mcf.c b/drivers/serial/mcf.c index b9f7dfe..b5aaef9 100644 --- a/drivers/serial/mcf.c +++ b/drivers/serial/mcf.c @@ -445,7 +445,7 @@ static const struct uart_ops mcf_uart_ops = { .verify_port = mcf_verify_port, }; -static struct mcf_uart mcf_ports[3]; +static struct mcf_uart mcf_ports[4]; #define MCF_MAXPORTS ARRAY_SIZE(mcf_ports) -- cgit v1.1 From 0ad707407d933841a0f337d6edccbc1d6c83e186 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Wed, 7 Apr 2010 09:34:56 +0100 Subject: ARM: 6030/1: KS8695: enable console Add add_preferred_console() to ks8695_console_init() to enable the console Signed-off-by: Yegor Yefremov Acked-by: Andrew Victor Signed-off-by: Russell King --- drivers/serial/serial_ks8695.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/serial') diff --git a/drivers/serial/serial_ks8695.c b/drivers/serial/serial_ks8695.c index 2e71bbc..b196202 100644 --- a/drivers/serial/serial_ks8695.c +++ b/drivers/serial/serial_ks8695.c @@ -650,6 +650,7 @@ static struct console ks8695_console = { static int __init ks8695_console_init(void) { + add_preferred_console(SERIAL_KS8695_DEVNAME, 0, NULL); register_console(&ks8695_console); return 0; } -- cgit v1.1 From b0c06027c7d18d99e6f5e81382a7f06a8080b084 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 27 Apr 2010 11:25:27 +0200 Subject: serial/mpc52xx_uart: Drop outdated comments Most things mentioned are either obsolete (platform-support) or wrong (device numbering, DCD spport) these days. The remaining rest is obvious. Signed-off-by: Wolfram Sang Signed-off-by: Grant Likely --- drivers/serial/mpc52xx_uart.c | 33 --------------------------------- 1 file changed, 33 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 3119fdd..a176ab4 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -29,39 +29,6 @@ * kind, whether express or implied. */ -/* Platform device Usage : - * - * Since PSCs can have multiple function, the correct driver for each one - * is selected by calling mpc52xx_match_psc_function(...). The function - * handled by this driver is "uart". - * - * The driver init all necessary registers to place the PSC in uart mode without - * DCD. However, the pin multiplexing aren't changed and should be set either - * by the bootloader or in the platform init code. - * - * The idx field must be equal to the PSC index (e.g. 0 for PSC1, 1 for PSC2, - * and so on). So the PSC1 is mapped to /dev/ttyPSC0, PSC2 to /dev/ttyPSC1 and - * so on. But be warned, it's an ABSOLUTE REQUIREMENT ! This is needed mainly - * fpr the console code : without this 1:1 mapping, at early boot time, when we - * are parsing the kernel args console=ttyPSC?, we wouldn't know which PSC it - * will be mapped to. - */ - -/* OF Platform device Usage : - * - * This driver is only used for PSCs configured in uart mode. The device - * tree will have a node for each PSC with "mpc52xx-psc-uart" in the compatible - * list. - * - * By default, PSC devices are enumerated in the order they are found. However - * a particular PSC number can be forces by adding 'device_no = ' - * to the device node. - * - * The driver init all necessary registers to place the PSC in uart mode without - * DCD. However, the pin multiplexing aren't changed and should be set either - * by the bootloader or in the platform init code. - */ - #undef DEBUG #include -- cgit v1.1 From d9901660b53b92f0f3551c06588b8be38224b245 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Mon, 22 Mar 2010 13:40:29 -0700 Subject: serial: 8250_pnp - add Fujitsu Wacom device Add Fujitsu Wacom 1FGT Tablet PC device Signed-off-by: Ping Cheng Cc: stable Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/serial/8250_pnp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/serial') diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c index 24485cc..4822cb5 100644 --- a/drivers/serial/8250_pnp.c +++ b/drivers/serial/8250_pnp.c @@ -348,6 +348,8 @@ static const struct pnp_device_id pnp_dev_table[] = { { "FUJ02E6", 0 }, /* Fujitsu Wacom 2FGT Tablet PC device */ { "FUJ02E7", 0 }, + /* Fujitsu Wacom 1FGT Tablet PC device */ + { "FUJ02E9", 0 }, /* * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in * disguise) -- cgit v1.1 From c61fae964a5ee04c4e4a01caecaf778c2e26e589 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 27 Apr 2010 14:05:20 -0700 Subject: serial: drivers/serial/pmac_zilog.c: add missing unlock In an error handling case the lock is not unlocked. A simplified version of the semantic patch that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r exists@ expression E1; identifier f; @@ f (...) { <+... * spin_lock_irqsave (E1,...); ... when != E1 * return ...; ...+> } // Signed-off-by: Julia Lawall Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/serial/pmac_zilog.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 4eaa043..700e108 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -752,8 +752,10 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) uap->curregs[R5] = new_reg; /* NOTE: Not subject to 'transmitter active' rule. */ - if (ZS_IS_ASLEEP(uap)) + if (ZS_IS_ASLEEP(uap)) { + spin_unlock_irqrestore(&port->lock, flags); return; + } write_zsreg(uap, R5, uap->curregs[R5]); } -- cgit v1.1 From e8faff7330a3501eafc9bfe5f4f15af444be29f5 Mon Sep 17 00:00:00 2001 From: Claudio Scordino Date: Mon, 3 May 2010 13:31:28 +0100 Subject: ARM: 6092/1: atmel_serial: support for RS485 communications Final version of the patch that adds support for RS485 communications to the atmel_serial driver. The patch has been already sent and discussed on both linux-kernel and linux-arm-kernel mailing lists several times. Many people collaborated to improve and test the code: Tested-by: Sebastian Heutling Tested-by: Bernhard Roth Reviewed-by: Ryan Mallon Signed-off-by: Claudio Scordino Signed-off-by: Michael Trimarchi Signed-off-by: Rick Bronson Signed-off-by: Sebastian Heutling Signed-off-by: Russell King --- drivers/serial/atmel_serial.c | 207 +++++++++++++++++++++++++++++++++++------- 1 file changed, 173 insertions(+), 34 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 2c9bf9b..eed3c2d 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -59,6 +60,9 @@ #include +static void atmel_start_rx(struct uart_port *port); +static void atmel_stop_rx(struct uart_port *port); + #ifdef CONFIG_SERIAL_ATMEL_TTYAT /* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we @@ -93,6 +97,7 @@ #define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR) #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) +#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR) /* PDC registers */ #define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) @@ -147,6 +152,9 @@ struct atmel_uart_port { unsigned int irq_status_prev; struct circ_buf rx_ring; + + struct serial_rs485 rs485; /* rs485 settings */ + unsigned int tx_done_mask; }; static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; @@ -187,6 +195,46 @@ static bool atmel_use_dma_tx(struct uart_port *port) } #endif +/* Enable or disable the rs485 support */ +void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int mode; + + spin_lock(&port->lock); + + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + mode = UART_GET_MR(port); + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + atmel_port->rs485 = *rs485conf; + + if (rs485conf->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + UART_PUT_TTGR(port, rs485conf->delay_rts_before_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + if (atmel_use_dma_tx(port)) + atmel_port->tx_done_mask = ATMEL_US_ENDTX | + ATMEL_US_TXBUFE; + else + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } + UART_PUT_MR(port, mode); + + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + + spin_unlock(&port->lock); + +} + /* * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. */ @@ -202,6 +250,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) { unsigned int control = 0; unsigned int mode; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); #ifdef CONFIG_ARCH_AT91RM9200 if (cpu_is_at91rm9200()) { @@ -236,6 +285,17 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) mode |= ATMEL_US_CHMODE_LOC_LOOP; else mode |= ATMEL_US_CHMODE_NORMAL; + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } UART_PUT_MR(port, mode); } @@ -268,12 +328,17 @@ static u_int atmel_get_mctrl(struct uart_port *port) */ static void atmel_stop_tx(struct uart_port *port) { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + if (atmel_use_dma_tx(port)) { /* disable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); - } else - UART_PUT_IDR(port, ATMEL_US_TXRDY); + } + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_start_rx(port); } /* @@ -281,17 +346,39 @@ static void atmel_stop_tx(struct uart_port *port) */ static void atmel_start_tx(struct uart_port *port) { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + if (atmel_use_dma_tx(port)) { if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) /* The transmitter is already running. Yes, we really need this.*/ return; - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_stop_rx(port); + /* re-enable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - } else - UART_PUT_IER(port, ATMEL_US_TXRDY); + } + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); +} + +/* + * start receiving - port is in process of being opened. + */ +static void atmel_start_rx(struct uart_port *port) +{ + UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */ + + if (atmel_use_dma_rx(port)) { + /* enable PDC controller */ + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); + } else { + UART_PUT_IER(port, ATMEL_US_RXRDY); + } } /* @@ -302,9 +389,11 @@ static void atmel_stop_rx(struct uart_port *port) if (atmel_use_dma_rx(port)) { /* disable PDC receive */ UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); - UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); - } else + UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + } else { UART_PUT_IDR(port, ATMEL_US_RXRDY); + } } /* @@ -428,8 +517,9 @@ static void atmel_rx_chars(struct uart_port *port) static void atmel_tx_chars(struct uart_port *port) { struct circ_buf *xmit = &port->state->xmit; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) { + if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) { UART_PUT_CHAR(port, port->x_char); port->icount.tx++; port->x_char = 0; @@ -437,7 +527,7 @@ static void atmel_tx_chars(struct uart_port *port) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; - while (UART_GET_CSR(port) & ATMEL_US_TXRDY) { + while (UART_GET_CSR(port) & atmel_port->tx_done_mask) { UART_PUT_CHAR(port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; @@ -449,7 +539,8 @@ static void atmel_tx_chars(struct uart_port *port) uart_write_wakeup(port); if (!uart_circ_empty(xmit)) - UART_PUT_IER(port, ATMEL_US_TXRDY); + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); } /* @@ -501,18 +592,10 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_tx(port)) { - /* PDC transmit */ - if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) { - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); - tasklet_schedule(&atmel_port->tasklet); - } - } else { - /* Interrupt transmit */ - if (pending & ATMEL_US_TXRDY) { - UART_PUT_IDR(port, ATMEL_US_TXRDY); - tasklet_schedule(&atmel_port->tasklet); - } + if (pending & atmel_port->tx_done_mask) { + /* Either PDC or interrupt transmission */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + tasklet_schedule(&atmel_port->tasklet); } } @@ -590,9 +673,15 @@ static void atmel_tx_dma(struct uart_port *port) UART_PUT_TPR(port, pdc->dma_addr + xmit->tail); UART_PUT_TCR(port, count); - /* re-enable PDC transmit and interrupts */ + /* re-enable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + } else { + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + /* DMA done, stop TX, start RX for RS485 */ + atmel_start_rx(port); + } } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -1017,6 +1106,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, { unsigned long flags; unsigned int mode, imr, quot, baud; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); /* Get current mode register */ mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL @@ -1115,6 +1205,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, /* disable receiver and transmitter */ UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } + /* set the parity, stop bits and data size */ UART_PUT_MR(port, mode); @@ -1231,6 +1332,35 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) } #endif +static int +atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, + sizeof(rs485conf))) + return -EFAULT; + + atmel_config_rs485(port, &rs485conf); + break; + + case TIOCGRS485: + if (copy_to_user((struct serial_rs485 *) arg, + &(to_atmel_uart_port(port)->rs485), + sizeof(rs485conf))) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + + + static struct uart_ops atmel_pops = { .tx_empty = atmel_tx_empty, .set_mctrl = atmel_set_mctrl, @@ -1250,6 +1380,7 @@ static struct uart_ops atmel_pops = { .config_port = atmel_config_port, .verify_port = atmel_verify_port, .pm = atmel_serial_pm, + .ioctl = atmel_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = atmel_poll_get_char, .poll_put_char = atmel_poll_put_char, @@ -1265,13 +1396,12 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, struct uart_port *port = &atmel_port->uart; struct atmel_uart_data *data = pdev->dev.platform_data; - port->iotype = UPIO_MEM; - port->flags = UPF_BOOT_AUTOCONF; - port->ops = &atmel_pops; - port->fifosize = 1; - port->line = pdev->id; - port->dev = &pdev->dev; - + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &atmel_pops; + port->fifosize = 1; + port->line = pdev->id; + port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; @@ -1299,8 +1429,16 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, atmel_port->use_dma_rx = data->use_dma_rx; atmel_port->use_dma_tx = data->use_dma_tx; - if (atmel_use_dma_tx(port)) + atmel_port->rs485 = data->rs485; + /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + else if (atmel_use_dma_tx(port)) { port->fifosize = PDC_BUFFER_SIZE; + atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; + } else { + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } } /* @@ -1334,6 +1472,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch) static void atmel_console_write(struct console *co, const char *s, u_int count) { struct uart_port *port = &atmel_ports[co->index].uart; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned int status, imr; unsigned int pdc_tx; @@ -1341,7 +1480,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count) * First, save IMR and then disable interrupts */ imr = UART_GET_IMR(port); - UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY); + UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask); /* Store PDC transmit status and disable it */ pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN; -- cgit v1.1 From 4fcfce9f33cd2e173f83c20e93c8b0a9397bf0bc Mon Sep 17 00:00:00 2001 From: Yauhen Kharuzhy Date: Wed, 19 Aug 2009 16:31:06 +0300 Subject: serial: Use s3c2440 driver for S3C2416 SoC UARTs in the S3C2416 are almost same as in S3C2443 and can be handled by s3c2440 serial driver. Signed-off-by: Yauhen Kharuzhy Signed-off-by: Ben Dooks --- drivers/serial/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index f55c494..5f3f03d 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -518,12 +518,13 @@ config SERIAL_S3C2412 Serial port support for the Samsung S3C2412 and S3C2413 SoC config SERIAL_S3C2440 - tristate "Samsung S3C2440/S3C2442 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442) + tristate "Samsung S3C2440/S3C2442/S3C2416 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442 || CPU_S3C2416) default y if CPU_S3C2440 default y if CPU_S3C2442 + select SERIAL_SAMSUNG_UARTS_4 if CPU_S3C2416 help - Serial port support for the Samsung S3C2440 and S3C2442 SoC + Serial port support for the Samsung S3C2440, S3C2416 and S3C2442 SoC config SERIAL_S3C24A0 tristate "Samsung S3C24A0 Serial port support" -- cgit v1.1 From eb14120f743d29744d9475bffec56ff4ad43a749 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 7 Mar 2010 12:21:16 +0100 Subject: pcmcia: re-work pcmcia_request_irq() Instead of the old pcmcia_request_irq() interface, drivers may now choose between: - calling request_irq/free_irq directly. Use the IRQ from *p_dev->irq. - use pcmcia_request_irq(p_dev, handler_t); the PCMCIA core will clean up automatically on calls to pcmcia_disable_device() or device ejection. - drivers still not capable of IRQF_SHARED (or not telling us so) may use the deprecated pcmcia_request_exclusive_irq() for the time being; they might receive a shared IRQ nonetheless. CC: linux-bluetooth@vger.kernel.org CC: netdev@vger.kernel.org CC: linux-wireless@vger.kernel.org CC: linux-serial@vger.kernel.org CC: alsa-devel@alsa-project.org CC: linux-usb@vger.kernel.org CC: linux-ide@vger.kernel.org Signed-off-by: Dominik Brodowski --- drivers/serial/serial_cs.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index 8cfa5b1..6459252 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -343,7 +343,6 @@ static int serial_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1 = 8; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->conf.Attributes = CONF_ENABLE_IRQ; if (do_sound) { link->conf.Attributes |= CONF_ENABLE_SPKR; @@ -486,7 +485,7 @@ static int simple_config(struct pcmcia_device *link) } if (info->slave) { return setup_serial(link, info, port, - link->irq.AssignedIRQ); + link->irq); } } @@ -507,10 +506,6 @@ static int simple_config(struct pcmcia_device *link) return -1; found_port: - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) - link->irq.AssignedIRQ = 0; - if (info->multi && (info->manfid == MANFID_3COM)) link->conf.ConfigIndex &= ~(0x08); @@ -523,7 +518,7 @@ found_port: i = pcmcia_request_configuration(link, &link->conf); if (i != 0) return -1; - return setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); + return setup_serial(link, info, link->io.BasePort1, link->irq); } static int multi_config_check(struct pcmcia_device *p_dev, @@ -586,13 +581,9 @@ static int multi_config(struct pcmcia_device *link) } } - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) { - /* FIXME: comment does not fit, error handling does not fit */ - printk(KERN_NOTICE - "serial_cs: no usable port range found, giving up\n"); - link->irq.AssignedIRQ = 0; - } + if (!link->irq) + dev_warn(&link->dev, + "serial_cs: no usable IRQ found, continuing...\n"); /* * Apply any configuration quirks. @@ -615,11 +606,11 @@ static int multi_config(struct pcmcia_device *link) if (link->conf.ConfigIndex == 1 || link->conf.ConfigIndex == 3) { err = setup_serial(link, info, base2, - link->irq.AssignedIRQ); + link->irq); base2 = link->io.BasePort1; } else { err = setup_serial(link, info, link->io.BasePort1, - link->irq.AssignedIRQ); + link->irq); } info->c950ctrl = base2; @@ -633,10 +624,10 @@ static int multi_config(struct pcmcia_device *link) return 0; } - setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); + setup_serial(link, info, link->io.BasePort1, link->irq); for (i = 0; i < info->multi - 1; i++) setup_serial(link, info, base2 + (8 * i), - link->irq.AssignedIRQ); + link->irq); return 0; } -- cgit v1.1 From 317b6d63000b3cc48c43d582d76063500e531a6c Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sat, 20 Mar 2010 19:26:51 +0100 Subject: pcmcia: dev_node removal (write-only drivers) dev_node_t was only used to transport some minor/major numbers from the PCMCIA device drivers to deprecated userspace helpers. However, only a few drivers made use of it, and the userspace helpers are deprecated anyways. Therefore, get rid of dev_node_t . As a first step, remove any usage of dev_node_t from drivers which only wrote to this typedef/struct, but did not make use of it. CC: linux-bluetooth@vger.kernel.org CC: Harald Welte CC: linux-mtd@lists.infradead.org CC: linux-wireless@vger.kernel.org CC: netdev@vger.kernel.org CC: linux-serial@vger.kernel.org CC: alsa-devel@alsa-project.org Signed-off-by: Dominik Brodowski --- drivers/serial/serial_cs.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index 6459252..dadd686 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -89,7 +89,6 @@ struct serial_info { int manfid; int prodid; int c950ctrl; - dev_node_t node[4]; int line[4]; const struct serial_quirk *quirk; }; @@ -289,8 +288,6 @@ static void serial_remove(struct pcmcia_device *link) for (i = 0; i < info->ndev; i++) serial8250_unregister_port(info->line[i]); - info->p_dev->dev_node = NULL; - if (!info->slave) pcmcia_disable_device(link); } @@ -410,11 +407,6 @@ static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, } info->line[info->ndev] = line; - sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); - info->node[info->ndev].major = TTY_MAJOR; - info->node[info->ndev].minor = 0x40 + line; - if (info->ndev > 0) - info->node[info->ndev - 1].next = &info->node[info->ndev]; info->ndev++; return 0; @@ -711,7 +703,6 @@ static int serial_config(struct pcmcia_device * link) if (info->quirk->post(link)) goto failed; - link->dev_node = &info->node[0]; return 0; failed: -- cgit v1.1 From e6114fa1d1d6a588766f28081b971047dd3e5def Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Wed, 5 May 2010 00:18:59 +0200 Subject: serial: mpc52xx_uart: fix null pointer dereference Commit 6acc6833510db8f72b5ef343296d97480555fda9 introduced NULL pointer dereference and kernel crash on ppc32 machines while booting. Fix this bug now. Reported-by: Leonardo Chiquitto Tested-by: Leonardo Chiquitto Signed-off-by: Anatolij Gustschin Acked-by: Grant Likely Signed-off-by: Greg Kroah-Hartman --- drivers/serial/mpc52xx_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index a176ab4..02469c3 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -1467,7 +1467,7 @@ mpc52xx_uart_init(void) /* * Map the PSC FIFO Controller and init if on MPC512x. */ - if (psc_ops->fifoc_init) { + if (psc_ops && psc_ops->fifoc_init) { ret = psc_ops->fifoc_init(); if (ret) return ret; -- cgit v1.1 From 1c5250d6163dac28be3afabdfb6c723f107051b7 Mon Sep 17 00:00:00 2001 From: Valentin Longchamp Date: Wed, 5 May 2010 11:47:07 +0200 Subject: serial: imx.c: fix CTS trigger level lower to avoid lost chars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The imx CTS trigger level is left at its reset value that is 32 chars. Since the RX FIFO has 32 entries, when CTS is raised, the FIFO already is full. However, some serial port devices first empty their TX FIFO before stopping when CTS is raised, resulting in lost chars. This patch sets the trigger level lower so that other chars arrive after CTS is raised, there is still room for 16 of them. Signed-off-by: Valentin Longchamp Tested-by: Philippe Rétornaz Acked-by: Wolfram Sang Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/serial/imx.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index 4315b23..eacb588 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -120,7 +120,8 @@ #define MX2_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */ #define UCR3_INVT (1<<1) /* Inverted Infrared transmission */ #define UCR3_BPEN (1<<0) /* Preset registers enable */ -#define UCR4_CTSTL_32 (32<<10) /* CTS trigger level (32 chars) */ +#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */ +#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */ #define UCR4_INVR (1<<9) /* Inverted infrared reception */ #define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */ #define UCR4_WKEN (1<<7) /* Wake interrupt enable */ @@ -591,6 +592,9 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) return 0; } +/* half the RX buffer size */ +#define CTSTL 16 + static int imx_startup(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; @@ -607,6 +611,10 @@ static int imx_startup(struct uart_port *port) if (USE_IRDA(sport)) temp |= UCR4_IRSC; + /* set the trigger level for CTS */ + temp &= ~(UCR4_CTSTL_MASK<< UCR4_CTSTL_SHF); + temp |= CTSTL<< UCR4_CTSTL_SHF; + writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); if (USE_IRDA(sport)) { -- cgit v1.1 From 058276303dbc4ed089c1f7dad0871810b1f5ddf1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 17 May 2010 16:30:42 -0700 Subject: DMAENGINE: extend the control command to include an arg This adds an argument to the DMAengine control function, so that we can later provide control commands that need some external data passed in through an argument akin to the ioctl() operation prototype. [dan.j.williams@intel.com: fix up some missed conversions] Signed-off-by: Linus Walleij Signed-off-by: Dan Williams --- drivers/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 6909882..882f3d5 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -1087,7 +1087,7 @@ static void work_fn_rx(struct work_struct *work) unsigned long flags; int count; - chan->device->device_control(chan, DMA_TERMINATE_ALL); + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); dev_dbg(port->dev, "Read %u bytes with cookie %d\n", sh_desc->partial, sh_desc->cookie); -- cgit v1.1 From cb34a67472c3171f350e45bc66eebf480acec792 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 18 May 2010 12:38:39 +0200 Subject: drivers: serial: S5PC100 serial driver cleanup Remove unnecessary Kconfig entry. S5PC100 has exactly the same driver as S3C6400/S3C6410/S5P6440. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Ben Dooks --- drivers/serial/Kconfig | 16 ++++------------ drivers/serial/Makefile | 1 - 2 files changed, 4 insertions(+), 13 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 5f3f03d..302836a 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -534,21 +534,13 @@ config SERIAL_S3C24A0 Serial port support for the Samsung S3C24A0 SoC config SERIAL_S3C6400 - tristate "Samsung S3C6400/S3C6410/S5P6440 Seria port support" - depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440) + tristate "Samsung S3C6400/S3C6410/S5P6440/S5PC100 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440 || CPU_S5PC100) select SERIAL_SAMSUNG_UARTS_4 default y help - Serial port support for the Samsung S3C6400, S3C6410 and S5P6440 - SoCs - -config SERIAL_S5PC100 - tristate "Samsung S5PC100 Serial port support" - depends on SERIAL_SAMSUNG && CPU_S5PC100 - select SERIAL_SAMSUNG_UARTS_4 - default y - help - Serial port support for the Samsung S5PC100 SoCs + Serial port support for the Samsung S3C6400, S3C6410, S5P6440 + and S5PC100 SoCs config SERIAL_S5PV210 tristate "Samsung S5PV210 Serial port support" diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 6aa4723..328f107 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -44,7 +44,6 @@ obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o -obj-$(CONFIG_SERIAL_S5PC100) += s3c6400.o obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o -- cgit v1.1 From f5316b4aea024da9266d740322a5481657f6ce59 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 20 May 2010 21:04:22 -0500 Subject: kgdb,8250,pl011: Return immediately from console poll The design of the kdb shell requires that every device that can provide input to kdb have a polling routine that exits immediately if there is no character available. This is required in order to get the page scrolling mechanism working. Changing the kernel debugger I/O API to require all polling character routines to exit immediately if there is no data allows the kernel debugger to process multiple input channels. NO_POLL_CHAR will be the return code to the polling routine when ever there is no character available. CC: linux-serial@vger.kernel.org Signed-off-by: Jason Wessel --- drivers/serial/8250.c | 4 ++-- drivers/serial/amba-pl011.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 2b1ea3d..891e1dd 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1891,8 +1891,8 @@ static int serial8250_get_poll_char(struct uart_port *port) struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned char lsr = serial_inp(up, UART_LSR); - while (!(lsr & UART_LSR_DR)) - lsr = serial_inp(up, UART_LSR); + if (!(lsr & UART_LSR_DR)) + return NO_POLL_CHAR; return serial_inp(up, UART_RX); } diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index 743ebf5..eb4cb48 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -342,9 +342,9 @@ static int pl010_get_poll_char(struct uart_port *port) struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int status; - do { - status = readw(uap->port.membase + UART01x_FR); - } while (status & UART01x_FR_RXFE); + status = readw(uap->port.membase + UART01x_FR); + if (status & UART01x_FR_RXFE) + return NO_POLL_CHAR; return readw(uap->port.membase + UART01x_DR); } -- cgit v1.1 From 3f255eb37e97e97dfec7cb8d4c75d543de231812 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 20 May 2010 21:04:23 -0500 Subject: sh,sh-sci: Use NO_POLL_CHAR in the SCIF polled console code The sci_poll_get_char() needs to return immediately if there is no input from the chip to process, and must return a value of NO_POLL_CHAR. This allows kgdboc to process multiple polled devices while kgdb is active. Signed-off-by: Jason Wessel Acked-by: Paul Mundt --- drivers/serial/sh-sci.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 8d993c4..f250a61 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -151,7 +151,11 @@ static int sci_poll_get_char(struct uart_port *port) handle_error(port); continue; } - } while (!(status & SCxSR_RDxF(port))); + break; + } while (1); + + if (!(status & SCxSR_RDxF(port))) + return NO_POLL_CHAR; c = sci_in(port, SCxRDR); -- cgit v1.1 From 6d45a1aed34b0cd7b298967eb9cb72b77afcb33b Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 20 May 2010 21:04:23 -0500 Subject: sparc,sunzilog: Add console polling support for sunzilog serial driver Allow kgdboc to work on sparc hardware with the Zilog serial chips. Signed-off-by: Jason Wessel Acked-by: David S. Miller --- drivers/serial/sunzilog.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'drivers/serial') diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index 2c7a66a..978b3ce 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -102,6 +102,8 @@ struct uart_sunzilog_port { #endif }; +static void sunzilog_putchar(struct uart_port *port, int ch); + #define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel __iomem *)((PORT)->membase)) #define UART_ZILOG(PORT) ((struct uart_sunzilog_port *)(PORT)) @@ -996,6 +998,50 @@ static int sunzilog_verify_port(struct uart_port *port, struct serial_struct *se return -EINVAL; } +#ifdef CONFIG_CONSOLE_POLL +static int sunzilog_get_poll_char(struct uart_port *port) +{ + unsigned char ch, r1; + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct zilog_channel __iomem *channel + = ZILOG_CHANNEL_FROM_PORT(&up->port); + + + r1 = read_zsreg(channel, R1); + if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { + writeb(ERR_RES, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + } + + ch = readb(&channel->control); + ZSDELAY(); + + /* This funny hack depends upon BRK_ABRT not interfering + * with the other bits we care about in R1. + */ + if (ch & BRK_ABRT) + r1 |= BRK_ABRT; + + if (!(ch & Rx_CH_AV)) + return NO_POLL_CHAR; + + ch = readb(&channel->data); + ZSDELAY(); + + ch &= up->parity_mask; + return ch; +} + +static void sunzilog_put_poll_char(struct uart_port *port, + unsigned char ch) +{ + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *)port; + + sunzilog_putchar(&up->port, ch); +} +#endif /* CONFIG_CONSOLE_POLL */ + static struct uart_ops sunzilog_pops = { .tx_empty = sunzilog_tx_empty, .set_mctrl = sunzilog_set_mctrl, @@ -1013,6 +1059,10 @@ static struct uart_ops sunzilog_pops = { .request_port = sunzilog_request_port, .config_port = sunzilog_config_port, .verify_port = sunzilog_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = sunzilog_get_poll_char, + .poll_put_char = sunzilog_put_poll_char, +#endif }; static int uart_chip_count; -- cgit v1.1 From ada64e4c98eb5f04a9ca223c5ff9e7ac22ce6404 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 20 May 2010 21:04:24 -0500 Subject: kgdboc,keyboard: Keyboard driver for kdb with kgdb This patch adds in the kdb PS/2 keyboard driver. This was mostly a direct port from the original kdb where I cleaned up the code against checkpatch.pl and added the glue to stitch it into kgdb. This patch also enables early kdb debug via kgdbwait and the keyboard. All the access to configure kdb using either a serial console or the keyboard is done via kgdboc. If you want to use only the keyboard and want to break in early you would add to your kernel command arguments: kgdboc=kbd kgdbwait If you wanted serial and or the keyboard access you could use: kgdboc=kbd,ttyS0 You can also configure kgdboc as a kernel module or at run time with the sysfs where you can activate and deactivate kgdb. Turn it on: echo kbd,ttyS0 > /sys/module/kgdboc/parameters/kgdboc Turn it off: echo "" > /sys/module/kgdboc/parameters/kgdboc Signed-off-by: Jason Wessel Reviewed-by: Dmitry Torokhov --- drivers/serial/kgdboc.c | 61 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 7 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/kgdboc.c b/drivers/serial/kgdboc.c index eadc1ab..ecef6e1 100644 --- a/drivers/serial/kgdboc.c +++ b/drivers/serial/kgdboc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define MAX_CONFIG_LEN 40 @@ -32,6 +33,40 @@ static struct kparam_string kps = { static struct tty_driver *kgdb_tty_driver; static int kgdb_tty_line; +#ifdef CONFIG_KDB_KEYBOARD +static int kgdboc_register_kbd(char **cptr) +{ + if (strncmp(*cptr, "kbd", 3) == 0) { + if (kdb_poll_idx < KDB_POLL_FUNC_MAX) { + kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char; + kdb_poll_idx++; + if (cptr[0][3] == ',') + *cptr += 4; + else + return 1; + } + } + return 0; +} + +static void kgdboc_unregister_kbd(void) +{ + int i; + + for (i = 0; i < kdb_poll_idx; i++) { + if (kdb_poll_funcs[i] == kdb_get_kbd_char) { + kdb_poll_idx--; + kdb_poll_funcs[i] = kdb_poll_funcs[kdb_poll_idx]; + kdb_poll_funcs[kdb_poll_idx] = NULL; + i--; + } + } +} +#else /* ! CONFIG_KDB_KEYBOARD */ +#define kgdboc_register_kbd(x) 0 +#define kgdboc_unregister_kbd() +#endif /* ! CONFIG_KDB_KEYBOARD */ + static int kgdboc_option_setup(char *opt) { if (strlen(opt) > MAX_CONFIG_LEN) { @@ -45,25 +80,38 @@ static int kgdboc_option_setup(char *opt) __setup("kgdboc=", kgdboc_option_setup); +static void cleanup_kgdboc(void) +{ + kgdboc_unregister_kbd(); + if (configured == 1) + kgdb_unregister_io_module(&kgdboc_io_ops); +} + static int configure_kgdboc(void) { struct tty_driver *p; int tty_line = 0; int err; + char *cptr = config; err = kgdboc_option_setup(config); if (err || !strlen(config) || isspace(config[0])) goto noconfig; err = -ENODEV; + kgdb_tty_driver = NULL; + + if (kgdboc_register_kbd(&cptr)) + goto do_register; - p = tty_find_polling_driver(config, &tty_line); + p = tty_find_polling_driver(cptr, &tty_line); if (!p) goto noconfig; kgdb_tty_driver = p; kgdb_tty_line = tty_line; +do_register: err = kgdb_register_io_module(&kgdboc_io_ops); if (err) goto noconfig; @@ -75,6 +123,7 @@ static int configure_kgdboc(void) noconfig: config[0] = 0; configured = 0; + cleanup_kgdboc(); return err; } @@ -88,20 +137,18 @@ static int __init init_kgdboc(void) return configure_kgdboc(); } -static void cleanup_kgdboc(void) -{ - if (configured == 1) - kgdb_unregister_io_module(&kgdboc_io_ops); -} - static int kgdboc_get_char(void) { + if (!kgdb_tty_driver) + return -1; return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver, kgdb_tty_line); } static void kgdboc_put_char(u8 chr) { + if (!kgdb_tty_driver) + return; kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver, kgdb_tty_line, chr); } -- cgit v1.1 From efe2f29e324fd20e0449bcd6dc6dbe4734c2ba94 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 20 May 2010 21:04:26 -0500 Subject: kgdboc,kdb: Allow kdb to work on a non open console port If kdb is open on a serial port that is not actually a console make sure to call the poll routines to emit and receive characters. Signed-off-by: Jason Wessel Acked-by: Martin Hicks --- drivers/serial/kgdboc.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/serial') diff --git a/drivers/serial/kgdboc.c b/drivers/serial/kgdboc.c index ecef6e1..b765ab4 100644 --- a/drivers/serial/kgdboc.c +++ b/drivers/serial/kgdboc.c @@ -16,6 +16,7 @@ #include #include #include +#include #define MAX_CONFIG_LEN 40 @@ -93,12 +94,14 @@ static int configure_kgdboc(void) int tty_line = 0; int err; char *cptr = config; + struct console *cons; err = kgdboc_option_setup(config); if (err || !strlen(config) || isspace(config[0])) goto noconfig; err = -ENODEV; + kgdboc_io_ops.is_console = 0; kgdb_tty_driver = NULL; if (kgdboc_register_kbd(&cptr)) @@ -108,6 +111,17 @@ static int configure_kgdboc(void) if (!p) goto noconfig; + cons = console_drivers; + while (cons) { + int idx; + if (cons->device && cons->device(cons, &idx) == p && + idx == tty_line) { + kgdboc_io_ops.is_console = 1; + break; + } + cons = cons->next; + } + kgdb_tty_driver = p; kgdb_tty_line = tty_line; -- cgit v1.1 From 9731191f75a54c4fa17e9b9b88f3144cf4b47836 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 20 May 2010 21:04:30 -0500 Subject: kgdboc: Add ekgdboc for early use of the kernel debugger The ekgdboc= differs from kgdboc= in that you can begin debuggin as soon as the exceptions are setup and the kgdb I/O driver is available, instead of waiting until the tty subsystem is available. CC: kgdb-bugreport@lists.sourceforge.net Signed-off-by: Jason Wessel --- drivers/serial/kgdboc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/serial') diff --git a/drivers/serial/kgdboc.c b/drivers/serial/kgdboc.c index b765ab4..a9a94ae 100644 --- a/drivers/serial/kgdboc.c +++ b/drivers/serial/kgdboc.c @@ -223,6 +223,25 @@ static struct kgdb_io kgdboc_io_ops = { .post_exception = kgdboc_post_exp_handler, }; +#ifdef CONFIG_KGDB_SERIAL_CONSOLE +/* This is only available if kgdboc is a built in for early debugging */ +int __init kgdboc_early_init(char *opt) +{ + /* save the first character of the config string because the + * init routine can destroy it. + */ + char save_ch; + + kgdboc_option_setup(opt); + save_ch = config[0]; + init_kgdboc(); + config[0] = save_ch; + return 0; +} + +early_param("ekgdboc", kgdboc_early_init); +#endif /* CONFIG_KGDB_SERIAL_CONSOLE */ + module_init(init_kgdboc); module_exit(cleanup_kgdboc); module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644); -- cgit v1.1 From a5a420d207df40226afbf828c12bd9b4c6e058ef Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 9 Mar 2010 12:25:28 -0500 Subject: serial: bfin_sport_uart: work around anomaly 05000473 (make 32bit fifo read atomic) We cannot let a 32-bit RX FIFO read be interrupted otherwise a fake RX underflow error might be generated. URL: http://blackfin.uclinux.org/gf/tracker/5145 Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.h b/drivers/serial/bfin_sport_uart.h index abe0361..9791fcc 100644 --- a/drivers/serial/bfin_sport_uart.h +++ b/drivers/serial/bfin_sport_uart.h @@ -37,7 +37,21 @@ #define SPORT_GET_TFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TFSDIV)) #define SPORT_GET_TX(sport) bfin_read16(((sport)->port.membase + OFFSET_TX)) #define SPORT_GET_RX(sport) bfin_read16(((sport)->port.membase + OFFSET_RX)) -#define SPORT_GET_RX32(sport) bfin_read32(((sport)->port.membase + OFFSET_RX)) +/* + * If another interrupt fires while doing a 32-bit read from RX FIFO, + * a fake RX underflow error will be generated. So disable interrupts + * to prevent interruption while reading the FIFO. + */ +#define SPORT_GET_RX32(sport) \ +({ \ + unsigned int __ret; \ + if (ANOMALY_05000473) \ + local_irq_disable(); \ + __ret = bfin_read32((sport)->port.membase + OFFSET_RX); \ + if (ANOMALY_05000473) \ + local_irq_enable(); \ + __ret; \ +}) #define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1)) #define SPORT_GET_RCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR2)) #define SPORT_GET_RCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RCLKDIV)) -- cgit v1.1 From 3f3a978b9f4a513610b32f16670914006a61067f Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 9 Mar 2010 12:25:29 -0500 Subject: serial: bfin_sport_uart: shorten the SPORT TX waiting loop The waiting loop to stop SPORT TX from TX interrupt is too long. This may block the SPORT RX interrupts and cause the RX FIFO to overflow. So, do stop sport TX only after the last char in TX FIFO is moved into the shift register. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index c88f8ad..6991c36 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -271,7 +271,13 @@ static void sport_uart_tx_chars(struct sport_uart_port *up) } if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - sport_stop_tx(&up->port); + /* The waiting loop to stop SPORT TX from TX interrupt is + * too long. This may block SPORT RX interrupts and cause + * RX FIFO overflow. So, do stop sport TX only after the last + * char in TX FIFO is moved into the shift register. + */ + if (SPORT_GET_STAT(up) & TXHRE) + sport_stop_tx(&up->port); return; } -- cgit v1.1 From 00c1ff8c0fd14e1fa76fb5351ae7d11a513b8497 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 9 Mar 2010 12:25:30 -0500 Subject: serial: bfin_sport_uart: remove unused peripheral pin lists All the resources are in the boards files now. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.c | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 6991c36..5dcf5dc 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -39,27 +39,6 @@ #include "bfin_sport_uart.h" -#ifdef CONFIG_SERIAL_BFIN_SPORT0_UART -unsigned short bfin_uart_pin_req_sport0[] = - {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ - P_SPORT0_DRPRI, P_SPORT0_RSCLK, P_SPORT0_DRSEC, P_SPORT0_DTSEC, 0}; -#endif -#ifdef CONFIG_SERIAL_BFIN_SPORT1_UART -unsigned short bfin_uart_pin_req_sport1[] = - {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ - P_SPORT1_DRPRI, P_SPORT1_RSCLK, P_SPORT1_DRSEC, P_SPORT1_DTSEC, 0}; -#endif -#ifdef CONFIG_SERIAL_BFIN_SPORT2_UART -unsigned short bfin_uart_pin_req_sport2[] = - {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, P_SPORT2_RFS, \ - P_SPORT2_DRPRI, P_SPORT2_RSCLK, P_SPORT2_DRSEC, P_SPORT2_DTSEC, 0}; -#endif -#ifdef CONFIG_SERIAL_BFIN_SPORT3_UART -unsigned short bfin_uart_pin_req_sport3[] = - {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, P_SPORT3_RFS, \ - P_SPORT3_DRPRI, P_SPORT3_RSCLK, P_SPORT3_DRSEC, P_SPORT3_DTSEC, 0}; -#endif - struct sport_uart_port { struct uart_port port; int err_irq; -- cgit v1.1 From e8126b32f6782d2e275e632a5e51e2433ceda7b0 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 9 Mar 2010 12:25:31 -0500 Subject: serial: bfin_sport_uart: add missing mapbase initialization The driver doesn't care about this, but the common serial core wants it. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 5dcf5dc..49e7e4f 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -712,6 +712,7 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) ret = -ENXIO; goto out_error_free_peripherals; } + sport->port.mapbase = res->start; sport->port.irq = platform_get_irq(pdev, 0); if (sport->port.irq < 0) { @@ -809,7 +810,7 @@ static int __init sport_uart_init(void) { int ret; - pr_info("Serial: Blackfin uart over sport driver\n"); + pr_info("Blackfin uart over sport driver\n"); ret = uart_register_driver(&sport_uart_reg); if (ret) { -- cgit v1.1 From b59588aae8356caf52b653a7c448c3fffdd27628 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 9 Mar 2010 12:25:32 -0500 Subject: serial: bfin_sport_uart: rename early platform driver class string Clarifies command line set up for devices between consoles and early devices. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 49e7e4f..7925266 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -527,6 +527,8 @@ struct uart_ops sport_uart_ops = { static struct sport_uart_port *bfin_sport_uart_ports[BFIN_SPORT_UART_MAX_PORTS]; #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE +#define CLASS_BFIN_SPORT_CONSOLE "bfin-sport-console" + static int __init sport_uart_console_setup(struct console *co, char *options) { @@ -788,7 +790,7 @@ static struct platform_driver sport_uart_driver = { #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE static __initdata struct early_platform_driver early_sport_uart_driver = { - .class_str = DRV_NAME, + .class_str = CLASS_BFIN_SPORT_CONSOLE, .pdrv = &sport_uart_driver, .requested_id = EARLY_PLATFORM_ID_UNSET, }; @@ -797,7 +799,8 @@ static int __init sport_uart_rs_console_init(void) { early_platform_driver_register(&early_sport_uart_driver, DRV_NAME); - early_platform_driver_probe(DRV_NAME, BFIN_SPORT_UART_MAX_PORTS, 0); + early_platform_driver_probe(CLASS_BFIN_SPORT_CONSOLE, + BFIN_SPORT_UART_MAX_PORTS, 0); register_console(&sport_uart_console); -- cgit v1.1 From 1f7d1c85df5b63359e7f7e3fbd1509c1cdd6414f Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 9 Mar 2010 12:25:33 -0500 Subject: serial: bfin_sport_uart: add support for CTS/RTS via GPIOs Some people need flow control on their ports, so now boards can support that via any GPIOs. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/Kconfig | 24 +++++++++ drivers/serial/bfin_sport_uart.c | 111 +++++++++++++++++++++++++++++++++++---- drivers/serial/bfin_sport_uart.h | 11 ++++ 3 files changed, 135 insertions(+), 11 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 302836a..6325830 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1443,24 +1443,48 @@ config SERIAL_BFIN_SPORT0_UART help Enable UART over SPORT0 +config SERIAL_BFIN_SPORT0_UART_CTSRTS + bool "Enable UART over SPORT0 hardware flow control" + depends on SERIAL_BFIN_SPORT0_UART + help + Enable hardware flow control in the driver. + config SERIAL_BFIN_SPORT1_UART bool "Enable UART over SPORT1" depends on SERIAL_BFIN_SPORT help Enable UART over SPORT1 +config SERIAL_BFIN_SPORT1_UART_CTSRTS + bool "Enable UART over SPORT1 hardware flow control" + depends on SERIAL_BFIN_SPORT1_UART + help + Enable hardware flow control in the driver. + config SERIAL_BFIN_SPORT2_UART bool "Enable UART over SPORT2" depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) help Enable UART over SPORT2 +config SERIAL_BFIN_SPORT2_UART_CTSRTS + bool "Enable UART over SPORT2 hardware flow control" + depends on SERIAL_BFIN_SPORT2_UART + help + Enable hardware flow control in the driver. + config SERIAL_BFIN_SPORT3_UART bool "Enable UART over SPORT3" depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) help Enable UART over SPORT3 +config SERIAL_BFIN_SPORT3_UART_CTSRTS + bool "Enable UART over SPORT3 hardware flow control" + depends on SERIAL_BFIN_SPORT3_UART + help + Enable hardware flow control in the driver. + config SERIAL_TIMBERDALE tristate "Support for timberdale UART" select SERIAL_CORE diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 7925266..8cb0a40 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -48,6 +48,10 @@ struct sport_uart_port { unsigned short txmask2; unsigned char stopb; /* unsigned char parib; */ +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + int cts_pin; + int rts_pin; +#endif }; static void sport_uart_tx_chars(struct sport_uart_port *up); @@ -198,6 +202,59 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + if (up->cts_pin < 0) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + + /* CTS PIN is negative assertive. */ + if (SPORT_UART_GET_CTS(up)) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + else + return TIOCM_DSR | TIOCM_CAR; +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + if (up->rts_pin < 0) + return; + + /* RTS PIN is negative assertive. */ + if (mctrl & TIOCM_RTS) + SPORT_UART_ENABLE_RTS(up); + else + SPORT_UART_DISABLE_RTS(up); +} + +/* + * Handle any change of modem status signal. + */ +static irqreturn_t sport_mctrl_cts_int(int irq, void *dev_id) +{ + struct sport_uart_port *up = (struct sport_uart_port *)dev_id; + unsigned int status; + + status = sport_get_mctrl(&up->port); + uart_handle_cts_change(&up->port, status & TIOCM_CTS); + + return IRQ_HANDLED; +} +#else +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + pr_debug("%s enter\n", __func__); + return TIOCM_CTS | TIOCM_CD | TIOCM_DSR; +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + pr_debug("%s enter\n", __func__); +} +#endif + /* Reqeust IRQ, Setup clock */ static int sport_startup(struct uart_port *port) { @@ -226,6 +283,21 @@ static int sport_startup(struct uart_port *port) goto fail2; } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + if (up->cts_pin >= 0) { + if (request_irq(gpio_to_irq(up->cts_pin), + sport_mctrl_cts_int, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_DISABLED, "BFIN_SPORT_UART_CTS", up)) { + up->cts_pin = -1; + dev_info(port->dev, "Unable to attach BlackFin UART \ + over SPORT CTS interrupt. So, disable it.\n"); + } + } + if (up->rts_pin >= 0) + gpio_direction_output(up->rts_pin, 0); +#endif + return 0; fail2: free_irq(up->port.irq+1, up); @@ -283,17 +355,6 @@ static unsigned int sport_tx_empty(struct uart_port *port) return 0; } -static unsigned int sport_get_mctrl(struct uart_port *port) -{ - pr_debug("%s enter\n", __func__); - return (TIOCM_CTS | TIOCM_CD | TIOCM_DSR); -} - -static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - pr_debug("%s enter\n", __func__); -} - static void sport_stop_tx(struct uart_port *port) { struct sport_uart_port *up = (struct sport_uart_port *)port; @@ -364,6 +425,10 @@ static void sport_shutdown(struct uart_port *port) free_irq(up->port.irq, up); free_irq(up->port.irq+1, up); free_irq(up->err_irq, up); +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + if (up->cts_pin >= 0) + free_irq(gpio_to_irq(up->cts_pin), up); +#endif } static const char *sport_type(struct uart_port *port) @@ -536,7 +601,11 @@ sport_uart_console_setup(struct console *co, char *options) int baud = 57600; int bits = 8; int parity = 'n'; +# ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + int flow = 'r'; +# else int flow = 'n'; +# endif /* Check whether an invalid uart number has been specified */ if (co->index < 0 || co->index >= BFIN_SPORT_UART_MAX_PORTS) @@ -729,6 +798,22 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) ret = -ENOENT; goto out_error_unmap; } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) + sport->cts_pin = -1; + else + sport->cts_pin = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (res == NULL) + sport->rts_pin = -1; + else + sport->rts_pin = res->start; + + if (sport->rts_pin >= 0) + gpio_request(sport->rts_pin, DRV_NAME); +#endif } #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE @@ -767,6 +852,10 @@ static int __devexit sport_uart_remove(struct platform_device *pdev) if (sport) { uart_remove_one_port(&sport_uart_reg, &sport->port); +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (sport->rts_pin >= 0) + gpio_free(sport->rts_pin); +#endif iounmap(sport->port.membase); peripheral_free_list( (unsigned short *)pdev->dev.platform_data); diff --git a/drivers/serial/bfin_sport_uart.h b/drivers/serial/bfin_sport_uart.h index 9791fcc..9ce253e 100644 --- a/drivers/serial/bfin_sport_uart.h +++ b/drivers/serial/bfin_sport_uart.h @@ -72,4 +72,15 @@ #define SPORT_TX_FIFO_SIZE 8 +#define SPORT_UART_GET_CTS(x) gpio_get_value(x->cts_pin) +#define SPORT_UART_DISABLE_RTS(x) gpio_set_value(x->rts_pin, 1) +#define SPORT_UART_ENABLE_RTS(x) gpio_set_value(x->rts_pin, 0) + +#if defined(CONFIG_SERIAL_BFIN_SPORT0_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT1_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT2_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT3_UART_CTSRTS) +# define CONFIG_SERIAL_BFIN_SPORT_CTSRTS +#endif + #endif /* _BFIN_SPORT_UART_H */ -- cgit v1.1 From 9498dc95ca6fa6e5b044f39696ab317996fd2fe5 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 9 Mar 2010 12:25:34 -0500 Subject: serial: bfin_sport_uart: protect changes to uart_port Common serial API says we need to grab the port lock before modifying the port state to prevent inconsistent state between threads. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 8cb0a40..5224db2 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -498,6 +498,8 @@ static void sport_set_termios(struct uart_port *port, /* up->parib = 1; */ } + spin_lock_irqsave(&up->port.lock, flags); + port->read_status_mask = OE; if (termios->c_iflag & INPCK) port->read_status_mask |= (FE | PE); @@ -538,8 +540,6 @@ static void sport_set_termios(struct uart_port *port, /* uart baud rate */ port->uartclk = uart_get_baud_rate(port, termios, old, 0, get_sclk()/16); - spin_lock_irqsave(&up->port.lock, flags); - /* Disable UART */ SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); -- cgit v1.1 From f4d10ca895841ec0090bed81e614b6c731ac7d7d Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 9 Mar 2010 12:25:35 -0500 Subject: serial: bfin_sport_uart: zero sport_uart_port if allocated dynamically Need to initialize the SPORT state rather than using random memory. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 5224db2..6ee73da 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -746,11 +746,11 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) if (bfin_sport_uart_ports[pdev->id] == NULL) { bfin_sport_uart_ports[pdev->id] = - kmalloc(sizeof(struct sport_uart_port), GFP_KERNEL); + kzalloc(sizeof(struct sport_uart_port), GFP_KERNEL); sport = bfin_sport_uart_ports[pdev->id]; if (!sport) { dev_err(&pdev->dev, - "Fail to kmalloc sport_uart_port\n"); + "Fail to malloc sport_uart_port\n"); return -ENOMEM; } -- cgit v1.1 From 60bd940f142741092b95a2e572f81741dc867545 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 9 Mar 2010 12:25:36 -0500 Subject: serial: bfin_sport_uart: drop useless status masks These were all copied over from the Blackfin UART driver, but they don't make sense here because these bits are all specific to the Blackfin UART. Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 6ee73da..b9938ac 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -500,27 +500,12 @@ static void sport_set_termios(struct uart_port *port, spin_lock_irqsave(&up->port.lock, flags); - port->read_status_mask = OE; - if (termios->c_iflag & INPCK) - port->read_status_mask |= (FE | PE); - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= BI; + port->read_status_mask = 0; /* * Characters to ignore */ port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= FE | PE; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= OE; - } /* RX extract mask */ up->rxmask = 0x01 | (((up->csize + up->stopb) * 2 - 1) << 0x8); -- cgit v1.1 From 9356c46170e0551f72467c9b0fb9a856a36aa2de Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 9 Mar 2010 12:25:37 -0500 Subject: serial: bfin_sport_uart: only enable SPORT TX if data is to be sent Rather than always turn on the SPORT TX interrupt, only do it when we've actually queued up data for transmission. This avoids useless interrupt processing. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index b9938ac..0b459cf 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -54,7 +54,7 @@ struct sport_uart_port { #endif }; -static void sport_uart_tx_chars(struct sport_uart_port *up); +static int sport_uart_tx_chars(struct sport_uart_port *up); static void sport_stop_tx(struct uart_port *port); static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value) @@ -307,18 +307,24 @@ static int sport_startup(struct uart_port *port) return ret; } -static void sport_uart_tx_chars(struct sport_uart_port *up) +/* + * sport_uart_tx_chars + * + * ret 1 means need to enable sport. + * ret 0 means do nothing. + */ +static int sport_uart_tx_chars(struct sport_uart_port *up) { struct circ_buf *xmit = &up->port.state->xmit; if (SPORT_GET_STAT(up) & TXF) - return; + return 0; if (up->port.x_char) { tx_one_byte(up, up->port.x_char); up->port.icount.tx++; up->port.x_char = 0; - return; + return 1; } if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { @@ -329,7 +335,7 @@ static void sport_uart_tx_chars(struct sport_uart_port *up) */ if (SPORT_GET_STAT(up) & TXHRE) sport_stop_tx(&up->port); - return; + return 0; } while(!(SPORT_GET_STAT(up) & TXF) && !uart_circ_empty(xmit)) { @@ -340,6 +346,8 @@ static void sport_uart_tx_chars(struct sport_uart_port *up) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&up->port); + + return 1; } static unsigned int sport_tx_empty(struct uart_port *port) @@ -361,6 +369,9 @@ static void sport_stop_tx(struct uart_port *port) pr_debug("%s enter\n", __func__); + if (!(SPORT_GET_TCR1(up) & TSPEN)) + return; + /* Although the hold register is empty, last byte is still in shift * register and not sent out yet. So, put a dummy data into TX FIFO. * Then, sport tx stops when last byte is shift out and the dummy @@ -383,11 +394,12 @@ static void sport_start_tx(struct uart_port *port) pr_debug("%s enter\n", __func__); /* Write data into SPORT FIFO before enable SPROT to transmit */ - sport_uart_tx_chars(up); + if (sport_uart_tx_chars(up)) { + /* Enable transmit, then an interrupt will generated */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + } - /* Enable transmit, then an interrupt will generated */ - SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); - SSYNC(); pr_debug("%s exit\n", __func__); } -- cgit v1.1 From 2ce1efc9e3af2d735e7dfca31553676229c8bfcf Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 9 Mar 2010 12:25:38 -0500 Subject: serial: bfin_sport_uart: pull in bfin_sport.h for SPORT defines Now that the SPORT MMR defines have been unified, switch over to it. Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 0b459cf..66cda22 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -34,6 +34,7 @@ #include #include +#include #include #include -- cgit v1.1 From d671250b983a2ca79daa37ba8915c0602cc2aee8 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 9 Mar 2010 12:25:39 -0500 Subject: serial: bfin_sport_uart: drop the experimental markings Should be stable now ... Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 6325830..f41cfce 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1423,8 +1423,8 @@ config SERIAL_SC26XX_CONSOLE Support for Console on SC2681/SC2692 serial ports. config SERIAL_BFIN_SPORT - tristate "Blackfin SPORT emulate UART (EXPERIMENTAL)" - depends on BLACKFIN && EXPERIMENTAL + tristate "Blackfin SPORT emulate UART" + depends on BLACKFIN select SERIAL_CORE help Enable SPORT emulate UART on Blackfin series. -- cgit v1.1 From be991593f8e9994697a53d6a53112bfb951a8892 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 9 Mar 2010 12:25:40 -0500 Subject: serial: bfin_sport_uart: drop redundant cpu depends The BF54xM procs imply the related BF54x define, so no need to check both. Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index f41cfce..577d276 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1439,7 +1439,7 @@ config SERIAL_BFIN_SPORT_CONSOLE config SERIAL_BFIN_SPORT0_UART bool "Enable UART over SPORT0" - depends on SERIAL_BFIN_SPORT && !(BF542 || BF542M || BF544 || BF544M) + depends on SERIAL_BFIN_SPORT && !(BF542 || BF544) help Enable UART over SPORT0 -- cgit v1.1 From e5bbbb18d872cb4e29815a000485c2c5eebcfaca Mon Sep 17 00:00:00 2001 From: John Linn Date: Wed, 7 Apr 2010 09:32:55 -0600 Subject: serial: uartlite: move from byte accesses to word accesses Byte accesses for I/O devices in Xilinx IP is going to be less desired in the future such that the driver is being changed to use 32 bit accesses. This change facilitates using the uartlite IP over a PCIe bus which only allows 32 bit accesses. Signed-off-by: John Linn Tested-by: Michal Simek Acked-by: Grant Likely Acked-by: Peter Korsgaard Signed-off-by: Greg Kroah-Hartman --- drivers/serial/uartlite.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index f0a6c61..e6639a9 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -86,7 +86,7 @@ static int ulite_receive(struct uart_port *port, int stat) /* stats */ if (stat & ULITE_STATUS_RXVALID) { port->icount.rx++; - ch = readb(port->membase + ULITE_RX); + ch = ioread32be(port->membase + ULITE_RX); if (stat & ULITE_STATUS_PARITY) port->icount.parity++; @@ -131,7 +131,7 @@ static int ulite_transmit(struct uart_port *port, int stat) return 0; if (port->x_char) { - writeb(port->x_char, port->membase + ULITE_TX); + iowrite32be(port->x_char, port->membase + ULITE_TX); port->x_char = 0; port->icount.tx++; return 1; @@ -140,7 +140,7 @@ static int ulite_transmit(struct uart_port *port, int stat) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return 0; - writeb(xmit->buf[xmit->tail], port->membase + ULITE_TX); + iowrite32be(xmit->buf[xmit->tail], port->membase + ULITE_TX); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); port->icount.tx++; @@ -157,7 +157,7 @@ static irqreturn_t ulite_isr(int irq, void *dev_id) int busy, n = 0; do { - int stat = readb(port->membase + ULITE_STATUS); + int stat = ioread32be(port->membase + ULITE_STATUS); busy = ulite_receive(port, stat); busy |= ulite_transmit(port, stat); n++; @@ -178,7 +178,7 @@ static unsigned int ulite_tx_empty(struct uart_port *port) unsigned int ret; spin_lock_irqsave(&port->lock, flags); - ret = readb(port->membase + ULITE_STATUS); + ret = ioread32be(port->membase + ULITE_STATUS); spin_unlock_irqrestore(&port->lock, flags); return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; @@ -201,7 +201,7 @@ static void ulite_stop_tx(struct uart_port *port) static void ulite_start_tx(struct uart_port *port) { - ulite_transmit(port, readb(port->membase + ULITE_STATUS)); + ulite_transmit(port, ioread32be(port->membase + ULITE_STATUS)); } static void ulite_stop_rx(struct uart_port *port) @@ -230,17 +230,17 @@ static int ulite_startup(struct uart_port *port) if (ret) return ret; - writeb(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, + iowrite32be(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, port->membase + ULITE_CONTROL); - writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); return 0; } static void ulite_shutdown(struct uart_port *port) { - writeb(0, port->membase + ULITE_CONTROL); - readb(port->membase + ULITE_CONTROL); /* dummy */ + iowrite32be(0, port->membase + ULITE_CONTROL); + ioread32be(port->membase + ULITE_CONTROL); /* dummy */ free_irq(port->irq, port); } @@ -352,7 +352,7 @@ static void ulite_console_wait_tx(struct uart_port *port) /* Spin waiting for TX fifo to have space available */ for (i = 0; i < 100000; i++) { - val = readb(port->membase + ULITE_STATUS); + val = ioread32be(port->membase + ULITE_STATUS); if ((val & ULITE_STATUS_TXFULL) == 0) break; cpu_relax(); @@ -362,7 +362,7 @@ static void ulite_console_wait_tx(struct uart_port *port) static void ulite_console_putchar(struct uart_port *port, int ch) { ulite_console_wait_tx(port); - writeb(ch, port->membase + ULITE_TX); + iowrite32be(ch, port->membase + ULITE_TX); } static void ulite_console_write(struct console *co, const char *s, @@ -379,8 +379,8 @@ static void ulite_console_write(struct console *co, const char *s, spin_lock_irqsave(&port->lock, flags); /* save and disable interrupt */ - ier = readb(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; - writeb(0, port->membase + ULITE_CONTROL); + ier = ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; + iowrite32be(0, port->membase + ULITE_CONTROL); uart_console_write(port, s, count, ulite_console_putchar); @@ -388,7 +388,7 @@ static void ulite_console_write(struct console *co, const char *s, /* restore interrupt state */ if (ier) - writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); if (locked) spin_unlock_irqrestore(&port->lock, flags); @@ -601,7 +601,7 @@ ulite_of_probe(struct of_device *op, const struct of_device_id *match) id = of_get_property(op->node, "port-number", NULL); - return ulite_assign(&op->dev, id ? *id : -1, res.start+3, irq); + return ulite_assign(&op->dev, id ? *id : -1, res.start, irq); } static int __devexit ulite_of_remove(struct of_device *op) -- cgit v1.1 From 44051996230510ccb125cfa552d464950d1767b9 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 27 Apr 2010 14:16:33 -0700 Subject: serial: two branches the same in timbuart_set_mctrl() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CTS is a read only bit and we are to stop signal RTS if modem line TIOCM_RTS is not set. Thanks for suggestions by Richard Röjfors. Signed-off-by: Roel Kluin Acked-by: Richard Röjfors Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/serial/timbuart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/serial') diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c index 786ba85..62f389f 100644 --- a/drivers/serial/timbuart.c +++ b/drivers/serial/timbuart.c @@ -220,7 +220,7 @@ static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) if (mctrl & TIOCM_RTS) iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); else - iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); + iowrite8(0, port->membase + TIMBUART_CTRL); } static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier) -- cgit v1.1 From 24cd73a3942f3df214d7953f0cfd65d5085fa583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20R=C3=B6jfors?= Date: Tue, 27 Apr 2010 14:16:34 -0700 Subject: serial: timbuart: make sure last byte is sent when port is closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a problem in early versions of the FPGA IP. In certain situations the IP reports that the FIFO is empty, but a byte is still clocked out. If a flush is done at that point the currently clocked byte is canceled. This causes incompatibilities with the upper layers when a port is closed, it waits until the FIFO is empty and then closes the port. During close the FIFO is flushed -> the last byte is not sent properly. Now the FIFO is only flushed if it is reported to be non-empty. Which makes the currently clocked out byte to finish. [akpm@linux-foundation.org: fix build] Signed-off-by: Richard Röjfors Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/serial/timbuart.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c index 62f389f..67ca642 100644 --- a/drivers/serial/timbuart.c +++ b/drivers/serial/timbuart.c @@ -68,12 +68,22 @@ static void timbuart_start_tx(struct uart_port *port) tasklet_schedule(&uart->tasklet); } +static unsigned int timbuart_tx_empty(struct uart_port *port) +{ + u32 isr = ioread32(port->membase + TIMBUART_ISR); + + return (isr & TXBE) ? TIOCSER_TEMT : 0; +} + static void timbuart_flush_buffer(struct uart_port *port) { - u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | TIMBUART_CTRL_FLSHTX; + if (!timbuart_tx_empty(port)) { + u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | + TIMBUART_CTRL_FLSHTX; - iowrite8(ctl, port->membase + TIMBUART_CTRL); - iowrite32(TXBF, port->membase + TIMBUART_ISR); + iowrite8(ctl, port->membase + TIMBUART_CTRL); + iowrite32(TXBF, port->membase + TIMBUART_ISR); + } } static void timbuart_rx_chars(struct uart_port *port) @@ -195,13 +205,6 @@ void timbuart_tasklet(unsigned long arg) dev_dbg(uart->port.dev, "%s leaving\n", __func__); } -static unsigned int timbuart_tx_empty(struct uart_port *port) -{ - u32 isr = ioread32(port->membase + TIMBUART_ISR); - - return (isr & TXBE) ? TIOCSER_TEMT : 0; -} - static unsigned int timbuart_get_mctrl(struct uart_port *port) { u8 cts = ioread8(port->membase + TIMBUART_CTRL); -- cgit v1.1 From 5bcd601049c6b2ad52733d4cd2794bfbaf1b9314 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 5 May 2010 10:35:22 +0200 Subject: serial: Add driver for the Altera JTAG UART Add an UART driver for the JTAG UART component available as a SOPC (System on Programmable Chip) component for Altera FPGAs. Signed-off-by: Tobias Klauser Signed-off-by: Greg Kroah-Hartman --- drivers/serial/Kconfig | 21 ++ drivers/serial/Makefile | 1 + drivers/serial/altera_jtaguart.c | 504 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 526 insertions(+) create mode 100644 drivers/serial/altera_jtaguart.c (limited to 'drivers/serial') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 577d276..f4050e6 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1523,4 +1523,25 @@ config SERIAL_GRLIB_GAISLER_APBUART_CONSOLE help Support for running a console on the GRLIB APBUART +config SERIAL_ALTERA_JTAGUART + tristate "Altera JTAG UART support" + select SERIAL_CORE + help + This driver supports the Altera JTAG UART port. + +config SERIAL_ALTERA_JTAGUART_CONSOLE + bool "Altera JTAG UART console support" + depends on SERIAL_ALTERA_JTAGUART=y + select SERIAL_CORE_CONSOLE + help + Enable a Altera JTAG UART port to be the system console. + +config SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS + bool "Bypass output when no connection" + depends on SERIAL_ALTERA_JTAGUART_CONSOLE + select SERIAL_CORE_CONSOLE + help + Bypass console output and keep going even if there is no + JTAG terminal connection with the host. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 328f107..7f9d225 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -82,3 +82,4 @@ obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o +obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o diff --git a/drivers/serial/altera_jtaguart.c b/drivers/serial/altera_jtaguart.c new file mode 100644 index 0000000..f9b49b5 --- /dev/null +++ b/drivers/serial/altera_jtaguart.c @@ -0,0 +1,504 @@ +/* + * altera_jtaguart.c -- Altera JTAG UART driver + * + * Based on mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer + * (C) Copyright 2008, Thomas Chou + * (C) Copyright 2010, Tobias Klauser + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "altera_jtaguart" + +/* + * Altera JTAG UART register definitions according to the Altera JTAG UART + * datasheet: http://www.altera.com/literature/hb/nios2/n2cpu_nii51009.pdf + */ + +#define ALTERA_JTAGUART_SIZE 8 + +#define ALTERA_JTAGUART_DATA_REG 0 + +#define ALTERA_JTAGUART_DATA_DATA_MSK 0x000000FF +#define ALTERA_JTAGUART_DATA_RVALID_MSK 0x00008000 +#define ALTERA_JTAGUART_DATA_RAVAIL_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_DATA_RAVAIL_OFF 16 + +#define ALTERA_JTAGUART_CONTROL_REG 4 + +#define ALTERA_JTAGUART_CONTROL_RE_MSK 0x00000001 +#define ALTERA_JTAGUART_CONTROL_WE_MSK 0x00000002 +#define ALTERA_JTAGUART_CONTROL_RI_MSK 0x00000100 +#define ALTERA_JTAGUART_CONTROL_RI_OFF 8 +#define ALTERA_JTAGUART_CONTROL_WI_MSK 0x00000200 +#define ALTERA_JTAGUART_CONTROL_AC_MSK 0x00000400 +#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_CONTROL_WSPACE_OFF 16 + +/* + * Local per-uart structure. + */ +struct altera_jtaguart { + struct uart_port port; + unsigned int sigs; /* Local copy of line sigs */ + unsigned long imr; /* Local IMR mirror */ +}; + +static unsigned int altera_jtaguart_tx_empty(struct uart_port *port) +{ + return (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) ? TIOCSER_TEMT : 0; +} + +static unsigned int altera_jtaguart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void altera_jtaguart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ +} + +static void altera_jtaguart_start_tx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr |= ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_stop_tx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_stop_rx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr &= ~ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_break_ctl(struct uart_port *port, int break_state) +{ +} + +static void altera_jtaguart_enable_ms(struct uart_port *port) +{ +} + +static void altera_jtaguart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + /* Just copy the old termios settings back */ + if (old) + tty_termios_copy_hw(termios, old); +} + +static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + unsigned long status; + + while ((status = readl(port->membase + ALTERA_JTAGUART_DATA_REG)) & + ALTERA_JTAGUART_DATA_RVALID_MSK) { + ch = status & ALTERA_JTAGUART_DATA_DATA_MSK; + flag = TTY_NORMAL; + port->icount.rx++; + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, 0, 0, ch, flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned int pending, count; + + if (port->x_char) { + /* Send special char - probably flow control */ + writel(port->x_char, port->membase + ALTERA_JTAGUART_DATA_REG); + port->x_char = 0; + port->icount.tx++; + return; + } + + pending = uart_circ_chars_pending(xmit); + if (pending > 0) { + count = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) >> + ALTERA_JTAGUART_CONTROL_WSPACE_OFF; + if (count > pending) + count = pending; + if (count > 0) { + pending -= count; + while (count--) { + writel(xmit->buf[xmit->tail], + port->membase + ALTERA_JTAGUART_DATA_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + if (pending < WAKEUP_CHARS) + uart_write_wakeup(port); + } + } + + if (pending == 0) { + pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + } +} + +static irqreturn_t altera_jtaguart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned int isr; + + isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >> + ALTERA_JTAGUART_CONTROL_RI_OFF) & pp->imr; + + spin_lock(&port->lock); + + if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK) + altera_jtaguart_rx_chars(pp); + if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK) + altera_jtaguart_tx_chars(pp); + + spin_unlock(&port->lock); + + return IRQ_RETVAL(isr); +} + +static void altera_jtaguart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ALTERA_JTAGUART; + + /* Clear mask, so no surprise interrupts. */ + writel(0, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static int altera_jtaguart_startup(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned long flags; + int ret; + + ret = request_irq(port->irq, altera_jtaguart_interrupt, IRQF_DISABLED, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void altera_jtaguart_shutdown(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +static const char *altera_jtaguart_type(struct uart_port *port) +{ + return (port->type == PORT_ALTERA_JTAGUART) ? "Altera JTAG UART" : NULL; +} + +static int altera_jtaguart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void altera_jtaguart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int altera_jtaguart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != PORT_ALTERA_JTAGUART) + return -EINVAL; + return 0; +} + +/* + * Define the basic serial functions we support. + */ +static struct uart_ops altera_jtaguart_ops = { + .tx_empty = altera_jtaguart_tx_empty, + .get_mctrl = altera_jtaguart_get_mctrl, + .set_mctrl = altera_jtaguart_set_mctrl, + .start_tx = altera_jtaguart_start_tx, + .stop_tx = altera_jtaguart_stop_tx, + .stop_rx = altera_jtaguart_stop_rx, + .enable_ms = altera_jtaguart_enable_ms, + .break_ctl = altera_jtaguart_break_ctl, + .startup = altera_jtaguart_startup, + .shutdown = altera_jtaguart_shutdown, + .set_termios = altera_jtaguart_set_termios, + .type = altera_jtaguart_type, + .request_port = altera_jtaguart_request_port, + .release_port = altera_jtaguart_release_port, + .config_port = altera_jtaguart_config_port, + .verify_port = altera_jtaguart_verify_port, +}; + +#define ALTERA_JTAGUART_MAXPORTS 1 +static struct altera_jtaguart altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS]; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) + +int __init early_altera_jtaguart_setup(struct altera_jtaguart_platform_uart + *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_jtaguart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_JTAGUART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->flags = ASYNC_BOOT_AUTOCONF; + port->ops = &altera_jtaguart_ops; + } + + return 0; +} + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) +static void altera_jtaguart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; + unsigned long status; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while (((status = readl(port->membase + ALTERA_JTAGUART_CONTROL_REG)) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { + if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) { + spin_unlock_irqrestore(&port->lock, flags); + return; /* no connection activity */ + } + spin_unlock_irqrestore(&port->lock, flags); + cpu_relax(); + spin_lock_irqsave(&port->lock, flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); + spin_unlock_irqrestore(&port->lock, flags); +} +#else +static void altera_jtaguart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while ((readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { + spin_unlock_irqrestore(&port->lock, flags); + cpu_relax(); + spin_lock_irqsave(&port->lock, flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); + spin_unlock_irqrestore(&port->lock, flags); +} +#endif + +static void altera_jtaguart_console_write(struct console *co, const char *s, + unsigned int count) +{ + for (; count; count--, s++) { + altera_jtaguart_console_putc(co, *s); + if (*s == '\n') + altera_jtaguart_console_putc(co, '\r'); + } +} + +static int __init altera_jtaguart_console_setup(struct console *co, + char *options) +{ + struct uart_port *port; + + if (co->index < 0 || co->index >= ALTERA_JTAGUART_MAXPORTS) + return -EINVAL; + port = &altera_jtaguart_ports[co->index].port; + if (port->membase == 0) + return -ENODEV; + return 0; +} + +static struct uart_driver altera_jtaguart_driver; + +static struct console altera_jtaguart_console = { + .name = "ttyJ", + .write = altera_jtaguart_console_write, + .device = uart_console_device, + .setup = altera_jtaguart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &altera_jtaguart_driver, +}; + +static int __init altera_jtaguart_console_init(void) +{ + register_console(&altera_jtaguart_console); + return 0; +} + +console_initcall(altera_jtaguart_console_init); + +#define ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console) + +#else + +#define ALTERA_JTAGUART_CONSOLE NULL + +#endif /* CONFIG_ALTERA_JTAGUART_CONSOLE */ + +static struct uart_driver altera_jtaguart_driver = { + .owner = THIS_MODULE, + .driver_name = "altera_jtaguart", + .dev_name = "ttyJ", + .major = ALTERA_JTAGUART_MAJOR, + .minor = ALTERA_JTAGUART_MINOR, + .nr = ALTERA_JTAGUART_MAXPORTS, + .cons = ALTERA_JTAGUART_CONSOLE, +}; + +static int __devinit altera_jtaguart_probe(struct platform_device *pdev) +{ + struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_jtaguart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_JTAGUART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->ops = &altera_jtaguart_ops; + port->flags = ASYNC_BOOT_AUTOCONF; + + uart_add_one_port(&altera_jtaguart_driver, port); + } + + return 0; +} + +static int __devexit altera_jtaguart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS; i++) { + port = &altera_jtaguart_ports[i].port; + if (port) + uart_remove_one_port(&altera_jtaguart_driver, port); + } + + return 0; +} + +static struct platform_driver altera_jtaguart_platform_driver = { + .probe = altera_jtaguart_probe, + .remove = __devexit_p(altera_jtaguart_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init altera_jtaguart_init(void) +{ + int rc; + + rc = uart_register_driver(&altera_jtaguart_driver); + if (rc) + return rc; + rc = platform_driver_register(&altera_jtaguart_platform_driver); + if (rc) { + uart_unregister_driver(&altera_jtaguart_driver); + return rc; + } + return 0; +} + +static void __exit altera_jtaguart_exit(void) +{ + platform_driver_unregister(&altera_jtaguart_platform_driver); + uart_unregister_driver(&altera_jtaguart_driver); +} + +module_init(altera_jtaguart_init); +module_exit(altera_jtaguart_exit); + +MODULE_DESCRIPTION("Altera JTAG UART driver"); +MODULE_AUTHOR("Thomas Chou "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.1 From 6b7d8f8b5c43f7bedda750d8a9dab0658da1d2ba Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 5 May 2010 10:35:23 +0200 Subject: serial: Add driver for the Altera UART Add an UART driver for the UART component available as a SOPC (System on Programmable Chip) component for Altera FPGAs. Signed-off-by: Tobias Klauser Signed-off-by: Greg Kroah-Hartman --- drivers/serial/Kconfig | 31 +++ drivers/serial/Makefile | 1 + drivers/serial/altera_uart.c | 570 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 602 insertions(+) create mode 100644 drivers/serial/altera_uart.c (limited to 'drivers/serial') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index f4050e6..8b23165 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1544,4 +1544,35 @@ config SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS Bypass console output and keep going even if there is no JTAG terminal connection with the host. +config SERIAL_ALTERA_UART + tristate "Altera UART support" + select SERIAL_CORE + help + This driver supports the Altera softcore UART port. + +config SERIAL_ALTERA_UART_MAXPORTS + int "Maximum number of Altera UART ports" + depends on SERIAL_ALTERA_UART + default 4 + help + This setting lets you define the maximum number of the Altera + UART ports. The usual default varies from board to board, and + this setting is a way of catering for that. + +config SERIAL_ALTERA_UART_BAUDRATE + int "Default baudrate for Altera UART ports" + depends on SERIAL_ALTERA_UART + default 115200 + help + This setting lets you define what the default baudrate is for the + Altera UART ports. The usual default varies from board to board, + and this setting is a way of catering for that. + +config SERIAL_ALTERA_UART_CONSOLE + bool "Altera UART console support" + depends on SERIAL_ALTERA_UART=y + select SERIAL_CORE_CONSOLE + help + Enable a Altera UART port to be the system console. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 7f9d225..208a855 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -83,3 +83,4 @@ obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o +obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o diff --git a/drivers/serial/altera_uart.c b/drivers/serial/altera_uart.c new file mode 100644 index 0000000..bcee156 --- /dev/null +++ b/drivers/serial/altera_uart.c @@ -0,0 +1,570 @@ +/* + * altera_uart.c -- Altera UART driver + * + * Based on mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer + * (C) Copyright 2008, Thomas Chou + * (C) Copyright 2010, Tobias Klauser + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "altera_uart" + +/* + * Altera UART register definitions according to the Nios UART datasheet: + * http://www.altera.com/literature/ds/ds_nios_uart.pdf + */ + +#define ALTERA_UART_SIZE 32 + +#define ALTERA_UART_RXDATA_REG 0 +#define ALTERA_UART_TXDATA_REG 4 +#define ALTERA_UART_STATUS_REG 8 +#define ALTERA_UART_CONTROL_REG 12 +#define ALTERA_UART_DIVISOR_REG 16 +#define ALTERA_UART_EOP_REG 20 + +#define ALTERA_UART_STATUS_PE_MSK 0x0001 /* parity error */ +#define ALTERA_UART_STATUS_FE_MSK 0x0002 /* framing error */ +#define ALTERA_UART_STATUS_BRK_MSK 0x0004 /* break */ +#define ALTERA_UART_STATUS_ROE_MSK 0x0008 /* RX overrun error */ +#define ALTERA_UART_STATUS_TOE_MSK 0x0010 /* TX overrun error */ +#define ALTERA_UART_STATUS_TMT_MSK 0x0020 /* TX shift register state */ +#define ALTERA_UART_STATUS_TRDY_MSK 0x0040 /* TX ready */ +#define ALTERA_UART_STATUS_RRDY_MSK 0x0080 /* RX ready */ +#define ALTERA_UART_STATUS_E_MSK 0x0100 /* exception condition */ +#define ALTERA_UART_STATUS_DCTS_MSK 0x0400 /* CTS logic-level change */ +#define ALTERA_UART_STATUS_CTS_MSK 0x0800 /* CTS logic state */ +#define ALTERA_UART_STATUS_EOP_MSK 0x1000 /* EOP written/read */ + + /* Enable interrupt on... */ +#define ALTERA_UART_CONTROL_PE_MSK 0x0001 /* ...parity error */ +#define ALTERA_UART_CONTROL_FE_MSK 0x0002 /* ...framing error */ +#define ALTERA_UART_CONTROL_BRK_MSK 0x0004 /* ...break */ +#define ALTERA_UART_CONTROL_ROE_MSK 0x0008 /* ...RX overrun */ +#define ALTERA_UART_CONTROL_TOE_MSK 0x0010 /* ...TX overrun */ +#define ALTERA_UART_CONTROL_TMT_MSK 0x0020 /* ...TX shift register empty */ +#define ALTERA_UART_CONTROL_TRDY_MSK 0x0040 /* ...TX ready */ +#define ALTERA_UART_CONTROL_RRDY_MSK 0x0080 /* ...RX ready */ +#define ALTERA_UART_CONTROL_E_MSK 0x0100 /* ...exception*/ + +#define ALTERA_UART_CONTROL_TRBK_MSK 0x0200 /* TX break */ +#define ALTERA_UART_CONTROL_DCTS_MSK 0x0400 /* Interrupt on CTS change */ +#define ALTERA_UART_CONTROL_RTS_MSK 0x0800 /* RTS signal */ +#define ALTERA_UART_CONTROL_EOP_MSK 0x1000 /* Interrupt on EOP */ + +/* + * Local per-uart structure. + */ +struct altera_uart { + struct uart_port port; + unsigned int sigs; /* Local copy of line sigs */ + unsigned short imr; /* Local IMR mirror */ +}; + +static unsigned int altera_uart_tx_empty(struct uart_port *port) +{ + return (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TMT_MSK) ? TIOCSER_TEMT : 0; +} + +static unsigned int altera_uart_get_mctrl(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + unsigned int sigs; + + spin_lock_irqsave(&port->lock, flags); + sigs = + (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_CTS_MSK) ? TIOCM_CTS : 0; + sigs |= (pp->sigs & TIOCM_RTS); + spin_unlock_irqrestore(&port->lock, flags); + + return sigs; +} + +static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->sigs = sigs; + if (sigs & TIOCM_RTS) + pp->imr |= ALTERA_UART_CONTROL_RTS_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_start_tx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_stop_tx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_stop_rx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_break_ctl(struct uart_port *port, int break_state) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (break_state == -1) + pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_enable_ms(struct uart_port *port) +{ +} + +static void altera_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, baudclk; + + baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + baudclk = port->uartclk / baud; + + if (old) + tty_termios_copy_hw(termios, old); + tty_termios_encode_baud_rate(termios, baud, baud); + + spin_lock_irqsave(&port->lock, flags); + writel(baudclk, port->membase + ALTERA_UART_DIVISOR_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_rx_chars(struct altera_uart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + unsigned short status; + + while ((status = readl(port->membase + ALTERA_UART_STATUS_REG)) & + ALTERA_UART_STATUS_RRDY_MSK) { + ch = readl(port->membase + ALTERA_UART_RXDATA_REG); + flag = TTY_NORMAL; + port->icount.rx++; + + if (status & ALTERA_UART_STATUS_E_MSK) { + writel(status, port->membase + ALTERA_UART_STATUS_REG); + + if (status & ALTERA_UART_STATUS_BRK_MSK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (status & ALTERA_UART_STATUS_PE_MSK) { + port->icount.parity++; + } else if (status & ALTERA_UART_STATUS_ROE_MSK) { + port->icount.overrun++; + } else if (status & ALTERA_UART_STATUS_FE_MSK) { + port->icount.frame++; + } + + status &= port->read_status_mask; + + if (status & ALTERA_UART_STATUS_BRK_MSK) + flag = TTY_BREAK; + else if (status & ALTERA_UART_STATUS_PE_MSK) + flag = TTY_PARITY; + else if (status & ALTERA_UART_STATUS_FE_MSK) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, status, ALTERA_UART_STATUS_ROE_MSK, ch, + flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +static void altera_uart_tx_chars(struct altera_uart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + /* Send special char - probably flow control */ + writel(port->x_char, port->membase + ALTERA_UART_TXDATA_REG); + port->x_char = 0; + port->icount.tx++; + return; + } + + while (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) { + if (xmit->head == xmit->tail) + break; + writel(xmit->buf[xmit->tail], + port->membase + ALTERA_UART_TXDATA_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (xmit->head == xmit->tail) { + pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + } +} + +static irqreturn_t altera_uart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned int isr; + + isr = readl(port->membase + ALTERA_UART_STATUS_REG) & pp->imr; + if (isr & ALTERA_UART_STATUS_RRDY_MSK) + altera_uart_rx_chars(pp); + if (isr & ALTERA_UART_STATUS_TRDY_MSK) + altera_uart_tx_chars(pp); + return IRQ_RETVAL(isr); +} + +static void altera_uart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ALTERA_UART; + + /* Clear mask, so no surprise interrupts. */ + writel(0, port->membase + ALTERA_UART_CONTROL_REG); + /* Clear status register */ + writel(0, port->membase + ALTERA_UART_STATUS_REG); +} + +static int altera_uart_startup(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + int ret; + + ret = request_irq(port->irq, altera_uart_interrupt, IRQF_DISABLED, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_UART_CONTROL_RRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void altera_uart_shutdown(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +static const char *altera_uart_type(struct uart_port *port) +{ + return (port->type == PORT_ALTERA_UART) ? "Altera UART" : NULL; +} + +static int altera_uart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void altera_uart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int altera_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_ALTERA_UART)) + return -EINVAL; + return 0; +} + +/* + * Define the basic serial functions we support. + */ +static struct uart_ops altera_uart_ops = { + .tx_empty = altera_uart_tx_empty, + .get_mctrl = altera_uart_get_mctrl, + .set_mctrl = altera_uart_set_mctrl, + .start_tx = altera_uart_start_tx, + .stop_tx = altera_uart_stop_tx, + .stop_rx = altera_uart_stop_rx, + .enable_ms = altera_uart_enable_ms, + .break_ctl = altera_uart_break_ctl, + .startup = altera_uart_startup, + .shutdown = altera_uart_shutdown, + .set_termios = altera_uart_set_termios, + .type = altera_uart_type, + .request_port = altera_uart_request_port, + .release_port = altera_uart_release_port, + .config_port = altera_uart_config_port, + .verify_port = altera_uart_verify_port, +}; + +static struct altera_uart altera_uart_ports[CONFIG_SERIAL_ALTERA_UART_MAXPORTS]; + +#if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + +int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_uart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_UART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->uartclk = platp[i].uartclk; + port->flags = ASYNC_BOOT_AUTOCONF; + port->ops = &altera_uart_ops; + } + + return 0; +} + +static void altera_uart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_uart_ports + co->index)->port; + int i; + + for (i = 0; i < 0x10000; i++) { + if (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) + break; + } + writel(c, port->membase + ALTERA_UART_TXDATA_REG); + for (i = 0; i < 0x10000; i++) { + if (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) + break; + } +} + +static void altera_uart_console_write(struct console *co, const char *s, + unsigned int count) +{ + for (; count; count--, s++) { + altera_uart_console_putc(co, *s); + if (*s == '\n') + altera_uart_console_putc(co, '\r'); + } +} + +static int __init altera_uart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) + return -EINVAL; + port = &altera_uart_ports[co->index].port; + if (port->membase == 0) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver altera_uart_driver; + +static struct console altera_uart_console = { + .name = "ttyS", + .write = altera_uart_console_write, + .device = uart_console_device, + .setup = altera_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &altera_uart_driver, +}; + +static int __init altera_uart_console_init(void) +{ + register_console(&altera_uart_console); + return 0; +} + +console_initcall(altera_uart_console_init); + +#define ALTERA_UART_CONSOLE (&altera_uart_console) + +#else + +#define ALTERA_UART_CONSOLE NULL + +#endif /* CONFIG_ALTERA_UART_CONSOLE */ + +/* + * Define the altera_uart UART driver structure. + */ +static struct uart_driver altera_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = CONFIG_SERIAL_ALTERA_UART_MAXPORTS, + .cons = ALTERA_UART_CONSOLE, +}; + +static int __devinit altera_uart_probe(struct platform_device *pdev) +{ + struct altera_uart_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_uart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_UART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->uartclk = platp[i].uartclk; + port->ops = &altera_uart_ops; + port->flags = ASYNC_BOOT_AUTOCONF; + + uart_add_one_port(&altera_uart_driver, port); + } + + return 0; +} + +static int altera_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS; i++) { + port = &altera_uart_ports[i].port; + if (port) + uart_remove_one_port(&altera_uart_driver, port); + } + + return 0; +} + +static struct platform_driver altera_uart_platform_driver = { + .probe = altera_uart_probe, + .remove = __devexit_p(altera_uart_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = NULL, + }, +}; + +static int __init altera_uart_init(void) +{ + int rc; + + rc = uart_register_driver(&altera_uart_driver); + if (rc) + return rc; + rc = platform_driver_register(&altera_uart_platform_driver); + if (rc) { + uart_unregister_driver(&altera_uart_driver); + return rc; + } + return 0; +} + +static void __exit altera_uart_exit(void) +{ + platform_driver_unregister(&altera_uart_platform_driver); + uart_unregister_driver(&altera_uart_driver); +} + +module_init(altera_uart_init); +module_exit(altera_uart_exit); + +MODULE_DESCRIPTION("Altera UART driver"); +MODULE_AUTHOR("Thomas Chou "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.1 From e114474cf5c7fe7ab94dd4f5938ffa51c9af77c4 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 11 May 2010 09:10:23 +0200 Subject: serial: bfin_sport_uart: Use resource size to fix off-by-one error Use the resource_size function instead of manually calculating the resource size. This actually fixes an off-by-one error. Signed-off-by: Tobias Klauser Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/serial/bfin_sport_uart.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/serial') diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 66cda22..e57fb3d 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -774,8 +774,7 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) goto out_error_free_peripherals; } - sport->port.membase = ioremap(res->start, - res->end - res->start); + sport->port.membase = ioremap(res->start, resource_size(res)); if (!sport->port.membase) { dev_err(&pdev->dev, "Cannot map sport IO\n"); ret = -ENXIO; -- cgit v1.1