commit f3afd8162fad9e712b67ada1eb07763230154d78
parent a074501aeb8c9764eb13c50813cf51fcf5142792
Author: Dimitris Papastamos <dimitris.papastamos@arm.com>
Date: Wed, 20 Feb 2019 13:04:55 +0000
Merge "[uart] Implement remaining bits"
Diffstat:
| M | drivers/devuart.c | | | 80 | +++++++++++++++++++++++++++++++++++-------------------------------------------- |
| M | drivers/pl011.c | | | 206 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | drivers/uart.h | | | 32 | +++++++++++--------------------- |
3 files changed, 252 insertions(+), 66 deletions(-)
diff --git a/drivers/devuart.c b/drivers/devuart.c
@@ -91,16 +91,16 @@ uartstatus(Uart *up, void *buf, long long offset, int n)
len = ksnprint(tmp, sizeof(tmp),
"b%ld c%d d%d e%d l%d m%d p%c r%d s%d i%d\n",
- st.rate,
- (st.hang & SIG_DCD) != 0,
- st.dtr,
- (st.hang & SIG_DSR) != 0,
- st.nbits,
- st.ctsflow,
- st.parity,
- st.rts,
- st.stop,
- st.fifo);
+ up->rate,
+ (up->hang & SIG_DCD) != 0,
+ up->dtr,
+ (up->hang & SIG_DSR) != 0,
+ up->nbits,
+ up->ctsflow,
+ up->parity,
+ up->rts,
+ up->nstop,
+ up->fifo);
if (offset >= len)
return 0;
@@ -123,7 +123,6 @@ uartdrain(Uart *up)
* cn Set hangup on DCD if n is non-zero; else clear it.
* dn Set DTR if n is non-zero; else clear it.
* f Flush output queue.
- * h Close input and output queues.
* in Enable/disable the FIFOs. If n is zero the FIFOs are
* disabled; otherwise n is taken as a trigger level for
* the FIFOs. The trigger levels supported are device
@@ -131,17 +130,13 @@ uartdrain(Uart *up)
* nised, but non-zero, value of n causes the maximum-
* supported trigger level to be set.
* en Set hangup on DSR if n is non-zero; else clear it.
- * kn Send a break lasting n milliseconds.
* ln Set number of bits per byte to n. Legal values are 5,
* 6, 7, or 8.
* mn Obey modem CTS signal if n is non-zero; else clear it.
- * n Make writes non-blocking.
* pc Set parity to odd if c is o, to even if c is e; else
* set no parity.
- * qn Set input and output queue limits to n.
* rn Set RTS if n is non-zero; else clear it.
* sn Set number of stop bits to n. Legal values are 1 or 2.
- * wn Set the uart clock timer to n times 100us.
*/
static int
uartctl(Uart *up, char *cfg, int n)
@@ -150,8 +145,8 @@ uartctl(Uart *up, char *cfg, int n)
Uartphy *phy = up->phy;
unsigned char *s = (unsigned char *) cfg;
- for (cnt = i = 0; i < n; cnt = i++) {
- int par, c = s[i];
+ for (cnt = i = 0; i < n; cnt = ++i) {
+ int sig, par, c = s[i];
if (isspace(c))
continue;
@@ -165,51 +160,47 @@ uartctl(Uart *up, char *cfg, int n)
uartdrain(up);
r = (*phy->setbauds)(up, par);
break;
+ case 'e':
+ up->hang &= ~ (1 << SIG_DSR);
+ up->hang |= (par != 0) << SIG_DSR;
+ r = 0;
+ break;
case 'c':
+ up->hang &= ~ (1 << SIG_DCD);
+ up->hang |= (par != 0) << SIG_DCD;
r = 0;
- up->hang = par;
break;
+ case 'r':
+ par = (par != 0) << SIG_RTS;
+ sig = SIG_RTS;
+ goto signal;
case 'd':
- par = (par != 0) << SIG_DTR;
- r = (*phy->setsignal)(up, SIG_DTR, par);
+ par = (par != 0) << SIG_DCD;
+ sig = SIG_DCD;
+ signal:
+ r = (*phy->signal)(up, sig, par);
break;
case 'f':
- // r = drv->flush(drv);
- case 'h':
- // r = drv->cleanqueues(drv);
- case 'i':
- // r = drv->setfifo(drv, par);
+ r = (*phy->flush)(up);
break;
- case 'k':
- // r = drv->break(drv, par);
+ case 'i':
+ r = (*phy->setfifo)(up, par);
break;
case 'l':
- // r = drv->numbits(drv, par);
+ r = (*phy->numbits)(up, par);
break;
case 'm':
- // r = drv->obey(drv, CTS, par);
- break;
- case 'n':
- // r = drv->wrmode(drv, par);
+ r = (*phy->obey)(up, SIG_CTS, par != 0);
break;
case 'p':
if (par != OPARITY && par != EPARITY)
par = NPARITY;
- // r = drv->parity(drv, par);
- break;
- case 'q':
- // r = drv->queuesiz(drv, par);
- break;
- case 'r':
- // r = drv->signal(drv, RTS);
+ r = (*phy->parity)(up, par);
break;
case 's':
if (par != 1 && par != 2)
return -1;
- // r = drv->bitstop(drv, par);
- break;
- case 'w':
- // r = drv->timer(drv, par);
+ r = (*phy->bitstop)(up, par);
break;
default:
r = -1;
@@ -312,8 +303,7 @@ uartlink(Uartphy *phy, Attr *attr)
siz = strlen(cfg);
if (uartctl(up, cfg, siz) != siz)
- //panic("uartlink");
- ;
+ panic("uartlink");
}
const Dev uartdevtab = {
diff --git a/drivers/pl011.c b/drivers/pl011.c
@@ -62,6 +62,7 @@ enum uartlcr_h {
WLEN_6 = 1 << 5, /* word lenght (6 bits) */
WLEN_7 = 2 << 5, /* word lenght (7 bits) */
WLEN_8 = 3 << 5, /* word lenght (8 bits) */
+ WLEN = 3 << 5, /* clean mask for word length */
SPS = 1 << 7, /* Stick parity select */
};
@@ -95,6 +96,11 @@ enum uartfr {
FR_RI = 1 << 8, /* Ring indicator */
};
+enum uartifls {
+ TXIFLSEL = 0,
+ RXIFLSEL = 3,
+};
+
static unsigned long
plin(Uart *up, int reg)
{
@@ -147,6 +153,14 @@ pl011init(Uart *up, Attr *attr)
plout(up, UARTIBRD, 0);
plout(up, UARTFBRD, 0);
plout(up, UARTCR, cr | UARTEN | TXE | RXE);
+
+ up->rate = 0;
+ up->nbits = 8;
+ up->nstop = 1;
+ up->hang = 0;
+ up->ctsflow = 0;
+ up->parity = NPARITY;
+ up->fifo = 0;
}
static int
@@ -177,9 +191,201 @@ pl011write(Uart *up, void *buf, int n)
return n;
}
+static int
+pl011signal(Uart *up, int sign, int val)
+{
+ unsigned cr;
+
+ cr = plin(up, UARTCR);
+
+ switch (sign) {
+ case SIG_DTR:
+ cr &= ~RTS;
+ if (val)
+ cr |= RTS;
+ up->dtr = (val != 0);
+ break;
+ case SIG_RTS:
+ cr &= ~RTS;
+ if (val)
+ cr |= RTS;
+ up->rts = (val != 0);
+ break;
+ default:
+ return -1;
+ }
+
+ plout(up, UARTCR, cr);
+
+ return 1;
+}
+
+static int
+pl011flush(Uart *up)
+{
+ unsigned crl;
+
+ crl = plin(up, UARTLCR_H);
+
+ plout(up, UARTLCR_H, crl & ~FEN);
+ plout(up, UARTLCR_H, crl);
+
+ return 0;
+}
+
+static int
+pl011fifo(Uart *up, int trigger)
+{
+ unsigned fil, cr, val;
+
+ cr = plin(up, UARTCR);
+
+ up->fifo = trigger;
+ switch (trigger) {
+ case 0:
+ case 1:
+ plout(up, UARTCR, cr & ~FEN);
+ return 0;
+ case 4:
+ val = 1;
+ case 8:
+ val = 2;
+ break;
+ default:
+ val = 4;
+ break;
+ }
+ fil = (val << TXIFLSEL) | (val << RXIFLSEL);
+
+ plout(up, UARTCR, cr | FEN);
+ plout(up, UARTIFLS, fil);
+
+ return 0;
+}
+
+static int
+pl011bits(Uart *up, int nbits)
+{
+ unsigned lcr_h, val;
+
+ lcr_h = plin(up, UARTLCR_H);
+
+ switch (nbits) {
+ case 5:
+ val = WLEN_5;
+ break;
+ case 6:
+ val = WLEN_6;
+ break;
+ case 7:
+ val = WLEN_7;
+ break;
+ case 8:
+ val = WLEN_8;
+ break;
+ default:
+ return -1;
+ }
+
+ up->nbits = val;
+ lcr_h &= ~WLEN;
+ lcr_h |= val;
+ plout(up, UARTLCR_H, lcr_h);
+
+ return 0;
+}
+
+static int
+pl011parity(Uart *up, int mode)
+{
+ unsigned lcr_h;
+
+ lcr_h = plin(up, UARTLCR_H);
+ lcr_h &= ~(SPS | EPS | PEN);
+
+ switch (mode) {
+ case OPARITY:
+ lcr_h |= PEN;
+ break;
+ case EPARITY:
+ lcr_h |= PEN | EPS;
+ break;
+ case NPARITY:
+ break;
+ default:
+ return -1;
+ }
+
+ plout(up, UARTLCR_H, lcr_h);
+
+ return 0;
+}
+
+static int
+pl011stop(Uart *up, int nbits)
+{
+ unsigned lcr_h;
+
+ lcr_h = plin(up, UARTLCR_H);
+ lcr_h &= ~STP2;
+
+ switch (nbits) {
+ case 1:
+ break;
+ case 2:
+ lcr_h |= STP2;
+ break;
+ default:
+ return -1;
+ }
+
+ up->nbits = nbits;
+ plout(up, UARTLCR_H, lcr_h);
+
+ return 0;
+}
+
+static int
+pl011obey(Uart *up, int signal, int ena)
+{
+ unsigned cr;
+
+ cr = plin(up, UARTCR);
+ ena = (ena != 0);
+
+ switch (signal) {
+ case SIG_CTS:
+ cr &= ~CTSEN;
+ if (ena)
+ cr |= CTSEN;
+ up->ctsflow = ena;
+ break;
+ default:
+ return -1;
+ }
+
+ plout(up, UARTCR, cr);
+
+ return 0;
+}
+
+static int
+pl011status(Uart *up, struct uartstat *st)
+{
+ return 0;
+}
+
const Uartphy pl011phy = {
.init = pl011init,
.setbauds = setbauds,
.read = pl011read,
.write = pl011write,
+ .signal = pl011signal,
+ .flush = pl011flush,
+ .setfifo = pl011fifo,
+ .numbits = pl011bits,
+ .parity = pl011parity,
+ .bitstop = pl011stop,
+ .obey = pl011obey,
+ .status = pl011status,
};
diff --git a/drivers/uart.h b/drivers/uart.h
@@ -18,17 +18,9 @@ enum parity {
NPARITY = 'n',
};
-
struct uartstat {
- long rate;
- int nbits;
- int hang;
- int ctsflow;
- int parity;
- int rts;
- int dtr;
- int stop;
- int fifo;
+ /* TODO: add stats */
+ int dummy;
};
struct uart {
@@ -36,9 +28,13 @@ struct uart {
int nbits;
int nstop;
int hang;
+ int ctsflow;
+ int parity;
+ int fifo;
+ int dtr;
+ int rts;
long clk;
void *base;
- void *phydata;
mutex_t m;
Uartphy *phy;
@@ -47,23 +43,17 @@ struct uart {
struct uartphy {
void (*init)(Uart *up, Attr *attr);
int (*setbauds)(Uart *up, long rate);
- int (*setsignal)(Uart *up, int mask, int val);
- int (*getsignal)(Uart *up, int mask, int *val);
+ int (*signal)(Uart *up, int mask, int val);
int (*read)(Uart *up, void *buf, int n);
int (*write)(Uart *up, void *buf, int n);
- int (*status)(Uart *up, struct uartstat *st);
-
int (*flush)(Uart *uart);
- int (*cleanqueues)(Uart *uart);
int (*setfifo)(Uart *uart, int size);
- int (*dobreak)(Uart *uart, int mils);
int (*numbits)(Uart *uart, int nbits);
- int (*flow)(Uart *uart, int type, int ena);
- int (*nonblocking)(Uart *uart, int ena);
int (*parity)(Uart *uart, int type);
- int (*queuesize)(Uart *up, int size);
int (*bitstop)(Uart *up, int nstops);
- int (*settimer)(Uart *up, int ntimes);
+ int (*obey)(Uart *uart, int type, int ena);
+
+ int (*status)(Uart *up, struct uartstat *st);
};
extern void uartlink(Uartphy *phy, Attr *attr);