diff options
Diffstat (limited to 'drivers/tty/serial/sh-sci.c')
-rw-r--r-- | drivers/tty/serial/sh-sci.c | 320 |
1 files changed, 153 insertions, 167 deletions
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 7d8103cd3e2e..e4bf0e435af6 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -23,35 +23,34 @@ #undef DEBUG -#include <linux/module.h> +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/ctype.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> #include <linux/errno.h> -#include <linux/sh_dma.h> -#include <linux/timer.h> +#include <linux/init.h> #include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/sysrq.h> #include <linux/ioport.h> +#include <linux/major.h> +#include <linux/module.h> #include <linux/mm.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/console.h> -#include <linux/platform_device.h> -#include <linux/serial_sci.h> #include <linux/notifier.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/cpufreq.h> -#include <linux/clk.h> -#include <linux/ctype.h> -#include <linux/err.h> -#include <linux/dmaengine.h> -#include <linux/dma-mapping.h> #include <linux/scatterlist.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_dma.h> #include <linux/slab.h> -#include <linux/gpio.h> +#include <linux/string.h> +#include <linux/sysrq.h> +#include <linux/timer.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #ifdef CONFIG_SUPERH #include <asm/sh_bios.h> @@ -64,6 +63,10 @@ struct sci_port { /* Platform configuration */ struct plat_sci_port *cfg; + int overrun_bit; + unsigned int error_mask; + unsigned int sampling_rate; + /* Break timer */ struct timer_list break_timer; @@ -74,8 +77,8 @@ struct sci_port { /* Function clock */ struct clk *fclk; + int irqs[SCIx_NR_IRQS]; char *irqstr[SCIx_NR_IRQS]; - char *gpiostr[SCIx_NR_FNS]; struct dma_chan *chan_tx; struct dma_chan *chan_rx; @@ -421,9 +424,9 @@ static void sci_port_enable(struct sci_port *sci_port) pm_runtime_get_sync(sci_port->port.dev); - clk_enable(sci_port->iclk); + clk_prepare_enable(sci_port->iclk); sci_port->port.uartclk = clk_get_rate(sci_port->iclk); - clk_enable(sci_port->fclk); + clk_prepare_enable(sci_port->fclk); } static void sci_port_disable(struct sci_port *sci_port) @@ -431,8 +434,16 @@ static void sci_port_disable(struct sci_port *sci_port) if (!sci_port->port.dev) return; - clk_disable(sci_port->fclk); - clk_disable(sci_port->iclk); + /* Cancel the break timer to ensure that the timer handler will not try + * to access the hardware with clocks and power disabled. Reset the + * break flag to make the break debouncing state machine ready for the + * next break. + */ + del_timer_sync(&sci_port->break_timer); + sci_port->break_flag = 0; + + clk_disable_unprepare(sci_port->fclk); + clk_disable_unprepare(sci_port->iclk); pm_runtime_put_sync(sci_port->port.dev); } @@ -557,7 +568,7 @@ static inline int sci_rxd_in(struct uart_port *port) return 1; /* Cast for ARM damage */ - return !!__raw_readb((void __iomem *)s->cfg->port_reg); + return !!__raw_readb((void __iomem *)(uintptr_t)s->cfg->port_reg); } /* ********************************************************************** * @@ -733,8 +744,6 @@ static void sci_break_timer(unsigned long data) { struct sci_port *port = (struct sci_port *)data; - sci_port_enable(port); - if (sci_rxd_in(&port->port) == 0) { port->break_flag = 1; sci_schedule_break_timer(port); @@ -744,8 +753,6 @@ static void sci_break_timer(unsigned long data) sci_schedule_break_timer(port); } else port->break_flag = 0; - - sci_port_disable(port); } static int sci_handle_errors(struct uart_port *port) @@ -755,19 +762,15 @@ static int sci_handle_errors(struct uart_port *port) struct tty_port *tport = &port->state->port; struct sci_port *s = to_sci_port(port); - /* - * Handle overruns, if supported. - */ - if (s->cfg->overrun_bit != SCIx_NOT_SUPPORTED) { - if (status & (1 << s->cfg->overrun_bit)) { - port->icount.overrun++; + /* Handle overruns */ + if (status & (1 << s->overrun_bit)) { + port->icount.overrun++; - /* overrun error */ - if (tty_insert_flip_char(tport, 0, TTY_OVERRUN)) - copied++; + /* overrun error */ + if (tty_insert_flip_char(tport, 0, TTY_OVERRUN)) + copied++; - dev_notice(port->dev, "overrun error"); - } + dev_notice(port->dev, "overrun error"); } if (status & SCxSR_FER(port)) { @@ -829,7 +832,7 @@ static int sci_handle_fifo_overrun(struct uart_port *port) if (!reg->size) return 0; - if ((serial_port_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) { + if ((serial_port_in(port, SCLSR) & (1 << s->overrun_bit))) { serial_port_out(port, SCLSR, 0); port->icount.overrun++; @@ -1075,19 +1078,19 @@ static int sci_request_irq(struct sci_port *port) for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) { struct sci_irq_desc *desc; - unsigned int irq; + int irq; if (SCIx_IRQ_IS_MUXED(port)) { i = SCIx_MUX_IRQ; irq = up->irq; } else { - irq = port->cfg->irqs[i]; + irq = port->irqs[i]; /* * Certain port types won't support all of the * available interrupt sources. */ - if (unlikely(!irq)) + if (unlikely(irq < 0)) continue; } @@ -1112,7 +1115,7 @@ static int sci_request_irq(struct sci_port *port) out_noirq: while (--i >= 0) - free_irq(port->cfg->irqs[i], port); + free_irq(port->irqs[i], port); out_nomem: while (--j >= 0) @@ -1130,16 +1133,16 @@ static void sci_free_irq(struct sci_port *port) * IRQ first. */ for (i = 0; i < SCIx_NR_IRQS; i++) { - unsigned int irq = port->cfg->irqs[i]; + int irq = port->irqs[i]; /* * Certain port types won't support all of the available * interrupt sources. */ - if (unlikely(!irq)) + if (unlikely(irq < 0)) continue; - free_irq(port->cfg->irqs[i], port); + free_irq(port->irqs[i], port); kfree(port->irqstr[i]); if (SCIx_IRQ_IS_MUXED(port)) { @@ -1149,67 +1152,6 @@ static void sci_free_irq(struct sci_port *port) } } -static const char *sci_gpio_names[SCIx_NR_FNS] = { - "sck", "rxd", "txd", "cts", "rts", -}; - -static const char *sci_gpio_str(unsigned int index) -{ - return sci_gpio_names[index]; -} - -static void sci_init_gpios(struct sci_port *port) -{ - struct uart_port *up = &port->port; - int i; - - if (!port->cfg) - return; - - for (i = 0; i < SCIx_NR_FNS; i++) { - const char *desc; - int ret; - - if (!port->cfg->gpios[i]) - continue; - - desc = sci_gpio_str(i); - - port->gpiostr[i] = kasprintf(GFP_KERNEL, "%s:%s", - dev_name(up->dev), desc); - - /* - * If we've failed the allocation, we can still continue - * on with a NULL string. - */ - if (!port->gpiostr[i]) - dev_notice(up->dev, "%s string allocation failure\n", - desc); - - ret = gpio_request(port->cfg->gpios[i], port->gpiostr[i]); - if (unlikely(ret != 0)) { - dev_notice(up->dev, "failed %s gpio request\n", desc); - - /* - * If we can't get the GPIO for whatever reason, - * no point in keeping the verbose string around. - */ - kfree(port->gpiostr[i]); - } - } -} - -static void sci_free_gpios(struct sci_port *port) -{ - int i; - - for (i = 0; i < SCIx_NR_FNS; i++) - if (port->cfg->gpios[i]) { - gpio_free(port->cfg->gpios[i]); - kfree(port->gpiostr[i]); - } -} - static unsigned int sci_tx_empty(struct uart_port *port) { unsigned short status = serial_port_in(port, SCxSR); @@ -1309,7 +1251,7 @@ static int sci_dma_rx_push(struct sci_port *s, size_t count) } if (room < count) - dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", + dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n", count - room); if (!room) return room; @@ -1442,7 +1384,7 @@ static void work_fn_rx(struct work_struct *work) int count; chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); - dev_dbg(port->dev, "Read %u bytes with cookie %d\n", + dev_dbg(port->dev, "Read %zu bytes with cookie %d\n", sh_desc->partial, sh_desc->cookie); spin_lock_irqsave(&port->lock, flags); @@ -1655,7 +1597,7 @@ static void rx_timer_fn(unsigned long arg) if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { scr &= ~0x4000; - enable_irq(s->cfg->irqs[1]); + enable_irq(s->irqs[SCIx_RXI_IRQ]); } serial_port_out(port, SCSCR, scr | SCSCR_RIE); dev_dbg(port->dev, "DMA Rx timed out\n"); @@ -1691,16 +1633,17 @@ static void sci_request_dma(struct uart_port *port) s->chan_tx = chan; sg_init_table(&s->sg_tx, 1); /* UART circular tx buffer is an aligned page. */ - BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + BUG_ON((uintptr_t)port->state->xmit.buf & ~PAGE_MASK); sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf), - UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK); + UART_XMIT_SIZE, + (uintptr_t)port->state->xmit.buf & ~PAGE_MASK); nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); if (!nent) sci_tx_dma_release(s, false); else - dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, - sg_dma_len(&s->sg_tx), - port->state->xmit.buf, sg_dma_address(&s->sg_tx)); + dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__, + sg_dma_len(&s->sg_tx), port->state->xmit.buf, + &sg_dma_address(&s->sg_tx)); s->sg_len_tx = nent; @@ -1740,7 +1683,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); + (uintptr_t)buf[i] & ~PAGE_MASK); sg_dma_address(sg) = dma[i]; } @@ -1808,20 +1751,21 @@ static void sci_shutdown(struct uart_port *port) sci_free_irq(s); } -static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps, +static unsigned int sci_scbrr_calc(struct sci_port *s, unsigned int bps, unsigned long freq) { - switch (algo_id) { + if (s->sampling_rate) + return DIV_ROUND_CLOSEST(freq, s->sampling_rate * bps) - 1; + + switch (s->cfg->scbrr_algo_id) { case SCBRR_ALGO_1: - return ((freq + 16 * bps) / (16 * bps) - 1); + return freq / (16 * bps); case SCBRR_ALGO_2: - return ((freq + 16 * bps) / (32 * bps) - 1); + return DIV_ROUND_CLOSEST(freq, 32 * bps) - 1; case SCBRR_ALGO_3: - return (((freq * 2) + 16 * bps) / (16 * bps) - 1); + return freq / (8 * bps); case SCBRR_ALGO_4: - return (((freq * 2) + 16 * bps) / (32 * bps) - 1); - case SCBRR_ALGO_5: - return (((freq * 1000 / 32) / bps) - 1); + return DIV_ROUND_CLOSEST(freq, 16 * bps) - 1; } /* Warn, but use a safe default */ @@ -1903,12 +1847,11 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, baud = uart_get_baud_rate(port, termios, old, 0, max_baud); if (likely(baud && port->uartclk)) { - if (s->cfg->scbrr_algo_id == SCBRR_ALGO_6) { + if (s->cfg->type == PORT_HSCIF) { sci_baud_calc_hscif(baud, port->uartclk, &t, &srr, &cks); } else { - t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud, - port->uartclk); + t = sci_scbrr_calc(s, baud, port->uartclk); for (cks = 0; t >= 256 && cks <= 3; cks++) t >>= 2; } @@ -2115,10 +2058,6 @@ static void sci_config_port(struct uart_port *port, int flags) static int sci_verify_port(struct uart_port *port, struct serial_struct *ser) { - struct sci_port *s = to_sci_port(port); - - if (ser->irq != s->cfg->irqs[SCIx_TXI_IRQ] || ser->irq > nr_irqs) - return -EINVAL; if (ser->baud_base < 2400) /* No paper tape reader for Mitch.. */ return -EINVAL; @@ -2151,11 +2090,13 @@ static struct uart_ops sci_uart_ops = { }; static int sci_init_single(struct platform_device *dev, - struct sci_port *sci_port, - unsigned int index, - struct plat_sci_port *p) + struct sci_port *sci_port, unsigned int index, + struct plat_sci_port *p, bool early) { struct uart_port *port = &sci_port->port; + const struct resource *res; + unsigned int sampling_rate; + unsigned int i; int ret; sci_port->cfg = p; @@ -2164,31 +2105,90 @@ static int sci_init_single(struct platform_device *dev, port->iotype = UPIO_MEM; port->line = index; + if (dev->num_resources) { + /* Device has resources, use them. */ + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (res == NULL) + return -ENOMEM; + + port->mapbase = res->start; + + for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i) + sci_port->irqs[i] = platform_get_irq(dev, i); + + /* The SCI generates several interrupts. They can be muxed + * together or connected to different interrupt lines. In the + * muxed case only one interrupt resource is specified. In the + * non-muxed case three or four interrupt resources are + * specified, as the BRI interrupt is optional. + */ + if (sci_port->irqs[0] < 0) + return -ENXIO; + + if (sci_port->irqs[1] < 0) { + sci_port->irqs[1] = sci_port->irqs[0]; + sci_port->irqs[2] = sci_port->irqs[0]; + sci_port->irqs[3] = sci_port->irqs[0]; + } + } else { + /* No resources, use old-style platform data. */ + port->mapbase = p->mapbase; + for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i) + sci_port->irqs[i] = p->irqs[i] ? p->irqs[i] : -ENXIO; + } + + if (p->regtype == SCIx_PROBE_REGTYPE) { + ret = sci_probe_regmap(p); + if (unlikely(ret)) + return ret; + } + switch (p->type) { case PORT_SCIFB: port->fifosize = 256; + sci_port->overrun_bit = 9; + sampling_rate = 16; break; case PORT_HSCIF: port->fifosize = 128; + sampling_rate = 0; + sci_port->overrun_bit = 0; break; case PORT_SCIFA: port->fifosize = 64; + sci_port->overrun_bit = 9; + sampling_rate = 16; break; case PORT_SCIF: port->fifosize = 16; + if (p->regtype == SCIx_SH7705_SCIF_REGTYPE) { + sci_port->overrun_bit = 9; + sampling_rate = 16; + } else { + sci_port->overrun_bit = 0; + sampling_rate = 32; + } break; default: port->fifosize = 1; + sci_port->overrun_bit = 5; + sampling_rate = 32; break; } - if (p->regtype == SCIx_PROBE_REGTYPE) { - ret = sci_probe_regmap(p); - if (unlikely(ret)) - return ret; + /* Set the sampling rate if the baud rate calculation algorithm isn't + * specified. + */ + if (p->scbrr_algo_id == SCBRR_ALGO_NONE) { + /* SCIFA on sh7723 and sh7724 need a custom sampling rate that + * doesn't match the SoC datasheet, this should be investigated. + * Let platform data override the sampling rate for now. + */ + sci_port->sampling_rate = p->sampling_rate ? p->sampling_rate + : sampling_rate; } - if (dev) { + if (!early) { sci_port->iclk = clk_get(&dev->dev, "sci_ick"); if (IS_ERR(sci_port->iclk)) { sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); @@ -2208,8 +2208,6 @@ static int sci_init_single(struct platform_device *dev, port->dev = &dev->dev; - sci_init_gpios(sci_port); - pm_runtime_enable(&dev->dev); } @@ -2220,32 +2218,22 @@ static int sci_init_single(struct platform_device *dev, /* * Establish some sensible defaults for the error detection. */ - if (!p->error_mask) - p->error_mask = (p->type == PORT_SCI) ? + sci_port->error_mask = (p->type == PORT_SCI) ? SCI_DEFAULT_ERROR_MASK : SCIF_DEFAULT_ERROR_MASK; /* * Establish sensible defaults for the overrun detection, unless * the part has explicitly disabled support for it. */ - if (p->overrun_bit != SCIx_NOT_SUPPORTED) { - if (p->type == PORT_SCI) - p->overrun_bit = 5; - else if (p->scbrr_algo_id == SCBRR_ALGO_4) - p->overrun_bit = 9; - else - p->overrun_bit = 0; - /* - * Make the error mask inclusive of overrun detection, if - * supported. - */ - p->error_mask |= (1 << p->overrun_bit); - } + /* + * Make the error mask inclusive of overrun detection, if + * supported. + */ + sci_port->error_mask |= 1 << sci_port->overrun_bit; - port->mapbase = p->mapbase; port->type = p->type; - port->flags = p->flags; + port->flags = UPF_FIXED_PORT | p->flags; port->regshift = p->regshift; /* @@ -2255,7 +2243,7 @@ static int sci_init_single(struct platform_device *dev, * * For the muxed case there's nothing more to do. */ - port->irq = p->irqs[SCIx_RXI_IRQ]; + port->irq = sci_port->irqs[SCIx_RXI_IRQ]; port->irqflags = 0; port->serial_in = sci_serial_in; @@ -2270,8 +2258,6 @@ static int sci_init_single(struct platform_device *dev, static void sci_cleanup_single(struct sci_port *port) { - sci_free_gpios(port); - clk_put(port->iclk); clk_put(port->fclk); @@ -2387,7 +2373,7 @@ static int sci_probe_earlyprintk(struct platform_device *pdev) early_serial_console.index = pdev->id; - sci_init_single(NULL, &sci_ports[pdev->id], pdev->id, cfg); + sci_init_single(pdev, &sci_ports[pdev->id], pdev->id, cfg, true); serial_console_setup(&early_serial_console, early_serial_buf); @@ -2454,7 +2440,7 @@ static int sci_probe_single(struct platform_device *dev, return -EINVAL; } - ret = sci_init_single(dev, sciport, index, p); + ret = sci_init_single(dev, sciport, index, p, false); if (ret) return ret; |