2 * Copyright (C) 2010 Lars-Peter Clausen <lars@metafoo.de>
3 * Copyright (C) 2015 Imagination Technologies
5 * Ingenic SoC UART support
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
12 * You should have received a copy of the GNU General Public License along
13 * with this program; if not, write to the Free Software Foundation, Inc.,
14 * 675 Mass Ave, Cambridge, MA 02139, USA.
17 #include <linux/clk.h>
18 #include <linux/console.h>
20 #include <linux/libfdt.h>
21 #include <linux/module.h>
23 #include <linux/of_fdt.h>
24 #include <linux/of_device.h>
25 #include <linux/platform_device.h>
26 #include <linux/serial_8250.h>
27 #include <linux/serial_core.h>
28 #include <linux/serial_reg.h>
32 /** ingenic_uart_config: SOC specific config data. */
33 struct ingenic_uart_config
{
38 struct ingenic_uart_data
{
39 struct clk
*clk_module
;
44 static const struct of_device_id of_match
[];
46 #define UART_FCR_UME BIT(4)
48 #define UART_MCR_MDCE BIT(7)
49 #define UART_MCR_FCM BIT(6)
51 #if defined(CONFIG_SERIAL_EARLYCON) && !defined(MODULE)
52 static struct earlycon_device
*early_device
;
54 static uint8_t __init
early_in(struct uart_port
*port
, int offset
)
56 return readl(port
->membase
+ (offset
<< 2));
59 static void __init
early_out(struct uart_port
*port
, int offset
, uint8_t value
)
61 writel(value
, port
->membase
+ (offset
<< 2));
64 static void __init
ingenic_early_console_putc(struct uart_port
*port
, int c
)
69 lsr
= early_in(port
, UART_LSR
);
70 } while ((lsr
& UART_LSR_TEMT
) == 0);
72 early_out(port
, UART_TX
, c
);
75 static void __init
ingenic_early_console_write(struct console
*console
,
76 const char *s
, unsigned int count
)
78 uart_console_write(&early_device
->port
, s
, count
,
79 ingenic_early_console_putc
);
82 static void __init
ingenic_early_console_setup_clock(struct earlycon_device
*dev
)
84 void *fdt
= initial_boot_params
;
88 offset
= fdt_path_offset(fdt
, "/ext");
92 prop
= fdt_getprop(fdt
, offset
, "clock-frequency", NULL
);
96 dev
->port
.uartclk
= be32_to_cpup(prop
);
99 static int __init
ingenic_early_console_setup(struct earlycon_device
*dev
,
102 struct uart_port
*port
= &dev
->port
;
103 unsigned int baud
, divisor
;
105 if (!dev
->port
.membase
)
108 ingenic_early_console_setup_clock(dev
);
110 baud
= dev
->baud
?: 115200;
111 divisor
= DIV_ROUND_CLOSEST(port
->uartclk
, 16 * baud
);
113 early_out(port
, UART_IER
, 0);
114 early_out(port
, UART_LCR
, UART_LCR_DLAB
| UART_LCR_WLEN8
);
115 early_out(port
, UART_DLL
, 0);
116 early_out(port
, UART_DLM
, 0);
117 early_out(port
, UART_LCR
, UART_LCR_WLEN8
);
118 early_out(port
, UART_FCR
, UART_FCR_UME
| UART_FCR_CLEAR_XMIT
|
119 UART_FCR_CLEAR_RCVR
| UART_FCR_ENABLE_FIFO
);
120 early_out(port
, UART_MCR
, UART_MCR_RTS
| UART_MCR_DTR
);
122 early_out(port
, UART_LCR
, UART_LCR_DLAB
| UART_LCR_WLEN8
);
123 early_out(port
, UART_DLL
, divisor
& 0xff);
124 early_out(port
, UART_DLM
, (divisor
>> 8) & 0xff);
125 early_out(port
, UART_LCR
, UART_LCR_WLEN8
);
128 dev
->con
->write
= ingenic_early_console_write
;
133 EARLYCON_DECLARE(jz4740_uart
, ingenic_early_console_setup
);
134 OF_EARLYCON_DECLARE(jz4740_uart
, "ingenic,jz4740-uart",
135 ingenic_early_console_setup
);
137 EARLYCON_DECLARE(jz4775_uart
, ingenic_early_console_setup
);
138 OF_EARLYCON_DECLARE(jz4775_uart
, "ingenic,jz4775-uart",
139 ingenic_early_console_setup
);
141 EARLYCON_DECLARE(jz4780_uart
, ingenic_early_console_setup
);
142 OF_EARLYCON_DECLARE(jz4780_uart
, "ingenic,jz4780-uart",
143 ingenic_early_console_setup
);
144 #endif /* CONFIG_SERIAL_EARLYCON */
146 static void ingenic_uart_serial_out(struct uart_port
*p
, int offset
, int value
)
152 /* UART module enable */
153 value
|= UART_FCR_UME
;
158 * Enable receive timeout interrupt with the receive line
161 value
|= (value
& 0x4) << 2;
166 * If we have enabled modem status IRQs we should enable
169 ier
= p
->serial_in(p
, UART_IER
);
171 if (ier
& UART_IER_MSI
)
172 value
|= UART_MCR_MDCE
| UART_MCR_FCM
;
174 value
&= ~(UART_MCR_MDCE
| UART_MCR_FCM
);
181 writeb(value
, p
->membase
+ (offset
<< p
->regshift
));
184 static unsigned int ingenic_uart_serial_in(struct uart_port
*p
, int offset
)
188 value
= readb(p
->membase
+ (offset
<< p
->regshift
));
190 /* Hide non-16550 compliant bits from higher levels */
193 value
&= ~UART_FCR_UME
;
197 value
&= ~(UART_MCR_MDCE
| UART_MCR_FCM
);
206 static int ingenic_uart_probe(struct platform_device
*pdev
)
208 struct uart_8250_port uart
= {};
209 struct resource
*regs
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
210 struct resource
*irq
= platform_get_resource(pdev
, IORESOURCE_IRQ
, 0);
211 struct ingenic_uart_data
*data
;
212 const struct ingenic_uart_config
*cdata
;
213 const struct of_device_id
*match
;
216 match
= of_match_device(of_match
, &pdev
->dev
);
218 dev_err(&pdev
->dev
, "Error: No device match found\n");
224 dev_err(&pdev
->dev
, "no registers/irq defined\n");
228 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
232 spin_lock_init(&uart
.port
.lock
);
233 uart
.port
.type
= PORT_16550A
;
234 uart
.port
.flags
= UPF_SKIP_TEST
| UPF_IOREMAP
| UPF_FIXED_TYPE
;
235 uart
.port
.iotype
= UPIO_MEM
;
236 uart
.port
.mapbase
= regs
->start
;
237 uart
.port
.regshift
= 2;
238 uart
.port
.serial_out
= ingenic_uart_serial_out
;
239 uart
.port
.serial_in
= ingenic_uart_serial_in
;
240 uart
.port
.irq
= irq
->start
;
241 uart
.port
.dev
= &pdev
->dev
;
242 uart
.port
.fifosize
= cdata
->fifosize
;
243 uart
.tx_loadsz
= cdata
->tx_loadsz
;
244 uart
.capabilities
= UART_CAP_FIFO
| UART_CAP_RTOIE
;
246 /* Check for a fixed line number */
247 line
= of_alias_get_id(pdev
->dev
.of_node
, "serial");
249 uart
.port
.line
= line
;
251 uart
.port
.membase
= devm_ioremap(&pdev
->dev
, regs
->start
,
252 resource_size(regs
));
253 if (!uart
.port
.membase
)
256 data
->clk_module
= devm_clk_get(&pdev
->dev
, "module");
257 if (IS_ERR(data
->clk_module
)) {
258 err
= PTR_ERR(data
->clk_module
);
259 if (err
!= -EPROBE_DEFER
)
261 "unable to get module clock: %d\n", err
);
265 data
->clk_baud
= devm_clk_get(&pdev
->dev
, "baud");
266 if (IS_ERR(data
->clk_baud
)) {
267 err
= PTR_ERR(data
->clk_baud
);
268 if (err
!= -EPROBE_DEFER
)
270 "unable to get baud clock: %d\n", err
);
274 err
= clk_prepare_enable(data
->clk_module
);
276 dev_err(&pdev
->dev
, "could not enable module clock: %d\n", err
);
280 err
= clk_prepare_enable(data
->clk_baud
);
282 dev_err(&pdev
->dev
, "could not enable baud clock: %d\n", err
);
283 goto out_disable_moduleclk
;
285 uart
.port
.uartclk
= clk_get_rate(data
->clk_baud
);
287 data
->line
= serial8250_register_8250_port(&uart
);
288 if (data
->line
< 0) {
290 goto out_disable_baudclk
;
293 platform_set_drvdata(pdev
, data
);
297 clk_disable_unprepare(data
->clk_baud
);
298 out_disable_moduleclk
:
299 clk_disable_unprepare(data
->clk_module
);
304 static int ingenic_uart_remove(struct platform_device
*pdev
)
306 struct ingenic_uart_data
*data
= platform_get_drvdata(pdev
);
308 serial8250_unregister_port(data
->line
);
309 clk_disable_unprepare(data
->clk_module
);
310 clk_disable_unprepare(data
->clk_baud
);
314 static const struct ingenic_uart_config jz4740_uart_config
= {
319 static const struct ingenic_uart_config jz4760_uart_config
= {
324 static const struct ingenic_uart_config jz4780_uart_config
= {
329 static const struct of_device_id of_match
[] = {
330 { .compatible
= "ingenic,jz4740-uart", .data
= &jz4740_uart_config
},
331 { .compatible
= "ingenic,jz4760-uart", .data
= &jz4760_uart_config
},
332 { .compatible
= "ingenic,jz4775-uart", .data
= &jz4760_uart_config
},
333 { .compatible
= "ingenic,jz4780-uart", .data
= &jz4780_uart_config
},
336 MODULE_DEVICE_TABLE(of
, of_match
);
338 static struct platform_driver ingenic_uart_platform_driver
= {
340 .name
= "ingenic-uart",
341 .of_match_table
= of_match
,
343 .probe
= ingenic_uart_probe
,
344 .remove
= ingenic_uart_remove
,
347 module_platform_driver(ingenic_uart_platform_driver
);
349 MODULE_AUTHOR("Paul Burton");
350 MODULE_LICENSE("GPL");
351 MODULE_DESCRIPTION("Ingenic SoC UART driver");