Skip to content

Commit bdc5f30

Browse files
msperlgregkh
authored andcommitted
serial: bcm2835: add driver for bcm2835-aux-uart
The bcm2835 SOC contains an auxiliary uart, which is very close to the ns16550 with some differences. The big difference is that the uart HW is not using an internal divider of 16 but 8, which results in an effictive baud-rate being twice the requested baud-rate. This driver handles this device correctly and handles the difference in the HW divider by scaling up the clock by a factor of 2. The approach to write a separate (wrapper) driver instead of using a multiplying clock and "ns16550" as compatibility in the device-tree has been recommended by Stephen Warren. Signed-off-by: Martin Sperl <[email protected]> Acked-by: Eric Anholt <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 45d7e9a commit bdc5f30

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Serial port driver for BCM2835AUX UART
3+
*
4+
* Copyright (C) 2016 Martin Sperl <[email protected]>
5+
*
6+
* Based on 8250_lpc18xx.c:
7+
* Copyright (C) 2015 Joachim Eastwood <[email protected]>
8+
*
9+
* This program is free software; you can redistribute it and/or modify
10+
* it under the terms of the GNU General Public License version 2 as
11+
* published by the Free Software Foundation.
12+
*
13+
*/
14+
15+
#include <linux/clk.h>
16+
#include <linux/io.h>
17+
#include <linux/module.h>
18+
#include <linux/of.h>
19+
#include <linux/platform_device.h>
20+
21+
#include "8250.h"
22+
23+
struct bcm2835aux_data {
24+
struct uart_8250_port uart;
25+
struct clk *clk;
26+
int line;
27+
};
28+
29+
static int bcm2835aux_serial_probe(struct platform_device *pdev)
30+
{
31+
struct bcm2835aux_data *data;
32+
struct resource *res;
33+
int ret;
34+
35+
/* allocate the custom structure */
36+
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
37+
if (!data)
38+
return -ENOMEM;
39+
40+
/* initialize data */
41+
spin_lock_init(&data->uart.port.lock);
42+
data->uart.capabilities = UART_CAP_FIFO;
43+
data->uart.port.dev = &pdev->dev;
44+
data->uart.port.regshift = 2;
45+
data->uart.port.type = PORT_16550;
46+
data->uart.port.iotype = UPIO_MEM;
47+
data->uart.port.fifosize = 8;
48+
data->uart.port.flags = UPF_SHARE_IRQ |
49+
UPF_FIXED_PORT |
50+
UPF_FIXED_TYPE |
51+
UPF_SKIP_TEST;
52+
53+
/* get the clock - this also enables the HW */
54+
data->clk = devm_clk_get(&pdev->dev, NULL);
55+
ret = PTR_ERR_OR_ZERO(data->clk);
56+
if (ret) {
57+
dev_err(&pdev->dev, "could not get clk: %d\n", ret);
58+
return ret;
59+
}
60+
61+
/* get the interrupt */
62+
data->uart.port.irq = platform_get_irq(pdev, 0);
63+
if (data->uart.port.irq < 0) {
64+
dev_err(&pdev->dev, "irq not found - %i",
65+
data->uart.port.irq);
66+
return data->uart.port.irq;
67+
}
68+
69+
/* map the main registers */
70+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
71+
if (!res) {
72+
dev_err(&pdev->dev, "memory resource not found");
73+
return -EINVAL;
74+
}
75+
data->uart.port.membase = devm_ioremap_resource(&pdev->dev, res);
76+
ret = PTR_ERR_OR_ZERO(data->uart.port.membase);
77+
if (ret)
78+
return ret;
79+
80+
/* Check for a fixed line number */
81+
ret = of_alias_get_id(pdev->dev.of_node, "serial");
82+
if (ret >= 0)
83+
data->uart.port.line = ret;
84+
85+
/* enable the clock as a last step */
86+
ret = clk_prepare_enable(data->clk);
87+
if (ret) {
88+
dev_err(&pdev->dev, "unable to enable uart clock - %d\n",
89+
ret);
90+
return ret;
91+
}
92+
93+
/* the HW-clock divider for bcm2835aux is 8,
94+
* but 8250 expects a divider of 16,
95+
* so we have to multiply the actual clock by 2
96+
* to get identical baudrates.
97+
*/
98+
data->uart.port.uartclk = clk_get_rate(data->clk) * 2;
99+
100+
/* register the port */
101+
ret = serial8250_register_8250_port(&data->uart);
102+
if (ret < 0) {
103+
dev_err(&pdev->dev, "unable to register 8250 port - %d\n",
104+
ret);
105+
goto dis_clk;
106+
}
107+
data->line = ret;
108+
109+
platform_set_drvdata(pdev, data);
110+
111+
return 0;
112+
113+
dis_clk:
114+
clk_disable_unprepare(data->clk);
115+
return ret;
116+
}
117+
118+
static int bcm2835aux_serial_remove(struct platform_device *pdev)
119+
{
120+
struct bcm2835aux_data *data = platform_get_drvdata(pdev);
121+
122+
serial8250_unregister_port(data->uart.port.line);
123+
clk_disable_unprepare(data->clk);
124+
125+
return 0;
126+
}
127+
128+
static const struct of_device_id bcm2835aux_serial_match[] = {
129+
{ .compatible = "brcm,bcm2835-aux-uart" },
130+
{ },
131+
};
132+
MODULE_DEVICE_TABLE(of, bcm2835aux_serial_match);
133+
134+
static struct platform_driver bcm2835aux_serial_driver = {
135+
.driver = {
136+
.name = "bcm2835-aux-uart",
137+
.of_match_table = bcm2835aux_serial_match,
138+
},
139+
.probe = bcm2835aux_serial_probe,
140+
.remove = bcm2835aux_serial_remove,
141+
};
142+
module_platform_driver(bcm2835aux_serial_driver);
143+
144+
MODULE_DESCRIPTION("BCM2835 auxiliar UART driver");
145+
MODULE_AUTHOR("Martin Sperl <[email protected]>");
146+
MODULE_LICENSE("GPL v2");

drivers/tty/serial/8250/Kconfig

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,30 @@ config SERIAL_8250_ACORN
272272
system, say Y to this option. The driver can handle 1, 2, or 3 port
273273
cards. If unsure, say N.
274274

275+
config SERIAL_8250_BCM2835AUX
276+
tristate "BCM2835 auxiliar mini UART support"
277+
depends on ARCH_BCM2835 || COMPILE_TEST
278+
depends on SERIAL_8250 && SERIAL_8250_SHARE_IRQ
279+
help
280+
Support for the BCM2835 auxiliar mini UART.
281+
282+
Features and limitations of the UART are
283+
Registers are similar to 16650 registers,
284+
set bits in the control registers that are unsupported
285+
are ignored and read back as 0
286+
7/8 bit operation with 1 start and 1 stop bit
287+
8 symbols deep fifo for rx and tx
288+
SW controlled RTS and SW readable CTS
289+
Clock rate derived from system clock
290+
Uses 8 times oversampling (compared to 16 times for 16650)
291+
Missing break detection (but break generation)
292+
Missing framing error detection
293+
Missing parity bit
294+
Missing receive time-out interrupt
295+
Missing DCD, DSR, DTR and RI signals
296+
297+
If unsure, say N.
298+
275299
config SERIAL_8250_FSL
276300
bool
277301
depends on SERIAL_8250_CONSOLE

drivers/tty/serial/8250/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o
1212
obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o
1313
obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o
1414
obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o
15+
obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o
1516
obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o
1617
obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o
1718
obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o

0 commit comments

Comments
 (0)