1 /*
2 * xen/drivers/char/meson-uart.c
3 *
4 * Driver for Amlogic MESON UART
5 *
6 * Copyright (c) 2019, Amit Singh Tomar <amittomer25@gmail.com>.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms and conditions of the GNU General Public
10 * License, version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <xen/errno.h>
22 #include <xen/irq.h>
23 #include <xen/mm.h>
24 #include <xen/serial.h>
25 #include <xen/vmap.h>
26 #include <asm/io.h>
27
28 /* Register offsets */
29 #define AML_UART_WFIFO_REG 0x00
30 #define AML_UART_RFIFO_REG 0x04
31 #define AML_UART_CONTROL_REG 0x08
32 #define AML_UART_STATUS_REG 0x0c
33 #define AML_UART_MISC_REG 0x10
34
35 /* UART_CONTROL bits */
36 #define AML_UART_TX_RST BIT(22, UL)
37 #define AML_UART_RX_RST BIT(23, UL)
38 #define AML_UART_CLEAR_ERR BIT(24, UL)
39 #define AML_UART_RX_INT_EN BIT(27, UL)
40 #define AML_UART_TX_INT_EN BIT(28, UL)
41
42 /* UART_STATUS bits */
43 #define AML_UART_RX_FIFO_EMPTY BIT(20, UL)
44 #define AML_UART_TX_FIFO_FULL BIT(21, UL)
45 #define AML_UART_TX_FIFO_EMPTY BIT(22, UL)
46 #define AML_UART_TX_CNT_MASK GENMASK(14, 8)
47
48 /* AML_UART_MISC bits */
49 #define AML_UART_XMIT_IRQ(c) (((c) & 0xff) << 8)
50 #define AML_UART_RECV_IRQ(c) ((c) & 0xff)
51
52 #define TX_FIFO_SIZE 64
53
54 #define setbits(addr, set) writel((readl(addr) | (set)), (addr))
55 #define clrbits(addr, clear) writel((readl(addr) & ~(clear)), (addr))
56
57 static struct meson_uart {
58 unsigned int irq;
59 void __iomem *regs;
60 struct irqaction irqaction;
61 struct vuart_info vuart;
62 } meson_com;
63
meson_uart_interrupt(int irq,void * data)64 static void meson_uart_interrupt(int irq, void *data)
65 {
66 struct serial_port *port = data;
67 struct meson_uart *uart = port->uart;
68 uint32_t st = readl(uart->regs + AML_UART_STATUS_REG);
69
70 if ( !(st & AML_UART_RX_FIFO_EMPTY) )
71 serial_rx_interrupt(port);
72
73 if ( !(st & AML_UART_TX_FIFO_FULL) )
74 serial_tx_interrupt(port);
75 }
76
meson_uart_init_preirq(struct serial_port * port)77 static void __init meson_uart_init_preirq(struct serial_port *port)
78 {
79 struct meson_uart *uart = port->uart;
80
81 /* Reset UART */
82 setbits(uart->regs + AML_UART_CONTROL_REG,
83 (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLEAR_ERR));
84
85 clrbits(uart->regs + AML_UART_CONTROL_REG,
86 (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLEAR_ERR));
87
88 /* Disable Rx/Tx interrupts */
89 clrbits(uart->regs + AML_UART_CONTROL_REG,
90 (AML_UART_RX_INT_EN | AML_UART_TX_INT_EN));
91 }
92
meson_uart_init_postirq(struct serial_port * port)93 static void __init meson_uart_init_postirq(struct serial_port *port)
94 {
95 struct meson_uart *uart = port->uart;
96
97 uart->irqaction.handler = meson_uart_interrupt;
98 uart->irqaction.name = "meson_uart";
99 uart->irqaction.dev_id = port;
100
101 if ( setup_irq(uart->irq, 0, &uart->irqaction) != 0 )
102 {
103 printk("Failed to allocated Meson UART IRQ %d\n", uart->irq);
104 return;
105 }
106
107 /*
108 * Configure Rx/Tx interrupts based on bytes in FIFO, these bits have
109 * taken from Linux driver
110 */
111 writel((AML_UART_RECV_IRQ(1) | AML_UART_XMIT_IRQ(TX_FIFO_SIZE / 2)),
112 uart->regs + AML_UART_MISC_REG);
113
114 /* Make sure Rx/Tx interrupts are enabled now */
115 setbits(uart->regs + AML_UART_CONTROL_REG,
116 (AML_UART_RX_INT_EN | AML_UART_TX_INT_EN));
117 }
118
meson_uart_putc(struct serial_port * port,char c)119 static void meson_uart_putc(struct serial_port *port, char c)
120 {
121 struct meson_uart *uart = port->uart;
122
123 writel(c, uart->regs + AML_UART_WFIFO_REG);
124 }
125
meson_uart_getc(struct serial_port * port,char * c)126 static int meson_uart_getc(struct serial_port *port, char *c)
127 {
128 struct meson_uart *uart = port->uart;
129
130 if ( (readl(uart->regs + AML_UART_STATUS_REG) & AML_UART_RX_FIFO_EMPTY) )
131 return 0;
132
133 *c = readl(uart->regs + AML_UART_RFIFO_REG) & 0xff;
134
135 return 1;
136 }
137
meson_irq(struct serial_port * port)138 static int __init meson_irq(struct serial_port *port)
139 {
140 struct meson_uart *uart = port->uart;
141
142 return uart->irq;
143 }
144
meson_vuart_info(struct serial_port * port)145 static const struct vuart_info *meson_vuart_info(struct serial_port *port)
146 {
147 struct meson_uart *uart = port->uart;
148
149 return &uart->vuart;
150 }
151
meson_uart_stop_tx(struct serial_port * port)152 static void meson_uart_stop_tx(struct serial_port *port)
153 {
154 struct meson_uart *uart = port->uart;
155
156 clrbits(uart->regs + AML_UART_CONTROL_REG, AML_UART_TX_INT_EN);
157 }
158
meson_uart_start_tx(struct serial_port * port)159 static void meson_uart_start_tx(struct serial_port *port)
160 {
161 struct meson_uart *uart = port->uart;
162
163 setbits(uart->regs + AML_UART_CONTROL_REG, AML_UART_TX_INT_EN);
164 }
165
meson_uart_tx_ready(struct serial_port * port)166 static int meson_uart_tx_ready(struct serial_port *port)
167 {
168 struct meson_uart *uart = port->uart;
169 uint32_t reg;
170
171 reg = readl(uart->regs + AML_UART_STATUS_REG);
172
173 if ( reg & AML_UART_TX_FIFO_EMPTY )
174 return TX_FIFO_SIZE;
175 if ( reg & AML_UART_TX_FIFO_FULL )
176 return 0;
177
178 return (reg & AML_UART_TX_CNT_MASK) >> 8;
179 }
180
181 static struct uart_driver __read_mostly meson_uart_driver = {
182 .init_preirq = meson_uart_init_preirq,
183 .init_postirq = meson_uart_init_postirq,
184 .putc = meson_uart_putc,
185 .getc = meson_uart_getc,
186 .tx_ready = meson_uart_tx_ready,
187 .stop_tx = meson_uart_stop_tx,
188 .start_tx = meson_uart_start_tx,
189 .irq = meson_irq,
190 .vuart_info = meson_vuart_info,
191 };
192
meson_uart_init(struct dt_device_node * dev,const void * data)193 static int __init meson_uart_init(struct dt_device_node *dev, const void *data)
194 {
195 const char *config = data;
196 struct meson_uart *uart;
197 int res;
198 paddr_t addr, size;
199
200 if ( strcmp(config, "") )
201 printk("WARNING: UART configuration is not supported\n");
202
203 uart = &meson_com;
204
205 res = dt_device_get_paddr(dev, 0, &addr, &size);
206 if ( res )
207 {
208 printk("meson: Unable to retrieve the base address of the UART\n");
209 return res;
210 }
211
212 res = platform_get_irq(dev, 0);
213 if ( res < 0 )
214 {
215 printk("meson: Unable to retrieve the IRQ\n");
216 return -EINVAL;
217 }
218
219 uart->irq = res;
220
221 uart->regs = ioremap_nocache(addr, size);
222 if ( !uart->regs )
223 {
224 printk("meson: Unable to map the UART\n");
225 return -ENOMEM;
226 }
227
228 uart->vuart.base_addr = addr;
229 uart->vuart.size = size;
230 uart->vuart.data_off = AML_UART_WFIFO_REG;
231 uart->vuart.status_off = AML_UART_STATUS_REG;
232 uart->vuart.status = AML_UART_RX_FIFO_EMPTY | AML_UART_TX_FIFO_EMPTY;
233
234 /* Register with generic serial driver. */
235 serial_register_uart(SERHND_DTUART, &meson_uart_driver, uart);
236
237 dt_device_set_used_by(dev, DOMID_XEN);
238
239 return 0;
240 }
241
242 static const struct dt_device_match meson_dt_match[] __initconst =
243 {
244 DT_MATCH_COMPATIBLE("amlogic,meson-uart"),
245 DT_MATCH_COMPATIBLE("amlogic,meson6-uart"),
246 DT_MATCH_COMPATIBLE("amlogic,meson8-uart"),
247 DT_MATCH_COMPATIBLE("amlogic,meson8b-uart"),
248 DT_MATCH_COMPATIBLE("amlogic,meson-gx-uart"),
249 { /* sentinel */ },
250 };
251
252 DT_DEVICE_START(meson, "Amlogic UART", DEVICE_SERIAL)
253 .dt_match = meson_dt_match,
254 .init = meson_uart_init,
255 DT_DEVICE_END
256
257 /*
258 * Local variables:
259 * mode: C
260 * c-file-style: "BSD"
261 * c-basic-offset: 4
262 * indent-tabs-mode: nil
263 * End:
264 */
265