Files
linux-legacy/drivers/serial/stmp-app.c
Frank.Li 2e21083d70 ENGR00118544 iMX23 Clear dangerous macro stmp3xxx_setl(clearl)
No all regiser use _SCT. stmp3xxx_setl may write to wrong address.
please use __raw_write (value, REG_XXX_SET) to write date to SCT
register.  Only REG_XXX_SCT is defined when it exist at hardware.

Signed-off-by: Frank.Li <Frank.Li@freescale.com>
2010-08-10 11:45:57 -05:00

1082 lines
26 KiB
C

/*
* Freescale STMP37XX/STMP378X Application UART driver
*
* Author: dmitry pervushin <dimka@embeddedalley.com>
*
* 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 <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
#include <mach/hardware.h>
#include <mach/regs-apbx.h>
#include <mach/regs-uartapp.h>
#include <mach/regs-pinctrl.h>
#include <mach/stmp3xxx.h>
#include <mach/platform.h>
#include <asm/mach-types.h>
#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 <dimka@embeddedalley.com>");
module_param(pio_mode, int, 0);