Introduce grab and ungrab upcalls. When the kernel desires to grab the

console, it calls the grab functions. These functions should turn off
the RX interrupts, and any others that interfere. This makes mountroot
prompt work again. If there's more generalized need other than
prompting, many of these routines should be expanded to do those new
things.

Should have been part of r260889, but waasn't due to command line typo.

Reviewed by:	bde (with reservations)
This commit is contained in:
Warner Losh 2014-01-19 19:39:13 +00:00
parent 294ef64a17
commit d76a1ef4e1
11 changed files with 271 additions and 37 deletions

View File

@ -411,6 +411,13 @@ uart_bus_attach(device_t dev)
} else
sc = sc0;
/*
* Now that we know the softc for this device, connect the back
* pointer from the sysdev for this device, if any
*/
if (sc->sc_sysdev != NULL)
sc->sc_sysdev->sc = sc;
/*
* Protect ourselves against interrupts while we're not completely
* finished attaching and initializing. We don't expect interrupts
@ -617,3 +624,23 @@ uart_bus_resume(device_t dev)
sc = device_get_softc(dev);
return (UART_ATTACH(sc));
}
void
uart_grab(struct uart_devinfo *di)
{
uart_lock(di->hwmtx);
if (di->sc)
UART_GRAB(di->sc);
uart_unlock(di->hwmtx);
}
void
uart_ungrab(struct uart_devinfo *di)
{
uart_lock(di->hwmtx);
if (di->sc)
UART_UNGRAB(di->sc);
uart_unlock(di->hwmtx);
}

View File

@ -33,6 +33,8 @@
#include <sys/lock.h>
#include <sys/mutex.h>
struct uart_softc;
/*
* Low-level operations for use by console and/or debug port support.
*/
@ -43,8 +45,6 @@ struct uart_ops {
void (*putc)(struct uart_bas *, int);
int (*rxready)(struct uart_bas *);
int (*getc)(struct uart_bas *, struct mtx *);
void (*grab)(struct uart_bas *);
void (*ungrab)(struct uart_bas *);
};
extern bus_space_tag_t uart_bus_space_io;
@ -53,7 +53,6 @@ extern bus_space_tag_t uart_bus_space_mem;
/*
* Console and debug port device info.
*/
struct uart_softc;
struct uart_devinfo {
SLIST_ENTRY(uart_devinfo) next;
struct uart_ops *ops;
@ -70,6 +69,7 @@ struct uart_devinfo {
int (*detach)(struct uart_softc*);
void *cookie; /* Type dependent use. */
struct mtx *hwmtx;
struct uart_softc *sc; /* valid only from start of attach */
};
int uart_cpu_eqres(struct uart_bas *, struct uart_bas *);
@ -137,27 +137,6 @@ uart_putc(struct uart_devinfo *di, int c)
uart_unlock(di->hwmtx);
}
static __inline void
uart_grab(struct uart_devinfo *di)
{
uart_lock(di->hwmtx);
if (di->ops->grab)
di->ops->grab(&di->bas);
uart_unlock(di->hwmtx);
}
static __inline void
uart_ungrab(struct uart_devinfo *di)
{
uart_lock(di->hwmtx);
if (di->ops->ungrab)
di->ops->ungrab(&di->bas);
uart_unlock(di->hwmtx);
}
static __inline int
uart_rxready(struct uart_devinfo *di)
{
@ -190,4 +169,7 @@ uart_getc(struct uart_devinfo *di)
return (di->ops->getc(&di->bas, di->hwmtx));
}
void uart_grab(struct uart_devinfo *di);
void uart_ungrab(struct uart_devinfo *di);
#endif /* _DEV_UART_CPU_H_ */

View File

