363 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Serial Port driver for Open Firmware platform devices4 *5 * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.6 */7 8#include <linux/bits.h>9#include <linux/console.h>10#include <linux/math.h>11#include <linux/module.h>12#include <linux/slab.h>13#include <linux/serial_core.h>14#include <linux/serial_reg.h>15#include <linux/of_address.h>16#include <linux/of_irq.h>17#include <linux/of_platform.h>18#include <linux/pm_runtime.h>19#include <linux/clk.h>20#include <linux/reset.h>21#include <linux/notifier.h>22 23#include "8250.h"24 25struct of_serial_info {26 struct clk *clk;27 struct reset_control *rst;28 int type;29 int line;30 struct notifier_block clk_notifier;31};32 33/* Nuvoton NPCM timeout register */34#define UART_NPCM_TOR 735#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */36 37static int npcm_startup(struct uart_port *port)38{39 /*40 * Nuvoton calls the scratch register 'UART_TOR' (timeout41 * register). Enable it, and set TIOC (timeout interrupt42 * comparator) to be 0x20 for correct operation.43 */44 serial_port_out(port, UART_NPCM_TOR, UART_NPCM_TOIE | 0x20);45 46 return serial8250_do_startup(port);47}48 49/* Nuvoton NPCM UARTs have a custom divisor calculation */50static unsigned int npcm_get_divisor(struct uart_port *port, unsigned int baud,51 unsigned int *frac)52{53 return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2;54}55 56static int npcm_setup(struct uart_port *port)57{58 port->get_divisor = npcm_get_divisor;59 port->startup = npcm_startup;60 return 0;61}62 63static inline struct of_serial_info *clk_nb_to_info(struct notifier_block *nb)64{65 return container_of(nb, struct of_serial_info, clk_notifier);66}67 68static int of_platform_serial_clk_notifier_cb(struct notifier_block *nb, unsigned long event,69 void *data)70{71 struct of_serial_info *info = clk_nb_to_info(nb);72 struct uart_8250_port *port8250 = serial8250_get_port(info->line);73 struct clk_notifier_data *ndata = data;74 75 if (event == POST_RATE_CHANGE) {76 serial8250_update_uartclk(&port8250->port, ndata->new_rate);77 return NOTIFY_OK;78 }79 80 return NOTIFY_DONE;81}82 83/*84 * Fill a struct uart_port for a given device node85 */86static int of_platform_serial_setup(struct platform_device *ofdev,87 int type, struct uart_8250_port *up,88 struct of_serial_info *info)89{90 struct resource resource;91 struct device *dev = &ofdev->dev;92 struct device_node *np = dev->of_node;93 struct uart_port *port = &up->port;94 u32 spd;95 int ret;96 97 memset(port, 0, sizeof *port);98 99 pm_runtime_enable(&ofdev->dev);100 pm_runtime_get_sync(&ofdev->dev);101 102 ret = of_address_to_resource(np, 0, &resource);103 if (ret) {104 dev_err_probe(dev, ret, "invalid address\n");105 goto err_pmruntime;106 }107 108 port->dev = &ofdev->dev;109 port->flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE;110 spin_lock_init(&port->lock);111 112 if (resource_type(&resource) == IORESOURCE_IO) {113 port->iotype = UPIO_PORT;114 port->iobase = resource.start;115 } else {116 port->mapbase = resource.start;117 port->mapsize = resource_size(&resource);118 port->flags |= UPF_IOREMAP;119 }120 121 ret = uart_read_and_validate_port_properties(port);122 if (ret)123 goto err_pmruntime;124 125 /* Get clk rate through clk driver if present */126 if (!port->uartclk) {127 info->clk = devm_clk_get_enabled(dev, NULL);128 if (IS_ERR(info->clk)) {129 ret = dev_err_probe(dev, PTR_ERR(info->clk), "failed to get clock\n");130 goto err_pmruntime;131 }132 133 port->uartclk = clk_get_rate(info->clk);134 }135 /* If current-speed was set, then try not to change it. */136 if (of_property_read_u32(np, "current-speed", &spd) == 0)137 port->custom_divisor = port->uartclk / (16 * spd);138 139 /* Compatibility with the deprecated pxa driver and 8250_pxa drivers. */140 if (of_device_is_compatible(np, "mrvl,mmp-uart"))141 port->regshift = 2;142 143 info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL);144 if (IS_ERR(info->rst)) {145 ret = PTR_ERR(info->rst);146 goto err_pmruntime;147 }148 149 ret = reset_control_deassert(info->rst);150 if (ret)151 goto err_pmruntime;152 153 port->type = type;154 port->rs485_config = serial8250_em485_config;155 port->rs485_supported = serial8250_em485_supported;156 up->rs485_start_tx = serial8250_em485_start_tx;157 up->rs485_stop_tx = serial8250_em485_stop_tx;158 159 switch (type) {160 case PORT_RT2880:161 ret = rt288x_setup(port);162 break;163 case PORT_NPCM:164 ret = npcm_setup(port);165 break;166 default:167 /* Nothing to do */168 ret = 0;169 break;170 }171 if (ret)172 goto err_pmruntime;173 174 if (IS_REACHABLE(CONFIG_SERIAL_8250_FSL) &&175 (of_device_is_compatible(np, "fsl,ns16550") ||176 of_device_is_compatible(np, "fsl,16550-FIFO64"))) {177 port->handle_irq = fsl8250_handle_irq;178 port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);179 }180 181 return 0;182err_pmruntime:183 pm_runtime_put_sync(&ofdev->dev);184 pm_runtime_disable(&ofdev->dev);185 return ret;186}187 188/*189 * Try to register a serial port190 */191static int of_platform_serial_probe(struct platform_device *ofdev)192{193 struct of_serial_info *info;194 struct uart_8250_port port8250;195 unsigned int port_type;196 u32 tx_threshold;197 int ret;198 199 if (IS_ENABLED(CONFIG_SERIAL_8250_BCM7271) &&200 of_device_is_compatible(ofdev->dev.of_node, "brcm,bcm7271-uart"))201 return -ENODEV;202 203 port_type = (unsigned long)of_device_get_match_data(&ofdev->dev);204 if (port_type == PORT_UNKNOWN)205 return -EINVAL;206 207 if (of_property_read_bool(ofdev->dev.of_node, "used-by-rtas"))208 return -EBUSY;209 210 info = kzalloc(sizeof(*info), GFP_KERNEL);211 if (info == NULL)212 return -ENOMEM;213 214 memset(&port8250, 0, sizeof(port8250));215 ret = of_platform_serial_setup(ofdev, port_type, &port8250, info);216 if (ret)217 goto err_free;218 219 if (port8250.port.fifosize)220 port8250.capabilities = UART_CAP_FIFO;221 222 /* Check for TX FIFO threshold & set tx_loadsz */223 if ((of_property_read_u32(ofdev->dev.of_node, "tx-threshold",224 &tx_threshold) == 0) &&225 (tx_threshold < port8250.port.fifosize))226 port8250.tx_loadsz = port8250.port.fifosize - tx_threshold;227 228 if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control"))229 port8250.capabilities |= UART_CAP_AFE;230 231 if (of_property_read_u32(ofdev->dev.of_node,232 "overrun-throttle-ms",233 &port8250.overrun_backoff_time_ms) != 0)234 port8250.overrun_backoff_time_ms = 0;235 236 ret = serial8250_register_8250_port(&port8250);237 if (ret < 0)238 goto err_dispose;239 240 info->type = port_type;241 info->line = ret;242 platform_set_drvdata(ofdev, info);243 244 if (info->clk) {245 info->clk_notifier.notifier_call = of_platform_serial_clk_notifier_cb;246 ret = clk_notifier_register(info->clk, &info->clk_notifier);247 if (ret) {248 dev_err_probe(port8250.port.dev, ret, "Failed to set the clock notifier\n");249 goto err_unregister;250 }251 }252 253 return 0;254err_unregister:255 serial8250_unregister_port(info->line);256err_dispose:257 pm_runtime_put_sync(&ofdev->dev);258 pm_runtime_disable(&ofdev->dev);259err_free:260 kfree(info);261 return ret;262}263 264/*265 * Release a line266 */267static void of_platform_serial_remove(struct platform_device *ofdev)268{269 struct of_serial_info *info = platform_get_drvdata(ofdev);270 271 if (info->clk)272 clk_notifier_unregister(info->clk, &info->clk_notifier);273 274 serial8250_unregister_port(info->line);275 276 reset_control_assert(info->rst);277 pm_runtime_put_sync(&ofdev->dev);278 pm_runtime_disable(&ofdev->dev);279 kfree(info);280}281 282#ifdef CONFIG_PM_SLEEP283static int of_serial_suspend(struct device *dev)284{285 struct of_serial_info *info = dev_get_drvdata(dev);286 struct uart_8250_port *port8250 = serial8250_get_port(info->line);287 struct uart_port *port = &port8250->port;288 289 serial8250_suspend_port(info->line);290 291 if (!uart_console(port) || console_suspend_enabled) {292 pm_runtime_put_sync(dev);293 clk_disable_unprepare(info->clk);294 }295 return 0;296}297 298static int of_serial_resume(struct device *dev)299{300 struct of_serial_info *info = dev_get_drvdata(dev);301 struct uart_8250_port *port8250 = serial8250_get_port(info->line);302 struct uart_port *port = &port8250->port;303 304 if (!uart_console(port) || console_suspend_enabled) {305 pm_runtime_get_sync(dev);306 clk_prepare_enable(info->clk);307 }308 309 serial8250_resume_port(info->line);310 311 return 0;312}313#endif314static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume);315 316/*317 * A few common types, add more as needed.318 */319static const struct of_device_id of_platform_serial_table[] = {320 { .compatible = "ns8250", .data = (void *)PORT_8250, },321 { .compatible = "ns16450", .data = (void *)PORT_16450, },322 { .compatible = "ns16550a", .data = (void *)PORT_16550A, },323 { .compatible = "ns16550", .data = (void *)PORT_16550, },324 { .compatible = "ns16750", .data = (void *)PORT_16750, },325 { .compatible = "ns16850", .data = (void *)PORT_16850, },326 { .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, },327 { .compatible = "ralink,rt2880-uart", .data = (void *)PORT_RT2880, },328 { .compatible = "intel,xscale-uart", .data = (void *)PORT_XSCALE, },329 { .compatible = "altr,16550-FIFO32",330 .data = (void *)PORT_ALTR_16550_F32, },331 { .compatible = "altr,16550-FIFO64",332 .data = (void *)PORT_ALTR_16550_F64, },333 { .compatible = "altr,16550-FIFO128",334 .data = (void *)PORT_ALTR_16550_F128, },335 { .compatible = "fsl,16550-FIFO64",336 .data = (void *)PORT_16550A_FSL64, },337 { .compatible = "mediatek,mtk-btif",338 .data = (void *)PORT_MTK_BTIF, },339 { .compatible = "mrvl,mmp-uart",340 .data = (void *)PORT_XSCALE, },341 { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, },342 { .compatible = "nuvoton,wpcm450-uart", .data = (void *)PORT_NPCM, },343 { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, },344 { /* end of list */ },345};346MODULE_DEVICE_TABLE(of, of_platform_serial_table);347 348static struct platform_driver of_platform_serial_driver = {349 .driver = {350 .name = "of_serial",351 .of_match_table = of_platform_serial_table,352 .pm = &of_serial_pm_ops,353 },354 .probe = of_platform_serial_probe,355 .remove_new = of_platform_serial_remove,356};357 358module_platform_driver(of_platform_serial_driver);359 360MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>");361MODULE_LICENSE("GPL");362MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices");363