Merge tag 'soundwire-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 7 Oct 2022 23:13:55 +0000 (16:13 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 7 Oct 2022 23:13:55 +0000 (16:13 -0700)
Pull soundwire updates from Vinod Koul:
 "Updates for Intel, Cadence and Qualcomm drivers:

   - another round of Intel driver cleanup to prepare for future code
     reorg which is expected in next cycle (Pierre-Louis Bossart)

   - bus unattach notifications processing during re-enumeration along
     with Cadence driver updates for this (Richard Fitzgerald)

   - Qualcomm driver updates to handle device0 status (Srinivas
     Kandagatla)"

* tag 'soundwire-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (42 commits)
  soundwire: intel: add helper to stop bus
  soundwire: intel: introduce helpers to start bus
  soundwire: intel: introduce intel_shim_check_wake() helper
  soundwire: intel: simplify read ops assignment
  soundwire: intel: remove intel_init() wrapper
  soundwire: intel: move shim initialization before power up/down
  soundwire: intel: remove clock_stop parameter in intel_shim_init()
  soundwire: intel: move all PDI initialization under intel_register_dai()
  soundwire: intel: move DAI registration and debugfs init earlier
  soundwire: intel: simplify flow and use devm_ for DAI registration
  soundwire: intel: fix error handling on dai registration issues
  soundwire: cadence: Simplify error paths in cdns_xfer_msg()
  soundwire: cadence: Fix error check in cdns_xfer_msg()
  soundwire: cadence: Write to correct address for each FIFO chunk
  soundwire: bus: Fix wrong port number in sdw_handle_slave_alerts()
  soundwire: qcom: do not send status of device 0 during alert
  soundwire: qcom: update status from device id 1
  soundwire: cadence: Don't overwrite msg->buf during write commands
  soundwire: bus: Don't exit early if no device IDs were programmed
  soundwire: cadence: Fix lost ATTACHED interrupts when enumerating
  ...

1  2 
drivers/soundwire/bus.c
drivers/soundwire/cadence_master.c
drivers/soundwire/intel.c
drivers/soundwire/qcom.c
include/linux/soundwire/sdw.h

diff --combined drivers/soundwire/bus.c
index d95b07896a3e6838acbf7076bc1d0b103ca265b2,df0ae869ee51dd3f816ac939dfb6d533b4645173..76515c33e639ed646209b40b612fab517d3bea43
  #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 +76,6 @@@ int sdw_bus_master_add(struct sdw_bus *
  
        /*
         * 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 +157,11 @@@ static int sdw_delete_slave(struct devi
  
        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 +181,7 @@@ void sdw_bus_master_delete(struct sdw_b
        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);
  
@@@ -297,38 -299,6 +299,38 @@@ int sdw_transfer(struct sdw_bus *bus, s
        return ret;
  }
  
 +/**
 + * 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
@@@ -671,10 -641,18 +673,18 @@@ static int sdw_get_device_num(struct sd
  {
        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;
+               }
        }
  
        /*
@@@ -751,7 -729,7 +761,7 @@@ void sdw_extract_slave_id(struct sdw_bu
  }
  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;
        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);
                        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
                                        return ret;
                                }
  
+                               *programmed = true;
                                break;
                        }
                }
@@@ -848,13 -840,13 +872,13 @@@ static void sdw_modify_slave_status(str
        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);
        } 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);
        }
@@@ -1630,7 -1622,7 +1654,7 @@@ static int sdw_handle_slave_alerts(stru
                        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]);
                        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]);
@@@ -1768,7 -1760,7 +1792,7 @@@ int sdw_handle_slave_status(struct sdw_
  {
        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 */
                        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 */
                                "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);
  
index 615b0b63a3e12d865e3d97b9da68a3b4737fdc97,30b8c628fdbd201b806b745d1b332c7213fc32c7..93929f19d0831b511fb83c0c4d9c6bd428b8af36
@@@ -544,9 -544,12 +544,12 @@@ cdns_fill_msg_resp(struct sdw_cdns *cdn
                return SDW_CMD_IGNORED;
        }
  
-       /* fill response */
-       for (i = 0; i < count; i++)
-               msg->buf[i + offset] = FIELD_GET(CDNS_MCP_RESP_RDATA, cdns->response_buf[i]);
+       if (msg->flags == SDW_MSG_FLAG_READ) {
+               /* fill response */
+               for (i = 0; i < count; i++)
+                       msg->buf[i + offset] = FIELD_GET(CDNS_MCP_RESP_RDATA,
+                                                        cdns->response_buf[i]);
+       }
  
        return SDW_CMD_OK;
  }
@@@ -566,7 -569,7 +569,7 @@@ _cdns_xfer_msg(struct sdw_cdns *cdns, s
        }
  
        base = CDNS_MCP_CMD_BASE;
-       addr = msg->addr;
+       addr = msg->addr + offset;
  
        for (i = 0; i < count; i++) {
                data = FIELD_PREP(CDNS_MCP_CMD_DEV_ADDR, msg->dev_num);
@@@ -705,18 -708,15 +708,15 @@@ cdns_xfer_msg(struct sdw_bus *bus, stru
        for (i = 0; i < msg->len / CDNS_MCP_CMD_LEN; i++) {
                ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN,
                                     CDNS_MCP_CMD_LEN, false);
-               if (ret < 0)
-                       goto exit;
+               if (ret != SDW_CMD_OK)
+                       return ret;
        }
  
        if (!(msg->len % CDNS_MCP_CMD_LEN))
-               goto exit;
-       ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN,
-                            msg->len % CDNS_MCP_CMD_LEN, false);
+               return SDW_CMD_OK;
  
