/* * Freescale STMP37XX/STMP378X Application UART driver * * Author: dmitry pervushin * * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stmp-app.h" static int pio_mode /* = 0 */; /* PIO mode = 1, DMA mode = 0 */ static struct platform_driver stmp_appuart_driver = { .probe = stmp_appuart_probe, .remove = __devexit_p(stmp_appuart_remove), .suspend = stmp_appuart_suspend, .resume = stmp_appuart_resume, .driver = { .name = "stmp3xxx-appuart", .owner = THIS_MODULE, }, }; static struct uart_driver stmp_appuart_uart = { .owner = THIS_MODULE, .driver_name = "appuart", .dev_name = "ttySP", .major = 242, .minor = 0, .nr = 1, }; static inline struct stmp_appuart_port *to_appuart(struct uart_port *u) { return container_of(u, struct stmp_appuart_port, port); } static struct uart_ops stmp_appuart_ops = { .tx_empty = stmp_appuart_tx_empty, .start_tx = stmp_appuart_start_tx, .stop_tx = stmp_appuart_stop_tx, .stop_rx = stmp_appuart_stop_rx, .enable_ms = stmp_appuart_enable_ms, .break_ctl = stmp_appuart_break_ctl, .set_mctrl = stmp_appuart_set_mctrl, .get_mctrl = stmp_appuart_get_mctrl, .startup = stmp_appuart_startup, .shutdown = stmp_appuart_shutdown, .set_termios = stmp_appuart_settermios, .type = stmp_appuart_type, .release_port = stmp_appuart_release_port, .request_port = stmp_appuart_request_port, .config_port = stmp_appuart_config_port, .verify_port = stmp_appuart_verify_port, }; static inline int chr(int c) { if (c < 0x20 || c > 0x7F) return '#'; return c; } /* Allocate and initialize rx and tx DMA chains */ static inline int stmp_appuart_dma_init(struct stmp_appuart_port *s) { int err = 0; struct stmp3xxx_dma_descriptor *t = &s->tx_desc; #ifndef RX_CHAIN struct stmp3xxx_dma_descriptor *r = &s->rx_desc; #else int i; #endif err = stmp3xxx_dma_request(s->dma_rx, s->dev, dev_name(s->dev)); if (err) goto out; err = stmp3xxx_dma_request(s->dma_tx, s->dev, dev_name(s->dev)); if (err) goto out1; #ifndef RX_CHAIN err = stmp3xxx_dma_allocate_command(s->dma_rx, r); if (err) goto out2; #endif err = stmp3xxx_dma_allocate_command(s->dma_tx, t); if (err) goto out3; t->virtual_buf_ptr = dma_alloc_coherent(s->dev, TX_BUFFER_SIZE, &t->command->buf_ptr, GFP_DMA); if (!t->virtual_buf_ptr) goto out4; #ifdef DEBUG memset(t->virtual_buf_ptr, 0x4B, TX_BUFFER_SIZE); #endif #ifndef RX_CHAIN r->virtual_buf_ptr = dma_alloc_coherent(s->dev, RX_BUFFER_SIZE, &r->command->buf_ptr, GFP_DMA); if (!r->virtual_buf_ptr) goto out5; #ifdef DEBUG memset(r->virtual_buf_ptr, 0x4C, RX_BUFFER_SIZE); #endif #else stmp3xxx_dma_make_chain(s->dma_rx, &s->rx_chain, s->rxd, RX_CHAIN); for (i = 0; i < RX_CHAIN; i++) { struct stmp3xxx_dma_descriptor *r = s->rxd + i; r->command->cmd = BF(RX_BUFFER_SIZE, APBX_CHn_CMD_XFER_COUNT) | BF(1, APBX_CHn_CMD_CMDWORDS) | BM_APBX_CHn_CMD_WAIT4ENDCMD | BM_APBX_CHn_CMD_SEMAPHORE | BM_APBX_CHn_CMD_IRQONCMPLT | BM_APBX_CHn_CMD_CHAIN | BF_APBX_CHn_CMD_COMMAND(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE); r->virtual_buf_ptr = dma_alloc_coherent(s->dev, RX_BUFFER_SIZE, &r->command->buf_ptr, GFP_DMA); r->command->pio_words[0] = /* BM_UARTAPP_CTRL0_RUN | */ BF(RX_BUFFER_SIZE, UARTAPP_CTRL0_XFER_COUNT) | BM_UARTAPP_CTRL0_RXTO_ENABLE | BF(3, UARTAPP_CTRL0_RXTIMEOUT); } #endif return 0; /* * would be necessary on other error paths dma_free_coherent( s->dev, RX_BUFFER_SIZE, r->virtual_buf_ptr, r->command->buf_ptr); */ out5: dma_free_coherent(s->dev, TX_BUFFER_SIZE, t->virtual_buf_ptr, t->command->buf_ptr); out4: stmp3xxx_dma_free_command(s->dma_tx, t); out3: #ifndef RX_CHAIN stmp3xxx_dma_free_command(s->dma_rx, r); #endif out2: stmp3xxx_dma_release(s->dma_tx); out1: stmp3xxx_dma_release(s->dma_rx); out: WARN_ON(err); return err; } static void stmp_appuart_on(struct platform_device *dev) { struct stmp_appuart_port *s = platform_get_drvdata(dev); if (!pio_mode) { /* Tell DMA to select UART. Both DMA channels are shared between app UART and IrDA. Target id of 0 means UART, 1 means IrDA */ stmp3xxx_dma_set_alt_target(s->dma_rx, 0); stmp3xxx_dma_set_alt_target(s->dma_tx, 0); /* Reset DMA channels */ stmp3xxx_dma_reset_channel(s->dma_rx); stmp3xxx_dma_reset_channel(s->dma_tx); stmp3xxx_dma_enable_interrupt(s->dma_rx); stmp3xxx_dma_enable_interrupt(s->dma_tx); } } #ifdef CONFIG_CPU_FREQ static int stmp_appuart_updateclk(struct device *dev, void *clkdata) { struct stmp_appuart_port *s = dev_get_drvdata(dev); if (s) { s->port.uartclk = clk_get_rate(s->clk) * 1000; /* FIXME: perform actual update */ } return 0; } static int stmp_appuart_notifier(struct notifier_block *self, unsigned long phase, void *p) { int r = 0; if ((phase == CPUFREQ_POSTCHANGE) || (phase == CPUFREQ_RESUMECHANGE)) { /* get new uartclock and setspeed */ r = driver_for_each_device(&stmp_appuart_driver.driver, NULL, p, stmp_appuart_updateclk); } return (r == 0) ? NOTIFY_OK : NOTIFY_DONE; } static struct notifier_block stmp_appuart_nb = { .notifier_call = &stmp_appuart_notifier, }; #endif /* CONFIG_CPU_FREQ */ static int __devinit stmp_appuart_probe(struct platform_device *device) { struct stmp_appuart_port *s; int err = 0; struct resource *r; int i; u32 version; int (*pinctl)(int req, int id); s = kzalloc(sizeof(struct stmp_appuart_port), GFP_KERNEL); if (!s) { err = -ENOMEM; goto out; } spin_lock_init(&s->lock); s->clk = clk_get(NULL, "uart"); if (IS_ERR(s->clk)) { err = PTR_ERR(s->clk); goto out_free; } clk_enable(s->clk); r = platform_get_resource(device, IORESOURCE_MEM, 0); if (!r) { err = -ENXIO; goto out_free_clk; } s->port.mapbase = r->start; s->port.irq = platform_get_irq(device, 0); s->port.ops = &stmp_appuart_ops; s->port.iotype = UPIO_MEM; s->port.line = device->id < 0 ? 0 : device->id; s->port.fifosize = 16; s->port.timeout = HZ/10; s->port.uartclk = clk_get_rate(s->clk) * 1000; s->port.type = PORT_IMX; s->port.dev = s->dev = get_device(&device->dev); s->ctrl = 0; s->keep_irq = 0; r = platform_get_resource(device, IORESOURCE_MEM, 0); if (!r) { err = -ENXIO; goto out_free_clk; } dev_dbg(s->dev, "%s\n", __func__); for (i = 0; i < ARRAY_SIZE(s->irq); i++) { s->irq[i] = platform_get_irq(device, i); dev_dbg(s->dev, "Resources: irq[%d] = %d\n", i, s->irq[i]); if (s->irq[i] < 0) { err = s->irq[i]; goto out_free_clk; } } r = platform_get_resource(device, IORESOURCE_DMA, 0); if (!r) { err = -ENXIO; goto out_free; } s->dma_rx = r->start; r = platform_get_resource(device, IORESOURCE_DMA, 1); if (!r) { err = -ENXIO; goto out_free; } s->dma_tx = r->start; r = platform_get_resource(device, IORESOURCE_MEM, 0); if (!r) { err = -ENXIO; goto out_free; } s->mem = (void __iomem *)(r->start - STMP3XXX_REGS_PHBASE + (u32)STMP3XXX_REGS_BASE); s->memsize = r->end - r->start; #ifdef CONFIG_CPU_FREQ cpufreq_register_notifier(&stmp_appuart_nb, CPUFREQ_TRANSITION_NOTIFIER); #endif platform_set_drvdata(device, s); device_init_wakeup(&device->dev, 1); stmp_appuart_dma_init(s); stmp_appuart_on(device); pinctl = device->dev.platform_data; if (pinctl) { err = pinctl(1, device->id); if (err) goto out_free_clk; } err = uart_add_one_port(&stmp_appuart_uart, &s->port); if (err) goto out_free_pins; version = __raw_readl(REGS_UARTAPP1_BASE + HW_UARTAPP_VERSION); printk(KERN_INFO "Found APPUART %d.%d.%d\n", (version >> 24) & 0xFF, (version >> 16) & 0xFF, version & 0xFFFF); return 0; out_free_pins: if (pinctl) pinctl(0, device->id); out_free_clk: clk_put(s->clk); out_free: platform_set_drvdata(device, NULL); kfree(s); out: return err; } static int __devexit stmp_appuart_remove(struct platform_device *device) { struct stmp_appuart_port *s; void (*pinctl)(int req, int id); s = platform_get_drvdata(device); if (s) { pinctl = device->dev.platform_data; put_device(s->dev); clk_disable(s->clk); clk_put(s->clk); uart_remove_one_port(&stmp_appuart_uart, &s->port); if (pinctl) pinctl(0, device->id); kfree(s); platform_set_drvdata(device, NULL); } return 0; } static int stmp_appuart_suspend(struct platform_device *device, pm_message_t state) { #ifdef CONFIG_PM struct stmp_appuart_port *s = platform_get_drvdata(device); if (!s) return 0; s->keep_irq = device_may_wakeup(&device->dev); uart_suspend_port(&stmp_appuart_uart, &s->port); if (!s->keep_irq) clk_disable(s->clk); #endif return 0; } static int stmp_appuart_resume(struct platform_device *device) { #ifdef CONFIG_PM struct stmp_appuart_port *s = platform_get_drvdata(device); if (!s) return 0; if (!s->keep_irq) clk_enable(s->clk); stmp_appuart_on(device); uart_resume_port(&stmp_appuart_uart, &s->port); s->keep_irq = 0; #endif return 0; } static int __init stmp_appuart_init() { int r; r = uart_register_driver(&stmp_appuart_uart); if (r) goto out; r = platform_driver_register(&stmp_appuart_driver); if (r) goto out_err; return 0; out_err: uart_unregister_driver(&stmp_appuart_uart); out: return r; } static void __exit stmp_appuart_exit() { platform_driver_unregister(&stmp_appuart_driver); uart_unregister_driver(&stmp_appuart_uart); } module_init(stmp_appuart_init) module_exit(stmp_appuart_exit) static void stmp_appuart_stop_rx(struct uart_port *u) { struct stmp_appuart_port *s = to_appuart(u); dev_dbg(s->dev, "%s\n", __func__); __raw_writel(BM_UARTAPP_CTRL2_RXE, s->mem + HW_STMP3XXX_CLR); } static void stmp_appuart_break_ctl(struct uart_port *u, int ctl) { struct stmp_appuart_port *s = to_appuart(u); dev_dbg(s->dev, "%s: break = %s\n", __func__, ctl ? "on" : "off"); if (ctl) __raw_writel(BM_UARTAPP_LINECTRL_BRK, s->mem + HW_UARTAPP_LINECTRL_SET); else __raw_writel(BM_UARTAPP_LINECTRL_BRK, s->mem + HW_UARTAPP_LINECTRL_CLR); } static void stmp_appuart_enable_ms(struct uart_port *port) { /* just empty */ } static void stmp_appuart_set_mctrl(struct uart_port *u, unsigned mctrl) { struct stmp_appuart_port *s = to_appuart(u); u32 ctrl = __raw_readl(s->mem + HW_UARTAPP_CTRL2); dev_dbg(s->dev, "%s (%x)\n", __func__, mctrl); ctrl &= ~BM_UARTAPP_CTRL2_RTS; if (mctrl & TIOCM_RTS) { dev_dbg(s->dev, "...RTS\n"); ctrl |= BM_UARTAPP_CTRL2_RTS; } s->ctrl = mctrl; dev_dbg(s->dev, "...%x; ctrl = %x\n", s->ctrl, ctrl); __raw_writel(ctrl, s->mem + HW_UARTAPP_CTRL2); } static u32 stmp_appuart_get_mctrl(struct uart_port *u) { struct stmp_appuart_port *s = to_appuart(u); u32 stat = __raw_readl(s->mem + HW_UARTAPP_STAT); int ctrl2 = __raw_readl(s->mem + HW_UARTAPP_CTRL2); u32 mctrl = s->ctrl; dev_dbg(s->dev, "%s:\n", __func__); mctrl &= ~TIOCM_CTS; if (stat & BM_UARTAPP_STAT_CTS) { dev_dbg(s->dev, "CTS"); mctrl |= TIOCM_CTS; } if (ctrl2 & BM_UARTAPP_CTRL2_RTS) { dev_dbg(s->dev, "RTS"); mctrl |= TIOCM_RTS; } dev_dbg(s->dev, "...%x\n", mctrl); return mctrl; } static int stmp_appuart_request_port(struct uart_port *u) { struct stmp_appuart_port *s = to_appuart(u); int err = 0; if (!request_mem_region((u32)s->mem, s->memsize, dev_name(s->dev))) err = -ENXIO; return err; } static void stmp_appuart_release_port(struct uart_port *u) { struct stmp_appuart_port *s = to_appuart(u); release_mem_region((u32)s->mem, s->memsize); } static int stmp_appuart_verify_port(struct uart_port *u, struct serial_struct *ser) { struct stmp_appuart_port *s = to_appuart(u); dev_dbg(s->dev, "%s\n", __func__); return 0; } static void stmp_appuart_config_port(struct uart_port *u, int flags) { struct stmp_appuart_port *s = to_appuart(u); dev_dbg(s->dev, "%s\n", __func__); } static const char *stmp_appuart_type(struct uart_port *u) { struct stmp_appuart_port *s = to_appuart(u); dev_dbg(s->dev, "%s\n", __func__); return dev_name(s->dev); } static void stmp_appuart_settermios(struct uart_port *u, struct ktermios *nw, struct ktermios *old) { static struct ktermios saved; struct stmp_appuart_port *s = to_appuart(u); unsigned int cflag; u32 bm, ctrl, ctrl2, div; int err = 0; unsigned baud; dev_dbg(s->dev, "%s\n", __func__); if (nw) memcpy(&saved, nw, sizeof *nw); else nw = old = &saved; cflag = nw->c_cflag; ctrl = BM_UARTAPP_LINECTRL_FEN; ctrl2 = __raw_readl(s->mem + HW_UARTAPP_CTRL2); /* byte size */ switch (cflag & CSIZE) { case CS5: bm = 0; break; case CS6: bm = 1; break; case CS7: bm = 2; break; case CS8: bm = 3; break; default: err = -EINVAL; break; } if (err) goto out; dev_dbg(s->dev, "Byte size %d bytes, mask %x\n", bm + 5, BF(bm, UARTAPP_LINECTRL_WLEN)); ctrl |= BF(bm, UARTAPP_LINECTRL_WLEN); /* parity */ if (cflag & PARENB) { dev_dbg(s->dev, "Parity check enabled\n"); ctrl |= BM_UARTAPP_LINECTRL_PEN | BM_UARTAPP_LINECTRL_SPS; if ((cflag & PARODD) == 0) { dev_dbg(s->dev, "(Even) mask = %x\n", BM_UARTAPP_LINECTRL_PEN | BM_UARTAPP_LINECTRL_SPS | BM_UARTAPP_LINECTRL_EPS); ctrl |= BM_UARTAPP_LINECTRL_EPS; } else dev_dbg(s->dev, "(Odd) mask = %x\n", BM_UARTAPP_LINECTRL_PEN | BM_UARTAPP_LINECTRL_SPS); } else dev_dbg(s->dev, "Parity check disabled.\n"); /* figure out the stop bits requested */ if (cflag & CSTOPB) { dev_dbg(s->dev, "Stop bits, mask = %x\n", BM_UARTAPP_LINECTRL_STP2); ctrl |= BM_UARTAPP_LINECTRL_STP2; } else dev_dbg(s->dev, "No stop bits\n"); /* figure out the hardware flow control settings */ if (cflag & CRTSCTS) { dev_dbg(s->dev, "RTS/CTS flow control\n"); ctrl2 |= BM_UARTAPP_CTRL2_CTSEN /* | BM_UARTAPP_CTRL2_RTSEN */ ; } else { dev_dbg(s->dev, "RTS/CTS disabled\n"); ctrl2 &= ~BM_UARTAPP_CTRL2_CTSEN; } /* set baud rate */ baud = uart_get_baud_rate(u, nw, old, 0, u->uartclk); dev_dbg(s->dev, "Baud rate requested: %d (clk = %d)\n", baud, u->uartclk); div = u->uartclk * 32 / baud; ctrl |= BF(div & 0x3F, UARTAPP_LINECTRL_BAUD_DIVFRAC); ctrl |= BF(div >> 6, UARTAPP_LINECTRL_BAUD_DIVINT); if ((cflag & CREAD) != 0) { dev_dbg(s->dev, "RX started\n"); ctrl2 |= BM_UARTAPP_CTRL2_RXE | BM_UARTAPP_CTRL2_RXDMAE; } if (!err) { dev_dbg(s->dev, "CTRLS = %x + %x\n", ctrl, ctrl2); __raw_writel(ctrl, s->mem + HW_UARTAPP_LINECTRL); __raw_writel(ctrl2, s->mem + HW_UARTAPP_CTRL2); } out: return /* err */ ; } static int stmp_appuart_free_irqs(struct stmp_appuart_port *s) { int irqn = 0; if (s->keep_irq) { dev_dbg(s->dev, "keep_irq != 0, ignoring\n"); return 0; } for (irqn = 0; irqn < ARRAY_SIZE(s->irq); irqn++) free_irq(s->irq[irqn], s); return 0; } void stmp_appuart_rx(struct stmp_appuart_port *s, u8 * rx_buffer, int count) { u8 c; int flag; struct tty_struct *tty = s->port.info->port.tty; u32 stat; spin_lock(&s->lock); stat = __raw_readl(s->mem + HW_UARTAPP_STAT); if (count < 0) { count = __raw_readl(s->mem + HW_UARTAPP_STAT) & BM_UARTAPP_STAT_RXCOUNT; dev_dbg(s->dev, "count = %d\n", count); } for (;;) { if (!rx_buffer) { if (stat & BM_UARTAPP_STAT_RXFE) break; c = __raw_readl(s->mem + HW_UARTAPP_DATA) & 0xFF; } else { if (count-- <= 0) break; c = *rx_buffer++; dev_dbg(s->dev, "Received: %x(%c)\n", c, chr(c)); } flag = TTY_NORMAL; if (stat & BM_UARTAPP_STAT_BERR) { stat &= ~BM_UARTAPP_STAT_BERR; s->port.icount.brk++; if (uart_handle_break(&s->port)) goto ignore; flag = TTY_BREAK; } else if (stat & BM_UARTAPP_STAT_PERR) { stat &= ~BM_UARTAPP_STAT_PERR; s->port.icount.parity++; flag = TTY_PARITY; } else if (stat & BM_UARTAPP_STAT_FERR) { stat &= ~BM_UARTAPP_STAT_FERR; s->port.icount.frame++; flag = TTY_FRAME; } if (stat & BM_UARTAPP_STAT_OERR) s->port.icount.overrun++; if (uart_handle_sysrq_char(&s->port, c)) goto ignore; uart_insert_char(&s->port, stat, BM_UARTAPP_STAT_OERR, c, flag); ignore: if (pio_mode) { __raw_writel(stat, s->mem + HW_UARTAPP_STAT); stat = __raw_readl(s->mem + HW_UARTAPP_STAT); } } __raw_writel(stat, s->mem + HW_UARTAPP_STAT); tty_flip_buffer_push(tty); spin_unlock(&s->lock); } static inline void stmp_appuart_submit_rx(struct stmp_appuart_port *s) { #ifndef RX_CHAIN struct stmp3xxx_dma_descriptor *r = &s->rx_desc; dev_dbg(s->dev, "Submitting RX DMA request\n"); r->command->cmd = BM_APBX_CHn_CMD_HALTONTERMINATE | BF(RX_BUFFER_SIZE, APBX_CHn_CMD_XFER_COUNT) | BF(1, APBX_CHn_CMD_CMDWORDS) | BM_APBX_CHn_CMD_WAIT4ENDCMD | BM_APBX_CHn_CMD_SEMAPHORE | BM_APBX_CHn_CMD_IRQONCMPLT | BF(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE, APBX_CHn_CMD_COMMAND); r->command->pio_words[0] = __raw_readl(REGS_UARTAPP1_BASE + HW_UARTAPP_CTRL0) | BF(RX_BUFFER_SIZE, UARTAPP_CTRL0_XFER_COUNT) | BM_UARTAPP_CTRL0_RXTO_ENABLE | BF(3, UARTAPP_CTRL0_RXTIMEOUT); r->command->pio_words[0] &= ~BM_UARTAPP_CTRL0_RUN; stmp3xxx_dma_reset_channel(s->dma_rx); stmp3xxx_dma_go(s->dma_rx, r, 1); #endif } static irqreturn_t stmp_appuart_irq_int(int irq, void *context) { u32 istatus; struct stmp_appuart_port *s = context; u32 stat = __raw_readl(s->mem + HW_UARTAPP_STAT); istatus = __raw_readl(s->mem + HW_UARTAPP_INTR); dev_dbg(s->dev, "IRQ: int(%d), status = %08X\n", irq, istatus); if (istatus & BM_UARTAPP_INTR_CTSMIS) { uart_handle_cts_change(&s->port, stat & BM_UARTAPP_STAT_CTS); dev_dbg(s->dev, "CTS change: %x\n", stat & BM_UARTAPP_STAT_CTS); __raw_writel(BM_UARTAPP_INTR_CTSMIS, s->mem + HW_UARTAPP_INTR_CLR); } else if (istatus & BM_UARTAPP_INTR_RTIS) { dev_dbg(s->dev, "RX timeout, draining out\n"); stmp_appuart_submit_rx(s); } else dev_info(s->dev, "Unhandled status %x\n", istatus); __raw_writel(istatus & 0xFFFF, s->mem + HW_UARTAPP_INTR_CLR); return IRQ_HANDLED; } static irqreturn_t stmp_appuart_irq_rx(int irq, void *context) { struct stmp_appuart_port *s = context; int count = -1; stmp3xxx_dma_clear_interrupt(s->dma_rx); dev_dbg(s->dev, "%s(%d), count = %d\n", __func__, irq, count); #ifndef RX_CHAIN stmp_appuart_rx(s, s->rx_desc.virtual_buf_ptr, count); stmp_appuart_submit_rx(s); #else if (circ_advance_cooked(&s->rx_chain) == 0) { BUG(); return IRQ_HANDLED; } circ_advance_active(&s->rx_chain, 1); while (s->rx_chain.cooked_count) { stmp_appuart_rx(s, stmp3xxx_dma_circ_get_cooked_head(&s-> rx_chain)->virtual_buf_ptr, -1); circ_advance_free(&s->rx_chain, 1); } #endif return IRQ_HANDLED; } static void stmp_appuart_submit_tx(struct stmp_appuart_port *s, int size) { struct stmp3xxx_dma_descriptor *d = &s->tx_desc; dev_dbg(s->dev, "Submitting TX DMA request, %d bytes\n", size); d->command->pio_words[0] = /* BM_UARTAPP_CTRL1_RUN | */ BF(size, UARTAPP_CTRL1_XFER_COUNT); d->command->cmd = BF(size, APBX_CHn_CMD_XFER_COUNT) | BF(1, APBX_CHn_CMD_CMDWORDS) | BM_APBX_CHn_CMD_WAIT4ENDCMD | BM_APBX_CHn_CMD_SEMAPHORE | BM_APBX_CHn_CMD_IRQONCMPLT | BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND); stmp3xxx_dma_go(s->dma_tx, d, 1); } static irqreturn_t stmp_appuart_irq_tx(int irq, void *context) { struct stmp_appuart_port *s = context; struct uart_port *u = &s->port; int bytes; stmp3xxx_dma_clear_interrupt(s->dma_tx); dev_dbg(s->dev, "%s(%d)\n", __func__, irq); bytes = stmp_appuart_copy_tx(u, s->tx_desc.virtual_buf_ptr, TX_BUFFER_SIZE); if (bytes > 0) { dev_dbg(s->dev, "Sending %d bytes\n", bytes); stmp_appuart_submit_tx(s, bytes); } return IRQ_HANDLED; } static int stmp_appuart_request_irqs(struct stmp_appuart_port *s) { int err = 0; /* * order counts. resources should be listed in the same order */ irq_handler_t handlers[] = { stmp_appuart_irq_int, stmp_appuart_irq_rx, stmp_appuart_irq_tx, }; char *handlers_names[] = { "appuart internal", "appuart rx", "appuart tx", }; int irqn; if (s->keep_irq) { dev_dbg(s->dev, "keep_irq is set, skipping request_irq"); return 0; } for (irqn = 0; irqn < ARRAY_SIZE(handlers); irqn++) { err = request_irq(s->irq[irqn], handlers[irqn], 0, handlers_names[irqn], s); dev_dbg(s->dev, "Requested IRQ %d with status %d\n", s->irq[irqn], err); if (err) goto out; } return 0; out: stmp_appuart_free_irqs(s); return err; } static struct timer_list timer_task; static void stmp_appuart_check_rx(unsigned long data) { stmp_appuart_rx((struct stmp_appuart_port *)data, NULL, -1); mod_timer(&timer_task, jiffies + 2 * HZ); } static int stmp_appuart_startup(struct uart_port *u) { struct stmp_appuart_port *s = to_appuart(u); int err; dev_dbg(s->dev, "%s\n", __func__); s->tx_buffer_index = 0; err = stmp_appuart_request_irqs(s); if (err) goto out; if (!s->keep_irq) /* Release the block from reset and start the clocks. */ stmp3xxx_reset_block(s->mem, 0); __raw_writel(BM_UARTAPP_CTRL2_UARTEN, s->mem + HW_UARTAPP_CTRL2_SET); /* Enable the Application UART DMA bits. */ if (!pio_mode) { __raw_writel(BM_UARTAPP_CTRL2_TXDMAE | BM_UARTAPP_CTRL2_RXDMAE | BM_UARTAPP_CTRL2_DMAONERR, s->mem + HW_UARTAPP_CTRL2_SET); /* clear any pending interrupts */ __raw_writel(0, s->mem + HW_UARTAPP_INTR); /* reset all dma channels */ stmp3xxx_dma_reset_channel(s->dma_tx); stmp3xxx_dma_reset_channel(s->dma_rx); } else { __raw_writel(BM_UARTAPP_INTR_RXIEN | BM_UARTAPP_INTR_RTIEN, s->mem + HW_UARTAPP_INTR); } __raw_writel(BM_UARTAPP_INTR_CTSMIEN, s->mem + HW_UARTAPP_INTR_SET); /* * Enable fifo so all four bytes of a DMA word are written to * output (otherwise, only the LSB is written, ie. 1 in 4 bytes) */ __raw_writel(BM_UARTAPP_LINECTRL_FEN, s->mem + HW_UARTAPP_LINECTRL_SET); if (!pio_mode) { #ifndef RX_CHAIN stmp_appuart_submit_rx(s); #else circ_clear_chain(&s->rx_chain); stmp3xxx_dma_go(s->dma_rx, &s->rxd[0], 0); circ_advance_active(&s->rx_chain, 1); #endif } else { init_timer(&timer_task); timer_task.function = stmp_appuart_check_rx; timer_task.expires = jiffies + HZ; timer_task.data = (unsigned long)s; add_timer(&timer_task); } out: return err; } static void stmp_appuart_shutdown(struct uart_port *u) { struct stmp_appuart_port *s = to_appuart(u); dev_dbg(s->dev, "%s\n", __func__); if (!s->keep_irq) /* set the IP block to RESET; this should disable clock too. */ __raw_writel( BM_UARTAPP_CTRL0_SFTRST, s->mem + HW_UARTAPP_CTRL0_SET); if (!pio_mode) { /* reset all dma channels */ stmp3xxx_dma_reset_channel(s->dma_tx); stmp3xxx_dma_reset_channel(s->dma_rx); } else { del_timer(&timer_task); } stmp_appuart_free_irqs(s); } static unsigned int stmp_appuart_tx_empty(struct uart_port *u) { struct stmp_appuart_port *s = to_appuart(u); if (pio_mode) if (__raw_readl(s->mem + HW_UARTAPP_STAT) & BM_UARTAPP_STAT_TXFE) return TIOCSER_TEMT; else return 0; else return stmp3xxx_dma_running(s->dma_tx) ? 0 : TIOCSER_TEMT; } static void stmp_appuart_start_tx(struct uart_port *u) { struct stmp_appuart_port *s = to_appuart(u); int bytes; dev_dbg(s->dev, "%s\n", __func__); /* enable transmitter */ __raw_writel(BM_UARTAPP_CTRL2_TXE, s->mem + HW_UARTAPP_CTRL2_SET); if (!pio_mode) { if (stmp3xxx_dma_running(s->dma_tx)) return; bytes = stmp_appuart_copy_tx(u, s->tx_desc.virtual_buf_ptr, TX_BUFFER_SIZE); if (bytes <= 0) return; dev_dbg(s->dev, "Started DMA transfer with descriptor %p, " "command %p, %d bytes long\n", &s->tx_desc, s->tx_desc.command, bytes); stmp_appuart_submit_tx(s, bytes); } else { int count = 0; u8 c; while (! (__raw_readl (s->mem + HW_UARTAPP_STAT) & BM_UARTAPP_STAT_TXFF)) { if (stmp_appuart_copy_tx(u, &c, 1) <= 0) break; dev_dbg(s->dev, "%d: '%c'/%x\n", ++count, chr(c), c); __raw_writel(c, s->mem + HW_UARTAPP_DATA); } } } static void stmp_appuart_stop_tx(struct uart_port *u) { struct stmp_appuart_port *s = to_appuart(u); dev_dbg(s->dev, "%s\n", __func__); __raw_writel(BM_UARTAPP_CTRL2_TXE, s->mem + HW_UARTAPP_CTRL2_CLR); } static int stmp_appuart_copy_tx(struct uart_port *u, u8 * target, int tx_buffer_size) { int last = 0, portion; struct circ_buf *xmit = &u->info->xmit; while (last < tx_buffer_size) { /* let's fill the only descriptor */ if (u->x_char) { target[last++] = u->x_char; u->x_char = 0; } else if (!uart_circ_empty(xmit) && !uart_tx_stopped(u)) { portion = min((u32) tx_buffer_size, (u32) uart_circ_chars_pending(xmit)); portion = min((u32) portion, (u32) CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); memcpy(target + last, &xmit->buf[xmit->tail], portion); xmit->tail = (xmit->tail + portion) & (UART_XMIT_SIZE - 1); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(u); last += portion; } else { /* All tx data copied into buffer */ return last; } } return last; } MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("stmp3xxx app uart driver"); MODULE_AUTHOR("dmitry pervushin "); module_param(pio_mode, int, 0);