1 // SPDX-License-Identifier: GPL-2.0+
3 * Serial Port driver for Aspeed VUART device
5 * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp.
6 * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.
8 #include <linux/device.h>
9 #include <linux/module.h>
10 #include <linux/of_address.h>
11 #include <linux/of_irq.h>
12 #include <linux/of_platform.h>
13 #include <linux/clk.h>
17 #define ASPEED_VUART_GCRA 0x20
18 #define ASPEED_VUART_GCRA_VUART_EN BIT(0)
19 #define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5)
20 #define ASPEED_VUART_GCRB 0x24
21 #define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4)
22 #define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT 4
23 #define ASPEED_VUART_ADDRL 0x28
24 #define ASPEED_VUART_ADDRH 0x2c
34 * The VUART is basically two UART 'front ends' connected by their FIFO
35 * (no actual serial line in between). One is on the BMC side (management
36 * controller) and one is on the host CPU side.
38 * It allows the BMC to provide to the host a "UART" that pipes into
39 * the BMC itself and can then be turned by the BMC into a network console
40 * of some sort for example.
42 * This driver is for the BMC side. The sysfs files allow the BMC
43 * userspace which owns the system configuration policy, to specify
44 * at what IO port and interrupt number the host side will appear
45 * to the host on the Host <-> BMC LPC bus. It could be different on a
46 * different system (though most of them use 3f8/4).
49 static ssize_t
lpc_address_show(struct device
*dev
,
50 struct device_attribute
*attr
, char *buf
)
52 struct aspeed_vuart
*vuart
= dev_get_drvdata(dev
);
55 addr
= (readb(vuart
->regs
+ ASPEED_VUART_ADDRH
) << 8) |
56 (readb(vuart
->regs
+ ASPEED_VUART_ADDRL
));
58 return snprintf(buf
, PAGE_SIZE
- 1, "0x%x\n", addr
);
61 static ssize_t
lpc_address_store(struct device
*dev
,
62 struct device_attribute
*attr
,
63 const char *buf
, size_t count
)
65 struct aspeed_vuart
*vuart
= dev_get_drvdata(dev
);
69 err
= kstrtoul(buf
, 0, &val
);
73 writeb(val
>> 8, vuart
->regs
+ ASPEED_VUART_ADDRH
);
74 writeb(val
>> 0, vuart
->regs
+ ASPEED_VUART_ADDRL
);
79 static DEVICE_ATTR_RW(lpc_address
);
81 static ssize_t
sirq_show(struct device
*dev
,
82 struct device_attribute
*attr
, char *buf
)
84 struct aspeed_vuart
*vuart
= dev_get_drvdata(dev
);
87 reg
= readb(vuart
->regs
+ ASPEED_VUART_GCRB
);
88 reg
&= ASPEED_VUART_GCRB_HOST_SIRQ_MASK
;
89 reg
>>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT
;
91 return snprintf(buf
, PAGE_SIZE
- 1, "%u\n", reg
);
94 static ssize_t
sirq_store(struct device
*dev
, struct device_attribute
*attr
,
95 const char *buf
, size_t count
)
97 struct aspeed_vuart
*vuart
= dev_get_drvdata(dev
);
102 err
= kstrtoul(buf
, 0, &val
);
106 val
<<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT
;
107 val
&= ASPEED_VUART_GCRB_HOST_SIRQ_MASK
;
109 reg
= readb(vuart
->regs
+ ASPEED_VUART_GCRB
);
110 reg
&= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK
;
112 writeb(reg
, vuart
->regs
+ ASPEED_VUART_GCRB
);
117 static DEVICE_ATTR_RW(sirq
);
119 static struct attribute
*aspeed_vuart_attrs
[] = {
121 &dev_attr_lpc_address
.attr
,
125 static const struct attribute_group aspeed_vuart_attr_group
= {
126 .attrs
= aspeed_vuart_attrs
,
129 static void aspeed_vuart_set_enabled(struct aspeed_vuart
*vuart
, bool enabled
)
131 u8 reg
= readb(vuart
->regs
+ ASPEED_VUART_GCRA
);
134 reg
|= ASPEED_VUART_GCRA_VUART_EN
;
136 reg
&= ~ASPEED_VUART_GCRA_VUART_EN
;
138 writeb(reg
, vuart
->regs
+ ASPEED_VUART_GCRA
);
141 static void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart
*vuart
,
146 reg
= readb(vuart
->regs
+ ASPEED_VUART_GCRA
);
148 /* If the DISABLE_HOST_TX_DISCARD bit is set, discard is disabled */
150 reg
|= ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD
;
152 reg
&= ~ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD
;
154 writeb(reg
, vuart
->regs
+ ASPEED_VUART_GCRA
);
157 static int aspeed_vuart_startup(struct uart_port
*uart_port
)
159 struct uart_8250_port
*uart_8250_port
= up_to_u8250p(uart_port
);
160 struct aspeed_vuart
*vuart
= uart_8250_port
->port
.private_data
;
163 rc
= serial8250_do_startup(uart_port
);
167 aspeed_vuart_set_host_tx_discard(vuart
, false);
172 static void aspeed_vuart_shutdown(struct uart_port
*uart_port
)
174 struct uart_8250_port
*uart_8250_port
= up_to_u8250p(uart_port
);
175 struct aspeed_vuart
*vuart
= uart_8250_port
->port
.private_data
;
177 aspeed_vuart_set_host_tx_discard(vuart
, true);
179 serial8250_do_shutdown(uart_port
);
182 static int aspeed_vuart_probe(struct platform_device
*pdev
)
184 struct uart_8250_port port
;
185 struct aspeed_vuart
*vuart
;
186 struct device_node
*np
;
187 struct resource
*res
;
191 np
= pdev
->dev
.of_node
;
193 vuart
= devm_kzalloc(&pdev
->dev
, sizeof(*vuart
), GFP_KERNEL
);
197 vuart
->dev
= &pdev
->dev
;
199 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
200 vuart
->regs
= devm_ioremap_resource(&pdev
->dev
, res
);
201 if (IS_ERR(vuart
->regs
))
202 return PTR_ERR(vuart
->regs
);
204 memset(&port
, 0, sizeof(port
));
205 port
.port
.private_data
= vuart
;
206 port
.port
.membase
= vuart
->regs
;
207 port
.port
.mapbase
= res
->start
;
208 port
.port
.mapsize
= resource_size(res
);
209 port
.port
.startup
= aspeed_vuart_startup
;
210 port
.port
.shutdown
= aspeed_vuart_shutdown
;
211 port
.port
.dev
= &pdev
->dev
;
213 rc
= sysfs_create_group(&vuart
->dev
->kobj
, &aspeed_vuart_attr_group
);
217 if (of_property_read_u32(np
, "clock-frequency", &clk
)) {
218 vuart
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
219 if (IS_ERR(vuart
->clk
)) {
221 "clk or clock-frequency not defined\n");
222 rc
= PTR_ERR(vuart
->clk
);
223 goto err_sysfs_remove
;
226 rc
= clk_prepare_enable(vuart
->clk
);
228 goto err_sysfs_remove
;
230 clk
= clk_get_rate(vuart
->clk
);
233 /* If current-speed was set, then try not to change it. */
234 if (of_property_read_u32(np
, "current-speed", &prop
) == 0)
235 port
.port
.custom_divisor
= clk
/ (16 * prop
);
237 /* Check for shifted address mapping */
238 if (of_property_read_u32(np
, "reg-offset", &prop
) == 0)
239 port
.port
.mapbase
+= prop
;
241 /* Check for registers offset within the devices address range */
242 if (of_property_read_u32(np
, "reg-shift", &prop
) == 0)
243 port
.port
.regshift
= prop
;
245 /* Check for fifo size */
246 if (of_property_read_u32(np
, "fifo-size", &prop
) == 0)
247 port
.port
.fifosize
= prop
;
249 /* Check for a fixed line number */
250 rc
= of_alias_get_id(np
, "serial");
254 port
.port
.irq
= irq_of_parse_and_map(np
, 0);
255 port
.port
.irqflags
= IRQF_SHARED
;
256 port
.port
.iotype
= UPIO_MEM
;
257 port
.port
.type
= PORT_16550A
;
258 port
.port
.uartclk
= clk
;
259 port
.port
.flags
= UPF_SHARE_IRQ
| UPF_BOOT_AUTOCONF
260 | UPF_FIXED_PORT
| UPF_FIXED_TYPE
| UPF_NO_THRE_TEST
;
262 if (of_property_read_bool(np
, "no-loopback-test"))
263 port
.port
.flags
|= UPF_SKIP_TEST
;
265 if (port
.port
.fifosize
)
266 port
.capabilities
= UART_CAP_FIFO
;
268 if (of_property_read_bool(np
, "auto-flow-control"))
269 port
.capabilities
|= UART_CAP_AFE
;
271 rc
= serial8250_register_8250_port(&port
);
273 goto err_clk_disable
;
277 aspeed_vuart_set_enabled(vuart
, true);
278 aspeed_vuart_set_host_tx_discard(vuart
, true);
279 platform_set_drvdata(pdev
, vuart
);
284 clk_disable_unprepare(vuart
->clk
);
285 irq_dispose_mapping(port
.port
.irq
);
287 sysfs_remove_group(&vuart
->dev
->kobj
, &aspeed_vuart_attr_group
);
291 static int aspeed_vuart_remove(struct platform_device
*pdev
)
293 struct aspeed_vuart
*vuart
= platform_get_drvdata(pdev
);
295 aspeed_vuart_set_enabled(vuart
, false);
296 serial8250_unregister_port(vuart
->line
);
297 sysfs_remove_group(&vuart
->dev
->kobj
, &aspeed_vuart_attr_group
);
298 clk_disable_unprepare(vuart
->clk
);
303 static const struct of_device_id aspeed_vuart_table
[] = {
304 { .compatible
= "aspeed,ast2400-vuart" },
305 { .compatible
= "aspeed,ast2500-vuart" },
309 static struct platform_driver aspeed_vuart_driver
= {
311 .name
= "aspeed-vuart",
312 .of_match_table
= aspeed_vuart_table
,
314 .probe
= aspeed_vuart_probe
,
315 .remove
= aspeed_vuart_remove
,
318 module_platform_driver(aspeed_vuart_driver
);
320 MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>");
321 MODULE_LICENSE("GPL");
322 MODULE_DESCRIPTION("Driver for Aspeed VUART device");