Merge tag 'soundwire-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul...
[linux-block.git] / drivers / soundwire / intel.c
index a5965e8827b9c20d90b62ef1cb32199f24062eb9..244209358784ffb2c5f9348a497d12b4a2bf2052 100644 (file)
@@ -22,6 +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 @@ static int intel_reg_show(struct seq_file *s_file, void *data)
        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");
@@ -167,9 +170,8 @@ static int intel_reg_show(struct seq_file *s_file, void *data)
                        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 @@ static void intel_debugfs_exit(struct sdw_intel *sdw) {}
 /*
  * 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 @@ static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw)
        /* 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);
@@ -422,10 +342,17 @@ static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop)
        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)
@@ -454,6 +381,88 @@ 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;
@@ -476,7 +485,7 @@ static int intel_link_power_down(struct sdw_intel *sdw)
 
        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 @@ static int intel_create_dai(struct sdw_cdns *cdns,
 
 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;
 
@@ -1201,8 +1219,208 @@ static int intel_register_dai(struct sdw_intel *sdw)
        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 @@ static int intel_prop_read(struct sdw_bus *bus)
 }
 
 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,
@@ -1265,20 +1483,6 @@ static struct sdw_master_ops sdw_intel_ops = {
        .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 +1512,11 @@ static int intel_link_probe(struct auxiliary_device *auxdev,
        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() */
@@ -1345,7 +1549,6 @@ static int intel_link_probe(struct auxiliary_device *auxdev,
 
 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);
@@ -1366,7 +1569,6 @@ int intel_link_startup(struct auxiliary_device *auxdev)
        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
@@ -1374,68 +1576,31 @@ int intel_link_startup(struct auxiliary_device *auxdev)
                 * 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,
@@ -1480,15 +1645,14 @@ int intel_link_startup(struct auxiliary_device *auxdev)
        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;
@@ -1501,7 +1665,6 @@ static void intel_link_remove(struct auxiliary_device *auxdev)
        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 +1674,6 @@ int intel_link_process_wakeen_event(struct auxiliary_device *auxdev)
        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;
@@ -1523,10 +1684,7 @@ int intel_link_process_wakeen_event(struct auxiliary_device *auxdev)
                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 +1712,11 @@ static int intel_resume_child_device(struct device *dev, void *data)
        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 +1802,7 @@ static int __maybe_unused intel_suspend(struct device *dev)
        }
 
        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;
 
@@ -1665,20 +1823,12 @@ static int __maybe_unused intel_suspend(struct device *dev)
                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 +1849,19 @@ static int __maybe_unused intel_suspend_runtime(struct device *dev)
        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 +1877,6 @@ static int __maybe_unused intel_resume(struct device *dev)
        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) {
@@ -1762,10 +1886,9 @@ static int __maybe_unused intel_resume(struct device *dev)
        }
 
        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);
@@ -1779,7 +1902,7 @@ static int __maybe_unused intel_resume(struct device *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;
@@ -1791,41 +1914,13 @@ static int __maybe_unused intel_resume(struct device *dev)
         */
        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
@@ -1838,7 +1933,7 @@ static int __maybe_unused intel_resume(struct device *dev)
         */
        pm_runtime_mark_last_busy(dev);
 
-       return ret;
+       return 0;
 }
 
 static int __maybe_unused intel_resume_runtime(struct device *dev)
@@ -1847,10 +1942,6 @@ 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) {
@@ -1862,15 +1953,12 @@ static int __maybe_unused intel_resume_runtime(struct device *dev)
        /* 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;
                }
 
@@ -1880,145 +1968,45 @@ static int __maybe_unused intel_resume_runtime(struct device *dev)
                 */
                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;
        }