@ -140,6 +140,8 @@ static int imx_uart_bus_probe(struct uart_softc *);
static int imx_uart_bus_receive(struct uart_softc *);
static int imx_uart_bus_setsig(struct uart_softc *, int);
static int imx_uart_bus_transmit(struct uart_softc *);
static void imx_uart_bus_grab(struct uart_softc *);
static void imx_uart_bus_ungrab(struct uart_softc *);
static kobj_method_t imx_uart_methods[] = {
KOBJMETHOD(uart_attach, imx_uart_bus_attach),
@ -153,6 +155,8 @@ static kobj_method_t imx_uart_methods[] = {
KOBJMETHOD(uart_receive, imx_uart_bus_receive),
KOBJMETHOD(uart_setsig, imx_uart_bus_setsig),
KOBJMETHOD(uart_transmit, imx_uart_bus_transmit),
KOBJMETHOD(uart_grab, imx_uart_bus_grab),
KOBJMETHOD(uart_ungrab, imx_uart_bus_ungrab),
{ 0, 0 }
};
@ -189,12 +193,7 @@ imx_uart_bus_attach(struct uart_softc *sc)
(void)imx_uart_bus_getsig(sc);
/* XXX workaround to have working console on mount prompt */
if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE){
DIS(bas, UCR4, DREN);
} else {
ENA(bas, UCR4, DREN);
}
ENA(bas, UCR4, DREN);
DIS(bas, UCR1, RRDYEN);
DIS(bas, UCR1, IDEN);
DIS(bas, UCR3, RXDSEN);
@ -402,13 +401,6 @@ static int
imx_uart_bus_setsig(struct uart_softc *sc, int sig)
{
/* TODO: implement (?) */
/* XXX workaround to have working console on mount prompt */
/* Enable RX interrupt */
if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE)
if (!IS(&sc->sc_bas, UCR4, DREN))
ENA(&sc->sc_bas, UCR4, DREN);
return (0);
}
@ -434,3 +426,25 @@ imx_uart_bus_transmit(struct uart_softc *sc)
return (0);
}
static void
imx_uart_bus_grab(struct uart_softc *sc)
{
struct uart_bas *bas = &sc->sc_bas;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
DIS(bas, UCR4, DREN);
uart_unlock(sc->sc_hwmtx);
}
static void
imx_uart_bus_ungrab(struct uart_softc *sc)
{
struct uart_bas *bas = &sc->sc_bas;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
ENA(bas, UCR4, DREN);
uart_unlock(sc->sc_hwmtx);
}

View File

@ -401,6 +401,8 @@ static int lpc_ns8250_bus_probe(struct uart_softc *);
static int lpc_ns8250_bus_receive(struct uart_softc *);
static int lpc_ns8250_bus_setsig(struct uart_softc *, int);
static int lpc_ns8250_bus_transmit(struct uart_softc *);
static void lpc_ns8250_bus_grab(struct uart_softc *);
static void lpc_ns8250_bus_ungrab(struct uart_softc *);
static kobj_method_t lpc_ns8250_methods[] = {
KOBJMETHOD(uart_attach, lpc_ns8250_bus_attach),
@ -414,6 +416,8 @@ static kobj_method_t lpc_ns8250_methods[] = {
KOBJMETHOD(uart_receive, lpc_ns8250_bus_receive),
KOBJMETHOD(uart_setsig, lpc_ns8250_bus_setsig),
KOBJMETHOD(uart_transmit, lpc_ns8250_bus_transmit),
KOBJMETHOD(uart_grab, lpc_ns8250_bus_grab),
KOBJMETHOD(uart_ungrab, lpc_ns8250_bus_ungrab),
{ 0, 0 }
};
@ -890,3 +894,34 @@ lpc_ns8250_bus_transmit(struct uart_softc *sc)
uart_unlock(sc->sc_hwmtx);
return (0);
}
void
lpc_ns8250_bus_grab(struct uart_softc *sc)
{
struct uart_bas *bas = &sc->sc_bas;
/*
* turn off all interrupts to enter polling mode. Leave the
* saved mask alone. We'll restore whatever it was in ungrab.
* All pending interupt signals are reset when IER is set to 0.
*/
uart_lock(sc->sc_hwmtx);
uart_setreg(bas, REG_IER, 0);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
}
void
lpc_ns8250_bus_ungrab(struct uart_softc *sc)
{
struct lpc_ns8250_softc *lpc_ns8250 = (struct lpc_ns8250_softc*)sc;
struct uart_bas *bas = &sc->sc_bas;
/*
* Restore previous interrupt mask
*/
uart_lock(sc->sc_hwmtx);
uart_setreg(bas, REG_IER, lpc_ns8250->ier);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
}

View File

@ -365,6 +365,8 @@ static kobj_method_t ns8250_methods[] = {
KOBJMETHOD(uart_receive, ns8250_bus_receive),
KOBJMETHOD(uart_setsig, ns8250_bus_setsig),
KOBJMETHOD(uart_transmit, ns8250_bus_transmit),
KOBJMETHOD(uart_grab, ns8250_bus_grab),
KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab),
{ 0, 0 }
};
@ -922,3 +924,34 @@ ns8250_bus_transmit(struct uart_softc *sc)
uart_sched_softih(sc, SER_INT_TXIDLE);
return (0);
}
void
ns8250_bus_grab(struct uart_softc *sc)
{
struct uart_bas *bas = &sc->sc_bas;
/*
* turn off all interrupts to enter polling mode. Leave the
* saved mask alone. We'll restore whatever it was in ungrab.
* All pending interupt signals are reset when IER is set to 0.
*/
uart_lock(sc->sc_hwmtx);
uart_setreg(bas, REG_IER, 0);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
}
void
ns8250_bus_ungrab(struct uart_softc *sc)
{
struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc;
struct uart_bas *bas = &sc->sc_bas;
/*
* Restore previous interrupt mask
*/
uart_lock(sc->sc_hwmtx);
uart_setreg(bas, REG_IER, ns8250->ier);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
}

