tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: Waiting for a bit in a register to be cleared: which strategy?
> Date: Fri, 3 Jul 2020 15:52:31 +0200
> From: Rocky Hotas <rockyhotas%firemail.cc@localhost>
>
> This works only because REG_A has all 0s except the LSB: when the LSB
> becomes 0, too, the while exits. But I am actually interested only in
> the LSB and would like to disregard the other 7 bits in the register.
> What could it be the most efficient way to accomplish this?
It's quite common to read a whole device register just to get at a
single bit. Don't worry about the efficiency -- the cost of the I/O
transaction over the PCI bus or similar far exceeds the cost of
pulling one bit out of 32.
> When a bit must be set, there's the macro __BIT(n) in
>
> <https://nxr.netbsd.org/xref/src/sys/sys/cdefs.h#640>
>
> Is there something similar for when just a single bit must be read?
The usual idea for __BIT is that if there's a device register FOO, and
it has various fields BAR, BAZ, and QUUX, with a hardware manual that
says:
FOO (0x01234):
BAR (0:1) The bar is open.
BAZ (2:19) The number of orcs at the bar.
QUUX (20:23) The number of dwarves hiding in the bathroom.
Then you write in your mumblereg.h file for the mumble(4) driver:
#define FOO 0x01234
#define FOO_BAR __BIT(0)
#define FOO_BAZ __BITS(2,19)
#define FOO_QUUX __BITS(20:23)
and then you get at these by doing:
uint32_t foo = bus_space_read_4(bst, bsh, FOO);
if ((foo & FOO_BAR) == 0)
return ENOENT;
baz = __SHIFTOUT(foo, FOO_BAZ);
quux = __SHIFTOUT(foo, FOO_QUUX);
...
> This way, if for some reason the LSB in REG_A is never cleared (the
> device is not working, or similar), the while never exits. Is it
> available, inside the kernel, some function like sleep, or wait, so
> that a maximum timeout can be set?
A typical approach is to set a reasonable timeout, either in register
reads or in microseconds, and count down to it:
unsigned timo = 1000;
while ((bus_space_read_4(bst, bsh, FOO) & FOO_BAR) == 0) {
if (--timo == 0)
return ETIMEDOUT;
/* optionally, space the reads out by a microsecond */
DELAY(1);
}
If you might need to wait for longer periods of time, like
milliseconds, then you can use kpause with mstohz which lets other
threads run, and if you're working under a lock, you can pass it to
kpause to release the lock while other threads run.
unsigned timo = 1000;
mutex_enter(&sc->sc_lock);
while ((bus_space_read_4(bst, bsh, FOO) & FOO_BAR) == 0) {
if (--timo == 0)
return ETIMEDOUT;
kpause("foobar", false, mstohz(10), &sc->sc_lock);
}
However, if you may need to wait for a long period of time, you should
see if there's a way to get an interrupt notification instead of
polling the device register, and use a condvar to signal the
notification from the interrupt handler and to wait for the
notification elsewhere in software.
Home |
Main Index |
Thread Index |
Old Index