Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 38 additions & 29 deletions drivers/soundwire/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,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;
Expand All @@ -772,6 +772,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);
Expand Down Expand Up @@ -806,6 +808,16 @@ static int sdw_program_device_num(struct sdw_bus *bus)
if (sdw_compare_devid(slave, id) == 0) {
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
Expand All @@ -820,6 +832,8 @@ static int sdw_program_device_num(struct sdw_bus *bus)
return ret;
}

*programmed = true;

break;
}
}
Expand Down Expand Up @@ -1779,7 +1793,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 */
Expand All @@ -1797,41 +1811,36 @@ int sdw_handle_slave_status(struct sdw_bus *bus,

if (status[i] == SDW_SLAVE_UNATTACHED &&
slave->status != SDW_SLAVE_UNATTACHED) {
dev_err(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n",
i, slave->status);

if (bus->recheck_unattached && bus->ops->read_ping_status) {
u32 ping_status;

mutex_lock(&bus->msg_lock);

ping_status = bus->ops->read_ping_status(bus);

mutex_unlock(&bus->msg_lock);

ping_status >>= (i * 2);
ping_status &= 0x3;

if (ping_status != 0) {
dev_err(&slave->dev, "Slave %d state in PING frame is %d, ignoring earlier detection\n",
i, ping_status);
continue;
}
}
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 */
Expand Down
80 changes: 44 additions & 36 deletions drivers/soundwire/cadence_master.c
Original file line number Diff line number Diff line change
Expand Up @@ -790,48 +790,46 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
bool is_slave = false;
u32 mask;
u32 val;
int i, set_status;

memset(status, 0, sizeof(status));

for (i = 0; i <= SDW_MAX_DEVICES; i++) {
mask = (slave_intstat >> (i * CDNS_MCP_SLAVE_STATUS_NUM)) &
CDNS_MCP_SLAVE_STATUS_BITS;
if (!mask)
continue;

is_slave = true;
set_status = 0;

if (mask & CDNS_MCP_SLAVE_INTSTAT_RESERVED) {
status[i] = SDW_SLAVE_RESERVED;
set_status++;
}

if (mask & CDNS_MCP_SLAVE_INTSTAT_ATTACHED) {
status[i] = SDW_SLAVE_ATTACHED;
set_status++;
}
if (mask) {
is_slave = true;

if (mask & CDNS_MCP_SLAVE_INTSTAT_ALERT) {
status[i] = SDW_SLAVE_ALERT;
set_status++;
}
if (mask & CDNS_MCP_SLAVE_INTSTAT_RESERVED) {
status[i] = SDW_SLAVE_RESERVED;
set_status++;
}

if (mask & CDNS_MCP_SLAVE_INTSTAT_NPRESENT) {
status[i] = SDW_SLAVE_UNATTACHED;
set_status++;
}
if (mask & CDNS_MCP_SLAVE_INTSTAT_ATTACHED) {
status[i] = SDW_SLAVE_ATTACHED;
set_status++;
}

/* first check if Slave reported multiple status */
if (set_status > 1) {
u32 val;
if (mask & CDNS_MCP_SLAVE_INTSTAT_ALERT) {
status[i] = SDW_SLAVE_ALERT;
set_status++;
}

dev_warn_ratelimited(cdns->dev,
"Slave %d reported multiple Status: %d\n",
i, mask);
if (mask & CDNS_MCP_SLAVE_INTSTAT_NPRESENT) {
status[i] = SDW_SLAVE_UNATTACHED;
set_status++;
}
}

/* check latest status extracted from PING commands */
/*
* check that there was a single reported Slave status and when
* there is not use the latest status extracted from PING commands
*/
if (set_status != 1) {
val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
val >>= (i * 2);

Expand All @@ -850,11 +848,6 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
status[i] = SDW_SLAVE_RESERVED;
break;
}

dev_warn_ratelimited(cdns->dev,
"Slave %d status updated to %d\n",
i, status[i]);

}
}

Expand Down Expand Up @@ -969,18 +962,29 @@ static void cdns_update_slave_status_work(struct work_struct *work)
u32 device0_status;
int retry_count = 0;

/*
* Clear main interrupt first so we don't lose any assertions
* that happen during this function.
*/
cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK);

slave0 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0);
slave1 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1);

/*
* Clear the bits before handling so we don't lose any
* bits that re-assert.
*/
cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave0);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave1);

/* combine the two status */
slave_intstat = ((u64)slave1 << 32) | slave0;

dev_dbg_ratelimited(cdns->dev, "Slave status change: 0x%llx\n", slave_intstat);

update_status:
cdns_update_slave_status(cdns, slave_intstat);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave0);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave1);

/*
* When there is more than one peripheral per link, it's
Expand All @@ -997,6 +1001,11 @@ static void cdns_update_slave_status_work(struct work_struct *work)
* attention with PING commands. There is no need to check for
* ALERTS since they are not allowed until a non-zero
* device_number is assigned.
*
* Do not clear the INTSTAT0/1. While looping to enumerate devices on
* #0 there could be status changes on other devices - these must
* be kept in the INTSTAT so they can be handled when all #0 devices
* have been handled.
*/

device0_status = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
Expand All @@ -1016,8 +1025,7 @@ static void cdns_update_slave_status_work(struct work_struct *work)
}
}

/* clear and unmask Slave interrupt now */
cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK);
/* unmask Slave interrupt now */
cdns_updatel(cdns, CDNS_MCP_INTMASK,
CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK);

Expand Down
1 change: 0 additions & 1 deletion drivers/soundwire/intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1513,7 +1513,6 @@ static int intel_link_probe(struct auxiliary_device *auxdev,

bus->link_id = auxdev->id;
bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN;
bus->recheck_unattached = true;

sdw_cdns_probe(cdns);

Expand Down
3 changes: 0 additions & 3 deletions include/linux/soundwire/sdw.h
Original file line number Diff line number Diff line change
Expand Up @@ -895,8 +895,6 @@ struct sdw_master_ops {
* @dev_num_ida_min: if set, defines the minimum values for the IDA
* used to allocate system-unique device numbers. This value needs to be
* identical across all SoundWire bus in the system.
* @recheck_unattached: if set, double-check UNATTACHED status changes
* by reading PING frame status.
*/
struct sdw_bus {
struct device *dev;
Expand All @@ -922,7 +920,6 @@ struct sdw_bus {
bool multi_link;
int hw_sync_min_links;
int dev_num_ida_min;
bool recheck_unattached;
};

int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
Expand Down