View File

@ -56,5 +56,7 @@ int ns8250_bus_probe(struct uart_softc *);
int ns8250_bus_receive(struct uart_softc *);
int ns8250_bus_setsig(struct uart_softc *, int);
int ns8250_bus_transmit(struct uart_softc *);
void ns8250_bus_grab(struct uart_softc *);
void ns8250_bus_ungrab(struct uart_softc *);
#endif /* _DEV_UART_DEV_NS8250_H_ */

View File

@ -242,6 +242,8 @@ static int uart_pl011_bus_probe(struct uart_softc *);
static int uart_pl011_bus_receive(struct uart_softc *);
static int uart_pl011_bus_setsig(struct uart_softc *, int);
static int uart_pl011_bus_transmit(struct uart_softc *);
static void uart_pl011_bus_grab(struct uart_softc *);
static void uart_pl011_bus_ungrab(struct uart_softc *);
static kobj_method_t uart_pl011_methods[] = {
KOBJMETHOD(uart_attach, uart_pl011_bus_attach),
@ -255,6 +257,9 @@ static kobj_method_t uart_pl011_methods[] = {
KOBJMETHOD(uart_receive, uart_pl011_bus_receive),
KOBJMETHOD(uart_setsig, uart_pl011_bus_setsig),
KOBJMETHOD(uart_transmit, uart_pl011_bus_transmit),
KOBJMETHOD(uart_grab, uart_pl011_bus_grab),
KOBJMETHOD(uart_ungrab, uart_pl011_bus_ungrab),
{ 0, 0 }
};
@ -441,3 +446,27 @@ uart_pl011_bus_transmit(struct uart_softc *sc)
return (0);
}
static void
uart_pl011_bus_grab(struct uart_softc *sc)
{
struct uart_bas *bas;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
__uart_setreg(bas, UART_IMSC, /* Switch to RX polling while grabbed */
~UART_RXREADY & __uart_getreg(bas, UART_IMSC));
uart_unlock(sc->sc_hwmtx);
}
static void
uart_pl011_bus_ungrab(struct uart_softc *sc)
{
struct uart_bas *bas;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
__uart_setreg(bas, UART_IMSC, /* Switch to RX interrupts while not grabbed */
UART_RXREADY | __uart_getreg(bas, UART_IMSC));
uart_unlock(sc->sc_hwmtx);
}

View File

@ -245,6 +245,8 @@ static int quicc_bus_probe(struct uart_softc *);
static int quicc_bus_receive(struct uart_softc *);
static int quicc_bus_setsig(struct uart_softc *, int);
static int quicc_bus_transmit(struct uart_softc *);
static void quicc_bus_grab(struct uart_softc *);
static void quicc_bus_ungrab(struct uart_softc *);
static kobj_method_t quicc_methods[] = {
KOBJMETHOD(uart_attach, quicc_bus_attach),
@ -258,6 +260,8 @@ static kobj_method_t quicc_methods[] = {
KOBJMETHOD(uart_receive, quicc_bus_receive),
KOBJMETHOD(uart_setsig, quicc_bus_setsig),
KOBJMETHOD(uart_transmit, quicc_bus_transmit),
KOBJMETHOD(uart_grab, quicc_bus_grab),
KOBJMETHOD(uart_ungrab, quicc_bus_ungrab),
{ 0, 0 }
};
@ -485,3 +489,34 @@ quicc_bus_transmit(struct uart_softc *sc)
uart_unlock(sc->sc_hwmtx);
return (0);
}
static void
quicc_bus_grab(struct uart_softc *sc)
{
struct uart_bas *bas;
uint16_t st, rb;
/* Disable interrupts on the receive buffer. */
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1));
st = quicc_read2(bas, rb);
quicc_write2(bas, rb, st & ~0x9000);
uart_unlock(sc->sc_hwmtx);
}
static void
quicc_bus_ungrab(struct uart_softc *sc)
{
struct uart_bas *bas;
uint16_t st, rb;
/* Enable interrupts on the receive buffer. */
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1));
st = quicc_read2(bas, rb);
quicc_write2(bas, rb, st | 0x9000);
uart_unlock(sc->sc_hwmtx);
}

