summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/8250
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-04-29 12:16:17 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-04-29 12:16:17 -0700
commit507ffe4f3840ac24890a8123c702cf1b7fe4d33c (patch)
tree1046888f9db00f268a0056d7f6e427e21502f84c /drivers/tty/serial/8250
parentfdc719b63ae35d6a2d8a2a2c76eed813294664bf (diff)
parent45efcb2d32d35f6509543e477568842d8467035d (diff)
Merge tag 'tty-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial driver update from Greg Kroah-Hartman: "Here's the big tty/serial driver merge request for 3.10-rc1 Once again, Jiri has a number of TTY driver fixes and cleanups, and Peter Hurley came through with a bunch of ldisc fixes that resolve a number of reported issues. There are some other serial driver cleanups as well. All of these have been in the linux-next tree for a while" * tag 'tty-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (117 commits) tty/serial/sirf: fix MODULE_DEVICE_TABLE serial: mxs: drop superfluous {get|put}_device serial: mxs: fix buffer overflow ARM: PL011: add support for extended FIFO-size of PL011-r1p5 serial_core.c: add put_device() after device_find_child() tty: Fix unsafe bit ops in tty_throttle_safe/unthrottle_safe serial: sccnxp: Replace pdata.init/exit with regulator API serial: sccnxp: Do not override device name TTY: pty, fix compilation warning TTY: rocket, fix compilation warning TTY: ircomm: fix DTR being raised on hang up TTY: synclinkmp: fix DTR being raised on hang up TTY: synclink_gt: fix DTR being raised on hang up TTY: synclink: fix DTR being raised on hang up serial: 8250_dw: Fix the stub for dw8250_probe_acpi() serial: 8250_dw: Convert to devm_ioremap() serial: 8250_dw: Set port capabilities based on CPR register serial: 8250_dw: Let ACPI code extract the DMA client info serial: 8250_dw: Support clk framework also with ACPI serial: 8250_dw: Enable runtime PM ...
Diffstat (limited to 'drivers/tty/serial/8250')
-rw-r--r--drivers/tty/serial/8250/8250.h7
-rw-r--r--drivers/tty/serial/8250/8250_core.c18
-rw-r--r--drivers/tty/serial/8250/8250_dma.c52
-rw-r--r--drivers/tty/serial/8250/8250_dw.c194
4 files changed, 141 insertions, 130 deletions
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 34eb676916fe..1ebf8538b4fa 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -117,13 +117,6 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value)
* is cleared, the machine locks up with endless interrupts.
*/
#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1)
-#elif defined(CONFIG_SBC8560)
-/*
- * WindRiver did something similarly broken on their SBC8560 board. The
- * UART tristates its IRQ output while OUT2 is clear, but they pulled
- * the interrupt line _up_ instead of down, so if we register the IRQ
- * while the UART is in that state, we die in an IRQ storm. */
-#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2)
#else
#define ALPHA_KLUDGE_MCR 0
#endif
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 35f9c96aada9..46528d57be72 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -2755,7 +2755,7 @@ static void __init serial8250_isa_init_ports(void)
if (nr_uarts > UART_NR)
nr_uarts = UART_NR;
- for (i = 0; i < nr_uarts; i++) {
+ for (i = 0; i < UART_NR; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
struct uart_port *port = &up->port;
@@ -2916,7 +2916,7 @@ static int __init serial8250_console_setup(struct console *co, char *options)
* if so, search for the first available port that does have
* console support.
*/
- if (co->index >= nr_uarts)
+ if (co->index >= UART_NR)
co->index = 0;
port = &serial8250_ports[co->index].port;
if (!port->iobase && !port->membase)
@@ -2957,7 +2957,7 @@ int serial8250_find_port(struct uart_port *p)
int line;
struct uart_port *port;
- for (line = 0; line < nr_uarts; line++) {
+ for (line = 0; line < UART_NR; line++) {
port = &serial8250_ports[line].port;
if (uart_match_port(p, port))
return line;
@@ -3110,7 +3110,7 @@ static int serial8250_remove(struct platform_device *dev)
{
int i;
- for (i = 0; i < nr_uarts; i++) {
+ for (i = 0; i < UART_NR; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
if (up->port.dev == &dev->dev)
@@ -3178,7 +3178,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
/*
* First, find a port entry which matches.
*/
- for (i = 0; i < nr_uarts; i++)
+ for (i = 0; i < UART_NR; i++)
if (uart_match_port(&serial8250_ports[i].port, port))
return &serial8250_ports[i];
@@ -3187,7 +3187,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
* free entry. We look for one which hasn't been previously
* used (indicated by zero iobase).
*/
- for (i = 0; i < nr_uarts; i++)
+ for (i = 0; i < UART_NR; i++)
if (serial8250_ports[i].port.type == PORT_UNKNOWN &&
serial8250_ports[i].port.iobase == 0)
return &serial8250_ports[i];
@@ -3196,7 +3196,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
* That also failed. Last resort is to find any entry which
* doesn't have a real port associated with it.
*/
- for (i = 0; i < nr_uarts; i++)
+ for (i = 0; i < UART_NR; i++)
if (serial8250_ports[i].port.type == PORT_UNKNOWN)
return &serial8250_ports[i];
@@ -3247,6 +3247,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
uart->tx_loadsz = up->tx_loadsz;
uart->capabilities = up->capabilities;
+ /* Take tx_loadsz from fifosize if it wasn't set separately */
+ if (uart->port.fifosize && !uart->tx_loadsz)
+ uart->tx_loadsz = uart->port.fifosize;
+
if (up->port.dev)
uart->port.dev = up->port.dev;
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index b9f7fd28112e..7046769608d4 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -33,10 +33,8 @@ static void __dma_tx_complete(void *param)
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);
- if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) {
+ if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port))
serial8250_tx_dma(p);
- uart_write_wakeup(&p->port);
- }
}
static void __dma_rx_complete(void *param)
@@ -67,12 +65,11 @@ int serial8250_tx_dma(struct uart_8250_port *p)
struct circ_buf *xmit = &p->port.state->xmit;
struct dma_async_tx_descriptor *desc;
- if (dma->tx_running)
- return -EBUSY;
+ if (uart_tx_stopped(&p->port) || dma->tx_running ||
+ uart_circ_empty(xmit))
+ return 0;
dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
- if (!dma->tx_size)
- return -EINVAL;
desc = dmaengine_prep_slave_single(dma->txchan,
dma->tx_addr + xmit->tail,
@@ -104,20 +101,29 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
struct dma_tx_state state;
int dma_status;
- /*
- * If RCVR FIFO trigger level was not reached, complete the transfer and
- * let 8250.c copy the remaining data.
- */
- if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) {
- dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie,
- &state);
+ dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+
+ switch (iir & 0x3f) {
+ case UART_IIR_RLSI:
+ /* 8250_core handles errors and break interrupts */
+ return -EIO;
+ case UART_IIR_RX_TIMEOUT:
+ /*
+ * If RCVR FIFO trigger level was not reached, complete the
+ * transfer and let 8250_core copy the remaining data.
+ */
if (dma_status == DMA_IN_PROGRESS) {
dmaengine_pause(dma->rxchan);
__dma_rx_complete(p);
}
return -ETIMEDOUT;
+ default:
+ break;
}
+ if (dma_status)
+ return 0;
+
desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
dma->rx_size, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
@@ -143,21 +149,31 @@ int serial8250_request_dma(struct uart_8250_port *p)
struct uart_8250_dma *dma = p->dma;
dma_cap_mask_t mask;
- dma->rxconf.src_addr = p->port.mapbase + UART_RX;
- dma->txconf.dst_addr = p->port.mapbase + UART_TX;
+ /* Default slave configuration parameters */
+ dma->rxconf.direction = DMA_DEV_TO_MEM;
+ dma->rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma->rxconf.src_addr = p->port.mapbase + UART_RX;
+
+ dma->txconf.direction = DMA_MEM_TO_DEV;
+ dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma->txconf.dst_addr = p->port.mapbase + UART_TX;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
/* Get a channel for RX */
- dma->rxchan = dma_request_channel(mask, dma->fn, dma->rx_param);
+ dma->rxchan = dma_request_slave_channel_compat(mask,
+ dma->fn, dma->rx_param,
+ p->port.dev, "rx");
if (!dma->rxchan)
return -ENODEV;
dmaengine_slave_config(dma->rxchan, &dma->rxconf);
/* Get a channel for TX */
- dma->txchan = dma_request_channel(mask, dma->fn, dma->tx_param);
+ dma->txchan = dma_request_slave_channel_compat(mask,
+ dma->fn, dma->tx_param,
+ p->port.dev, "tx");
if (!dma->txchan) {
dma_release_channel(dma->rxchan);
return -ENODEV;
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index db0e66f6dd0e..beaa283f5cc6 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -26,6 +26,8 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
#include "8250.h"
@@ -34,9 +36,6 @@
#define DW_UART_CPR 0xf4 /* Component Parameter Register */
#define DW_UART_UCV 0xf8 /* UART Component Version */
-/* Intel Low Power Subsystem specific */
-#define LPSS_PRV_CLOCK_PARAMS 0x800
-
/* Component Parameter Register bits */
#define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0)
#define DW_UART_CPR_AFCE_MODE (1 << 4)
@@ -55,8 +54,9 @@
struct dw8250_data {
- int last_lcr;
- int line;
+ int last_lcr;
+ int line;
+ struct clk *clk;
};
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
@@ -113,6 +113,18 @@ static int dw8250_handle_irq(struct uart_port *p)
return 0;
}
+static void
+dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
+{
+ if (!state)
+ pm_runtime_get_sync(port->dev);
+
+ serial8250_do_pm(port, state, old);
+
+ if (state)
+ pm_runtime_put_sync_suspend(port->dev);
+}
+
static int dw8250_probe_of(struct uart_port *p)
{
struct device_node *np = p->dev->of_node;
@@ -136,8 +148,13 @@ static int dw8250_probe_of(struct uart_port *p)
if (!of_property_read_u32(np, "reg-shift", &val))
p->regshift = val;
+ /* clock got configured through clk api, all done */
+ if (p->uartclk)
+ return 0;
+
+ /* try to find out clock frequency from DT as fallback */
if (of_property_read_u32(np, "clock-frequency", &val)) {
- dev_err(p->dev, "no clock-frequency property set\n");
+ dev_err(p->dev, "clk or clock-frequency not defined\n");
return -EINVAL;
}
p->uartclk = val;
@@ -146,67 +163,10 @@ static int dw8250_probe_of(struct uart_port *p)
}
#ifdef CONFIG_ACPI
-static bool dw8250_acpi_dma_filter(struct dma_chan *chan, void *parm)
-{
- return chan->chan_id == *(int *)parm;
-}
-
-static acpi_status
-dw8250_acpi_walk_resource(struct acpi_resource *res, void *data)
-{
- struct uart_port *p = data;
- struct uart_8250_port *port;
- struct uart_8250_dma *dma;
- struct acpi_resource_fixed_dma *fixed_dma;
- struct dma_slave_config *slave;
-
- port = container_of(p, struct uart_8250_port, port);
-
- switch (res->type) {
- case ACPI_RESOURCE_TYPE_FIXED_DMA:
- fixed_dma = &res->data.fixed_dma;
-
- /* TX comes first */
- if (!port->dma) {
- dma = devm_kzalloc(p->dev, sizeof(*dma), GFP_KERNEL);
- if (!dma)
- return AE_NO_MEMORY;
-
- port->dma = dma;
- slave = &dma->txconf;
-
- slave->direction = DMA_MEM_TO_DEV;
- slave->dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- slave->slave_id = fixed_dma->request_lines;
- slave->dst_maxburst = port->tx_loadsz / 4;
-
- dma->tx_chan_id = fixed_dma->channels;
- dma->tx_param = &dma->tx_chan_id;
- dma->fn = dw8250_acpi_dma_filter;
- } else {
- dma = port->dma;
- slave = &dma->rxconf;
-
- slave->direction = DMA_DEV_TO_MEM;
- slave->src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- slave->slave_id = fixed_dma->request_lines;
- slave->src_maxburst = p->fifosize / 4;
-
- dma->rx_chan_id = fixed_dma->channels;
- dma->rx_param = &dma->rx_chan_id;
- }
-
- break;
- }
-
- return AE_OK;
-}
-
-static int dw8250_probe_acpi(struct uart_port *p)
+static int dw8250_probe_acpi(struct uart_8250_port *up)
{
const struct acpi_device_id *id;
- acpi_status status;
- u32 reg;
+ struct uart_port *p = &up->port;
id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev);
if (!id)
@@ -216,26 +176,21 @@ static int dw8250_probe_acpi(struct uart_port *p)
p->serial_in = dw8250_serial_in32;
p->serial_out = dw8250_serial_out32;
p->regshift = 2;
- p->uartclk = (unsigned int)id->driver_data;
- status = acpi_walk_resources(ACPI_HANDLE(p->dev), METHOD_NAME__CRS,
- dw8250_acpi_walk_resource, p);
- if (ACPI_FAILURE(status)) {
- dev_err_ratelimited(p->dev, "%s failed \"%s\"\n", __func__,
- acpi_format_exception(status));
- return -ENODEV;
- }
+ if (!p->uartclk)
+ p->uartclk = (unsigned int)id->driver_data;
- /* Fix Haswell issue where the clocks do not get enabled */
- if (!strcmp(id->id, "INT33C4") || !strcmp(id->id, "INT33C5")) {
- reg = readl(p->membase + LPSS_PRV_CLOCK_PARAMS);
- writel(reg | 1, p->membase + LPSS_PRV_CLOCK_PARAMS);
- }
+ up->dma = devm_kzalloc(p->dev, sizeof(*up->dma), GFP_KERNEL);
+ if (!up->dma)
+ return -ENOMEM;
+
+ up->dma->rxconf.src_maxburst = p->fifosize / 4;
+ up->dma->txconf.dst_maxburst = p->fifosize / 4;
return 0;
}
#else
-static inline int dw8250_probe_acpi(struct uart_port *p)
+static inline int dw8250_probe_acpi(struct uart_8250_port *up)
{
return -ENODEV;
}
@@ -266,7 +221,11 @@ static void dw8250_setup_port(struct uart_8250_port *up)
p->flags |= UPF_FIXED_TYPE;
p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
up->tx_loadsz = p->fifosize;
+ up->capabilities = UART_CAP_FIFO;
}
+
+ if (reg & DW_UART_CPR_AFCE_MODE)
+ up->capabilities |= UART_CAP_AFE;
}
static int dw8250_probe(struct platform_device *pdev)
@@ -286,17 +245,30 @@ static int dw8250_probe(struct platform_device *pdev)
uart.port.mapbase = regs->start;
uart.port.irq = irq->start;
uart.port.handle_irq = dw8250_handle_irq;
+ uart.port.pm = dw8250_do_pm;
uart.port.type = PORT_8250;
uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT;
uart.port.dev = &pdev->dev;
- uart.port.membase = ioremap(regs->start, resource_size(regs));
+ uart.port.membase = devm_ioremap(&pdev->dev, regs->start,
+ resource_size(regs));
if (!uart.port.membase)
return -ENOMEM;
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->clk = devm_clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(data->clk)) {
+ clk_prepare_enable(data->clk);
+ uart.port.uartclk = clk_get_rate(data->clk);
+ }
+
uart.port.iotype = UPIO_MEM;
uart.port.serial_in = dw8250_serial_in;
uart.port.serial_out = dw8250_serial_out;
+ uart.port.private_data = data;
dw8250_setup_port(&uart);
@@ -305,25 +277,22 @@ static int dw8250_probe(struct platform_device *pdev)
if (err)
return err;
} else if (ACPI_HANDLE(&pdev->dev)) {
- err = dw8250_probe_acpi(&uart.port);
+ err = dw8250_probe_acpi(&uart);
if (err)
return err;
} else {
return -ENODEV;
}
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- uart.port.private_data = data;
-
data->line = serial8250_register_8250_port(&uart);
if (data->line < 0)
return data->line;
platform_set_drvdata(pdev, data);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
return 0;
}
@@ -331,34 +300,64 @@ static int dw8250_remove(struct platform_device *pdev)
{
struct dw8250_data *data = platform_get_drvdata(pdev);
+ pm_runtime_get_sync(&pdev->dev);
+
serial8250_unregister_port(data->line);
+ if (!IS_ERR(data->clk))
+ clk_disable_unprepare(data->clk);
+
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
return 0;
}
#ifdef CONFIG_PM
-static int dw8250_suspend(struct platform_device *pdev, pm_message_t state)
+static int dw8250_suspend(struct device *dev)
{
- struct dw8250_data *data = platform_get_drvdata(pdev);
+ struct dw8250_data *data = dev_get_drvdata(dev);
serial8250_suspend_port(data->line);
return 0;
}
-static int dw8250_resume(struct platform_device *pdev)
+static int dw8250_resume(struct device *dev)
{
- struct dw8250_data *data = platform_get_drvdata(pdev);
+ struct dw8250_data *data = dev_get_drvdata(dev);
serial8250_resume_port(data->line);
return 0;
}
-#else
-#define dw8250_suspend NULL
-#define dw8250_resume NULL
#endif /* CONFIG_PM */
+#ifdef CONFIG_PM_RUNTIME
+static int dw8250_runtime_suspend(struct device *dev)
+{
+ struct dw8250_data *data = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(data->clk);
+
+ return 0;
+}
+
+static int dw8250_runtime_resume(struct device *dev)
+{
+ struct dw8250_data *data = dev_get_drvdata(dev);
+
+ clk_prepare_enable(data->clk);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops dw8250_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dw8250_suspend, dw8250_resume)
+ SET_RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL)
+};
+
static const struct of_device_id dw8250_of_match[] = {
{ .compatible = "snps,dw-apb-uart" },
{ /* Sentinel */ }
@@ -366,8 +365,8 @@ static const struct of_device_id dw8250_of_match[] = {
MODULE_DEVICE_TABLE(of, dw8250_of_match);
static const struct acpi_device_id dw8250_acpi_match[] = {
- { "INT33C4", 100000000 },
- { "INT33C5", 100000000 },
+ { "INT33C4", 0 },
+ { "INT33C5", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
@@ -376,13 +375,12 @@ static struct platform_driver dw8250_platform_driver = {
.driver = {
.name = "dw-apb-uart",
.owner = THIS_MODULE,
+ .pm = &dw8250_pm_ops,
.of_match_table = dw8250_of_match,
.acpi_match_table = ACPI_PTR(dw8250_acpi_match),
},
.probe = dw8250_probe,
.remove = dw8250_remove,
- .suspend = dw8250_suspend,
- .resume = dw8250_resume,
};
module_platform_driver(dw8250_platform_driver);