serial: uartlite: Add support to work with any endianness
[platform/kernel/u-boot.git] / drivers / serial / serial_xuartlite.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2008 - 2015 Michal Simek <monstr@monstr.eu>
4  * Clean driver and add xilinx constant from header file
5  *
6  * (C) Copyright 2004 Atmark Techno, Inc.
7  * Yasushi SHOJI <yashi@atmark-techno.com>
8  */
9
10 #include <config.h>
11 #include <common.h>
12 #include <dm.h>
13 #include <asm/io.h>
14 #include <linux/bitops.h>
15 #include <linux/compiler.h>
16 #include <serial.h>
17
18 #define SR_TX_FIFO_FULL         BIT(3) /* transmit FIFO full */
19 #define SR_TX_FIFO_EMPTY        BIT(2) /* transmit FIFO empty */
20 #define SR_RX_FIFO_VALID_DATA   BIT(0) /* data in receive FIFO */
21 #define SR_RX_FIFO_FULL         BIT(1) /* receive FIFO full */
22
23 #define ULITE_CONTROL_RST_TX    0x01
24 #define ULITE_CONTROL_RST_RX    0x02
25
26 static bool little_endian;
27
28 struct uartlite {
29         unsigned int rx_fifo;
30         unsigned int tx_fifo;
31         unsigned int status;
32         unsigned int control;
33 };
34
35 struct uartlite_platdata {
36         struct uartlite *regs;
37 };
38
39 static u32 uart_in32(void __iomem *addr)
40 {
41         if (little_endian)
42                 return in_le32(addr);
43         else
44                 return in_be32(addr);
45 }
46
47 static void uart_out32(void __iomem *addr, u32 val)
48 {
49         if (little_endian)
50                 out_le32(addr, val);
51         else
52                 out_be32(addr, val);
53 }
54
55 static int uartlite_serial_putc(struct udevice *dev, const char ch)
56 {
57         struct uartlite_platdata *plat = dev_get_platdata(dev);
58         struct uartlite *regs = plat->regs;
59
60         if (uart_in32(&regs->status) & SR_TX_FIFO_FULL)
61                 return -EAGAIN;
62
63         uart_out32(&regs->tx_fifo, ch & 0xff);
64
65         return 0;
66 }
67
68 static int uartlite_serial_getc(struct udevice *dev)
69 {
70         struct uartlite_platdata *plat = dev_get_platdata(dev);
71         struct uartlite *regs = plat->regs;
72
73         if (!(uart_in32(&regs->status) & SR_RX_FIFO_VALID_DATA))
74                 return -EAGAIN;
75
76         return uart_in32(&regs->rx_fifo) & 0xff;
77 }
78
79 static int uartlite_serial_pending(struct udevice *dev, bool input)
80 {
81         struct uartlite_platdata *plat = dev_get_platdata(dev);
82         struct uartlite *regs = plat->regs;
83
84         if (input)
85                 return uart_in32(&regs->status) & SR_RX_FIFO_VALID_DATA;
86
87         return !(uart_in32(&regs->status) & SR_TX_FIFO_EMPTY);
88 }
89
90 static int uartlite_serial_probe(struct udevice *dev)
91 {
92         struct uartlite_platdata *plat = dev_get_platdata(dev);
93         struct uartlite *regs = plat->regs;
94         int ret;
95
96         uart_out32(&regs->control, 0);
97         uart_out32(&regs->control, ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX);
98         ret = uart_in32(&regs->status);
99         /* Endianness detection */
100         if ((ret & SR_TX_FIFO_EMPTY) != SR_TX_FIFO_EMPTY) {
101                 little_endian = true;
102                 uart_out32(&regs->control, ULITE_CONTROL_RST_RX |
103                            ULITE_CONTROL_RST_TX);
104         }
105
106         return 0;
107 }
108
109 static int uartlite_serial_ofdata_to_platdata(struct udevice *dev)
110 {
111         struct uartlite_platdata *plat = dev_get_platdata(dev);
112
113         plat->regs = dev_read_addr_ptr(dev);
114
115         return 0;
116 }
117
118 static const struct dm_serial_ops uartlite_serial_ops = {
119         .putc = uartlite_serial_putc,
120         .pending = uartlite_serial_pending,
121         .getc = uartlite_serial_getc,
122 };
123
124 static const struct udevice_id uartlite_serial_ids[] = {
125         { .compatible = "xlnx,opb-uartlite-1.00.b", },
126         { .compatible = "xlnx,xps-uartlite-1.00.a" },
127         { }
128 };
129
130 U_BOOT_DRIVER(serial_uartlite) = {
131         .name   = "serial_uartlite",
132         .id     = UCLASS_SERIAL,
133         .of_match = uartlite_serial_ids,
134         .ofdata_to_platdata = uartlite_serial_ofdata_to_platdata,
135         .platdata_auto_alloc_size = sizeof(struct uartlite_platdata),
136         .probe = uartlite_serial_probe,
137         .ops    = &uartlite_serial_ops,
138 };
139
140 #ifdef CONFIG_DEBUG_UART_UARTLITE
141
142 #include <debug_uart.h>
143
144 static inline void _debug_uart_init(void)
145 {
146         struct uartlite *regs = (struct uartlite *)CONFIG_DEBUG_UART_BASE;
147         int ret;
148
149         uart_out32(&regs->control, 0);
150         uart_out32(&regs->control, ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX);
151         uart_in32(&regs->status);
152         /* Endianness detection */
153         if ((ret & SR_TX_FIFO_EMPTY) != SR_TX_FIFO_EMPTY) {
154                 little_endian = true;
155                 uart_out32(&regs->control, ULITE_CONTROL_RST_RX |
156                            ULITE_CONTROL_RST_TX);
157         }
158 }
159
160 static inline void _debug_uart_putc(int ch)
161 {
162         struct uartlite *regs = (struct uartlite *)CONFIG_DEBUG_UART_BASE;
163
164         while (uart_in32(&regs->status) & SR_TX_FIFO_FULL)
165                 ;
166
167         uart_out32(&regs->tx_fifo, ch & 0xff);
168 }
169
170 DEBUG_UART_FUNCS
171 #endif