brintos

brintos / linux-shallow public Read only

0
0
Text · 9.7 KiB · e14f47e Raw
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