- exit:
-       return ret;
+       return _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN,
+                             msg->len % CDNS_MCP_CMD_LEN, false);
  }
  EXPORT_SYMBOL(cdns_xfer_msg);
  
@@@ -756,14 -756,6 +756,14 @@@ cdns_reset_page_addr(struct sdw_bus *bu
  }
  EXPORT_SYMBOL(cdns_reset_page_addr);
  
 +u32 cdns_read_ping_status(struct sdw_bus *bus)
 +{
 +      struct sdw_cdns *cdns = bus_to_cdns(bus);
 +
 +      return cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
 +}
 +EXPORT_SYMBOL(cdns_read_ping_status);
 +
  /*
   * IRQ handling
   */
@@@ -790,6 -782,7 +790,7 @@@ static int cdns_update_slave_status(str
        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);
  
                                status[i] = SDW_SLAVE_RESERVED;
                                break;
                        }
-                       dev_warn_ratelimited(cdns->dev,
-                                            "Slave %d status updated to %d\n",
-                                            i, status[i]);
                }
        }
  
@@@ -969,9 -954,22 +962,22 @@@ static void cdns_update_slave_status_wo
        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;
  
  
  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
         * 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);
                }
        }
  
-       /* 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);
  
index a5965e8827b9c20d90b62ef1cb32199f24062eb9,15fe083e04027910943fb84930ca7d0dae44cc5e..244209358784ffb2c5f9348a497d12b4a2bf2052
@@@ -22,6 -22,9 +22,9 @@@
  #include "bus.h"
  #include "intel.h"
  
+ /* IDA min selected to avoid conflicts with HDaudio/iDISP SDI values */
+ #define INTEL_DEV_NUM_IDA_MIN           4
  #define INTEL_MASTER_SUSPEND_DELAY_MS 3000
  #define INTEL_MASTER_RESET_ITERATIONS 10
  
@@@ -135,7 -138,7 +138,7 @@@ static int intel_reg_show(struct seq_fi
        if (!buf)
                return -ENOMEM;
  
-       links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0);
+       links = intel_readl(s, SDW_SHIM_LCAP) & SDW_SHIM_LCAP_LCOUNT_MASK;
  
        ret = scnprintf(buf, RD_BUF, "Register  Value\n");
        ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n");
                        ret += intel_sprintf(s, false, buf, ret,
                                        SDW_SHIM_PCMSYCHC(i, j));
                }
-               ret += scnprintf(buf + ret, RD_BUF - ret, "\n PDMSCAP, IOCTL, CTMCTL\n");
+               ret += scnprintf(buf + ret, RD_BUF - ret, "\n IOCTL, CTMCTL\n");
  
-               ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i));
                ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));
                ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));
        }
@@@ -258,86 -260,6 +260,6 @@@ static void intel_debugfs_exit(struct s
  /*
   * shim ops
   */
- static int intel_link_power_up(struct sdw_intel *sdw)
- {
-       unsigned int link_id = sdw->instance;
-       void __iomem *shim = sdw->link_res->shim;
-       u32 *shim_mask = sdw->link_res->shim_mask;
-       struct sdw_bus *bus = &sdw->cdns.bus;
-       struct sdw_master_prop *prop = &bus->prop;
-       u32 spa_mask, cpa_mask;
-       u32 link_control;
-       int ret = 0;
-       u32 syncprd;
-       u32 sync_reg;
-       mutex_lock(sdw->link_res->shim_lock);
-       /*
-        * The hardware relies on an internal counter, typically 4kHz,
-        * to generate the SoundWire SSP - which defines a 'safe'
-        * synchronization point between commands and audio transport
-        * and allows for multi link synchronization. The SYNCPRD value
-        * is only dependent on the oscillator clock provided to
-        * the IP, so adjust based on _DSD properties reported in DSDT
-        * tables. The values reported are based on either 24MHz
-        * (CNL/CML) or 38.4 MHz (ICL/TGL+).
-        */
-       if (prop->mclk_freq % 6000000)
-               syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
-       else
-               syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;
-       if (!*shim_mask) {
-               dev_dbg(sdw->cdns.dev, "%s: powering up all links\n", __func__);
-               /* we first need to program the SyncPRD/CPU registers */
-               dev_dbg(sdw->cdns.dev,
-                       "%s: first link up, programming SYNCPRD\n", __func__);
-               /* set SyncPRD period */
-               sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
-               u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD);
-               /* Set SyncCPU bit */
-               sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
-               intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
-               /* Link power up sequence */
-               link_control = intel_readl(shim, SDW_SHIM_LCTL);
-               /* only power-up enabled links */
-               spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask);
-               cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
-               link_control |=  spa_mask;
-               ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
-               if (ret < 0) {
-                       dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);
-                       goto out;
-               }
-               /* SyncCPU will change once link is active */
-               ret = intel_wait_bit(shim, SDW_SHIM_SYNC,
-                                    SDW_SHIM_SYNC_SYNCCPU, 0);
-               if (ret < 0) {
-                       dev_err(sdw->cdns.dev,
-                               "Failed to set SHIM_SYNC: %d\n", ret);
-                       goto out;
-               }
-       }
-       *shim_mask |= BIT(link_id);
-       sdw->cdns.link_up = true;
- out:
-       mutex_unlock(sdw->link_res->shim_lock);
-       return ret;
- }
  /* this needs to be called with shim_lock */
  static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw)
  {
@@@ -389,15 -311,13 +311,13 @@@ static void intel_shim_master_ip_to_glu
        /* at this point Integration Glue has full control of the I/Os */
  }
  