View File

@ -365,6 +365,8 @@ static int sab82532_bus_probe(struct uart_softc *);
static int sab82532_bus_receive(struct uart_softc *);
static int sab82532_bus_setsig(struct uart_softc *, int);
static int sab82532_bus_transmit(struct uart_softc *);
static void sab82532_bus_grab(struct uart_softc *);
static void sab82532_bus_ungrab(struct uart_softc *);
static kobj_method_t sab82532_methods[] = {
KOBJMETHOD(uart_attach, sab82532_bus_attach),
@ -378,6 +380,8 @@ static kobj_method_t sab82532_methods[] = {
KOBJMETHOD(uart_receive, sab82532_bus_receive),
KOBJMETHOD(uart_setsig, sab82532_bus_setsig),
KOBJMETHOD(uart_transmit, sab82532_bus_transmit),
KOBJMETHOD(uart_grab, sab82532_bus_grab),
KOBJMETHOD(uart_ungrab, sab82532_bus_ungrab),
{ 0, 0 }
};
@ -724,3 +728,32 @@ sab82532_bus_transmit(struct uart_softc *sc)
uart_unlock(sc->sc_hwmtx);
return (0);
}
static void
sab82532_bus_grab(struct uart_softc *sc)
{
struct uart_bas *bas;
uint8_t imr0;
bas = &sc->sc_bas;
imr0 = SAB_IMR0_TIME|SAB_IMR0_CDSC|SAB_IMR0_RFO; /* No TCD or RPF */
uart_lock(sc->sc_hwmtx);
uart_setreg(bas, SAB_IMR0, 0xff & ~imr0);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
}
static void
sab82532_bus_ungrab(struct uart_softc *sc)
{
struct uart_bas *bas;
uint8_t imr0;
bas = &sc->sc_bas;
imr0 = SAB_IMR0_TCD|SAB_IMR0_TIME|SAB_IMR0_CDSC|SAB_IMR0_RFO|
SAB_IMR0_RPF;
uart_lock(sc->sc_hwmtx);
uart_setreg(bas, SAB_IMR0, 0xff & ~imr0);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
}

View File

@ -281,6 +281,8 @@ static int z8530_bus_probe(struct uart_softc *);
static int z8530_bus_receive(struct uart_softc *);
static int z8530_bus_setsig(struct uart_softc *, int);
static int z8530_bus_transmit(struct uart_softc *);
static void z8530_bus_grab(struct uart_softc *);
static void z8530_bus_ungrab(struct uart_softc *);
static kobj_method_t z8530_methods[] = {
KOBJMETHOD(uart_attach, z8530_bus_attach),
@ -294,6 +296,8 @@ static kobj_method_t z8530_methods[] = {
KOBJMETHOD(uart_receive, z8530_bus_receive),
KOBJMETHOD(uart_setsig, z8530_bus_setsig),
KOBJMETHOD(uart_transmit, z8530_bus_transmit),
KOBJMETHOD(uart_grab, z8530_bus_grab),
KOBJMETHOD(uart_ungrab, z8530_bus_ungrab),
{ 0, 0 }
};
@ -621,3 +625,27 @@ z8530_bus_transmit(struct uart_softc *sc)
uart_unlock(sc->sc_hwmtx);
return (0);
}
static void
z8530_bus_grab(struct uart_softc *sc)
{
struct uart_bas *bas;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
uart_setmreg(bas, WR_IDT, IDT_XIE | IDT_TIE);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
}
static void
z8530_bus_ungrab(struct uart_softc *sc)
{
struct uart_bas *bas;
bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
uart_setmreg(bas, WR_IDT, IDT_XIE | IDT_TIE | IDT_RIA);
uart_barrier(bas);
uart_unlock(sc->sc_hwmtx);
}

View File

@ -141,3 +141,19 @@ METHOD int setsig {
METHOD int transmit {
struct uart_softc *this;
};
# grab() - Up call from the console to the upper layers of the driver when
# the kernel asks to grab the console. This is valid only for console
# drivers. This method is responsible for transitioning the hardware
# from an interrupt driven state to a polled state that works with the
# low-level console interface defined for this device. The kernel
# currently only calls this when it wants to grab input from the
# console. Output can still happen asyncrhonously to these calls.
METHOD void grab {
struct uart_softc *this;
};
# ungrab() - Undoes the effects of grab().
METHOD void ungrab {
struct uart_softc *this;
};