Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/riscv/sifive/sifive_uart.c
39478 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2019 Axiado Corporation
5
* All rights reserved.
6
*
7
* This software was developed in part by Kristof Provost under contract for
8
* Axiado Corporation.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
*
19
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
* SUCH DAMAGE.
30
*/
31
32
#include <sys/param.h>
33
#include <sys/systm.h>
34
#include <sys/bus.h>
35
#include <sys/kernel.h>
36
#include <sys/lock.h>
37
#include <sys/module.h>
38
#include <sys/mutex.h>
39
#include <sys/rman.h>
40
41
#include <machine/bus.h>
42
#include <machine/cpu.h>
43
44
#include <dev/clk/clk.h>
45
46
#include <dev/ofw/ofw_bus.h>
47
#include <dev/ofw/ofw_bus_subr.h>
48
#include <dev/ofw/openfirm.h>
49
50
#include <dev/uart/uart.h>
51
#include <dev/uart/uart_bus.h>
52
#include <dev/uart/uart_cpu.h>
53
#include <dev/uart/uart_cpu_fdt.h>
54
55
#include "uart_if.h"
56
57
#define SFUART_TXDATA 0x00
58
#define SFUART_TXDATA_FULL (1 << 31)
59
#define SFUART_RXDATA 0x04
60
#define SFUART_RXDATA_EMPTY (1 << 31)
61
#define SFUART_TXCTRL 0x08
62
#define SFUART_TXCTRL_ENABLE 0x01
63
#define SFUART_TXCTRL_NSTOP 0x02
64
#define SFUART_TXCTRL_TXCNT 0x70000
65
#define SFUART_TXCTRL_TXCNT_SHIFT 16
66
#define SFUART_RXCTRL 0x0c
67
#define SFUART_RXCTRL_ENABLE 0x01
68
#define SFUART_RXCTRL_RXCNT 0x70000
69
#define SFUART_RXCTRL_RXCNT_SHIFT 16
70
#define SFUART_IRQ_ENABLE 0x10
71
#define SFUART_IRQ_ENABLE_TXWM 0x01
72
#define SFUART_IRQ_ENABLE_RXWM 0x02
73
#define SFUART_IRQ_PENDING 0x14
74
#define SFUART_IRQ_PENDING_TXWM 0x01
75
#define SFUART_IRQ_PENDING_RXQM 0x02
76
#define SFUART_DIV 0x18
77
#define SFUART_REGS_SIZE 0x1c
78
79
#define SFUART_RX_FIFO_DEPTH 8
80
#define SFUART_TX_FIFO_DEPTH 8
81
82
struct sfuart_softc {
83
struct uart_softc uart_softc;
84
clk_t clk;
85
};
86
87
static int
88
sfuart_probe(struct uart_bas *bas)
89
{
90
91
bas->regiowidth = 4;
92
93
return (0);
94
}
95
96
static void
97
sfuart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
98
int parity)
99
{
100
uint32_t reg;
101
102
uart_setreg(bas, SFUART_IRQ_ENABLE, 0);
103
104
/* Enable RX and configure the watermark so that we get an interrupt
105
* when a single character arrives (if interrupts are enabled). */
106
reg = SFUART_RXCTRL_ENABLE;
107
reg |= (0 << SFUART_RXCTRL_RXCNT_SHIFT);
108
uart_setreg(bas, SFUART_RXCTRL, reg);
109
110
/* Enable TX and configure the watermark so that we get an interrupt
111
* when there's room for one more character in the TX fifo (if
112
* interrupts are enabled). */
113
reg = SFUART_TXCTRL_ENABLE;
114
reg |= (1 << SFUART_TXCTRL_TXCNT_SHIFT);
115
if (stopbits == 2)
116
reg |= SFUART_TXCTRL_NSTOP;
117
uart_setreg(bas, SFUART_TXCTRL, reg);
118
119
/* Don't touch DIV. Assume that's set correctly until we can
120
* reconfigure. */
121
}
122
123
static void
124
sfuart_putc(struct uart_bas *bas, int c)
125
{
126
127
while ((uart_getreg(bas, SFUART_TXDATA) & SFUART_TXDATA_FULL)
128
!= 0)
129
cpu_spinwait();
130
131
uart_setreg(bas, SFUART_TXDATA, c);
132
}
133
134
static int
135
sfuart_rxready(struct uart_bas *bas)
136
{
137
/*
138
* Unfortunately the FIFO empty flag is in the FIFO data register so
139
* reading it would dequeue the character. Instead, rely on the fact
140
* we've configured the watermark to be 0 and that interrupts are off
141
* when using the low-level console function, and read the interrupt
142
* pending state instead.
143
*/
144
return ((uart_getreg(bas, SFUART_IRQ_PENDING) &
145
SFUART_IRQ_PENDING_RXQM) != 0);
146
}
147
148
static int
149
sfuart_getc(struct uart_bas *bas, struct mtx *hwmtx)
150
{
151
int c;
152
153
uart_lock(hwmtx);
154
155
while (((c = uart_getreg(bas, SFUART_RXDATA)) &
156
SFUART_RXDATA_EMPTY) != 0) {
157
uart_unlock(hwmtx);
158
DELAY(4);
159
uart_lock(hwmtx);
160
}
161
162
uart_unlock(hwmtx);
163
164
return (c & 0xff);
165
}
166
167
static int
168
sfuart_bus_probe(struct uart_softc *sc)
169
{
170
int error;
171
172
error = sfuart_probe(&sc->sc_bas);
173
if (error)
174
return (error);
175
176
sc->sc_rxfifosz = SFUART_RX_FIFO_DEPTH;
177
sc->sc_txfifosz = SFUART_TX_FIFO_DEPTH;
178
sc->sc_hwiflow = 0;
179
sc->sc_hwoflow = 0;
180
181
device_set_desc(sc->sc_dev, "SiFive UART");
182
183
return (0);
184
}
185
186
static int
187
sfuart_bus_attach(struct uart_softc *sc)
188
{
189
struct uart_bas *bas;
190
struct sfuart_softc *sfsc;
191
uint64_t freq;
192
uint32_t reg;
193
int error;
194
195
sfsc = (struct sfuart_softc *)sc;
196
bas = &sc->sc_bas;
197
198
error = clk_get_by_ofw_index(sc->sc_dev, 0, 0, &sfsc->clk);
199
if (error) {
200
device_printf(sc->sc_dev, "couldn't allocate clock\n");
201
return (ENXIO);
202
}
203
204
error = clk_enable(sfsc->clk);
205
if (error) {
206
device_printf(sc->sc_dev, "couldn't enable clock\n");
207
return (ENXIO);
208
}
209
210
error = clk_get_freq(sfsc->clk, &freq);
211
if (error || freq == 0) {
212
clk_disable(sfsc->clk);
213
device_printf(sc->sc_dev, "couldn't get clock frequency\n");
214
return (ENXIO);
215
}
216
217
bas->rclk = freq;
218
219
/* Enable RX/RX */
220
reg = SFUART_RXCTRL_ENABLE;
221
reg |= (0 << SFUART_RXCTRL_RXCNT_SHIFT);
222
uart_setreg(bas, SFUART_RXCTRL, reg);
223
224
reg = SFUART_TXCTRL_ENABLE;
225
reg |= (1 << SFUART_TXCTRL_TXCNT_SHIFT);
226
uart_setreg(bas, SFUART_TXCTRL, reg);
227
228
/* Enable RX interrupt */
229
uart_setreg(bas, SFUART_IRQ_ENABLE, SFUART_IRQ_ENABLE_RXWM);
230
231
return (0);
232
}
233
234
static int
235
sfuart_bus_detach(struct uart_softc *sc)
236
{
237
struct sfuart_softc *sfsc;
238
struct uart_bas *bas;
239
240
sfsc = (struct sfuart_softc *)sc;
241
bas = &sc->sc_bas;
242
243
/* Disable RX/TX */
244
uart_setreg(bas, SFUART_RXCTRL, 0);
245
uart_setreg(bas, SFUART_TXCTRL, 0);
246
247
/* Disable interrupts */
248
uart_setreg(bas, SFUART_IRQ_ENABLE, 0);
249
250
clk_disable(sfsc->clk);
251
252
return (0);
253
}
254
255
static int
256
sfuart_bus_flush(struct uart_softc *sc, int what)
257
{
258
struct uart_bas *bas;
259
uint32_t reg;
260
261
bas = &sc->sc_bas;
262
uart_lock(sc->sc_hwmtx);
263
264
if (what & UART_FLUSH_TRANSMITTER) {
265
do {
266
reg = uart_getreg(bas, SFUART_TXDATA);
267
} while ((reg & SFUART_TXDATA_FULL) != 0);
268
}
269
270
if (what & UART_FLUSH_RECEIVER) {
271
do {
272
reg = uart_getreg(bas, SFUART_RXDATA);
273
} while ((reg & SFUART_RXDATA_EMPTY) == 0);
274
}
275
uart_unlock(sc->sc_hwmtx);
276
277
return (0);
278
}
279
280
#define SIGCHG(c, i, s, d) \
281
do { \
282
if (c) \
283
i |= ((i) & (s)) ? (s) : (s) | (d); \
284
else \
285
i = ((i) & (s)) ? ((i) & ~(s)) | (d) : (i); \
286
} while (0)
287
288
static int
289
sfuart_bus_getsig(struct uart_softc *sc)
290
{
291
uint32_t new, old, sig;
292
293
do {
294
old = sc->sc_hwsig;
295
sig = old;
296
SIGCHG(1, sig, SER_DSR, SER_DDSR);
297
SIGCHG(1, sig, SER_DCD, SER_DDCD);
298
SIGCHG(1, sig, SER_CTS, SER_DCTS);
299
new = sig & ~SER_MASK_DELTA;
300
} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
301
302
return (sig);
303
}
304
305
static int
306
sfuart_bus_setsig(struct uart_softc *sc, int sig)
307
{
308
uint32_t new, old;
309
310
do {
311
old = sc->sc_hwsig;
312
new = old;
313
if (sig & SER_DDTR) {
314
SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
315
}
316
if (sig & SER_DRTS) {
317
SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
318
}
319
} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
320
321
return (0);
322
}
323
324
static int
325
sfuart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
326
{
327
struct uart_bas *bas;
328
uint32_t reg;
329
int error;
330
331
bas = &sc->sc_bas;
332
333
uart_lock(sc->sc_hwmtx);
334
335
switch (request) {
336
case UART_IOCTL_BAUD:
337
reg = uart_getreg(bas, SFUART_DIV);
338
if (reg == 0) {
339
/* Possible if the divisor hasn't been set up yet. */
340
error = ENXIO;
341
break;
342
}
343
*(int*)data = bas->rclk / (reg + 1);
344
error = 0;
345
break;
346
default:
347
error = EINVAL;
348
break;
349
}
350
351
uart_unlock(sc->sc_hwmtx);
352
353
return (error);
354
}
355
356
static int
357
sfuart_bus_ipend(struct uart_softc *sc)
358
{
359
struct uart_bas *bas;
360
int ipend;
361
uint32_t reg, ie;
362
363
bas = &sc->sc_bas;
364
uart_lock(sc->sc_hwmtx);
365
366
ipend = 0;
367
reg = uart_getreg(bas, SFUART_IRQ_PENDING);
368
ie = uart_getreg(bas, SFUART_IRQ_ENABLE);
369
370
if ((reg & SFUART_IRQ_PENDING_TXWM) != 0 &&
371
(ie & SFUART_IRQ_ENABLE_TXWM) != 0) {
372
ipend |= SER_INT_TXIDLE;
373
374
/* Disable TX interrupt */
375
ie &= ~(SFUART_IRQ_ENABLE_TXWM);
376
uart_setreg(bas, SFUART_IRQ_ENABLE, ie);
377
}
378
379
if ((reg & SFUART_IRQ_PENDING_RXQM) != 0)
380
ipend |= SER_INT_RXREADY;
381
382
uart_unlock(sc->sc_hwmtx);
383
384
return (ipend);
385
}
386
387
static int
388
sfuart_bus_param(struct uart_softc *sc, int baudrate, int databits,
389
int stopbits, int parity)
390
{
391
struct uart_bas *bas;
392
uint32_t reg;
393
394
bas = &sc->sc_bas;
395
396
if (databits != 8)
397
return (EINVAL);
398
399
if (parity != UART_PARITY_NONE)
400
return (EINVAL);
401
402
uart_lock(sc->sc_hwmtx);
403
404
reg = uart_getreg(bas, SFUART_TXCTRL);
405
if (stopbits == 2) {
406
reg |= SFUART_TXCTRL_NSTOP;
407
} else if (stopbits == 1) {
408
reg &= ~SFUART_TXCTRL_NSTOP;
409
} else {
410
uart_unlock(sc->sc_hwmtx);
411
return (EINVAL);
412
}
413
414
if (baudrate > 0 && bas->rclk != 0) {
415
reg = (bas->rclk / baudrate) - 1;
416
uart_setreg(bas, SFUART_DIV, reg);
417
}
418
419
uart_unlock(sc->sc_hwmtx);
420
return (0);
421
}
422
423
static int
424
sfuart_bus_receive(struct uart_softc *sc)
425
{
426
struct uart_bas *bas;
427
uint32_t reg;
428
429
bas = &sc->sc_bas;
430
uart_lock(sc->sc_hwmtx);
431
432
reg = uart_getreg(bas, SFUART_RXDATA);
433
while ((reg & SFUART_RXDATA_EMPTY) == 0) {
434
if (uart_rx_full(sc)) {
435
sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
436
break;
437
}
438
439
uart_rx_put(sc, reg & 0xff);
440
441
reg = uart_getreg(bas, SFUART_RXDATA);
442
}
443
444
uart_unlock(sc->sc_hwmtx);
445
446
return (0);
447
}
448
449
static int
450
sfuart_bus_transmit(struct uart_softc *sc)
451
{
452
struct uart_bas *bas;
453
int i;
454
uint32_t reg;
455
456
bas = &sc->sc_bas;
457
uart_lock(sc->sc_hwmtx);
458
459
reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
460
reg |= SFUART_IRQ_ENABLE_TXWM;
461
uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
462
463
for (i = 0; i < sc->sc_txdatasz; i++)
464
sfuart_putc(bas, sc->sc_txbuf[i]);
465
466
sc->sc_txbusy = 1;
467
468
uart_unlock(sc->sc_hwmtx);
469
470
return (0);
471
}
472
473
static void
474
sfuart_bus_grab(struct uart_softc *sc)
475
{
476
struct uart_bas *bas;
477
uint32_t reg;
478
479
bas = &sc->sc_bas;
480
uart_lock(sc->sc_hwmtx);
481
482
reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
483
reg &= ~(SFUART_IRQ_ENABLE_TXWM | SFUART_IRQ_PENDING_RXQM);
484
uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
485
486
uart_unlock(sc->sc_hwmtx);
487
}
488
489
static void
490
sfuart_bus_ungrab(struct uart_softc *sc)
491
{
492
struct uart_bas *bas;
493
uint32_t reg;
494
495
bas = &sc->sc_bas;
496
uart_lock(sc->sc_hwmtx);
497
498
reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
499
reg |= SFUART_IRQ_ENABLE_TXWM | SFUART_IRQ_PENDING_RXQM;
500
uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
501
502
uart_unlock(sc->sc_hwmtx);
503
}
504
505
static kobj_method_t sfuart_methods[] = {
506
KOBJMETHOD(uart_probe, sfuart_bus_probe),
507
KOBJMETHOD(uart_attach, sfuart_bus_attach),
508
KOBJMETHOD(uart_detach, sfuart_bus_detach),
509
KOBJMETHOD(uart_flush, sfuart_bus_flush),
510
KOBJMETHOD(uart_getsig, sfuart_bus_getsig),
511
KOBJMETHOD(uart_setsig, sfuart_bus_setsig),
512
KOBJMETHOD(uart_ioctl, sfuart_bus_ioctl),
513
KOBJMETHOD(uart_ipend, sfuart_bus_ipend),
514
KOBJMETHOD(uart_param, sfuart_bus_param),
515
KOBJMETHOD(uart_receive, sfuart_bus_receive),
516
KOBJMETHOD(uart_transmit, sfuart_bus_transmit),
517
KOBJMETHOD(uart_grab, sfuart_bus_grab),
518
KOBJMETHOD(uart_ungrab, sfuart_bus_ungrab),
519
KOBJMETHOD_END
520
};
521
522
static struct uart_ops sfuart_ops = {
523
.probe = sfuart_probe,
524
.init = sfuart_init,
525
.term = NULL,
526
.putc = sfuart_putc,
527
.rxready = sfuart_rxready,
528
.getc = sfuart_getc,
529
};
530
531
struct uart_class sfuart_class = {
532
"sifiveuart",
533
sfuart_methods,
534
sizeof(struct sfuart_softc),
535
.uc_ops = &sfuart_ops,
536
.uc_range = SFUART_REGS_SIZE,
537
.uc_rclk = 0,
538
.uc_rshift = 0
539
};
540
541
static struct ofw_compat_data compat_data[] = {
542
{ "sifive,uart0", (uintptr_t)&sfuart_class },
543
{ NULL, (uintptr_t)NULL }
544
};
545
546
UART_FDT_CLASS_AND_DEVICE(compat_data);
547
548