xref: /linux/drivers/tty/serial/8250/8250_em.c (revision 17bb415fefedb74d2ff02656cf59e4e3ab92ee20)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
222886ee9SMagnus Damm /*
322886ee9SMagnus Damm  * Renesas Emma Mobile 8250 driver
422886ee9SMagnus Damm  *
522886ee9SMagnus Damm  *  Copyright (C) 2012 Magnus Damm
622886ee9SMagnus Damm  */
722886ee9SMagnus Damm 
822886ee9SMagnus Damm #include <linux/device.h>
922886ee9SMagnus Damm #include <linux/io.h>
1022886ee9SMagnus Damm #include <linux/module.h>
11ac316725SRandy Dunlap #include <linux/mod_devicetable.h>
1222886ee9SMagnus Damm #include <linux/serial_8250.h>
1322886ee9SMagnus Damm #include <linux/serial_reg.h>
1422886ee9SMagnus Damm #include <linux/platform_device.h>
1522886ee9SMagnus Damm #include <linux/clk.h>
1622886ee9SMagnus Damm #include <linux/slab.h>
1722886ee9SMagnus Damm 
1822886ee9SMagnus Damm #include "8250.h"
1922886ee9SMagnus Damm 
2022886ee9SMagnus Damm #define UART_DLL_EM 9
2122886ee9SMagnus Damm #define UART_DLM_EM 10
2222886ee9SMagnus Damm 
2322886ee9SMagnus Damm struct serial8250_em_priv {
2422886ee9SMagnus Damm 	struct clk *sclk;
2522886ee9SMagnus Damm 	int line;
2622886ee9SMagnus Damm };
2722886ee9SMagnus Damm 
2822886ee9SMagnus Damm static void serial8250_em_serial_out(struct uart_port *p, int offset, int value)
2922886ee9SMagnus Damm {
3022886ee9SMagnus Damm 	switch (offset) {
3122886ee9SMagnus Damm 	case UART_TX: /* TX @ 0x00 */
3222886ee9SMagnus Damm 		writeb(value, p->membase);
3322886ee9SMagnus Damm 		break;
3422886ee9SMagnus Damm 	case UART_FCR: /* FCR @ 0x0c (+1) */
3522886ee9SMagnus Damm 	case UART_LCR: /* LCR @ 0x10 (+1) */
3622886ee9SMagnus Damm 	case UART_MCR: /* MCR @ 0x14 (+1) */
3722886ee9SMagnus Damm 	case UART_SCR: /* SCR @ 0x20 (+1) */
3822886ee9SMagnus Damm 		writel(value, p->membase + ((offset + 1) << 2));
3922886ee9SMagnus Damm 		break;
4022886ee9SMagnus Damm 	case UART_IER: /* IER @ 0x04 */
4122886ee9SMagnus Damm 		value &= 0x0f; /* only 4 valid bits - not Xscale */
42*df561f66SGustavo A. R. Silva 		fallthrough;
4322886ee9SMagnus Damm 	case UART_DLL_EM: /* DLL @ 0x24 (+9) */
4422886ee9SMagnus Damm 	case UART_DLM_EM: /* DLM @ 0x28 (+9) */
4522886ee9SMagnus Damm 		writel(value, p->membase + (offset << 2));
4622886ee9SMagnus Damm 	}
4722886ee9SMagnus Damm }
4822886ee9SMagnus Damm 
4922886ee9SMagnus Damm static unsigned int serial8250_em_serial_in(struct uart_port *p, int offset)
5022886ee9SMagnus Damm {
5122886ee9SMagnus Damm 	switch (offset) {
5222886ee9SMagnus Damm 	case UART_RX: /* RX @ 0x00 */
5322886ee9SMagnus Damm 		return readb(p->membase);
5422886ee9SMagnus Damm 	case UART_MCR: /* MCR @ 0x14 (+1) */
5522886ee9SMagnus Damm 	case UART_LSR: /* LSR @ 0x18 (+1) */
5622886ee9SMagnus Damm 	case UART_MSR: /* MSR @ 0x1c (+1) */
5722886ee9SMagnus Damm 	case UART_SCR: /* SCR @ 0x20 (+1) */
5822886ee9SMagnus Damm 		return readl(p->membase + ((offset + 1) << 2));
5922886ee9SMagnus Damm 	case UART_IER: /* IER @ 0x04 */
6022886ee9SMagnus Damm 	case UART_IIR: /* IIR @ 0x08 */
6122886ee9SMagnus Damm 	case UART_DLL_EM: /* DLL @ 0x24 (+9) */
6222886ee9SMagnus Damm 	case UART_DLM_EM: /* DLM @ 0x28 (+9) */
6322886ee9SMagnus Damm 		return readl(p->membase + (offset << 2));
6422886ee9SMagnus Damm 	}
6522886ee9SMagnus Damm 	return 0;
6622886ee9SMagnus Damm }
6722886ee9SMagnus Damm 
6822886ee9SMagnus Damm static int serial8250_em_serial_dl_read(struct uart_8250_port *up)
6922886ee9SMagnus Damm {
7022886ee9SMagnus Damm 	return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8;
7122886ee9SMagnus Damm }
7222886ee9SMagnus Damm 
7322886ee9SMagnus Damm static void serial8250_em_serial_dl_write(struct uart_8250_port *up, int value)
7422886ee9SMagnus Damm {
7522886ee9SMagnus Damm 	serial_out(up, UART_DLL_EM, value & 0xff);
7622886ee9SMagnus Damm 	serial_out(up, UART_DLM_EM, value >> 8 & 0xff);
7722886ee9SMagnus Damm }
7822886ee9SMagnus Damm 
799671f099SBill Pemberton static int serial8250_em_probe(struct platform_device *pdev)
8022886ee9SMagnus Damm {
8122886ee9SMagnus Damm 	struct serial8250_em_priv *priv;
8222886ee9SMagnus Damm 	struct uart_8250_port up;
832a1dbd25SAndy Shevchenko 	struct resource *regs;
842a1dbd25SAndy Shevchenko 	int irq, ret;
8522886ee9SMagnus Damm 
862a1dbd25SAndy Shevchenko 	irq = platform_get_irq(pdev, 0);
872a1dbd25SAndy Shevchenko 	if (irq < 0)
882a1dbd25SAndy Shevchenko 		return irq;
892a1dbd25SAndy Shevchenko 
902a1dbd25SAndy Shevchenko 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
912a1dbd25SAndy Shevchenko 	if (!regs) {
922a1dbd25SAndy Shevchenko 		dev_err(&pdev->dev, "missing registers\n");
93299a6257SLaurent Pinchart 		return -EINVAL;
9422886ee9SMagnus Damm 	}
9522886ee9SMagnus Damm 
96299a6257SLaurent Pinchart 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
9786b20dfeSPeter Hurley 	if (!priv)
98299a6257SLaurent Pinchart 		return -ENOMEM;
9922886ee9SMagnus Damm 
100299a6257SLaurent Pinchart 	priv->sclk = devm_clk_get(&pdev->dev, "sclk");
10194e792abSMagnus Damm 	if (IS_ERR(priv->sclk)) {
10222886ee9SMagnus Damm 		dev_err(&pdev->dev, "unable to get clock\n");
103299a6257SLaurent Pinchart 		return PTR_ERR(priv->sclk);
10422886ee9SMagnus Damm 	}
10522886ee9SMagnus Damm 
10622886ee9SMagnus Damm 	memset(&up, 0, sizeof(up));
10722886ee9SMagnus Damm 	up.port.mapbase = regs->start;
1082a1dbd25SAndy Shevchenko 	up.port.irq = irq;
10922886ee9SMagnus Damm 	up.port.type = PORT_UNKNOWN;
11022886ee9SMagnus Damm 	up.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_IOREMAP;
11122886ee9SMagnus Damm 	up.port.dev = &pdev->dev;
11222886ee9SMagnus Damm 	up.port.private_data = priv;
11322886ee9SMagnus Damm 
11412082ba2SShinya Kuribayashi 	clk_prepare_enable(priv->sclk);
11522886ee9SMagnus Damm 	up.port.uartclk = clk_get_rate(priv->sclk);
11622886ee9SMagnus Damm 
11722886ee9SMagnus Damm 	up.port.iotype = UPIO_MEM32;
11822886ee9SMagnus Damm 	up.port.serial_in = serial8250_em_serial_in;
11922886ee9SMagnus Damm 	up.port.serial_out = serial8250_em_serial_out;
12022886ee9SMagnus Damm 	up.dl_read = serial8250_em_serial_dl_read;
12122886ee9SMagnus Damm 	up.dl_write = serial8250_em_serial_dl_write;
12222886ee9SMagnus Damm 
12322886ee9SMagnus Damm 	ret = serial8250_register_8250_port(&up);
12422886ee9SMagnus Damm 	if (ret < 0) {
12522886ee9SMagnus Damm 		dev_err(&pdev->dev, "unable to register 8250 port\n");
12612082ba2SShinya Kuribayashi 		clk_disable_unprepare(priv->sclk);
127299a6257SLaurent Pinchart 		return ret;
12822886ee9SMagnus Damm 	}
12922886ee9SMagnus Damm 
13022886ee9SMagnus Damm 	priv->line = ret;
13122886ee9SMagnus Damm 	platform_set_drvdata(pdev, priv);
13222886ee9SMagnus Damm 	return 0;
13322886ee9SMagnus Damm }
13422886ee9SMagnus Damm 
135ae8d8a14SBill Pemberton static int serial8250_em_remove(struct platform_device *pdev)
13622886ee9SMagnus Damm {
13722886ee9SMagnus Damm 	struct serial8250_em_priv *priv = platform_get_drvdata(pdev);
13822886ee9SMagnus Damm 
13922886ee9SMagnus Damm 	serial8250_unregister_port(priv->line);
14012082ba2SShinya Kuribayashi 	clk_disable_unprepare(priv->sclk);
14122886ee9SMagnus Damm 	return 0;
14222886ee9SMagnus Damm }
14322886ee9SMagnus Damm 
144512f82a0SBill Pemberton static const struct of_device_id serial8250_em_dt_ids[] = {
1453e62c413SMagnus Damm 	{ .compatible = "renesas,em-uart", },
1463e62c413SMagnus Damm 	{},
1473e62c413SMagnus Damm };
1483e62c413SMagnus Damm MODULE_DEVICE_TABLE(of, serial8250_em_dt_ids);
1493e62c413SMagnus Damm 
15022886ee9SMagnus Damm static struct platform_driver serial8250_em_platform_driver = {
15122886ee9SMagnus Damm 	.driver = {
15222886ee9SMagnus Damm 		.name		= "serial8250-em",
1533e62c413SMagnus Damm 		.of_match_table = serial8250_em_dt_ids,
15422886ee9SMagnus Damm 	},
15522886ee9SMagnus Damm 	.probe			= serial8250_em_probe,
1562d47b716SBill Pemberton 	.remove			= serial8250_em_remove,
15722886ee9SMagnus Damm };
15822886ee9SMagnus Damm 
15922886ee9SMagnus Damm module_platform_driver(serial8250_em_platform_driver);
16022886ee9SMagnus Damm 
16122886ee9SMagnus Damm MODULE_AUTHOR("Magnus Damm");
16222886ee9SMagnus Damm MODULE_DESCRIPTION("Renesas Emma Mobile 8250 Driver");
16322886ee9SMagnus Damm MODULE_LICENSE("GPL v2");
164