- static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop)
+ /* this needs to be called with shim_lock */
+ static void intel_shim_init(struct sdw_intel *sdw)
  {
        void __iomem *shim = sdw->link_res->shim;
        unsigned int link_id = sdw->instance;
-       int ret = 0;
        u16 ioctl = 0, act = 0;
  
-       mutex_lock(sdw->link_res->shim_lock);
        /* Initialize Shim */
        ioctl |= SDW_SHIM_IOCTL_BKE;
        intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
        act |= SDW_SHIM_CTMCTL_DODS;
        intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);
        usleep_range(10, 15);
+ }
  
-       mutex_unlock(sdw->link_res->shim_lock);
+ static int intel_shim_check_wake(struct sdw_intel *sdw)
+ {
+       void __iomem *shim;
+       u16 wake_sts;
  
-       return ret;
+       shim = sdw->link_res->shim;
+       wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
+       return wake_sts & BIT(sdw->instance);
  }
  
  static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
        mutex_unlock(sdw->link_res->shim_lock);
  }
  
+ static int intel_link_power_up(struct sdw_intel *sdw)
+ {
+       unsigned int link_id = sdw->instance;
+       void __iomem *shim = sdw->link_res->shim;
+       u32 *shim_mask = sdw->link_res->shim_mask;
+       struct sdw_bus *bus = &sdw->cdns.bus;
+       struct sdw_master_prop *prop = &bus->prop;
+       u32 spa_mask, cpa_mask;
+       u32 link_control;
+       int ret = 0;
+       u32 syncprd;
+       u32 sync_reg;
+       mutex_lock(sdw->link_res->shim_lock);
+       /*
+        * The hardware relies on an internal counter, typically 4kHz,
+        * to generate the SoundWire SSP - which defines a 'safe'
+        * synchronization point between commands and audio transport
+        * and allows for multi link synchronization. The SYNCPRD value
+        * is only dependent on the oscillator clock provided to
+        * the IP, so adjust based on _DSD properties reported in DSDT
+        * tables. The values reported are based on either 24MHz
+        * (CNL/CML) or 38.4 MHz (ICL/TGL+).
+        */
+       if (prop->mclk_freq % 6000000)
+               syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
+       else
+               syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;
+       if (!*shim_mask) {
+               dev_dbg(sdw->cdns.dev, "powering up all links\n");
+               /* we first need to program the SyncPRD/CPU registers */
+               dev_dbg(sdw->cdns.dev,
+                       "first link up, programming SYNCPRD\n");
+               /* set SyncPRD period */
+               sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
+               u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD);
+               /* Set SyncCPU bit */
+               sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
+               intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
+               /* Link power up sequence */
+               link_control = intel_readl(shim, SDW_SHIM_LCTL);
+               /* only power-up enabled links */
+               spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask);
+               cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
+               link_control |=  spa_mask;
+               ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
+               if (ret < 0) {
+                       dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);
+                       goto out;
+               }
+               /* SyncCPU will change once link is active */
+               ret = intel_wait_bit(shim, SDW_SHIM_SYNC,
+                                    SDW_SHIM_SYNC_SYNCCPU, 0);
+               if (ret < 0) {
+                       dev_err(sdw->cdns.dev,
+                               "Failed to set SHIM_SYNC: %d\n", ret);
+                       goto out;
+               }
+       }
+       *shim_mask |= BIT(link_id);
+       sdw->cdns.link_up = true;
+       intel_shim_init(sdw);
+ out:
+       mutex_unlock(sdw->link_res->shim_lock);
+       return ret;
+ }
  static int intel_link_power_down(struct sdw_intel *sdw)
  {
        u32 link_control, spa_mask, cpa_mask;
  
        if (!*shim_mask) {
  
-               dev_dbg(sdw->cdns.dev, "%s: powering down all links\n", __func__);
+               dev_dbg(sdw->cdns.dev, "powering down all links\n");
  
                /* Link power down sequence */
                link_control = intel_readl(shim, SDW_SHIM_LCTL);
@@@ -1169,11 -1178,20 +1178,20 @@@ static int intel_create_dai(struct sdw_
  
  static int intel_register_dai(struct sdw_intel *sdw)
  {
+       struct sdw_cdns_stream_config config;
        struct sdw_cdns *cdns = &sdw->cdns;
        struct sdw_cdns_streams *stream;
        struct snd_soc_dai_driver *dais;
        int num_dai, ret, off = 0;
  
+       /* Read the PDI config and initialize cadence PDI */
+       intel_pdi_init(sdw, &config);
+       ret = sdw_cdns_pdi_init(cdns, config);
+       if (ret)
+               return ret;
+       intel_pdi_ch_update(sdw);
        /* DAIs are created based on total number of PDIs supported */
        num_dai = cdns->pcm.num_pdi;
  
        if (ret)
                return ret;
  
-       return snd_soc_register_component(cdns->dev, &dai_component,
-                                         dais, num_dai);
+       return devm_snd_soc_register_component(cdns->dev, &dai_component,
+                                              dais, num_dai);
+ }
+ static int intel_start_bus(struct sdw_intel *sdw)
+ {
+       struct device *dev = sdw->cdns.dev;
+       struct sdw_cdns *cdns = &sdw->cdns;
+       struct sdw_bus *bus = &cdns->bus;
+       int ret;
+       ret = sdw_cdns_enable_interrupt(cdns, true);
+       if (ret < 0) {
+               dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret);
+               return ret;
+       }
+       /*
+        * follow recommended programming flows to avoid timeouts when
+        * gsync is enabled
+        */
+       if (bus->multi_link)
+               intel_shim_sync_arm(sdw);
+       ret = sdw_cdns_init(cdns);
+       if (ret < 0) {
+               dev_err(dev, "%s: unable to initialize Cadence IP: %d\n", __func__, ret);
+               goto err_interrupt;
+       }
+       ret = sdw_cdns_exit_reset(cdns);
+       if (ret < 0) {
+               dev_err(dev, "%s: unable to exit bus reset sequence: %d\n", __func__, ret);
+               goto err_interrupt;
+       }
+       if (bus->multi_link) {
+               ret = intel_shim_sync_go(sdw);
+               if (ret < 0) {
+                       dev_err(dev, "%s: sync go failed: %d\n", __func__, ret);
+                       goto err_interrupt;
+               }
+       }
+       sdw_cdns_check_self_clearing_bits(cdns, __func__,
+                                         true, INTEL_MASTER_RESET_ITERATIONS);
+       return 0;
+ err_interrupt:
+       sdw_cdns_enable_interrupt(cdns, false);
+       return ret;
+ }
+ static int intel_start_bus_after_reset(struct sdw_intel *sdw)
+ {
+       struct device *dev = sdw->cdns.dev;
+       struct sdw_cdns *cdns = &sdw->cdns;
+       struct sdw_bus *bus = &cdns->bus;
+       bool clock_stop0;
+       int status;
+       int ret;
+       /*
+        * An exception condition occurs for the CLK_STOP_BUS_RESET
+        * case if one or more masters remain active. In this condition,
+        * all the masters are powered on for they are in the same power
+        * domain. Master can preserve its context for clock stop0, so
+        * there is no need to clear slave status and reset bus.
+        */
+       clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
+       if (!clock_stop0) {
+               /*
+                * make sure all Slaves are tagged as UNATTACHED and
+                * provide reason for reinitialization
+                */
+               status = SDW_UNATTACH_REQUEST_MASTER_RESET;
+               sdw_clear_slave_status(bus, status);
+               ret = sdw_cdns_enable_interrupt(cdns, true);
+               if (ret < 0) {
+                       dev_err(dev, "cannot enable interrupts during resume\n");
+                       return ret;
+               }
+               /*
+                * follow recommended programming flows to avoid
+                * timeouts when gsync is enabled
+                */
+               if (bus->multi_link)
+                       intel_shim_sync_arm(sdw);
+               /*
+                * Re-initialize the IP since it was powered-off
+                */
+               sdw_cdns_init(&sdw->cdns);
+       } else {
+               ret = sdw_cdns_enable_interrupt(cdns, true);
+               if (ret < 0) {
+                       dev_err(dev, "cannot enable interrupts during resume\n");
+                       return ret;
+               }
+       }
+       ret = sdw_cdns_clock_restart(cdns, !clock_stop0);
+       if (ret < 0) {
+               dev_err(dev, "unable to restart clock during resume\n");
+               goto err_interrupt;
+       }
+       if (!clock_stop0) {
+               ret = sdw_cdns_exit_reset(cdns);
+               if (ret < 0) {
+                       dev_err(dev, "unable to exit bus reset sequence during resume\n");
+                       goto err_interrupt;
+               }
+               if (bus->multi_link) {
+                       ret = intel_shim_sync_go(sdw);
+                       if (ret < 0) {
+                               dev_err(sdw->cdns.dev, "sync go failed during resume\n");
+                               goto err_interrupt;
+                       }
+               }
+       }
+       sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS);
+       return 0;
+ err_interrupt:
+       sdw_cdns_enable_interrupt(cdns, false);
+       return ret;
+ }
+ static void intel_check_clock_stop(struct sdw_intel *sdw)
+ {
+       struct device *dev = sdw->cdns.dev;
+       bool clock_stop0;
+       clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
+       if (!clock_stop0)
+               dev_err(dev, "%s: invalid configuration, clock was not stopped\n", __func__);
+ }
+ static int intel_start_bus_after_clock_stop(struct sdw_intel *sdw)
+ {
+       struct device *dev = sdw->cdns.dev;
+       struct sdw_cdns *cdns = &sdw->cdns;
+       int 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_clock_restart(cdns, false);
+       if (ret < 0) {
+               dev_err(dev, "%s: unable to restart clock: %d\n", __func__, ret);
+               sdw_cdns_enable_interrupt(cdns, false);
+               return ret;
+       }
+       sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks",
+                                         true, INTEL_MASTER_RESET_ITERATIONS);
+       return 0;
+ }
+ static int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop)
+ {
+       struct device *dev = sdw->cdns.dev;
+       struct sdw_cdns *cdns = &sdw->cdns;
+       bool wake_enable = false;
+       int ret;
+       if (clock_stop) {
+               ret = sdw_cdns_clock_stop(cdns, true);
+               if (ret < 0)
+                       dev_err(dev, "%s: cannot stop clock: %d\n", __func__, ret);
+               else
+                       wake_enable = true;
+       }
+       ret = sdw_cdns_enable_interrupt(cdns, false);
+       if (ret < 0) {
+               dev_err(dev, "%s: cannot disable interrupts: %d\n", __func__, ret);
+               return ret;
+       }
+       ret = intel_link_power_down(sdw);
+       if (ret) {
+               dev_err(dev, "%s: Link power down failed: %d\n", __func__, ret);
+               return ret;
+       }
+       intel_shim_wake(sdw, wake_enable);
+       return 0;
  }
  
  static int sdw_master_read_intel_prop(struct sdw_bus *bus)
