diff options
Diffstat (limited to 'drivers/soundwire/bus.c')
-rw-r--r-- | drivers/soundwire/bus.c | 126 |
1 files changed, 98 insertions, 28 deletions
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 8d4000664fa3..76515c33e639 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -11,11 +11,12 @@ #include "bus.h" #include "sysfs_local.h" -static DEFINE_IDA(sdw_ida); +static DEFINE_IDA(sdw_bus_ida); +static DEFINE_IDA(sdw_peripheral_ida); static int sdw_get_id(struct sdw_bus *bus) { - int rc = ida_alloc(&sdw_ida, GFP_KERNEL); + int rc = ida_alloc(&sdw_bus_ida, GFP_KERNEL); if (rc < 0) return rc; @@ -75,7 +76,6 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, /* * Initialize multi_link flag - * TODO: populate this flag by reading property from FW node */ bus->multi_link = false; if (bus->ops->read_prop) { @@ -157,9 +157,11 @@ static int sdw_delete_slave(struct device *dev, void *data) mutex_lock(&bus->bus_lock); - if (slave->dev_num) /* clear dev_num if assigned */ + if (slave->dev_num) { /* clear dev_num if assigned */ clear_bit(slave->dev_num, bus->assigned); - + if (bus->dev_num_ida_min) + ida_free(&sdw_peripheral_ida, slave->dev_num); + } list_del_init(&slave->node); mutex_unlock(&bus->bus_lock); @@ -179,7 +181,7 @@ void sdw_bus_master_delete(struct sdw_bus *bus) sdw_master_device_del(bus); sdw_bus_debugfs_exit(bus); - ida_free(&sdw_ida, bus->id); + ida_free(&sdw_bus_ida, bus->id); } EXPORT_SYMBOL(sdw_bus_master_delete); @@ -298,6 +300,38 @@ int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg) } /** + * sdw_show_ping_status() - Direct report of PING status, to be used by Peripheral drivers + * @bus: SDW bus + * @sync_delay: Delay before reading status + */ +void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay) +{ + u32 status; + + if (!bus->ops->read_ping_status) + return; + + /* + * wait for peripheral to sync if desired. 10-15ms should be more than + * enough in most cases. + */ + if (sync_delay) + usleep_range(10000, 15000); + + mutex_lock(&bus->msg_lock); + + status = bus->ops->read_ping_status(bus); + + mutex_unlock(&bus->msg_lock); + + if (!status) + dev_warn(bus->dev, "%s: no peripherals attached\n", __func__); + else + dev_dbg(bus->dev, "PING status: %#x\n", status); +} +EXPORT_SYMBOL(sdw_show_ping_status); + +/** * sdw_transfer_defer() - Asynchronously transfer message to a SDW Slave device * @bus: SDW bus * @msg: SDW message to be xfered @@ -639,10 +673,18 @@ static int sdw_get_device_num(struct sdw_slave *slave) { int bit; - bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES); - if (bit == SDW_MAX_DEVICES) { - bit = -ENODEV; - goto err; + if (slave->bus->dev_num_ida_min) { + bit = ida_alloc_range(&sdw_peripheral_ida, + slave->bus->dev_num_ida_min, SDW_MAX_DEVICES, + GFP_KERNEL); + if (bit < 0) + goto err; + } else { + bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES); + if (bit == SDW_MAX_DEVICES) { + bit = -ENODEV; + goto err; + } } /* @@ -719,7 +761,7 @@ void sdw_extract_slave_id(struct sdw_bus *bus, } EXPORT_SYMBOL(sdw_extract_slave_id); -static int sdw_program_device_num(struct sdw_bus *bus) +static int sdw_program_device_num(struct sdw_bus *bus, bool *programmed) { u8 buf[SDW_NUM_DEV_ID_REGISTERS] = {0}; struct sdw_slave *slave, *_s; @@ -729,6 +771,8 @@ static int sdw_program_device_num(struct sdw_bus *bus) int count = 0, ret; u64 addr; + *programmed = false; + /* No Slave, so use raw xfer api */ ret = sdw_fill_msg(&msg, NULL, SDW_SCP_DEVID_0, SDW_NUM_DEV_ID_REGISTERS, 0, SDW_MSG_FLAG_READ, buf); @@ -764,6 +808,16 @@ static int sdw_program_device_num(struct sdw_bus *bus) found = true; /* + * To prevent skipping state-machine stages don't + * program a device until we've seen it UNATTACH. + * Must return here because no other device on #0 + * can be detected until this one has been + * assigned a device ID. + */ + if (slave->status != SDW_SLAVE_UNATTACHED) + return 0; + + /* * Assign a new dev_num to this Slave and * not mark it present. It will be marked * present after it reports ATTACHED on new @@ -777,6 +831,8 @@ static int sdw_program_device_num(struct sdw_bus *bus) return ret; } + *programmed = true; + break; } } @@ -816,13 +872,13 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, mutex_lock(&bus->bus_lock); dev_vdbg(bus->dev, - "%s: changing status slave %d status %d new status %d\n", - __func__, slave->dev_num, slave->status, status); + "changing status slave %d status %d new status %d\n", + slave->dev_num, slave->status, status); if (status == SDW_SLAVE_UNATTACHED) { dev_dbg(&slave->dev, - "%s: initializing enumeration and init completion for Slave %d\n", - __func__, slave->dev_num); + "initializing enumeration and init completion for Slave %d\n", + slave->dev_num); init_completion(&slave->enumeration_complete); init_completion(&slave->initialization_complete); @@ -830,8 +886,8 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, } else if ((status == SDW_SLAVE_ATTACHED) && (slave->status == SDW_SLAVE_UNATTACHED)) { dev_dbg(&slave->dev, - "%s: signaling enumeration completion for Slave %d\n", - __func__, slave->dev_num); + "signaling enumeration completion for Slave %d\n", + slave->dev_num); complete(&slave->enumeration_complete); } @@ -1598,7 +1654,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) port = buf2[0] & SDW_SCP_INTSTAT2_PORT4_10; for_each_set_bit(bit, &port, 8) { /* scp2 ports start from 4 */ - port_num = bit + 3; + port_num = bit + 4; sdw_handle_port_interrupt(slave, port_num, &port_status[port_num]); @@ -1610,7 +1666,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) port = buf2[1] & SDW_SCP_INTSTAT3_PORT11_14; for_each_set_bit(bit, &port, 8) { /* scp3 ports start from 11 */ - port_num = bit + 10; + port_num = bit + 11; sdw_handle_port_interrupt(slave, port_num, &port_status[port_num]); @@ -1736,7 +1792,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus, { enum sdw_slave_status prev_status; struct sdw_slave *slave; - bool attached_initializing; + bool attached_initializing, id_programmed; int i, ret = 0; /* first check if any Slaves fell off the bus */ @@ -1757,19 +1813,33 @@ int sdw_handle_slave_status(struct sdw_bus *bus, dev_warn(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n", i, slave->status); sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); + + /* Ensure driver knows that peripheral unattached */ + ret = sdw_update_slave_status(slave, status[i]); + if (ret < 0) + dev_warn(&slave->dev, "Update Slave status failed:%d\n", ret); } } if (status[0] == SDW_SLAVE_ATTACHED) { dev_dbg(bus->dev, "Slave attached, programming device number\n"); - ret = sdw_program_device_num(bus); - if (ret < 0) - dev_err(bus->dev, "Slave attach failed: %d\n", ret); + /* - * programming a device number will have side effects, - * so we deal with other devices at a later time + * Programming a device number will have side effects, + * so we deal with other devices at a later time. + * This relies on those devices reporting ATTACHED, which will + * trigger another call to this function. This will only + * happen if at least one device ID was programmed. + * Error returns from sdw_program_device_num() are currently + * ignored because there's no useful recovery that can be done. + * Returning the error here could result in the current status + * of other devices not being handled, because if no device IDs + * were programmed there's nothing to guarantee a status change + * to trigger another call to this function. */ - return ret; + sdw_program_device_num(bus, &id_programmed); + if (id_programmed) + return 0; } /* Continue to check other slave statuses */ @@ -1838,8 +1908,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, "Update Slave status failed:%d\n", ret); if (attached_initializing) { dev_dbg(&slave->dev, - "%s: signaling initialization completion for Slave %d\n", - __func__, slave->dev_num); + "signaling initialization completion for Slave %d\n", + slave->dev_num); complete(&slave->initialization_complete); |