xref: /openbmc/linux/drivers/tty/serial/8250/8250_em.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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 
1722886ee9SMagnus Damm #include "8250.h"
1822886ee9SMagnus Damm 
1922886ee9SMagnus Damm #define UART_DLL_EM 9
2022886ee9SMagnus Damm #define UART_DLM_EM 10
21*b22ea7dfSBiju Das #define UART_HCR0_EM 11
2222886ee9SMagnus Damm 
2359d6558fSBiju Das /*
2459d6558fSBiju Das  * A high value for UART_FCR_EM avoids overlapping with existing UART_*
2559d6558fSBiju Das  * register defines. UART_FCR_EM_HW is the real HW register offset.
2659d6558fSBiju Das  */
2759d6558fSBiju Das #define UART_FCR_EM 0x10003
2859d6558fSBiju Das #define UART_FCR_EM_HW 3
2959d6558fSBiju Das 
30*b22ea7dfSBiju Das #define UART_HCR0_EM_SW_RESET	BIT(7) /* SW Reset */
31*b22ea7dfSBiju Das 
3222886ee9SMagnus Damm struct serial8250_em_priv {
3322886ee9SMagnus Damm 	int line;
3422886ee9SMagnus Damm };
3522886ee9SMagnus Damm 
serial8250_em_serial_out_helper(struct uart_port * p,int offset,int value)36*b22ea7dfSBiju Das static void serial8250_em_serial_out_helper(struct uart_port *p, int offset,
37*b22ea7dfSBiju Das 					    int value)
3822886ee9SMagnus Damm {
3922886ee9SMagnus Damm 	switch (offset) {
4022886ee9SMagnus Damm 	case UART_TX: /* TX @ 0x00 */
4122886ee9SMagnus Damm 		writeb(value, p->membase);
4222886ee9SMagnus Damm 		break;
4322886ee9SMagnus Damm 	case UART_LCR: /* LCR @ 0x10 (+1) */
4422886ee9SMagnus Damm 	case UART_MCR: /* MCR @ 0x14 (+1) */
4522886ee9SMagnus Damm 	case UART_SCR: /* SCR @ 0x20 (+1) */
4622886ee9SMagnus Damm 		writel(value, p->membase + ((offset + 1) << 2));
4722886ee9SMagnus Damm 		break;
4859d6558fSBiju Das 	case UART_FCR_EM:
4959d6558fSBiju Das 		writel(value, p->membase + (UART_FCR_EM_HW << 2));
5059d6558fSBiju Das 		break;
5122886ee9SMagnus Damm 	case UART_IER: /* IER @ 0x04 */
5222886ee9SMagnus Damm 		value &= 0x0f; /* only 4 valid bits - not Xscale */
53df561f66SGustavo A. R. Silva 		fallthrough;
5422886ee9SMagnus Damm 	case UART_DLL_EM: /* DLL @ 0x24 (+9) */
5522886ee9SMagnus Damm 	case UART_DLM_EM: /* DLM @ 0x28 (+9) */
56*b22ea7dfSBiju Das 	case UART_HCR0_EM: /* HCR0 @ 0x2c */
5722886ee9SMagnus Damm 		writel(value, p->membase + (offset << 2));
5854769d86SBiju Das 		break;
5922886ee9SMagnus Damm 	}
6022886ee9SMagnus Damm }
6122886ee9SMagnus Damm 
serial8250_em_serial_in(struct uart_port * p,int offset)6222886ee9SMagnus Damm static unsigned int serial8250_em_serial_in(struct uart_port *p, int offset)
6322886ee9SMagnus Damm {
6422886ee9SMagnus Damm 	switch (offset) {
6522886ee9SMagnus Damm 	case UART_RX: /* RX @ 0x00 */
6622886ee9SMagnus Damm 		return readb(p->membase);
67*b22ea7dfSBiju Das 	case UART_LCR: /* LCR @ 0x10 (+1) */
6822886ee9SMagnus Damm 	case UART_MCR: /* MCR @ 0x14 (+1) */
6922886ee9SMagnus Damm 	case UART_LSR: /* LSR @ 0x18 (+1) */
7022886ee9SMagnus Damm 	case UART_MSR: /* MSR @ 0x1c (+1) */
7122886ee9SMagnus Damm 	case UART_SCR: /* SCR @ 0x20 (+1) */
7222886ee9SMagnus Damm 		return readl(p->membase + ((offset + 1) << 2));
7359d6558fSBiju Das 	case UART_FCR_EM:
7459d6558fSBiju Das 		return readl(p->membase + (UART_FCR_EM_HW << 2));
7522886ee9SMagnus Damm 	case UART_IER: /* IER @ 0x04 */
7622886ee9SMagnus Damm 	case UART_IIR: /* IIR @ 0x08 */
7722886ee9SMagnus Damm 	case UART_DLL_EM: /* DLL @ 0x24 (+9) */
7822886ee9SMagnus Damm 	case UART_DLM_EM: /* DLM @ 0x28 (+9) */
79*b22ea7dfSBiju Das 	case UART_HCR0_EM: /* HCR0 @ 0x2c */
8022886ee9SMagnus Damm 		return readl(p->membase + (offset << 2));
8122886ee9SMagnus Damm 	}
8222886ee9SMagnus Damm 	return 0;
8322886ee9SMagnus Damm }
8422886ee9SMagnus Damm 
serial8250_em_reg_update(struct uart_port * p,int off,int value)85*b22ea7dfSBiju Das static void serial8250_em_reg_update(struct uart_port *p, int off, int value)
86*b22ea7dfSBiju Das {
87*b22ea7dfSBiju Das 	unsigned int ier, fcr, lcr, mcr, hcr0;
88*b22ea7dfSBiju Das 
89*b22ea7dfSBiju Das 	ier = serial8250_em_serial_in(p, UART_IER);
90*b22ea7dfSBiju Das 	fcr = serial8250_em_serial_in(p, UART_FCR_EM);
91*b22ea7dfSBiju Das 	lcr = serial8250_em_serial_in(p, UART_LCR);
92*b22ea7dfSBiju Das 	mcr = serial8250_em_serial_in(p, UART_MCR);
93*b22ea7dfSBiju Das 	hcr0 = serial8250_em_serial_in(p, UART_HCR0_EM);
94*b22ea7dfSBiju Das 
95*b22ea7dfSBiju Das 	serial8250_em_serial_out_helper(p, UART_FCR_EM, fcr |
96*b22ea7dfSBiju Das 							UART_FCR_CLEAR_RCVR |
97*b22ea7dfSBiju Das 							UART_FCR_CLEAR_XMIT);
98*b22ea7dfSBiju Das 	serial8250_em_serial_out_helper(p, UART_HCR0_EM, hcr0 |
99*b22ea7dfSBiju Das 							 UART_HCR0_EM_SW_RESET);
100*b22ea7dfSBiju Das 	serial8250_em_serial_out_helper(p, UART_HCR0_EM, hcr0 &
101*b22ea7dfSBiju Das 							 ~UART_HCR0_EM_SW_RESET);
102*b22ea7dfSBiju Das 
103*b22ea7dfSBiju Das 	switch (off) {
104*b22ea7dfSBiju Das 	case UART_FCR_EM:
105*b22ea7dfSBiju Das 		fcr = value;
106*b22ea7dfSBiju Das 		break;
107*b22ea7dfSBiju Das 	case UART_LCR:
108*b22ea7dfSBiju Das 		lcr = value;
109*b22ea7dfSBiju Das 		break;
110*b22ea7dfSBiju Das 	case UART_MCR:
111*b22ea7dfSBiju Das 		mcr = value;
112*b22ea7dfSBiju Das 		break;
113*b22ea7dfSBiju Das 	}
114*b22ea7dfSBiju Das 
115*b22ea7dfSBiju Das 	serial8250_em_serial_out_helper(p, UART_IER, ier);
116*b22ea7dfSBiju Das 	serial8250_em_serial_out_helper(p, UART_FCR_EM, fcr);
117*b22ea7dfSBiju Das 	serial8250_em_serial_out_helper(p, UART_MCR, mcr);
118*b22ea7dfSBiju Das 	serial8250_em_serial_out_helper(p, UART_LCR, lcr);
119*b22ea7dfSBiju Das 	serial8250_em_serial_out_helper(p, UART_HCR0_EM, hcr0);
120*b22ea7dfSBiju Das }
121*b22ea7dfSBiju Das 
serial8250_em_serial_out(struct uart_port * p,int offset,int value)122*b22ea7dfSBiju Das static void serial8250_em_serial_out(struct uart_port *p, int offset, int value)
123*b22ea7dfSBiju Das {
124*b22ea7dfSBiju Das 	switch (offset) {
125*b22ea7dfSBiju Das 	case UART_TX:
126*b22ea7dfSBiju Das 	case UART_SCR:
127*b22ea7dfSBiju Das 	case UART_IER:
128*b22ea7dfSBiju Das 	case UART_DLL_EM:
129*b22ea7dfSBiju Das 	case UART_DLM_EM:
130*b22ea7dfSBiju Das 		serial8250_em_serial_out_helper(p, offset, value);
131*b22ea7dfSBiju Das 		break;
132*b22ea7dfSBiju Das 	case UART_FCR:
133*b22ea7dfSBiju Das 		serial8250_em_reg_update(p, UART_FCR_EM, value);
134*b22ea7dfSBiju Das 		break;
135*b22ea7dfSBiju Das 	case UART_LCR:
136*b22ea7dfSBiju Das 	case UART_MCR:
137*b22ea7dfSBiju Das 		serial8250_em_reg_update(p, offset, value);
138*b22ea7dfSBiju Das 		break;
139*b22ea7dfSBiju Das 	}
140*b22ea7dfSBiju Das }
141*b22ea7dfSBiju Das 
serial8250_em_serial_dl_read(struct uart_8250_port * up)14222886ee9SMagnus Damm static u32 serial8250_em_serial_dl_read(struct uart_8250_port *up)
14322886ee9SMagnus Damm {
14422886ee9SMagnus Damm 	return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8;
14522886ee9SMagnus Damm }
14622886ee9SMagnus Damm 
serial8250_em_serial_dl_write(struct uart_8250_port * up,u32 value)14722886ee9SMagnus Damm static void serial8250_em_serial_dl_write(struct uart_8250_port *up, u32 value)
14822886ee9SMagnus Damm {
14922886ee9SMagnus Damm 	serial_out(up, UART_DLL_EM, value & 0xff);
15022886ee9SMagnus Damm 	serial_out(up, UART_DLM_EM, value >> 8 & 0xff);
15122886ee9SMagnus Damm }
15222886ee9SMagnus Damm 
serial8250_em_probe(struct platform_device * pdev)1539671f099SBill Pemberton static int serial8250_em_probe(struct platform_device *pdev)
15422886ee9SMagnus Damm {
15522886ee9SMagnus Damm 	struct serial8250_em_priv *priv;
1566b5f1e2eSBiju Das 	struct device *dev = &pdev->dev;
15722886ee9SMagnus Damm 	struct uart_8250_port up;
1582a1dbd25SAndy Shevchenko 	struct resource *regs;
1597eada8a1SBiju Das 	struct clk *sclk;
1602a1dbd25SAndy Shevchenko 	int irq, ret;
16122886ee9SMagnus Damm 
1622a1dbd25SAndy Shevchenko 	irq = platform_get_irq(pdev, 0);
1632a1dbd25SAndy Shevchenko 	if (irq < 0)
1642a1dbd25SAndy Shevchenko 		return irq;
1652a1dbd25SAndy Shevchenko 
1662a1dbd25SAndy Shevchenko 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1676b5f1e2eSBiju Das 	if (!regs)
1686b5f1e2eSBiju Das 		return dev_err_probe(dev, -EINVAL, "missing registers\n");
16922886ee9SMagnus Damm 
1706b5f1e2eSBiju Das 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
17186b20dfeSPeter Hurley 	if (!priv)
172299a6257SLaurent Pinchart 		return -ENOMEM;
17322886ee9SMagnus Damm 
1747eada8a1SBiju Das 	sclk = devm_clk_get_enabled(dev, "sclk");
1757eada8a1SBiju Das 	if (IS_ERR(sclk))
1767eada8a1SBiju Das 		return dev_err_probe(dev, PTR_ERR(sclk), "unable to get clock\n");
17722886ee9SMagnus Damm 
17822886ee9SMagnus Damm 	memset(&up, 0, sizeof(up));
17922886ee9SMagnus Damm 	up.port.mapbase = regs->start;
1802a1dbd25SAndy Shevchenko 	up.port.irq = irq;
181302a22a4SBiju Das 	up.port.type = PORT_16750;
182302a22a4SBiju Das 	up.port.flags = UPF_FIXED_PORT | UPF_IOREMAP | UPF_FIXED_TYPE;
1836b5f1e2eSBiju Das 	up.port.dev = dev;
18422886ee9SMagnus Damm 	up.port.private_data = priv;
18522886ee9SMagnus Damm 
1867eada8a1SBiju Das 	up.port.uartclk = clk_get_rate(sclk);
18722886ee9SMagnus Damm 
18822886ee9SMagnus Damm 	up.port.iotype = UPIO_MEM32;
18922886ee9SMagnus Damm 	up.port.serial_in = serial8250_em_serial_in;
19022886ee9SMagnus Damm 	up.port.serial_out = serial8250_em_serial_out;
19122886ee9SMagnus Damm 	up.dl_read = serial8250_em_serial_dl_read;
19222886ee9SMagnus Damm 	up.dl_write = serial8250_em_serial_dl_write;
19322886ee9SMagnus Damm 
19422886ee9SMagnus Damm 	ret = serial8250_register_8250_port(&up);
1957eada8a1SBiju Das 	if (ret < 0)
1966b5f1e2eSBiju Das 		return dev_err_probe(dev, ret, "unable to register 8250 port\n");
19722886ee9SMagnus Damm 
19822886ee9SMagnus Damm 	priv->line = ret;
19922886ee9SMagnus Damm 	platform_set_drvdata(pdev, priv);
20022886ee9SMagnus Damm 	return 0;
20122886ee9SMagnus Damm }
20222886ee9SMagnus Damm 
serial8250_em_remove(struct platform_device * pdev)203ae8d8a14SBill Pemberton static int serial8250_em_remove(struct platform_device *pdev)
20422886ee9SMagnus Damm {
20522886ee9SMagnus Damm 	struct serial8250_em_priv *priv = platform_get_drvdata(pdev);
20622886ee9SMagnus Damm 
20722886ee9SMagnus Damm 	serial8250_unregister_port(priv->line);
20822886ee9SMagnus Damm 	return 0;
20922886ee9SMagnus Damm }
21022886ee9SMagnus Damm 
211512f82a0SBill Pemberton static const struct of_device_id serial8250_em_dt_ids[] = {
2123e62c413SMagnus Damm 	{ .compatible = "renesas,em-uart", },
2133e62c413SMagnus Damm 	{},
2143e62c413SMagnus Damm };
2153e62c413SMagnus Damm MODULE_DEVICE_TABLE(of, serial8250_em_dt_ids);
2163e62c413SMagnus Damm 
21722886ee9SMagnus Damm static struct platform_driver serial8250_em_platform_driver = {
21822886ee9SMagnus Damm 	.driver = {
21922886ee9SMagnus Damm 		.name		= "serial8250-em",
2203e62c413SMagnus Damm 		.of_match_table = serial8250_em_dt_ids,
22122886ee9SMagnus Damm 	},
22222886ee9SMagnus Damm 	.probe			= serial8250_em_probe,
2232d47b716SBill Pemberton 	.remove			= serial8250_em_remove,
22422886ee9SMagnus Damm };
22522886ee9SMagnus Damm 
22622886ee9SMagnus Damm module_platform_driver(serial8250_em_platform_driver);
22722886ee9SMagnus Damm 
22822886ee9SMagnus Damm MODULE_AUTHOR("Magnus Damm");
22922886ee9SMagnus Damm MODULE_DESCRIPTION("Renesas Emma Mobile 8250 Driver");
23022886ee9SMagnus Damm MODULE_LICENSE("GPL v2");
231