@@@ -1254,7 -1472,7 +1472,7 @@@ static int intel_prop_read(struct sdw_b
  }
  
  static struct sdw_master_ops sdw_intel_ops = {
-       .read_prop = sdw_master_read_prop,
+       .read_prop = intel_prop_read,
        .override_adr = sdw_dmi_override_adr,
        .xfer_msg = cdns_xfer_msg,
        .xfer_msg_defer = cdns_xfer_msg_defer,
        .set_bus_conf = cdns_bus_conf,
        .pre_bank_switch = intel_pre_bank_switch,
        .post_bank_switch = intel_post_bank_switch,
 +      .read_ping_status = cdns_read_ping_status,
  };
  
- static int intel_init(struct sdw_intel *sdw)
- {
-       bool clock_stop;
-       /* Initialize shim and controller */
-       intel_link_power_up(sdw);
-       clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns);
-       intel_shim_init(sdw, clock_stop);
-       return 0;
- }
  /*
   * probe and init (aux_dev_id argument is required by function prototype but not used)
   */
@@@ -1308,11 -1511,11 +1512,11 @@@ static int intel_link_probe(struct auxi
        cdns->msg_count = 0;
  
        bus->link_id = auxdev->id;
+       bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN;
  
        sdw_cdns_probe(cdns);
  
-       /* Set property read ops */
-       sdw_intel_ops.read_prop = intel_prop_read;
+       /* Set ops */
        bus->ops = &sdw_intel_ops;
  
        /* set driver data, accessed by snd_soc_dai_get_drvdata() */
  
  int intel_link_startup(struct auxiliary_device *auxdev)
  {
-       struct sdw_cdns_stream_config config;
        struct device *dev = &auxdev->dev;
        struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev);
        struct sdw_intel *sdw = cdns_to_intel(cdns);
        multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
        if (!multi_link) {
                dev_dbg(dev, "Multi-link is disabled\n");
-               bus->multi_link = false;
        } else {
                /*
                 * hardware-based synchronization is required regardless
                 * synchronization is gated by gsync when the multi-master
                 * mode is set.
                 */
-               bus->multi_link = true;
                bus->hw_sync_min_links = 1;
        }
+       bus->multi_link = multi_link;
  
        /* Initialize shim, controller */
-       ret = intel_init(sdw);
-       if (ret)
-               goto err_init;
-       /* Read the PDI config and initialize cadence PDI */
-       intel_pdi_init(sdw, &config);
-       ret = sdw_cdns_pdi_init(cdns, config);
+       ret = intel_link_power_up(sdw);
        if (ret)
                goto err_init;
  
-       intel_pdi_ch_update(sdw);
-       ret = sdw_cdns_enable_interrupt(cdns, true);
-       if (ret < 0) {
-               dev_err(dev, "cannot enable interrupts\n");
-               goto err_init;
-       }
-       /*
-        * follow recommended programming flows to avoid timeouts when
-        * gsync is enabled
-        */
-       if (multi_link)
-               intel_shim_sync_arm(sdw);
-       ret = sdw_cdns_init(cdns);
-       if (ret < 0) {
-               dev_err(dev, "unable to initialize Cadence IP\n");
-               goto err_interrupt;
-       }
-       ret = sdw_cdns_exit_reset(cdns);
-       if (ret < 0) {
-               dev_err(dev, "unable to exit bus reset sequence\n");
-               goto err_interrupt;
-       }
-       if (multi_link) {
-               ret = intel_shim_sync_go(sdw);
-               if (ret < 0) {
-                       dev_err(dev, "sync go failed: %d\n", ret);
-                       goto err_interrupt;
-               }
-       }
-       sdw_cdns_check_self_clearing_bits(cdns, __func__,
-                                         true, INTEL_MASTER_RESET_ITERATIONS);
        /* Register DAIs */
        ret = intel_register_dai(sdw);
        if (ret) {
                dev_err(dev, "DAI registration failed: %d\n", ret);
-               snd_soc_unregister_component(dev);
-               goto err_interrupt;
+               goto err_power_up;
        }
  
        intel_debugfs_init(sdw);
  
+       /* start bus */
+       ret = intel_start_bus(sdw);
+       if (ret) {
+               dev_err(dev, "bus start failed: %d\n", ret);
+               goto err_power_up;
+       }
        /* Enable runtime PM */
        if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) {
                pm_runtime_set_autosuspend_delay(dev,
        sdw->startup_done = true;
        return 0;
  
- err_interrupt:
-       sdw_cdns_enable_interrupt(cdns, false);
+ err_power_up:
+       intel_link_power_down(sdw);
  err_init:
        return ret;
  }
  
  static void intel_link_remove(struct auxiliary_device *auxdev)
  {
-       struct device *dev = &auxdev->dev;
        struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev);
        struct sdw_intel *sdw = cdns_to_intel(cdns);
        struct sdw_bus *bus = &cdns->bus;
        if (!bus->prop.hw_disabled) {
                intel_debugfs_exit(sdw);
                sdw_cdns_enable_interrupt(cdns, false);
-               snd_soc_unregister_component(dev);
        }
        sdw_bus_master_delete(bus);
  }
