Commit 73e256c2ac7a67b00be69a397997d80d04ec994d
1 parent
9208fffebc
Exists in
v2017.01-smarct4x
and in
37 other branches
dm: exynos: Move serial to driver model
Change the Exynos serial driver to work with driver model and switch over all relevant boards to use it. Signed-off-by: Simon Glass <sjg@chromium.org>
Showing 4 changed files with 75 additions and 183 deletions Side-by-side Diff
drivers/serial/serial_s5p.c
... | ... | @@ -9,6 +9,8 @@ |
9 | 9 | */ |
10 | 10 | |
11 | 11 | #include <common.h> |
12 | +#include <dm.h> | |
13 | +#include <errno.h> | |
12 | 14 | #include <fdtdec.h> |
13 | 15 | #include <linux/compiler.h> |
14 | 16 | #include <asm/io.h> |
15 | 17 | |
16 | 18 | |
17 | 19 | |
... | ... | @@ -18,27 +20,19 @@ |
18 | 20 | |
19 | 21 | DECLARE_GLOBAL_DATA_PTR; |
20 | 22 | |
21 | -#define RX_FIFO_COUNT_MASK 0xff | |
22 | -#define RX_FIFO_FULL_MASK (1 << 8) | |
23 | -#define TX_FIFO_FULL_MASK (1 << 24) | |
23 | +#define RX_FIFO_COUNT_SHIFT 0 | |
24 | +#define RX_FIFO_COUNT_MASK (0xff << RX_FIFO_COUNT_SHIFT) | |
25 | +#define RX_FIFO_FULL (1 << 8) | |
26 | +#define TX_FIFO_COUNT_SHIFT 16 | |
27 | +#define TX_FIFO_COUNT_MASK (0xff << TX_FIFO_COUNT_SHIFT) | |
28 | +#define TX_FIFO_FULL (1 << 24) | |
24 | 29 | |
25 | 30 | /* Information about a serial port */ |
26 | -struct fdt_serial { | |
27 | - u32 base_addr; /* address of registers in physical memory */ | |
31 | +struct s5p_serial_platdata { | |
32 | + struct s5p_uart *reg; /* address of registers in physical memory */ | |
28 | 33 | u8 port_id; /* uart port number */ |
29 | - u8 enabled; /* 1 if enabled, 0 if disabled */ | |
30 | -} config __attribute__ ((section(".data"))); | |
34 | +}; | |
31 | 35 | |
32 | -static inline struct s5p_uart *s5p_get_base_uart(int dev_index) | |
33 | -{ | |
34 | -#ifdef CONFIG_OF_CONTROL | |
35 | - return (struct s5p_uart *)(config.base_addr); | |
36 | -#else | |
37 | - u32 offset = dev_index * sizeof(struct s5p_uart); | |
38 | - return (struct s5p_uart *)(samsung_get_base_uart() + offset); | |
39 | -#endif | |
40 | -} | |
41 | - | |
42 | 36 | /* |
43 | 37 | * The coefficient, used to calculate the baudrate on S5P UARTs is |
44 | 38 | * calculated as |
45 | 39 | |
46 | 40 | |
... | ... | @@ -65,23 +59,13 @@ |
65 | 59 | 0xffdf, |
66 | 60 | }; |
67 | 61 | |
68 | -static void serial_setbrg_dev(const int dev_index) | |
62 | +int s5p_serial_setbrg(struct udevice *dev, int baudrate) | |
69 | 63 | { |
70 | - struct s5p_uart *const uart = s5p_get_base_uart(dev_index); | |
71 | - u32 uclk = get_uart_clk(dev_index); | |
72 | - u32 baudrate = gd->baudrate; | |
64 | + struct s5p_serial_platdata *plat = dev->platdata; | |
65 | + struct s5p_uart *const uart = plat->reg; | |
66 | + u32 uclk = get_uart_clk(plat->port_id); | |
73 | 67 | u32 val; |
74 | 68 | |
75 | -#if defined(CONFIG_SILENT_CONSOLE) && \ | |
76 | - defined(CONFIG_OF_CONTROL) && \ | |
77 | - !defined(CONFIG_SPL_BUILD) | |
78 | - if (fdtdec_get_config_int(gd->fdt_blob, "silent_console", 0)) | |
79 | - gd->flags |= GD_FLG_SILENT; | |
80 | -#endif | |
81 | - | |
82 | - if (!config.enabled) | |
83 | - return; | |
84 | - | |
85 | 69 | val = uclk / baudrate; |
86 | 70 | |
87 | 71 | writel(val / 16 - 1, &uart->ubrdiv); |
88 | 72 | |
89 | 73 | |
... | ... | @@ -90,15 +74,14 @@ |
90 | 74 | writew(udivslot[val % 16], &uart->rest.slot); |
91 | 75 | else |
92 | 76 | writeb(val % 16, &uart->rest.value); |
77 | + | |
78 | + return 0; | |
93 | 79 | } |
94 | 80 | |
95 | -/* | |
96 | - * Initialise the serial port with the given baudrate. The settings | |
97 | - * are always 8 data bits, no parity, 1 stop bit, no start bits. | |
98 | - */ | |
99 | -static int serial_init_dev(const int dev_index) | |
81 | +static int s5p_serial_probe(struct udevice *dev) | |
100 | 82 | { |
101 | - struct s5p_uart *const uart = s5p_get_base_uart(dev_index); | |
83 | + struct s5p_serial_platdata *plat = dev->platdata; | |
84 | + struct s5p_uart *const uart = plat->reg; | |
102 | 85 | |
103 | 86 | /* enable FIFOs, auto clear Rx FIFO */ |
104 | 87 | writel(0x3, &uart->ufcon); |
105 | 88 | |
106 | 89 | |
... | ... | @@ -108,14 +91,11 @@ |
108 | 91 | /* No interrupts, no DMA, pure polling */ |
109 | 92 | writel(0x245, &uart->ucon); |
110 | 93 | |
111 | - serial_setbrg_dev(dev_index); | |
112 | - | |
113 | 94 | return 0; |
114 | 95 | } |
115 | 96 | |
116 | -static int serial_err_check(const int dev_index, int op) | |
97 | +static int serial_err_check(const struct s5p_uart *const uart, int op) | |
117 | 98 | { |
118 | - struct s5p_uart *const uart = s5p_get_base_uart(dev_index); | |
119 | 99 | unsigned int mask; |
120 | 100 | |
121 | 101 | /* |
122 | 102 | |
123 | 103 | |
124 | 104 | |
125 | 105 | |
126 | 106 | |
127 | 107 | |
128 | 108 | |
129 | 109 | |
130 | 110 | |
131 | 111 | |
132 | 112 | |
133 | 113 | |
134 | 114 | |
135 | 115 | |
136 | 116 | |
137 | 117 | |
138 | 118 | |
139 | 119 | |
140 | 120 | |
141 | 121 | |
... | ... | @@ -133,170 +113,79 @@ |
133 | 113 | return readl(&uart->uerstat) & mask; |
134 | 114 | } |
135 | 115 | |
136 | -/* | |
137 | - * Read a single byte from the serial port. Returns 1 on success, 0 | |
138 | - * otherwise. When the function is succesfull, the character read is | |
139 | - * written into its argument c. | |
140 | - */ | |
141 | -static int serial_getc_dev(const int dev_index) | |
116 | +static int s5p_serial_getc(struct udevice *dev) | |
142 | 117 | { |
143 | - struct s5p_uart *const uart = s5p_get_base_uart(dev_index); | |
118 | + struct s5p_serial_platdata *plat = dev->platdata; | |
119 | + struct s5p_uart *const uart = plat->reg; | |
144 | 120 | |
145 | - if (!config.enabled) | |
146 | - return 0; | |
121 | + if (!(readl(&uart->ufstat) & RX_FIFO_COUNT_MASK)) | |
122 | + return -EAGAIN; | |
147 | 123 | |
148 | - /* wait for character to arrive */ | |
149 | - while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK | | |
150 | - RX_FIFO_FULL_MASK))) { | |
151 | - if (serial_err_check(dev_index, 0)) | |
152 | - return 0; | |
153 | - } | |
154 | - | |
124 | + serial_err_check(uart, 0); | |
155 | 125 | return (int)(readb(&uart->urxh) & 0xff); |
156 | 126 | } |
157 | 127 | |
158 | -/* | |
159 | - * Output a single byte to the serial port. | |
160 | - */ | |
161 | -static void serial_putc_dev(const char c, const int dev_index) | |
128 | +static int s5p_serial_putc(struct udevice *dev, const char ch) | |
162 | 129 | { |
163 | - struct s5p_uart *const uart = s5p_get_base_uart(dev_index); | |
130 | + struct s5p_serial_platdata *plat = dev->platdata; | |
131 | + struct s5p_uart *const uart = plat->reg; | |
164 | 132 | |
165 | - if (!config.enabled) | |
166 | - return; | |
133 | + if (readl(&uart->ufstat) & TX_FIFO_FULL) | |
134 | + return -EAGAIN; | |
167 | 135 | |
168 | - /* wait for room in the tx FIFO */ | |
169 | - while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) { | |
170 | - if (serial_err_check(dev_index, 1)) | |
171 | - return; | |
172 | - } | |
136 | + writeb(ch, &uart->utxh); | |
137 | + serial_err_check(uart, 1); | |
173 | 138 | |
174 | - writeb(c, &uart->utxh); | |
175 | - | |
176 | - /* If \n, also do \r */ | |
177 | - if (c == '\n') | |
178 | - serial_putc('\r'); | |
139 | + return 0; | |
179 | 140 | } |
180 | 141 | |
181 | -/* | |
182 | - * Test whether a character is in the RX buffer | |
183 | - */ | |
184 | -static int serial_tstc_dev(const int dev_index) | |
142 | +static int s5p_serial_pending(struct udevice *dev, bool input) | |
185 | 143 | { |
186 | - struct s5p_uart *const uart = s5p_get_base_uart(dev_index); | |
144 | + struct s5p_serial_platdata *plat = dev->platdata; | |
145 | + struct s5p_uart *const uart = plat->reg; | |
146 | + uint32_t ufstat = readl(&uart->ufstat); | |
187 | 147 | |
188 | - if (!config.enabled) | |
189 | - return 0; | |
190 | - | |
191 | - return (int)(readl(&uart->utrstat) & 0x1); | |
148 | + if (input) | |
149 | + return (ufstat & RX_FIFO_COUNT_MASK) >> RX_FIFO_COUNT_SHIFT; | |
150 | + else | |
151 | + return (ufstat & TX_FIFO_COUNT_MASK) >> TX_FIFO_COUNT_SHIFT; | |
192 | 152 | } |
193 | 153 | |
194 | -static void serial_puts_dev(const char *s, const int dev_index) | |
154 | +static int s5p_serial_ofdata_to_platdata(struct udevice *dev) | |
195 | 155 | { |
196 | - while (*s) | |
197 | - serial_putc_dev(*s++, dev_index); | |
198 | -} | |
156 | + struct s5p_serial_platdata *plat = dev->platdata; | |
157 | + fdt_addr_t addr; | |
199 | 158 | |
200 | -/* Multi serial device functions */ | |
201 | -#define DECLARE_S5P_SERIAL_FUNCTIONS(port) \ | |
202 | -static int s5p_serial##port##_init(void) { return serial_init_dev(port); } \ | |
203 | -static void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \ | |
204 | -static int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \ | |
205 | -static int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \ | |
206 | -static void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \ | |
207 | -static void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); } | |
159 | + addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); | |
160 | + if (addr == FDT_ADDR_T_NONE) | |
161 | + return -EINVAL; | |
208 | 162 | |
209 | -#define INIT_S5P_SERIAL_STRUCTURE(port, __name) { \ | |
210 | - .name = __name, \ | |
211 | - .start = s5p_serial##port##_init, \ | |
212 | - .stop = NULL, \ | |
213 | - .setbrg = s5p_serial##port##_setbrg, \ | |
214 | - .getc = s5p_serial##port##_getc, \ | |
215 | - .tstc = s5p_serial##port##_tstc, \ | |
216 | - .putc = s5p_serial##port##_putc, \ | |
217 | - .puts = s5p_serial##port##_puts, \ | |
218 | -} | |
163 | + plat->reg = (struct s5p_uart *)addr; | |
164 | + plat->port_id = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "id", -1); | |
219 | 165 | |
220 | -DECLARE_S5P_SERIAL_FUNCTIONS(0); | |
221 | -struct serial_device s5p_serial0_device = | |
222 | - INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0"); | |
223 | -DECLARE_S5P_SERIAL_FUNCTIONS(1); | |
224 | -struct serial_device s5p_serial1_device = | |
225 | - INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1"); | |
226 | -DECLARE_S5P_SERIAL_FUNCTIONS(2); | |
227 | -struct serial_device s5p_serial2_device = | |
228 | - INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2"); | |
229 | -DECLARE_S5P_SERIAL_FUNCTIONS(3); | |
230 | -struct serial_device s5p_serial3_device = | |
231 | - INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3"); | |
232 | - | |
233 | -#ifdef CONFIG_OF_CONTROL | |
234 | -int fdtdec_decode_console(int *index, struct fdt_serial *uart) | |
235 | -{ | |
236 | - const void *blob = gd->fdt_blob; | |
237 | - int node; | |
238 | - | |
239 | - node = fdt_path_offset(blob, "console"); | |
240 | - if (node < 0) | |
241 | - return node; | |
242 | - | |
243 | - uart->base_addr = fdtdec_get_addr(blob, node, "reg"); | |
244 | - if (uart->base_addr == FDT_ADDR_T_NONE) | |
245 | - return -FDT_ERR_NOTFOUND; | |
246 | - | |
247 | - uart->port_id = fdtdec_get_int(blob, node, "id", -1); | |
248 | - uart->enabled = fdtdec_get_is_enabled(blob, node); | |
249 | - | |
250 | 166 | return 0; |
251 | 167 | } |
252 | -#endif | |
253 | 168 | |
254 | -__weak struct serial_device *default_serial_console(void) | |
255 | -{ | |
256 | -#ifdef CONFIG_OF_CONTROL | |
257 | - int index = 0; | |
169 | +static const struct dm_serial_ops s5p_serial_ops = { | |
170 | + .putc = s5p_serial_putc, | |
171 | + .pending = s5p_serial_pending, | |
172 | + .getc = s5p_serial_getc, | |
173 | + .setbrg = s5p_serial_setbrg, | |
174 | +}; | |
258 | 175 | |
259 | - if ((!config.base_addr) && (fdtdec_decode_console(&index, &config))) { | |
260 | - debug("Cannot decode default console node\n"); | |
261 | - return NULL; | |
262 | - } | |
176 | +static const struct udevice_id s5p_serial_ids[] = { | |
177 | + { .compatible = "samsung,exynos4210-uart" }, | |
178 | + { } | |
179 | +}; | |
263 | 180 | |
264 | - switch (config.port_id) { | |
265 | - case 0: | |
266 | - return &s5p_serial0_device; | |
267 | - case 1: | |
268 | - return &s5p_serial1_device; | |
269 | - case 2: | |
270 | - return &s5p_serial2_device; | |
271 | - case 3: | |
272 | - return &s5p_serial3_device; | |
273 | - default: | |
274 | - debug("Unknown config.port_id: %d", config.port_id); | |
275 | - break; | |
276 | - } | |
277 | - | |
278 | - return NULL; | |
279 | -#else | |
280 | - config.enabled = 1; | |
281 | -#if defined(CONFIG_SERIAL0) | |
282 | - return &s5p_serial0_device; | |
283 | -#elif defined(CONFIG_SERIAL1) | |
284 | - return &s5p_serial1_device; | |
285 | -#elif defined(CONFIG_SERIAL2) | |
286 | - return &s5p_serial2_device; | |
287 | -#elif defined(CONFIG_SERIAL3) | |
288 | - return &s5p_serial3_device; | |
289 | -#else | |
290 | -#error "CONFIG_SERIAL? missing." | |
291 | -#endif | |
292 | -#endif | |
293 | -} | |
294 | - | |
295 | -void s5p_serial_initialize(void) | |
296 | -{ | |
297 | - serial_register(&s5p_serial0_device); | |
298 | - serial_register(&s5p_serial1_device); | |
299 | - serial_register(&s5p_serial2_device); | |
300 | - serial_register(&s5p_serial3_device); | |
301 | -} | |
181 | +U_BOOT_DRIVER(serial_s5p) = { | |
182 | + .name = "serial_s5p", | |
183 | + .id = UCLASS_SERIAL, | |
184 | + .of_match = s5p_serial_ids, | |
185 | + .ofdata_to_platdata = s5p_serial_ofdata_to_platdata, | |
186 | + .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata), | |
187 | + .probe = s5p_serial_probe, | |
188 | + .ops = &s5p_serial_ops, | |
189 | + .flags = DM_FLAG_PRE_RELOC, | |
190 | +}; |
include/configs/exynos-common.h
include/configs/s5p_goni.h