1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2010 Lars-Peter Clausen <lars@metafoo.de>
4 * Copyright (C) 2015 Imagination Technologies
6 * Ingenic SoC UART support
10 #include <linux/console.h>
12 #include <linux/libfdt.h>
13 #include <linux/module.h>
15 #include <linux/of_fdt.h>
16 #include <linux/of_device.h>
17 #include <linux/platform_device.h>
18 #include <linux/serial_8250.h>
19 #include <linux/serial_core.h>
20 #include <linux/serial_reg.h>
24 /** ingenic_uart_config: SOC specific config data. */
25 struct ingenic_uart_config
{
30 struct ingenic_uart_data
{
31 struct clk
*clk_module
;
36 static const struct of_device_id of_match
[];
38 #define UART_FCR_UME BIT(4)
40 #define UART_MCR_MDCE BIT(7)
41 #define UART_MCR_FCM BIT(6)
43 static struct earlycon_device
*early_device
;
45 static uint8_t early_in(struct uart_port
*port
, int offset
)
47 return readl(port
->membase
+ (offset
<< 2));
50 static void early_out(struct uart_port
*port
, int offset
, uint8_t value
)
52 writel(value
, port
->membase
+ (offset
<< 2));
55 static void ingenic_early_console_putc(struct uart_port
*port
, int c
)
60 lsr
= early_in(port
, UART_LSR
);
61 } while ((lsr
& UART_LSR_TEMT
) == 0);
63 early_out(port
, UART_TX
, c
);
66 static void ingenic_early_console_write(struct console
*console
,
67 const char *s
, unsigned int count
)
69 uart_console_write(&early_device
->port
, s
, count
,
70 ingenic_early_console_putc
);
73 static void __init
ingenic_early_console_setup_clock(struct earlycon_device
*dev
)
75 void *fdt
= initial_boot_params
;
79 offset
= fdt_path_offset(fdt
, "/ext");
83 prop
= fdt_getprop(fdt
, offset
, "clock-frequency", NULL
);
87 dev
->port
.uartclk
= be32_to_cpup(prop
);
90 static int __init
ingenic_early_console_setup(struct earlycon_device
*dev
,
93 struct uart_port
*port
= &dev
->port
;
97 if (!dev
->port
.membase
)
101 unsigned int parity
, bits
, flow
; /* unused for now */
103 uart_parse_options(opt
, &baud
, &parity
, &bits
, &flow
);
106 ingenic_early_console_setup_clock(dev
);
110 divisor
= DIV_ROUND_CLOSEST(port
->uartclk
, 16 * baud
);
112 early_out(port
, UART_IER
, 0);
113 early_out(port
, UART_LCR
, UART_LCR_DLAB
| UART_LCR_WLEN8
);
114 early_out(port
, UART_DLL
, 0);
115 early_out(port
, UART_DLM
, 0);
116 early_out(port
, UART_LCR
, UART_LCR_WLEN8
);
117 early_out(port
, UART_FCR
, UART_FCR_UME
| UART_FCR_CLEAR_XMIT
|
118 UART_FCR_CLEAR_RCVR
| UART_FCR_ENABLE_FIFO
);
119 early_out(port
, UART_MCR
, UART_MCR_RTS
| UART_MCR_DTR
);
121 early_out(port
, UART_LCR
, UART_LCR_DLAB
| UART_LCR_WLEN8
);
122 early_out(port
, UART_DLL
, divisor
& 0xff);
123 early_out(port
, UART_DLM
, (divisor
>> 8) & 0xff);
124 early_out(port
, UART_LCR
, UART_LCR_WLEN8
);
127 dev
->con
->write
= ingenic_early_console_write
;
132 OF_EARLYCON_DECLARE(jz4740_uart
, "ingenic,jz4740-uart",
133 ingenic_early_console_setup
);
135 OF_EARLYCON_DECLARE(jz4770_uart
, "ingenic,jz4770-uart",
136 ingenic_early_console_setup
);
138 OF_EARLYCON_DECLARE(jz4775_uart
, "ingenic,jz4775-uart",
139 ingenic_early_console_setup
);
141 OF_EARLYCON_DECLARE(jz4780_uart
, "ingenic,jz4780-uart",
142 ingenic_early_console_setup
);
144 OF_EARLYCON_DECLARE(x1000_uart
, "ingenic,x1000-uart",
145 ingenic_early_console_setup
);
147 static void ingenic_uart_serial_out(struct uart_port
*p
, int offset
, int value
)
153 /* UART module enable */
154 value
|= UART_FCR_UME
;
159 * Enable receive timeout interrupt with the receive line
162 value
|= (value
& 0x4) << 2;
167 * If we have enabled modem status IRQs we should enable
170 ier
= p
->serial_in(p
, UART_IER
);
172 if (ier
& UART_IER_MSI
)
173 value
|= UART_MCR_MDCE
| UART_MCR_FCM
;
175 value
&= ~(UART_MCR_MDCE
| UART_MCR_FCM
);
182 writeb(value
, p
->membase
+ (offset
<< p
->regshift
));
185 static unsigned int ingenic_uart_serial_in(struct uart_port
*p
, int offset
)
189 value
= readb(p
->membase
+ (offset
<< p
->regshift
));
191 /* Hide non-16550 compliant bits from higher levels */
194 value
&= ~UART_FCR_UME
;
198 value
&= ~(UART_MCR_MDCE
| UART_MCR_FCM
);
207 static int ingenic_uart_probe(struct platform_device
*pdev
)
209 struct uart_8250_port uart
= {};
210 struct ingenic_uart_data
*data
;
211 const struct ingenic_uart_config
*cdata
;
212 const struct of_device_id
*match
;
213 struct resource
*regs
;
216 match
= of_match_device(of_match
, &pdev
->dev
);
218 dev_err(&pdev
->dev
, "Error: No device match found\n");
223 irq
= platform_get_irq(pdev
, 0);
227 regs
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
229 dev_err(&pdev
->dev
, "no registers defined\n");
233 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
237 spin_lock_init(&uart
.port
.lock
);
238 uart
.port
.type
= PORT_16550A
;
239 uart
.port
.flags
= UPF_SKIP_TEST
| UPF_IOREMAP
| UPF_FIXED_TYPE
;
240 uart
.port
.iotype
= UPIO_MEM
;
241 uart
.port
.mapbase
= regs
->start
;
242 uart
.port
.regshift
= 2;
243 uart
.port
.serial_out
= ingenic_uart_serial_out
;
244 uart
.port
.serial_in
= ingenic_uart_serial_in
;
246 uart
.port
.dev
= &pdev
->dev
;
247 uart
.port
.fifosize
= cdata
->fifosize
;
248 uart
.tx_loadsz
= cdata
->tx_loadsz
;
249 uart
.capabilities
= UART_CAP_FIFO
| UART_CAP_RTOIE
;
251 /* Check for a fixed line number */
252 line
= of_alias_get_id(pdev
->dev
.of_node
, "serial");
254 uart
.port
.line
= line
;
256 uart
.port
.membase
= devm_ioremap(&pdev
->dev
, regs
->start
,
257 resource_size(regs
));
258 if (!uart
.port
.membase
)
261 data
->clk_module
= devm_clk_get(&pdev
->dev
, "module");
262 if (IS_ERR(data
->clk_module
))
263 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->clk_module
),
264 "unable to get module clock\n");
266 data
->clk_baud
= devm_clk_get(&pdev
->dev
, "baud");
267 if (IS_ERR(data
->clk_baud
))
268 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->clk_baud
),
269 "unable to get baud clock\n");
271 err
= clk_prepare_enable(data
->clk_module
);
273 dev_err(&pdev
->dev
, "could not enable module clock: %d\n", err
);
277 err
= clk_prepare_enable(data
->clk_baud
);
279 dev_err(&pdev
->dev
, "could not enable baud clock: %d\n", err
);
280 goto out_disable_moduleclk
;
282 uart
.port
.uartclk
= clk_get_rate(data
->clk_baud
);
284 data
->line
= serial8250_register_8250_port(&uart
);
285 if (data
->line
< 0) {
287 goto out_disable_baudclk
;
290 platform_set_drvdata(pdev
, data
);
294 clk_disable_unprepare(data
->clk_baud
);
295 out_disable_moduleclk
:
296 clk_disable_unprepare(data
->clk_module
);
301 static int ingenic_uart_remove(struct platform_device
*pdev
)
303 struct ingenic_uart_data
*data
= platform_get_drvdata(pdev
);
305 serial8250_unregister_port(data
->line
);
306 clk_disable_unprepare(data
->clk_module
);
307 clk_disable_unprepare(data
->clk_baud
);
311 static const struct ingenic_uart_config jz4740_uart_config
= {
316 static const struct ingenic_uart_config jz4760_uart_config
= {
321 static const struct ingenic_uart_config jz4780_uart_config
= {
326 static const struct ingenic_uart_config x1000_uart_config
= {
331 static const struct of_device_id of_match
[] = {
332 { .compatible
= "ingenic,jz4740-uart", .data
= &jz4740_uart_config
},
333 { .compatible
= "ingenic,jz4760-uart", .data
= &jz4760_uart_config
},
334 { .compatible
= "ingenic,jz4770-uart", .data
= &jz4760_uart_config
},
335 { .compatible
= "ingenic,jz4775-uart", .data
= &jz4760_uart_config
},
336 { .compatible
= "ingenic,jz4780-uart", .data
= &jz4780_uart_config
},
337 { .compatible
= "ingenic,x1000-uart", .data
= &x1000_uart_config
},
340 MODULE_DEVICE_TABLE(of
, of_match
);
342 static struct platform_driver ingenic_uart_platform_driver
= {
344 .name
= "ingenic-uart",
345 .of_match_table
= of_match
,
347 .probe
= ingenic_uart_probe
,
348 .remove
= ingenic_uart_remove
,
351 module_platform_driver(ingenic_uart_platform_driver
);
353 MODULE_AUTHOR("Paul Burton");
354 MODULE_LICENSE("GPL");
355 MODULE_DESCRIPTION("Ingenic SoC UART driver");