@@@ -1511,8 -1673,6 +1674,6 @@@ int intel_link_process_wakeen_event(str
        struct device *dev = &auxdev->dev;
        struct sdw_intel *sdw;
        struct sdw_bus *bus;
-       void __iomem *shim;
-       u16 wake_sts;
  
        sdw = auxiliary_get_drvdata(auxdev);
        bus = &sdw->cdns.bus;
                return 0;
        }
  
-       shim = sdw->link_res->shim;
-       wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
-       if (!(wake_sts & BIT(sdw->instance)))
+       if (!intel_shim_check_wake(sdw))
                return 0;
  
        /* disable WAKEEN interrupt ASAP to prevent interrupt flood */
@@@ -1554,11 -1711,11 +1712,11 @@@ static int intel_resume_child_device(st
        struct sdw_slave *slave = dev_to_sdw_dev(dev);
  
        if (!slave->probed) {
-               dev_dbg(dev, "%s: skipping device, no probed driver\n", __func__);
+               dev_dbg(dev, "skipping device, no probed driver\n");
                return 0;
        }
        if (!slave->dev_num_sticky) {
-               dev_dbg(dev, "%s: skipping device, never detected on bus\n", __func__);
+               dev_dbg(dev, "skipping device, never detected on bus\n");
                return 0;
        }
  
@@@ -1644,7 -1801,7 +1802,7 @@@ static int __maybe_unused intel_suspend
        }
  
        if (pm_runtime_suspended(dev)) {
-               dev_dbg(dev, "%s: pm_runtime status: suspended\n", __func__);
+               dev_dbg(dev, "pm_runtime status: suspended\n");
  
                clock_stop_quirks = sdw->link_res->clock_stop_quirks;
  
                return 0;
        }
  
