> Date: Fri, 3 Jul 2020 15:52:31 +0200 > From: Rocky Hotas <rockyho...@firemail.cc> > > 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.