Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ichiic/ig4_iic.c
| Show First 20 Lines • Show All 531 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| ig4iic_softc_t *sc = device_get_softc(dev); | ig4iic_softc_t *sc = device_get_softc(dev); | ||||
| const char *reason = NULL; | const char *reason = NULL; | ||||
| uint32_t i; | uint32_t i; | ||||
| int error; | int error; | ||||
| int unit; | int unit; | ||||
| bool rpstart; | bool rpstart; | ||||
| bool stop; | bool stop; | ||||
| bool allocated; | |||||
| /* | /* | ||||
| * The hardware interface imposes limits on allowed I2C messages. | * The hardware interface imposes limits on allowed I2C messages. | ||||
| * It is not possible to explicitly send a start or stop. | * It is not possible to explicitly send a start or stop. | ||||
| * They are automatically sent (or not sent, depending on the | * They are automatically sent (or not sent, depending on the | ||||
| * configuration) when a data byte is transferred. | * configuration) when a data byte is transferred. | ||||
| * For this reason it's impossible to send a message with no data | * For this reason it's impossible to send a message with no data | ||||
| * at all (like an SMBus quick message). | * at all (like an SMBus quick message). | ||||
| ▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | #endif | ||||
| } | } | ||||
| if (reason != NULL) { | if (reason != NULL) { | ||||
| if (bootverbose) | if (bootverbose) | ||||
| device_printf(dev, "%s\n", reason); | device_printf(dev, "%s\n", reason); | ||||
| return (IIC_ENOTSUPP); | return (IIC_ENOTSUPP); | ||||
| } | } | ||||
| /* Check if device is already allocated with iicbus_request_bus() */ | /* Check if device is already allocated with iicbus_request_bus() */ | ||||
| allocated = sx_xlocked(&sc->call_lock) != 0; | sx_xlock(&sc->bus_lock); | ||||
wulf: The iic_callback/sc->call_lock is not an advisory lock according to sx(9):
> sx_xlocked() will… | |||||
wulfUnsubmitted Not Done Inline Actionswulf: See https://cgit.freebsd.org/src/commit/?id=41b24e091771f231bb1f915ac9921337b95f04f9 | |||||
| if (!allocated) | |||||
| sx_xlock(&sc->call_lock); | |||||
| /* Debugging - dump registers. */ | /* Debugging - dump registers. */ | ||||
| if (ig4_dump) { | if (ig4_dump) { | ||||
| unit = device_get_unit(dev); | unit = device_get_unit(dev); | ||||
| if (ig4_dump & (1 << unit)) { | if (ig4_dump & (1 << unit)) { | ||||
| ig4_dump &= ~(1 << unit); | ig4_dump &= ~(1 << unit); | ||||
| ig4iic_dump(sc); | ig4iic_dump(sc); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | for (i = 0; i < nmsgs; i++) { | ||||
| rpstart = !stop; | rpstart = !stop; | ||||
| } | } | ||||
| if (error == IIC_ENOACK && bootverbose) | if (error == IIC_ENOACK && bootverbose) | ||||
| device_printf(dev, "Warning: NACK for slave address 0x%x\n", | device_printf(dev, "Warning: NACK for slave address 0x%x\n", | ||||
| msgs[i].slave >> 1); | msgs[i].slave >> 1); | ||||
| if (!allocated) | sx_unlock(&sc->bus_lock); | ||||
| sx_unlock(&sc->call_lock); | |||||
| return (error); | return (error); | ||||
| } | } | ||||
| int | int | ||||
| ig4iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) | ig4iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) | ||||
| { | { | ||||
| ig4iic_softc_t *sc = device_get_softc(dev); | ig4iic_softc_t *sc = device_get_softc(dev); | ||||
| bool allocated; | |||||
| allocated = sx_xlocked(&sc->call_lock) != 0; | sx_xlock(&sc->bus_lock); | ||||
| if (!allocated) | |||||
| sx_xlock(&sc->call_lock); | |||||
| /* TODO handle speed configuration? */ | /* TODO handle speed configuration? */ | ||||
| if (oldaddr != NULL) | if (oldaddr != NULL) | ||||
| *oldaddr = sc->last_slave << 1; | *oldaddr = sc->last_slave << 1; | ||||
| set_slave_addr(sc, addr >> 1); | set_slave_addr(sc, addr >> 1); | ||||
| if (addr == IIC_UNKNOWN) | if (addr == IIC_UNKNOWN) | ||||
| sc->slave_valid = false; | sc->slave_valid = false; | ||||
| if (!allocated) | sx_unlock(&sc->bus_lock); | ||||
| sx_unlock(&sc->call_lock); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| int | int | ||||
| ig4iic_callback(device_t dev, int index, caddr_t data) | ig4iic_callback(device_t dev, int index, caddr_t data) | ||||
| { | { | ||||
| ig4iic_softc_t *sc = device_get_softc(dev); | ig4iic_softc_t *sc = device_get_softc(dev); | ||||
| int error = 0; | int error = 0; | ||||
| ▲ Show 20 Lines • Show All 316 Lines • ▼ Show 20 Lines | |||||
| /* | /* | ||||
| * Called from ig4iic_pci_attach/detach() | * Called from ig4iic_pci_attach/detach() | ||||
| */ | */ | ||||
| int | int | ||||
| ig4iic_attach(ig4iic_softc_t *sc) | ig4iic_attach(ig4iic_softc_t *sc) | ||||
| { | { | ||||
| int error; | int error; | ||||
| mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_SPIN); | |||||
| sx_init(&sc->call_lock, "IG4 call lock"); | sx_init(&sc->call_lock, "IG4 call lock"); | ||||
| sx_init(&sc->bus_lock, "IG4 bus lock"); | |||||
| mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_SPIN); | |||||
| ig4iic_get_config(sc); | ig4iic_get_config(sc); | ||||
| error = ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version)); | error = ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version)); | ||||
| if (error) | if (error) | ||||
| goto done; | goto done; | ||||
| ig4iic_get_fifo(sc); | ig4iic_get_fifo(sc); | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | if (error) | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| if (sc->iicbus) | if (sc->iicbus) | ||||
| device_delete_child(sc->dev, sc->iicbus); | device_delete_child(sc->dev, sc->iicbus); | ||||
| if (sc->intr_handle) | if (sc->intr_handle) | ||||
| bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle); | bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle); | ||||
| sx_xlock(&sc->call_lock); | sx_xlock(&sc->call_lock); | ||||
| sx_xlock(&sc->bus_lock); | |||||
| sc->iicbus = NULL; | sc->iicbus = NULL; | ||||
| sc->intr_handle = NULL; | sc->intr_handle = NULL; | ||||
| reg_write(sc, IG4_REG_INTR_MASK, 0); | reg_write(sc, IG4_REG_INTR_MASK, 0); | ||||
| set_controller(sc, 0); | set_controller(sc, 0); | ||||
| sx_xunlock(&sc->bus_lock); | |||||
| sx_xunlock(&sc->call_lock); | sx_xunlock(&sc->call_lock); | ||||
| mtx_destroy(&sc->io_lock); | mtx_destroy(&sc->io_lock); | ||||
| sx_destroy(&sc->bus_lock); | |||||
| sx_destroy(&sc->call_lock); | sx_destroy(&sc->call_lock); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| int | int | ||||
| ig4iic_suspend(ig4iic_softc_t *sc) | ig4iic_suspend(ig4iic_softc_t *sc) | ||||
| { | { | ||||
| int error; | int error; | ||||
| /* suspend all children */ | /* suspend all children */ | ||||
| error = bus_generic_suspend(sc->dev); | error = bus_generic_suspend(sc->dev); | ||||
| sx_xlock(&sc->call_lock); | sx_xlock(&sc->bus_lock); | ||||
| set_controller(sc, 0); | set_controller(sc, 0); | ||||
| if (IG4_HAS_ADDREGS(sc->version)) { | if (IG4_HAS_ADDREGS(sc->version)) { | ||||
| /* | /* | ||||
| * Place the device in the idle state, just to be safe | * Place the device in the idle state, just to be safe | ||||
| */ | */ | ||||
| reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE); | reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE); | ||||
| /* | /* | ||||
| * Controller can become dysfunctional if I2C lines are pulled | * Controller can become dysfunctional if I2C lines are pulled | ||||
| * down when suspend procedure turns off power to I2C device. | * down when suspend procedure turns off power to I2C device. | ||||
| * Place device in the reset state to avoid this. | * Place device in the reset state to avoid this. | ||||
| */ | */ | ||||
| reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL); | reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL); | ||||
| } | } | ||||
| sx_xunlock(&sc->call_lock); | sx_xunlock(&sc->bus_lock); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| int ig4iic_resume(ig4iic_softc_t *sc) | int ig4iic_resume(ig4iic_softc_t *sc) | ||||
| { | { | ||||
| int error; | int error; | ||||
| sx_xlock(&sc->call_lock); | sx_xlock(&sc->bus_lock); | ||||
| if (ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version))) | if (ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version))) | ||||
| device_printf(sc->dev, "controller error during resume\n"); | device_printf(sc->dev, "controller error during resume\n"); | ||||
| sx_xunlock(&sc->call_lock); | sx_xunlock(&sc->bus_lock); | ||||
| error = bus_generic_resume(sc->dev); | error = bus_generic_resume(sc->dev); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| /* | /* | ||||
| * Interrupt Operation, see ig4_var.h for locking semantics. | * Interrupt Operation, see ig4_var.h for locking semantics. | ||||
| ▲ Show 20 Lines • Show All 78 Lines • Show Last 20 Lines | |||||
The iic_callback/sc->call_lock is not an advisory lock according to sx(9):
The whole point of this dances with sc->call_lock is to replace sx_xlock() with sx_try_xlock() when ig4iic_transfer() is called from ithread.
That is what iichid does if interrupts are available for HID devices. It calls iicbus_request_bus() passing IIC_DONTWAIT as flag parameter that triggers sx_try_xlock() in ig4 ig4iic_callback() method that allow skipping of sx_xlock() one line of code below.
Looks slightly hackish but should work.