-       ret = sdw_cdns_enable_interrupt(cdns, false);
+       ret = intel_stop_bus(sdw, false);
        if (ret < 0) {
-               dev_err(dev, "cannot disable interrupts on suspend\n");
-               return ret;
-       }
-       ret = intel_link_power_down(sdw);
-       if (ret) {
-               dev_err(dev, "Link power down failed: %d\n", ret);
+               dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret);
                return ret;
        }
  
-       intel_shim_wake(sdw, false);
        return 0;
  }
  
@@@ -1699,44 -1848,19 +1849,19 @@@ static int __maybe_unused intel_suspend
        clock_stop_quirks = sdw->link_res->clock_stop_quirks;
  
        if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
-               ret = sdw_cdns_enable_interrupt(cdns, false);
+               ret = intel_stop_bus(sdw, false);
                if (ret < 0) {
-                       dev_err(dev, "cannot disable interrupts on suspend\n");
-                       return ret;
-               }
-               ret = intel_link_power_down(sdw);
-               if (ret) {
-                       dev_err(dev, "Link power down failed: %d\n", ret);
+                       dev_err(dev, "%s: cannot stop bus during teardown: %d\n",
+                               __func__, ret);
                        return ret;
                }
-               intel_shim_wake(sdw, false);
-       } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET ||
-                  !clock_stop_quirks) {
-               bool wake_enable = true;
-               ret = sdw_cdns_clock_stop(cdns, true);
-               if (ret < 0) {
-                       dev_err(dev, "cannot enable clock stop on suspend\n");
-                       wake_enable = false;
-               }
-               ret = sdw_cdns_enable_interrupt(cdns, false);
+       } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || !clock_stop_quirks) {
+               ret = intel_stop_bus(sdw, true);
                if (ret < 0) {
-                       dev_err(dev, "cannot disable interrupts on suspend\n");
-                       return ret;
-               }
-               ret = intel_link_power_down(sdw);
-               if (ret) {
-                       dev_err(dev, "Link power down failed: %d\n", ret);
+                       dev_err(dev, "%s: cannot stop bus during clock_stop: %d\n",
+                               __func__, ret);
                        return ret;
                }
-               intel_shim_wake(sdw, wake_enable);
        } else {
                dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
                        __func__, clock_stop_quirks);
