1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Serial Port driver for Loongson family chips
4 *
5 * Copyright (C) 2020-2025 Loongson Technology Corporation Limited
6 */
7
8 #include <linux/bitfield.h>
9 #include <linux/bits.h>
10 #include <linux/clk.h>
11 #include <linux/console.h>
12 #include <linux/module.h>
13 #include <linux/io.h>
14 #include <linux/property.h>
15 #include <linux/math.h>
16 #include <linux/pm.h>
17 #include <linux/reset.h>
18
19 #include "8250.h"
20
21 /* Divisor Latch Fraction Register */
22 #define LOONGSON_UART_DLF 0x2
23
24 #define LOONGSON_QUOT_FRAC_MASK GENMASK(7, 0)
25 #define LOONGSON_QUOT_DIV_MASK GENMASK(15, 8)
26
27 struct loongson_uart_ddata {
28 bool has_frac;
29 u8 mcr_invert;
30 u8 msr_invert;
31 };
32
33 static const struct loongson_uart_ddata ls2k0500_uart_data = {
34 .has_frac = false,
35 .mcr_invert = UART_MCR_RTS | UART_MCR_DTR,
36 .msr_invert = UART_MSR_CTS | UART_MSR_DSR,
37 };
38
39 static const struct loongson_uart_ddata ls2k1500_uart_data = {
40 .has_frac = true,
41 .mcr_invert = UART_MCR_RTS | UART_MCR_DTR,
42 .msr_invert = 0,
43 };
44
45 struct loongson_uart_priv {
46 int line;
47 struct clk *clk;
48 struct resource *res;
49 struct reset_control *rst;
50 const struct loongson_uart_ddata *ddata;
51 };
52
serial_fixup(struct uart_port * p,unsigned int offset,u8 val)53 static u8 serial_fixup(struct uart_port *p, unsigned int offset, u8 val)
54 {
55 struct loongson_uart_priv *priv = p->private_data;
56
57 switch (offset) {
58 case UART_MCR:
59 return val ^ priv->ddata->mcr_invert;
60 case UART_MSR:
61 return val ^ priv->ddata->msr_invert;
62 default:
63 return val;
64 }
65 }
66
loongson_serial_in(struct uart_port * p,unsigned int offset)67 static u32 loongson_serial_in(struct uart_port *p, unsigned int offset)
68 {
69 u8 val;
70
71 val = readb(p->membase + (offset << p->regshift));
72
73 return serial_fixup(p, offset, val);
74 }
75
loongson_serial_out(struct uart_port * p,unsigned int offset,unsigned int value)76 static void loongson_serial_out(struct uart_port *p, unsigned int offset, unsigned int value)
77 {
78 u8 val;
79
80 offset <<= p->regshift;
81 val = serial_fixup(p, offset, value);
82 writeb(val, p->membase + offset);
83 }
84
loongson_frac_get_divisor(struct uart_port * port,unsigned int baud,unsigned int * frac)85 static unsigned int loongson_frac_get_divisor(struct uart_port *port, unsigned int baud,
86 unsigned int *frac)
87 {
88 unsigned int quot;
89
90 quot = DIV_ROUND_CLOSEST((port->uartclk << 4), baud);
91 *frac = FIELD_GET(LOONGSON_QUOT_FRAC_MASK, quot);
92
93 return FIELD_GET(LOONGSON_QUOT_DIV_MASK, quot);
94 }
95
loongson_frac_set_divisor(struct uart_port * port,unsigned int baud,unsigned int quot,unsigned int quot_frac)96 static void loongson_frac_set_divisor(struct uart_port *port, unsigned int baud,
97 unsigned int quot, unsigned int quot_frac)
98 {
99 struct uart_8250_port *up = up_to_u8250p(port);
100
101 serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
102 serial_dl_write(up, quot);
103 serial_port_out(port, LOONGSON_UART_DLF, quot_frac);
104 }
105
loongson_uart_probe(struct platform_device * pdev)106 static int loongson_uart_probe(struct platform_device *pdev)
107 {
108 struct device *dev = &pdev->dev;
109 struct uart_8250_port uart = {};
110 struct loongson_uart_priv *priv;
111 struct uart_port *port;
112 int ret;
113
114 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
115 if (!priv)
116 return -ENOMEM;
117
118 priv->ddata = device_get_match_data(dev);
119
120 port = &uart.port;
121 spin_lock_init(&port->lock);
122 port->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_IOREMAP;
123 port->iotype = UPIO_MEM;
124 port->regshift = 0;
125 port->dev = dev;
126 port->type = PORT_16550A;
127 port->private_data = priv;
128
129 port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &priv->res);
130 if (IS_ERR(port->membase))
131 return PTR_ERR(port->membase);
132
133 port->mapbase = priv->res->start;
134 port->mapsize = resource_size(priv->res);
135 port->serial_in = loongson_serial_in;
136 port->serial_out = loongson_serial_out;
137
138 if (priv->ddata->has_frac) {
139 port->get_divisor = loongson_frac_get_divisor;
140 port->set_divisor = loongson_frac_set_divisor;
141 }
142
143 ret = uart_read_port_properties(port);
144 if (ret)
145 return ret;
146
147 if (!port->uartclk) {
148 priv->clk = devm_clk_get_enabled(dev, NULL);
149 if (IS_ERR(priv->clk))
150 return dev_err_probe(dev, PTR_ERR(priv->clk),
151 "Unable to determine clock frequency!\n");
152 port->uartclk = clk_get_rate(priv->clk);
153 }
154
155 priv->rst = devm_reset_control_get_optional_shared(dev, NULL);
156 if (IS_ERR(priv->rst))
157 return PTR_ERR(priv->rst);
158
159 ret = reset_control_deassert(priv->rst);
160 if (ret)
161 return ret;
162
163 ret = serial8250_register_8250_port(&uart);
164 if (ret < 0) {
165 reset_control_assert(priv->rst);
166 return ret;
167 }
168
169 priv->line = ret;
170 platform_set_drvdata(pdev, priv);
171
172 return 0;
173 }
174
loongson_uart_remove(struct platform_device * pdev)175 static void loongson_uart_remove(struct platform_device *pdev)
176 {
177 struct loongson_uart_priv *priv = platform_get_drvdata(pdev);
178
179 serial8250_unregister_port(priv->line);
180 reset_control_assert(priv->rst);
181 }
182
loongson_uart_suspend(struct device * dev)183 static int loongson_uart_suspend(struct device *dev)
184 {
185 struct loongson_uart_priv *priv = dev_get_drvdata(dev);
186 struct uart_8250_port *up = serial8250_get_port(priv->line);
187
188 serial8250_suspend_port(priv->line);
189
190 if (!uart_console(&up->port) || console_suspend_enabled)
191 clk_disable_unprepare(priv->clk);
192
193 return 0;
194 }
195
loongson_uart_resume(struct device * dev)196 static int loongson_uart_resume(struct device *dev)
197 {
198 struct loongson_uart_priv *priv = dev_get_drvdata(dev);
199 struct uart_8250_port *up = serial8250_get_port(priv->line);
200 int ret;
201
202 if (!uart_console(&up->port) || console_suspend_enabled) {
203 ret = clk_prepare_enable(priv->clk);
204 if (ret)
205 return ret;
206 }
207
208 serial8250_resume_port(priv->line);
209
210 return 0;
211 }
212
213 static DEFINE_SIMPLE_DEV_PM_OPS(loongson_uart_pm_ops, loongson_uart_suspend,
214 loongson_uart_resume);
215
216 static const struct of_device_id loongson_uart_of_ids[] = {
217 { .compatible = "loongson,ls2k0500-uart", .data = &ls2k0500_uart_data },
218 { .compatible = "loongson,ls2k1500-uart", .data = &ls2k1500_uart_data },
219 { },
220 };
221 MODULE_DEVICE_TABLE(of, loongson_uart_of_ids);
222
223 static struct platform_driver loongson_uart_driver = {
224 .probe = loongson_uart_probe,
225 .remove = loongson_uart_remove,
226 .driver = {
227 .name = "loongson-uart",
228 .pm = pm_ptr(&loongson_uart_pm_ops),
229 .of_match_table = loongson_uart_of_ids,
230 },
231 };
232
233 module_platform_driver(loongson_uart_driver);
234
235 MODULE_DESCRIPTION("Loongson UART driver");
236 MODULE_AUTHOR("Loongson Technology Corporation Limited.");
237 MODULE_LICENSE("GPL");
238