Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | // SPDX-License-Identifier: GPL-2.0+ /* * drivers/tty/serial/8250/8250_pxa.c -- driver for PXA on-board UARTS * Copyright: (C) 2013 Sergei Ianovich <ynvich@gmail.com> * * replaces drivers/serial/pxa.c by Nicolas Pitre * Created: Feb 20, 2003 * Copyright: (C) 2003 Monta Vista Software, Inc. * * Based on drivers/serial/8250.c by Russell King. */ #include <linux/device.h> #include <linux/init.h> #include <linux/io.h> #include <linux/module.h> #include <linux/serial_8250.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/clk.h> #include <linux/pm_runtime.h> #include "8250.h" struct pxa8250_data { int line; struct clk *clk; }; static int __maybe_unused serial_pxa_suspend(struct device *dev) { struct pxa8250_data *data = dev_get_drvdata(dev); serial8250_suspend_port(data->line); return 0; } static int __maybe_unused serial_pxa_resume(struct device *dev) { struct pxa8250_data *data = dev_get_drvdata(dev); serial8250_resume_port(data->line); return 0; } static const struct dev_pm_ops serial_pxa_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(serial_pxa_suspend, serial_pxa_resume) }; static const struct of_device_id serial_pxa_dt_ids[] = { { .compatible = "mrvl,pxa-uart", }, { .compatible = "mrvl,mmp-uart", }, {} }; MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids); /* Uart divisor latch write */ static void serial_pxa_dl_write(struct uart_8250_port *up, int value) { unsigned int dll; serial_out(up, UART_DLL, value & 0xff); /* * work around Erratum #74 according to Marvel(R) PXA270M Processor * Specification Update (April 19, 2010) */ dll = serial_in(up, UART_DLL); WARN_ON(dll != (value & 0xff)); serial_out(up, UART_DLM, value >> 8 & 0xff); } static void serial_pxa_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { struct pxa8250_data *data = port->private_data; if (!state) clk_prepare_enable(data->clk); else clk_disable_unprepare(data->clk); } static int serial_pxa_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}; struct pxa8250_data *data; struct resource *mmres; int irq, ret; irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mmres) return -ENODEV; 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)) return PTR_ERR(data->clk); ret = clk_prepare(data->clk); if (ret) return ret; ret = of_alias_get_id(pdev->dev.of_node, "serial"); if (ret >= 0) uart.port.line = ret; uart.port.type = PORT_XSCALE; uart.port.iotype = UPIO_MEM32; uart.port.mapbase = mmres->start; uart.port.regshift = 2; uart.port.irq = irq; uart.port.fifosize = 64; uart.port.flags = UPF_IOREMAP | UPF_SKIP_TEST | UPF_FIXED_TYPE; uart.port.dev = &pdev->dev; uart.port.uartclk = clk_get_rate(data->clk); uart.port.pm = serial_pxa_pm; uart.port.private_data = data; uart.dl_write = serial_pxa_dl_write; ret = serial8250_register_8250_port(&uart); if (ret < 0) goto err_clk; data->line = ret; platform_set_drvdata(pdev, data); return 0; err_clk: clk_unprepare(data->clk); return ret; } static int serial_pxa_remove(struct platform_device *pdev) { struct pxa8250_data *data = platform_get_drvdata(pdev); serial8250_unregister_port(data->line); clk_unprepare(data->clk); return 0; } static struct platform_driver serial_pxa_driver = { .probe = serial_pxa_probe, .remove = serial_pxa_remove, .driver = { .name = "pxa2xx-uart", .pm = &serial_pxa_pm_ops, .of_match_table = serial_pxa_dt_ids, }, }; module_platform_driver(serial_pxa_driver); #ifdef CONFIG_SERIAL_8250_CONSOLE static int __init early_serial_pxa_setup(struct earlycon_device *device, const char *options) { struct uart_port *port = &device->port; if (!(device->port.membase || device->port.iobase)) return -ENODEV; port->regshift = 2; return early_serial8250_setup(device, NULL); } OF_EARLYCON_DECLARE(early_pxa, "mrvl,pxa-uart", early_serial_pxa_setup); #endif MODULE_AUTHOR("Sergei Ianovich"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:pxa2xx-uart"); |