@@@ -1752,7 -1876,6 +1877,6 @@@ static int __maybe_unused intel_resume(
        struct sdw_intel *sdw = cdns_to_intel(cdns);
        struct sdw_bus *bus = &cdns->bus;
        int link_flags;
-       bool multi_link;
        int ret;
  
        if (bus->prop.hw_disabled || !sdw->startup_done) {
        }
  
        link_flags = md_flags >> (bus->link_id * 8);
-       multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
  
        if (pm_runtime_suspended(dev)) {
-               dev_dbg(dev, "%s: pm_runtime status was suspended, forcing active\n", __func__);
+               dev_dbg(dev, "pm_runtime status was suspended, forcing active\n");
  
                /* follow required sequence from runtime_pm.rst */
                pm_runtime_disable(dev);
                        pm_runtime_idle(dev);
        }
  
-       ret = intel_init(sdw);
+       ret = intel_link_power_up(sdw);
        if (ret) {
                dev_err(dev, "%s failed: %d\n", __func__, ret);
                return ret;
         */
        sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
  
-       ret = sdw_cdns_enable_interrupt(cdns, true);
-       if (ret < 0) {
-               dev_err(dev, "cannot enable interrupts during resume\n");
-               return ret;
-       }
-       /*
-        * follow recommended programming flows to avoid timeouts when
-        * gsync is enabled
-        */
-       if (multi_link)
-               intel_shim_sync_arm(sdw);
-       ret = sdw_cdns_init(&sdw->cdns);
-       if (ret < 0) {
-               dev_err(dev, "unable to initialize Cadence IP during resume\n");
-               return ret;
-       }
-       ret = sdw_cdns_exit_reset(cdns);
+       ret = intel_start_bus(sdw);
        if (ret < 0) {
-               dev_err(dev, "unable to exit bus reset sequence during resume\n");
+               dev_err(dev, "cannot start bus during resume\n");
+               intel_link_power_down(sdw);
                return ret;
        }
  
-       if (multi_link) {
-               ret = intel_shim_sync_go(sdw);
-               if (ret < 0) {
-                       dev_err(dev, "sync go failed during resume\n");
-                       return ret;
-               }
-       }
-       sdw_cdns_check_self_clearing_bits(cdns, __func__,
-                                         true, INTEL_MASTER_RESET_ITERATIONS);
        /*
         * after system resume, the pm_runtime suspend() may kick in
         * during the enumeration, before any children device force the
         */
        pm_runtime_mark_last_busy(dev);
  
-       return ret;
+       return 0;
  }
  
  static int __maybe_unused intel_resume_runtime(struct device *dev)
        struct sdw_intel *sdw = cdns_to_intel(cdns);
        struct sdw_bus *bus = &cdns->bus;
        u32 clock_stop_quirks;
-       bool clock_stop0;
-       int link_flags;
-       bool multi_link;
-       int status;
        int ret;
  
        if (bus->prop.hw_disabled || !sdw->startup_done) {
        /* unconditionally disable WAKEEN interrupt */
        intel_shim_wake(sdw, false);
  
-       link_flags = md_flags >> (bus->link_id * 8);
-       multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
        clock_stop_quirks = sdw->link_res->clock_stop_quirks;
  
        if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
-               ret = intel_init(sdw);
+               ret = intel_link_power_up(sdw);
                if (ret) {
-                       dev_err(dev, "%s failed: %d\n", __func__, ret);
+                       dev_err(dev, "%s: power_up failed after teardown: %d\n", __func__, ret);
                        return ret;
                }
  
                 */
                sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
  
-               ret = sdw_cdns_enable_interrupt(cdns, true);
-               if (ret < 0) {
-                       dev_err(dev, "cannot enable interrupts during resume\n");
-                       return ret;
-               }
-               /*
-                * follow recommended programming flows to avoid
-                * timeouts when gsync is enabled
-                */
-               if (multi_link)
-                       intel_shim_sync_arm(sdw);
-               ret = sdw_cdns_init(&sdw->cdns);
-               if (ret < 0) {
-                       dev_err(dev, "unable to initialize Cadence IP during resume\n");
-                       return ret;
-               }
-               ret = sdw_cdns_exit_reset(cdns);
+               ret = intel_start_bus(sdw);
                if (ret < 0) {
-                       dev_err(dev, "unable to exit bus reset sequence during resume\n");
+                       dev_err(dev, "%s: cannot start bus after teardown: %d\n", __func__, ret);
+                       intel_link_power_down(sdw);
                        return ret;
                }
  
-               if (multi_link) {
-                       ret = intel_shim_sync_go(sdw);
-                       if (ret < 0) {
-                               dev_err(dev, "sync go failed during resume\n");
-                               return ret;
-                       }
-               }
-               sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime TEARDOWN",
-                                                 true, INTEL_MASTER_RESET_ITERATIONS);
  
        } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) {
-               ret = intel_init(sdw);
+               ret = intel_link_power_up(sdw);
                if (ret) {
-                       dev_err(dev, "%s failed: %d\n", __func__, ret);
+                       dev_err(dev, "%s: power_up failed after bus reset: %d\n", __func__, ret);
                        return ret;
                }
  
-               /*
-                * An exception condition occurs for the CLK_STOP_BUS_RESET
-                * case if one or more masters remain active. In this condition,
-                * all the masters are powered on for they are in the same power
-                * domain. Master can preserve its context for clock stop0, so
-                * there is no need to clear slave status and reset bus.
-                */
-               clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
-               if (!clock_stop0) {
-                       /*
-                        * make sure all Slaves are tagged as UNATTACHED and
-                        * provide reason for reinitialization
-                        */
-                       status = SDW_UNATTACH_REQUEST_MASTER_RESET;
-                       sdw_clear_slave_status(bus, status);
-                       ret = sdw_cdns_enable_interrupt(cdns, true);
-                       if (ret < 0) {
-                               dev_err(dev, "cannot enable interrupts during resume\n");
-                               return ret;
-                       }
-                       /*
-                        * follow recommended programming flows to avoid
-                        * timeouts when gsync is enabled
-                        */
-                       if (multi_link)
-                               intel_shim_sync_arm(sdw);
-                       /*
-                        * Re-initialize the IP since it was powered-off
-                        */
-                       sdw_cdns_init(&sdw->cdns);
-               } else {
-                       ret = sdw_cdns_enable_interrupt(cdns, true);
-                       if (ret < 0) {
-                               dev_err(dev, "cannot enable interrupts during resume\n");
-                               return ret;
-                       }
-               }
-               ret = sdw_cdns_clock_restart(cdns, !clock_stop0);
+               ret = intel_start_bus_after_reset(sdw);
                if (ret < 0) {
-                       dev_err(dev, "unable to restart clock during resume\n");
+                       dev_err(dev, "%s: cannot start bus after reset: %d\n", __func__, ret);
+                       intel_link_power_down(sdw);
                        return ret;
                }
-               if (!clock_stop0) {
-                       ret = sdw_cdns_exit_reset(cdns);
-                       if (ret < 0) {
-                               dev_err(dev, "unable to exit bus reset sequence during resume\n");
-                               return ret;
-                       }
-                       if (multi_link) {
-                               ret = intel_shim_sync_go(sdw);
-                               if (ret < 0) {
-                                       dev_err(sdw->cdns.dev, "sync go failed during resume\n");
-                                       return ret;
-                               }
-                       }
-               }
-               sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime BUS_RESET",
-                                                 true, INTEL_MASTER_RESET_ITERATIONS);
        } else if (!clock_stop_quirks) {
  
-               clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
-               if (!clock_stop0)
-                       dev_err(dev, "%s invalid configuration, clock was not stopped", __func__);
+               intel_check_clock_stop(sdw);
  
-               ret = intel_init(sdw);
+               ret = intel_link_power_up(sdw);
                if (ret) {
-                       dev_err(dev, "%s failed: %d\n", __func__, ret);
+                       dev_err(dev, "%s: power_up failed: %d\n", __func__, ret);
                        return ret;
                }
  
-               ret = sdw_cdns_enable_interrupt(cdns, true);
+               ret = intel_start_bus_after_clock_stop(sdw);
                if (ret < 0) {
-                       dev_err(dev, "cannot enable interrupts during resume\n");
+                       dev_err(dev, "%s: cannot start bus after clock stop: %d\n", __func__, ret);
+                       intel_link_power_down(sdw);
                        return ret;
                }
-               ret = sdw_cdns_clock_restart(cdns, false);
-               if (ret < 0) {
-                       dev_err(dev, "unable to resume master during resume\n");
-                       return ret;
-               }
-               sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks",
-                                                 true, INTEL_MASTER_RESET_ITERATIONS);
        } else {
-               dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
+               dev_err(dev, "%s: clock_stop_quirks %x unsupported\n",
                        __func__, clock_stop_quirks);
                ret = -EINVAL;
        }
