diff options
Diffstat (limited to 'drivers/tty/serial/uartlite.c')
-rw-r--r-- | drivers/tty/serial/uartlite.c | 112 |
1 files changed, 103 insertions, 9 deletions
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index c47db7826189..98d3eadd2fd0 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -21,6 +21,7 @@ #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/of_platform.h> +#include <linux/clk.h> #define ULITE_NAME "ttyUL" #define ULITE_MAJOR 204 @@ -54,6 +55,11 @@ #define ULITE_CONTROL_RST_RX 0x02 #define ULITE_CONTROL_IE 0x10 +struct uartlite_data { + const struct uartlite_reg_ops *reg_ops; + struct clk *clk; +}; + struct uartlite_reg_ops { u32 (*in)(void __iomem *addr); void (*out)(u32 val, void __iomem *addr); @@ -91,16 +97,16 @@ static const struct uartlite_reg_ops uartlite_le = { static inline u32 uart_in32(u32 offset, struct uart_port *port) { - const struct uartlite_reg_ops *reg_ops = port->private_data; + struct uartlite_data *pdata = port->private_data; - return reg_ops->in(port->membase + offset); + return pdata->reg_ops->in(port->membase + offset); } static inline void uart_out32(u32 val, u32 offset, struct uart_port *port) { - const struct uartlite_reg_ops *reg_ops = port->private_data; + struct uartlite_data *pdata = port->private_data; - reg_ops->out(val, port->membase + offset); + pdata->reg_ops->out(val, port->membase + offset); } static struct uart_port ulite_ports[ULITE_NR_UARTS]; @@ -257,8 +263,15 @@ static void ulite_break_ctl(struct uart_port *port, int ctl) static int ulite_startup(struct uart_port *port) { + struct uartlite_data *pdata = port->private_data; int ret; + ret = clk_enable(pdata->clk); + if (ret) { + dev_err(port->dev, "Failed to enable clock\n"); + return ret; + } + ret = request_irq(port->irq, ulite_isr, IRQF_SHARED | IRQF_TRIGGER_RISING, "uartlite", port); if (ret) @@ -273,9 +286,12 @@ static int ulite_startup(struct uart_port *port) static void ulite_shutdown(struct uart_port *port) { + struct uartlite_data *pdata = port->private_data; + uart_out32(0, ULITE_CONTROL, port); uart_in32(ULITE_CONTROL, port); /* dummy */ free_irq(port->irq, port); + clk_disable(pdata->clk); } static void ulite_set_termios(struct uart_port *port, struct ktermios *termios, @@ -325,6 +341,7 @@ static void ulite_release_port(struct uart_port *port) static int ulite_request_port(struct uart_port *port) { + struct uartlite_data *pdata = port->private_data; int ret; pr_debug("ulite console: port=%p; port->mapbase=%llx\n", @@ -342,13 +359,13 @@ static int ulite_request_port(struct uart_port *port) return -EBUSY; } - port->private_data = (void *)&uartlite_be; + pdata->reg_ops = &uartlite_be; ret = uart_in32(ULITE_CONTROL, port); uart_out32(ULITE_CONTROL_RST_TX, ULITE_CONTROL, port); ret = uart_in32(ULITE_STATUS, port); /* Endianess detection */ if ((ret & ULITE_STATUS_TXEMPTY) != ULITE_STATUS_TXEMPTY) - port->private_data = (void *)&uartlite_le; + pdata->reg_ops = &uartlite_le; return 0; } @@ -365,6 +382,17 @@ static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser) return -EINVAL; } +static void ulite_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uartlite_data *pdata = port->private_data; + + if (!state) + clk_enable(pdata->clk); + else + clk_disable(pdata->clk); +} + #ifdef CONFIG_CONSOLE_POLL static int ulite_get_poll_char(struct uart_port *port) { @@ -400,6 +428,7 @@ static const struct uart_ops ulite_ops = { .request_port = ulite_request_port, .config_port = ulite_config_port, .verify_port = ulite_verify_port, + .pm = ulite_pm, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = ulite_get_poll_char, .poll_put_char = ulite_put_poll_char, @@ -585,10 +614,12 @@ static struct uart_driver ulite_uart_driver = { * @id: requested id number. Pass -1 for automatic port assignment * @base: base address of uartlite registers * @irq: irq number for uartlite + * @pdata: private data for uartlite * * Returns: 0 on success, <0 otherwise */ -static int ulite_assign(struct device *dev, int id, u32 base, int irq) +static int ulite_assign(struct device *dev, int id, u32 base, int irq, + struct uartlite_data *pdata) { struct uart_port *port; int rc; @@ -625,6 +656,7 @@ static int ulite_assign(struct device *dev, int id, u32 base, int irq) port->dev = dev; port->type = PORT_UNKNOWN; port->line = id; + port->private_data = pdata; dev_set_drvdata(dev, port); @@ -658,10 +690,44 @@ static int ulite_release(struct device *dev) return rc; } +/** + * ulite_suspend - Stop the device. + * + * @dev: handle to the device structure. + * Return: 0 always. + */ +static int __maybe_unused ulite_suspend(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + + if (port) + uart_suspend_port(&ulite_uart_driver, port); + + return 0; +} + +/** + * ulite_resume - Resume the device. + * + * @dev: handle to the device structure. + * Return: 0 on success, errno otherwise. + */ +static int __maybe_unused ulite_resume(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + + if (port) + uart_resume_port(&ulite_uart_driver, port); + + return 0; +} + /* --------------------------------------------------------------------- * Platform bus binding */ +static SIMPLE_DEV_PM_OPS(ulite_pm_ops, ulite_suspend, ulite_resume); + #if defined(CONFIG_OF) /* Match table for of_platform binding */ static const struct of_device_id ulite_of_match[] = { @@ -675,7 +741,8 @@ MODULE_DEVICE_TABLE(of, ulite_of_match); static int ulite_probe(struct platform_device *pdev) { struct resource *res; - int irq; + struct uartlite_data *pdata; + int irq, ret; int id = pdev->id; #ifdef CONFIG_OF const __be32 *prop; @@ -684,6 +751,10 @@ static int ulite_probe(struct platform_device *pdev) if (prop) id = be32_to_cpup(prop); #endif + pdata = devm_kzalloc(&pdev->dev, sizeof(struct uartlite_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -693,11 +764,33 @@ static int ulite_probe(struct platform_device *pdev) if (irq <= 0) return -ENXIO; - return ulite_assign(&pdev->dev, id, res->start, irq); + pdata->clk = devm_clk_get(&pdev->dev, "s_axi_aclk"); + if (IS_ERR(pdata->clk)) { + if (PTR_ERR(pdata->clk) != -ENOENT) + return PTR_ERR(pdata->clk); + + /* + * Clock framework support is optional, continue on + * anyways if we don't find a matching clock. + */ + pdata->clk = NULL; + } + + ret = clk_prepare(pdata->clk); + if (ret) { + dev_err(&pdev->dev, "Failed to prepare clock\n"); + return ret; + } + + return ulite_assign(&pdev->dev, id, res->start, irq, pdata); } static int ulite_remove(struct platform_device *pdev) { + struct uart_port *port = dev_get_drvdata(&pdev->dev); + struct uartlite_data *pdata = port->private_data; + + clk_disable_unprepare(pdata->clk); return ulite_release(&pdev->dev); } @@ -710,6 +803,7 @@ static struct platform_driver ulite_platform_driver = { .driver = { .name = "uartlite", .of_match_table = of_match_ptr(ulite_of_match), + .pm = &ulite_pm_ops, }, }; |