Skip to content

Commit 1a8d290

Browse files
masahir0ygregkh
authored andcommitted
serial: 8250_uniphier: add UniPhier serial driver
Add the driver for on-chip UART used on UniPhier SoCs. This hardware is similar to 8250, but the register mapping is slightly different: - The offset to FCR, MCR is different. - The divisor latch access bit does not exist. Instead, the divisor latch register is available at offset 9. This driver overrides serial_{in,out}, dl_{read,write} callbacks, but wants to borrow most of code from 8250_core.c. Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> Reviewed-by: Matthias Brugger <matthias.bgg@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 0788c39 commit 1a8d290

File tree

3 files changed

+265
-0
lines changed

3 files changed

+265
-0
lines changed
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
/*
2+
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
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+
/* Most (but not all) of UniPhier UART devices have 64-depth FIFO. */
24+
#define UNIPHIER_UART_DEFAULT_FIFO_SIZE 64
25+
26+
#define UNIPHIER_UART_CHAR_FCR 3 /* Character / FIFO Control Register */
27+
#define UNIPHIER_UART_LCR_MCR 4 /* Line/Modem Control Register */
28+
#define UNIPHIER_UART_LCR_SHIFT 8
29+
#define UNIPHIER_UART_DLR 9 /* Divisor Latch Register */
30+
31+
struct uniphier8250_priv {
32+
int line;
33+
struct clk *clk;
34+
spinlock_t atomic_write_lock;
35+
};
36+
37+
/*
38+
* The register map is slightly different from that of 8250.
39+
* IO callbacks must be overridden for correct access to FCR, LCR, and MCR.
40+
*/
41+
static unsigned int uniphier_serial_in(struct uart_port *p, int offset)
42+
{
43+
unsigned int valshift = 0;
44+
45+
switch (offset) {
46+
case UART_LCR:
47+
valshift = UNIPHIER_UART_LCR_SHIFT;
48+
/* fall through */
49+
case UART_MCR:
50+
offset = UNIPHIER_UART_LCR_MCR;
51+
break;
52+
default:
53+
break;
54+
}
55+
56+
offset <<= p->regshift;
57+
58+
/*
59+
* The return value must be masked with 0xff because LCR and MCR reside
60+
* in the same register that must be accessed by 32-bit write/read.
61+
* 8 or 16 bit access to this hardware result in unexpected behavior.
62+
*/
63+
return (readl(p->membase + offset) >> valshift) & 0xff;
64+
}
65+
66+
static void uniphier_serial_out(struct uart_port *p, int offset, int value)
67+
{
68+
unsigned int valshift = 0;
69+
bool normal = false;
70+
71+
switch (offset) {
72+
case UART_FCR:
73+
offset = UNIPHIER_UART_CHAR_FCR;
74+
break;
75+
case UART_LCR:
76+
valshift = UNIPHIER_UART_LCR_SHIFT;
77+
/* Divisor latch access bit does not exist. */
78+
value &= ~(UART_LCR_DLAB << valshift);
79+
/* fall through */
80+
case UART_MCR:
81+
offset = UNIPHIER_UART_LCR_MCR;
82+
break;
83+
default:
84+
normal = true;
85+
break;
86+
}
87+
88+
offset <<= p->regshift;
89+
90+
if (normal) {
91+
writel(value, p->membase + offset);
92+
} else {
93+
/*
94+
* Special case: two registers share the same address that
95+
* must be 32-bit accessed. As this is not longer atomic safe,
96+
* take a lock just in case.
97+
*/
98+
struct uniphier8250_priv *priv = p->private_data;
99+
unsigned long flags;
100+
u32 tmp;
101+
102+
spin_lock_irqsave(&priv->atomic_write_lock, flags);
103+
tmp = readl(p->membase + offset);
104+
tmp &= ~(0xff << valshift);
105+
tmp |= value << valshift;
106+
writel(tmp, p->membase + offset);
107+
spin_unlock_irqrestore(&priv->atomic_write_lock, flags);
108+
}
109+
}
110+
111+
/*
112+
* This hardware does not have the divisor latch access bit.
113+
* The divisor latch register exists at different address.
114+
* Override dl_read/write callbacks.
115+
*/
116+
static int uniphier_serial_dl_read(struct uart_8250_port *up)
117+
{
118+
return readl(up->port.membase + UNIPHIER_UART_DLR);
119+
}
120+
121+
static void uniphier_serial_dl_write(struct uart_8250_port *up, int value)
122+
{
123+
writel(value, up->port.membase + UNIPHIER_UART_DLR);
124+
}
125+
126+
static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port,
127+
struct uniphier8250_priv *priv)
128+
{
129+
int ret;
130+
u32 prop;
131+
struct device_node *np = dev->of_node;
132+
133+
ret = of_alias_get_id(np, "serial");
134+
if (ret < 0) {
135+
dev_err(dev, "failed to get alias id\n");
136+
return ret;
137+
}
138+
port->line = priv->line = ret;
139+
140+
/* Get clk rate through clk driver */
141+
priv->clk = devm_clk_get(dev, NULL);
142+
if (IS_ERR(priv->clk)) {
143+
dev_err(dev, "failed to get clock\n");
144+
return PTR_ERR(priv->clk);
145+
}
146+
147+
ret = clk_prepare_enable(priv->clk);
148+
if (ret < 0)
149+
return ret;
150+
151+
port->uartclk = clk_get_rate(priv->clk);
152+
153+
/* Check for fifo size */
154+
if (of_property_read_u32(np, "fifo-size", &prop) == 0)
155+
port->fifosize = prop;
156+
else
157+
port->fifosize = UNIPHIER_UART_DEFAULT_FIFO_SIZE;
158+
159+
return 0;
160+
}
161+
162+
static int uniphier_uart_probe(struct platform_device *pdev)
163+
{
164+
struct device *dev = &pdev->dev;
165+
struct uart_8250_port up;
166+
struct uniphier8250_priv *priv;
167+
struct resource *regs;
168+
void __iomem *membase;
169+
int irq;
170+
int ret;
171+
172+
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
173+
if (!regs) {
174+
dev_err(dev, "failed to get memory resource");
175+
return -EINVAL;
176+
}
177+
178+
membase = devm_ioremap(dev, regs->start, resource_size(regs));
179+
if (!membase)
180+
return -ENOMEM;
181+
182+
irq = platform_get_irq(pdev, 0);
183+
if (irq < 0) {
184+
dev_err(dev, "failed to get IRQ number");
185+
return irq;
186+
}
187+
188+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
189+
if (!priv)
190+
return -ENOMEM;
191+
192+
memset(&up, 0, sizeof(up));
193+
194+
ret = uniphier_of_serial_setup(dev, &up.port, priv);
195+
if (ret < 0)
196+
return ret;
197+
198+
spin_lock_init(&priv->atomic_write_lock);
199+
200+
up.port.dev = dev;
201+
up.port.private_data = priv;
202+
up.port.mapbase = regs->start;
203+
up.port.mapsize = resource_size(regs);
204+
up.port.membase = membase;
205+
up.port.irq = irq;
206+
207+
up.port.type = PORT_16550A;
208+
up.port.iotype = UPIO_MEM32;
209+
up.port.regshift = 2;
210+
up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE;
211+
up.capabilities = UART_CAP_FIFO;
212+
213+
up.port.serial_in = uniphier_serial_in;
214+
up.port.serial_out = uniphier_serial_out;
215+
up.dl_read = uniphier_serial_dl_read;
216+
up.dl_write = uniphier_serial_dl_write;
217+
218+
ret = serial8250_register_8250_port(&up);
219+
if (ret < 0) {
220+
dev_err(dev, "failed to register 8250 port\n");
221+
return ret;
222+
}
223+
224+
platform_set_drvdata(pdev, priv);
225+
226+
return 0;
227+
}
228+
229+
static int uniphier_uart_remove(struct platform_device *pdev)
230+
{
231+
struct uniphier8250_priv *priv = platform_get_drvdata(pdev);
232+
233+
serial8250_unregister_port(priv->line);
234+
clk_disable_unprepare(priv->clk);
235+
236+
return 0;
237+
}
238+
239+
static const struct of_device_id uniphier_uart_match[] = {
240+
{ .compatible = "socionext,uniphier-uart" },
241+
{ /* sentinel */ }
242+
};
243+
MODULE_DEVICE_TABLE(of, uniphier_uart_match);
244+
245+
static struct platform_driver uniphier_uart_platform_driver = {
246+
.probe = uniphier_uart_probe,
247+
.remove = uniphier_uart_remove,
248+
.driver = {
249+
.name = "uniphier-uart",
250+
.of_match_table = uniphier_uart_match,
251+
},
252+
};
253+
module_platform_driver(uniphier_uart_platform_driver);
254+
255+
MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
256+
MODULE_DESCRIPTION("UniPhier UART driver");
257+
MODULE_LICENSE("GPL");

drivers/tty/serial/8250/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,3 +342,10 @@ config SERIAL_8250_MT6577
342342
help
343343
If you have a Mediatek based board and want to use the
344344
serial port, say Y to this option. If unsure, say N.
345+
346+
config SERIAL_8250_UNIPHIER
347+
tristate "Support for UniPhier on-chip UART"
348+
depends on SERIAL_8250 && ARCH_UNIPHIER
349+
help
350+
If you have a UniPhier based board and want to use the on-chip
351+
serial ports, say Y to this option. If unsure, say N.

drivers/tty/serial/8250/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
2323
obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o
2424
obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o
2525
obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o
26+
obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o

0 commit comments

Comments
 (0)