diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index d928258c6761..77dc094075e1 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -83,7 +83,6 @@ static int sdw_drv_probe(struct device *dev) struct sdw_slave *slave = dev_to_sdw_dev(dev); struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); const struct sdw_device_id *id; - const char *name; int ret; /* @@ -108,11 +107,6 @@ static int sdw_drv_probe(struct device *dev) ret = drv->probe(slave, id); if (ret) { - name = drv->name; - if (!name) - name = drv->driver.name; - - dev_err(dev, "Probe of %s failed: %d\n", name, ret); dev_pm_domain_detach(dev, false); return ret; } @@ -129,7 +123,7 @@ static int sdw_drv_probe(struct device *dev) /* init the dynamic sysfs attributes we need */ ret = sdw_slave_sysfs_dpn_init(slave); if (ret < 0) - dev_warn(dev, "Slave sysfs init failed:%d\n", ret); + dev_warn(dev, "failed to initialise sysfs: %d\n", ret); /* * Check for valid clk_stop_timeout, use DisCo worst case value of @@ -153,7 +147,7 @@ static int sdw_drv_probe(struct device *dev) if (drv->ops && drv->ops->update_status) { ret = drv->ops->update_status(slave, slave->status); if (ret < 0) - dev_warn(dev, "%s: update_status failed with status %d\n", __func__, ret); + dev_warn(dev, "failed to update status at probe: %d\n", ret); } mutex_unlock(&slave->sdw_dev_lock); @@ -204,16 +198,11 @@ static void sdw_drv_shutdown(struct device *dev) */ int __sdw_register_driver(struct sdw_driver *drv, struct module *owner) { - const char *name; - drv->driver.bus = &sdw_bus_type; if (!drv->probe) { - name = drv->name; - if (!name) - name = drv->driver.name; - - pr_err("driver %s didn't provide SDW probe routine\n", name); + pr_err("driver %s didn't provide SDW probe routine\n", + drv->driver.name); return -EINVAL; } diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index e0683a5975d1..05652e983539 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -890,8 +890,14 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns, } } - if (is_slave) - return sdw_handle_slave_status(&cdns->bus, status); + if (is_slave) { + int ret; + + mutex_lock(&cdns->status_update_lock); + ret = sdw_handle_slave_status(&cdns->bus, status); + mutex_unlock(&cdns->status_update_lock); + return ret; + } return 0; } @@ -988,6 +994,31 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) } EXPORT_SYMBOL(sdw_cdns_irq); +static void cdns_check_attached_status_dwork(struct work_struct *work) +{ + struct sdw_cdns *cdns = + container_of(work, struct sdw_cdns, attach_dwork.work); + enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; + u32 val; + int ret; + int i; + + val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT); + + for (i = 0; i <= SDW_MAX_DEVICES; i++) { + status[i] = val & 0x3; + if (status[i]) + dev_dbg(cdns->dev, "Peripheral %d status: %d\n", i, status[i]); + val >>= 2; + } + + mutex_lock(&cdns->status_update_lock); + ret = sdw_handle_slave_status(&cdns->bus, status); + mutex_unlock(&cdns->status_update_lock); + if (ret < 0) + dev_err(cdns->dev, "%s: sdw_handle_slave_status failed: %d\n", __func__, ret); +} + /** * cdns_update_slave_status_work - update slave status in a work since we will need to handle * other interrupts eg. CDNS_MCP_INT_RX_WL during the update slave @@ -1740,7 +1771,11 @@ int sdw_cdns_probe(struct sdw_cdns *cdns) init_completion(&cdns->tx_complete); cdns->bus.port_ops = &cdns_port_ops; + mutex_init(&cdns->status_update_lock); + INIT_WORK(&cdns->work, cdns_update_slave_status_work); + INIT_DELAYED_WORK(&cdns->attach_dwork, cdns_check_attached_status_dwork); + return 0; } EXPORT_SYMBOL(sdw_cdns_probe); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index bc84435e420f..e1d7969ba48a 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -117,6 +117,8 @@ struct sdw_cdns_dai_runtime { * @link_up: Link status * @msg_count: Messages sent on bus * @dai_runtime_array: runtime context for each allocated DAI. + * @status_update_lock: protect concurrency between interrupt-based and delayed work + * status update */ struct sdw_cdns { struct device *dev; @@ -148,10 +150,13 @@ struct sdw_cdns { bool interrupt_enabled; struct work_struct work; + struct delayed_work attach_dwork; struct list_head list; struct sdw_cdns_dai_runtime **dai_runtime_array; + + struct mutex status_update_lock; /* add mutual exclusion to sdw_handle_slave_status() */ }; #define bus_to_cdns(_bus) container_of(_bus, struct sdw_cdns, bus) diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 1db4d9d3a3ba..dddd29381441 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -103,6 +103,8 @@ static inline void intel_writew(void __iomem *base, int offset, u16 value) #define INTEL_MASTER_RESET_ITERATIONS 10 +#define SDW_INTEL_DELAYED_ENUMERATION_MS 100 + #define SDW_INTEL_CHECK_OPS(sdw, cb) ((sdw) && (sdw)->link_res && (sdw)->link_res->hw_ops && \ (sdw)->link_res->hw_ops->cb) #define SDW_INTEL_OPS(sdw, cb) ((sdw)->link_res->hw_ops->cb) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index d110f2b587d5..ae689d5d1ab9 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -489,6 +489,7 @@ static void intel_link_remove(struct auxiliary_device *auxdev) */ if (!bus->prop.hw_disabled) { sdw_intel_debugfs_exit(sdw); + cancel_delayed_work_sync(&cdns->attach_dwork); sdw_cdns_enable_interrupt(cdns, false); } sdw_bus_master_delete(bus); diff --git a/drivers/soundwire/intel_bus_common.c b/drivers/soundwire/intel_bus_common.c index df944e11b9ca..d3ff6c65b64c 100644 --- a/drivers/soundwire/intel_bus_common.c +++ b/drivers/soundwire/intel_bus_common.c @@ -45,21 +45,24 @@ int intel_start_bus(struct sdw_intel *sdw) return ret; } - ret = sdw_cdns_exit_reset(cdns); - if (ret < 0) { - dev_err(dev, "%s: unable to exit bus reset sequence: %d\n", __func__, ret); - return ret; - } - ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret); return ret; } + ret = sdw_cdns_exit_reset(cdns); + if (ret < 0) { + dev_err(dev, "%s: unable to exit bus reset sequence: %d\n", __func__, ret); + return ret; + } + sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS); + schedule_delayed_work(&cdns->attach_dwork, + msecs_to_jiffies(SDW_INTEL_DELAYED_ENUMERATION_MS)); + return 0; } @@ -136,21 +139,24 @@ int intel_start_bus_after_reset(struct sdw_intel *sdw) return ret; } - ret = sdw_cdns_exit_reset(cdns); - if (ret < 0) { - dev_err(dev, "unable to exit bus reset sequence during resume\n"); - return ret; - } - ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { dev_err(dev, "cannot enable interrupts during resume\n"); return ret; } + ret = sdw_cdns_exit_reset(cdns); + if (ret < 0) { + dev_err(dev, "unable to exit bus reset sequence during resume\n"); + return ret; + } + } sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS); + schedule_delayed_work(&cdns->attach_dwork, + msecs_to_jiffies(SDW_INTEL_DELAYED_ENUMERATION_MS)); + return 0; } @@ -184,6 +190,9 @@ int intel_start_bus_after_clock_stop(struct sdw_intel *sdw) sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS); + schedule_delayed_work(&cdns->attach_dwork, + msecs_to_jiffies(SDW_INTEL_DELAYED_ENUMERATION_MS)); + return 0; } @@ -194,6 +203,8 @@ int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) bool wake_enable = false; int ret; + cancel_delayed_work_sync(&cdns->attach_dwork); + if (clock_stop) { ret = sdw_cdns_clock_stop(cdns, true); if (ret < 0) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 94fc1b57c57b..5e0dd47a0412 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -704,8 +704,6 @@ struct sdw_master_device { container_of(d, struct sdw_master_device, dev) struct sdw_driver { - const char *name; - int (*probe)(struct sdw_slave *sdw, const struct sdw_device_id *id); int (*remove)(struct sdw_slave *sdw);