diff --combined drivers/soundwire/qcom.c
index 3a992a6478c306f23e61f368a75b6be39dcfd47c,ba3c2ce848175bd5429e21fccf9db7e5061ac358..b33d5db494a54ebbcebceaa675c2db423098d515
@@@ -169,7 -169,7 +169,7 @@@ struct qcom_swrm_ctrl 
        u8 wcmd_id;
        struct qcom_swrm_port_config pconfig[QCOM_SDW_MAX_PORTS];
        struct sdw_stream_runtime *sruntime[SWRM_MAX_DAIS];
 -      enum sdw_slave_status status[SDW_MAX_DEVICES];
 +      enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
        int (*reg_read)(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val);
        int (*reg_write)(struct qcom_swrm_ctrl *ctrl, int reg, int val);
        u32 slave_status;
@@@ -420,7 -420,7 +420,7 @@@ static int qcom_swrm_get_alert_slave_de
  
        ctrl->reg_read(ctrl, SWRM_MCP_SLV_STATUS, &val);
  
-       for (dev_num = 0; dev_num <= SDW_MAX_DEVICES; dev_num++) {
+       for (dev_num = 1; dev_num <= SDW_MAX_DEVICES; dev_num++) {
                status = (val >> (dev_num * SWRM_MCP_SLV_STATUS_SZ));
  
                if ((status & SWRM_MCP_SLV_STATUS_MASK) == SDW_SLAVE_ALERT) {
@@@ -440,7 -440,7 +440,7 @@@ static void qcom_swrm_get_device_status
        ctrl->reg_read(ctrl, SWRM_MCP_SLV_STATUS, &val);
        ctrl->slave_status = val;
  
-       for (i = 0; i <= SDW_MAX_DEVICES; i++) {
+       for (i = 1; i <= SDW_MAX_DEVICES; i++) {
                u32 s;
  
                s = (val >> (i * 2));
@@@ -573,11 -573,10 +573,10 @@@ static irqreturn_t qcom_swrm_irq_handle
                                break;
                        case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED:
                        case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS:
-                               dev_err_ratelimited(swrm->dev, "%s: SWR new slave attached\n",
-                                       __func__);
+                               dev_dbg_ratelimited(swrm->dev, "SWR new slave attached\n");
                                swrm->reg_read(swrm, SWRM_MCP_SLV_STATUS, &slave_status);
                                if (swrm->slave_status == slave_status) {
-                                       dev_err(swrm->dev, "Slave status not changed %x\n",
+                                       dev_dbg(swrm->dev, "Slave status not changed %x\n",
                                                slave_status);
                                } else {
                                        qcom_swrm_get_device_status(swrm);
@@@ -1356,6 -1355,10 +1355,6 @@@ static int qcom_swrm_probe(struct platf
        ctrl->bus.compute_params = &qcom_swrm_compute_params;
        ctrl->bus.clk_stop_timeout = 300;
  
 -      ctrl->audio_cgcr = devm_reset_control_get_exclusive(dev, "swr_audio_cgcr");
 -      if (IS_ERR(ctrl->audio_cgcr))
 -              dev_err(dev, "Failed to get audio_cgcr reset required for soundwire-v1.6.0\n");
 -
        ret = qcom_swrm_get_port_config(ctrl);
        if (ret)
                goto err_clk;
index 822599957b3531855e4484cb359599393dcae1f1,a2b31d25ea27e94ad606ff3588a5f04a5e7b8537..9e4537f409c295714354e16676223c541834974a
@@@ -839,8 -839,6 +839,8 @@@ struct sdw_defer 
   * @set_bus_conf: Set the bus configuration
   * @pre_bank_switch: Callback for pre bank switch
   * @post_bank_switch: Callback for post bank switch
 + * @read_ping_status: Read status from PING frames, reported with two bits per Device.
 + * Bits 31:24 are reserved.
   */
  struct sdw_master_ops {
        int (*read_prop)(struct sdw_bus *bus);
                        struct sdw_bus_params *params);
        int (*pre_bank_switch)(struct sdw_bus *bus);
        int (*post_bank_switch)(struct sdw_bus *bus);
 +      u32 (*read_ping_status)(struct sdw_bus *bus);
  
  };
  
   * meaningful if multi_link is set. If set to 1, hardware-based
   * synchronization will be used even if a stream only uses a single
   * SoundWire segment.
+  * @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.
   */
  struct sdw_bus {
        struct device *dev;
        u32 bank_switch_timeout;
        bool multi_link;
        int hw_sync_min_links;
+       int dev_num_ida_min;
  };
  
  int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
                       struct fwnode_handle *fwnode);
  void sdw_bus_master_delete(struct sdw_bus *bus);
  
 +void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay);
 +
  /**
   * sdw_port_config: Master or Slave Port configuration
   *