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