Merge tag 'spi-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
[linux-block.git] / drivers / spi / spi-geni-qcom.c
index c3972424af71442263d21bcb0c46597a1f52176f..80cea5cd361292604ba3fb52583a9f82222ef99f 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/log2.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/pm_opp.h>
 #include <linux/pm_runtime.h>
 #include <linux/qcom-geni-se.h>
 #include <linux/spi/spi.h>
@@ -51,7 +52,6 @@
 /* M_CMD OP codes for SPI */
 #define SPI_TX_ONLY            1
 #define SPI_RX_ONLY            2
-#define SPI_FULL_DUPLEX                3
 #define SPI_TX_RX              7
 #define SPI_CS_ASSERT          8
 #define SPI_CS_DEASSERT                9
 #define TIMESTAMP_AFTER                BIT(3)
 #define POST_CMD_DELAY         BIT(4)
 
-enum spi_m_cmd_opcode {
-       CMD_NONE,
-       CMD_XFER,
-       CMD_CS,
-       CMD_CANCEL,
-};
-
 struct spi_geni_master {
        struct geni_se se;
        struct device *dev;
        u32 tx_fifo_depth;
        u32 fifo_width_bits;
        u32 tx_wm;
+       u32 last_mode;
        unsigned long cur_speed_hz;
+       unsigned long cur_sclk_hz;
        unsigned int cur_bits_per_word;
        unsigned int tx_rem_bytes;
        unsigned int rx_rem_bytes;
        const struct spi_transfer *cur_xfer;
-       struct completion xfer_done;
+       struct completion cs_done;
+       struct completion cancel_done;
+       struct completion abort_done;
        unsigned int oversampling;
        spinlock_t lock;
-       enum spi_m_cmd_opcode cur_mcmd;
        int irq;
+       bool cs_flag;
 };
 
 static int get_spi_clk_cfg(unsigned int speed_hz,
@@ -95,7 +92,6 @@ static int get_spi_clk_cfg(unsigned int speed_hz,
 {
        unsigned long sclk_freq;
        unsigned int actual_hz;
-       struct geni_se *se = &mas->se;
        int ret;
 
        ret = geni_se_clk_freq_match(&mas->se,
@@ -112,9 +108,12 @@ static int get_spi_clk_cfg(unsigned int speed_hz,
 
        dev_dbg(mas->dev, "req %u=>%u sclk %lu, idx %d, div %d\n", speed_hz,
                                actual_hz, sclk_freq, *clk_idx, *clk_div);
-       ret = clk_set_rate(se->clk, sclk_freq);
+       ret = dev_pm_opp_set_rate(mas->dev, sclk_freq);
        if (ret)
-               dev_err(mas->dev, "clk_set_rate failed %d\n", ret);
+               dev_err(mas->dev, "dev_pm_opp_set_rate failed %d\n", ret);
+       else
+               mas->cur_sclk_hz = sclk_freq;
+
        return ret;
 }
 
@@ -122,24 +121,26 @@ static void handle_fifo_timeout(struct spi_master *spi,
                                struct spi_message *msg)
 {
        struct spi_geni_master *mas = spi_master_get_devdata(spi);
-       unsigned long time_left, flags;
+       unsigned long time_left;
        struct geni_se *se = &mas->se;
 
-       spin_lock_irqsave(&mas->lock, flags);
-       reinit_completion(&mas->xfer_done);
-       mas->cur_mcmd = CMD_CANCEL;
-       geni_se_cancel_m_cmd(se);
+       spin_lock_irq(&mas->lock);
+       reinit_completion(&mas->cancel_done);
        writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
-       spin_unlock_irqrestore(&mas->lock, flags);
-       time_left = wait_for_completion_timeout(&mas->xfer_done, HZ);
+       mas->cur_xfer = NULL;
+       geni_se_cancel_m_cmd(se);
+       spin_unlock_irq(&mas->lock);
+
+       time_left = wait_for_completion_timeout(&mas->cancel_done, HZ);
        if (time_left)
                return;
 
-       spin_lock_irqsave(&mas->lock, flags);
-       reinit_completion(&mas->xfer_done);
+       spin_lock_irq(&mas->lock);
+       reinit_completion(&mas->abort_done);
        geni_se_abort_m_cmd(se);
-       spin_unlock_irqrestore(&mas->lock, flags);
-       time_left = wait_for_completion_timeout(&mas->xfer_done, HZ);
+       spin_unlock_irq(&mas->lock);
+
+       time_left = wait_for_completion_timeout(&mas->abort_done, HZ);
        if (!time_left)
                dev_err(mas->dev, "Failed to cancel/abort m_cmd\n");
 }
@@ -151,18 +152,24 @@ static void spi_geni_set_cs(struct spi_device *slv, bool set_flag)
        struct geni_se *se = &mas->se;
        unsigned long time_left;
 
-       reinit_completion(&mas->xfer_done);
-       pm_runtime_get_sync(mas->dev);
        if (!(slv->mode & SPI_CS_HIGH))
                set_flag = !set_flag;
 
-       mas->cur_mcmd = CMD_CS;
+       if (set_flag == mas->cs_flag)
+               return;
+
+       mas->cs_flag = set_flag;
+
+       pm_runtime_get_sync(mas->dev);
+       spin_lock_irq(&mas->lock);
+       reinit_completion(&mas->cs_done);
        if (set_flag)
                geni_se_setup_m_cmd(se, SPI_CS_ASSERT, 0);
        else
                geni_se_setup_m_cmd(se, SPI_CS_DEASSERT, 0);
+       spin_unlock_irq(&mas->lock);
 
-       time_left = wait_for_completion_timeout(&mas->xfer_done, HZ);
+       time_left = wait_for_completion_timeout(&mas->cs_done, HZ);
        if (!time_left)
                handle_fifo_timeout(spi, NULL);
 
@@ -177,8 +184,6 @@ static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode,
        struct geni_se *se = &mas->se;
        u32 word_len;
 
-       word_len = readl(se->base + SE_SPI_WORD_LEN);
-
        /*
         * If bits_per_word isn't a byte aligned value, set the packing to be
         * 1 SPI word per FIFO word.
@@ -187,74 +192,94 @@ static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode,
                pack_words = mas->fifo_width_bits / bits_per_word;
        else
                pack_words = 1;
-       word_len &= ~WORD_LEN_MSK;
-       word_len |= ((bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK);
        geni_se_config_packing(&mas->se, bits_per_word, pack_words, msb_first,
                                                                true, true);
+       word_len = (bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK;
        writel(word_len, se->base + SE_SPI_WORD_LEN);
 }
 
-static int setup_fifo_params(struct spi_device *spi_slv,
-                                       struct spi_master *spi)
+static int geni_spi_set_clock_and_bw(struct spi_geni_master *mas,
+                                       unsigned long clk_hz)
 {
-       struct spi_geni_master *mas = spi_master_get_devdata(spi);
+       u32 clk_sel, m_clk_cfg, idx, div;
        struct geni_se *se = &mas->se;
-       u32 loopback_cfg, cpol, cpha, demux_output_inv;
-       u32 demux_sel, clk_sel, m_clk_cfg, idx, div;
        int ret;
 
-       loopback_cfg = readl(se->base + SE_SPI_LOOPBACK);
-       cpol = readl(se->base + SE_SPI_CPOL);
-       cpha = readl(se->base + SE_SPI_CPHA);
-       demux_output_inv = 0;
-       loopback_cfg &= ~LOOPBACK_MSK;
-       cpol &= ~CPOL;
-       cpha &= ~CPHA;
-
-       if (spi_slv->mode & SPI_LOOP)
-               loopback_cfg |= LOOPBACK_ENABLE;
-
-       if (spi_slv->mode & SPI_CPOL)
-               cpol |= CPOL;
-
-       if (spi_slv->mode & SPI_CPHA)
-               cpha |= CPHA;
-
-       if (spi_slv->mode & SPI_CS_HIGH)
-               demux_output_inv = BIT(spi_slv->chip_select);
-
-       demux_sel = spi_slv->chip_select;
-       mas->cur_speed_hz = spi_slv->max_speed_hz;
-       mas->cur_bits_per_word = spi_slv->bits_per_word;
+       if (clk_hz == mas->cur_speed_hz)
+               return 0;
 
-       ret = get_spi_clk_cfg(mas->cur_speed_hz, mas, &idx, &div);
+       ret = get_spi_clk_cfg(clk_hz, mas, &idx, &div);
        if (ret) {
-               dev_err(mas->dev, "Err setting clks ret(%d) for %ld\n",
-                                                       ret, mas->cur_speed_hz);
+               dev_err(mas->dev, "Err setting clk to %lu: %d\n", clk_hz, ret);
                return ret;
        }
 
+       /*
+        * SPI core clock gets configured with the requested frequency
+        * or the frequency closer to the requested frequency.
+        * For that reason requested frequency is stored in the
+        * cur_speed_hz and referred in the consecutive transfer instead
+        * of calling clk_get_rate() API.
+        */
+       mas->cur_speed_hz = clk_hz;
+
        clk_sel = idx & CLK_SEL_MSK;
        m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN;
-       spi_setup_word_len(mas, spi_slv->mode, spi_slv->bits_per_word);
-       writel(loopback_cfg, se->base + SE_SPI_LOOPBACK);
-       writel(demux_sel, se->base + SE_SPI_DEMUX_SEL);
-       writel(cpha, se->base + SE_SPI_CPHA);
-       writel(cpol, se->base + SE_SPI_CPOL);
-       writel(demux_output_inv, se->base + SE_SPI_DEMUX_OUTPUT_INV);
        writel(clk_sel, se->base + SE_GENI_CLK_SEL);
        writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG);
+
+       /* Set BW quota for CPU as driver supports FIFO mode only. */
+       se->icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(mas->cur_speed_hz);
+       ret = geni_icc_set_bw(se);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
+static int setup_fifo_params(struct spi_device *spi_slv,
+                                       struct spi_master *spi)
+{
+       struct spi_geni_master *mas = spi_master_get_devdata(spi);
+       struct geni_se *se = &mas->se;
+       u32 loopback_cfg = 0, cpol = 0, cpha = 0, demux_output_inv = 0;
+       u32 demux_sel;
+
+       if (mas->last_mode != spi_slv->mode) {
+               if (spi_slv->mode & SPI_LOOP)
+                       loopback_cfg = LOOPBACK_ENABLE;
+
+               if (spi_slv->mode & SPI_CPOL)
+                       cpol = CPOL;
+
+               if (spi_slv->mode & SPI_CPHA)
+                       cpha = CPHA;
+
+               if (spi_slv->mode & SPI_CS_HIGH)
+                       demux_output_inv = BIT(spi_slv->chip_select);
+
+               demux_sel = spi_slv->chip_select;
+               mas->cur_bits_per_word = spi_slv->bits_per_word;
+
+               spi_setup_word_len(mas, spi_slv->mode, spi_slv->bits_per_word);
+               writel(loopback_cfg, se->base + SE_SPI_LOOPBACK);
+               writel(demux_sel, se->base + SE_SPI_DEMUX_SEL);
+               writel(cpha, se->base + SE_SPI_CPHA);
+               writel(cpol, se->base + SE_SPI_CPOL);
+               writel(demux_output_inv, se->base + SE_SPI_DEMUX_OUTPUT_INV);
+
+               mas->last_mode = spi_slv->mode;
+       }
+
+       return geni_spi_set_clock_and_bw(mas, spi_slv->max_speed_hz);
+}
+
 static int spi_geni_prepare_message(struct spi_master *spi,
                                        struct spi_message *spi_msg)
 {
        int ret;
        struct spi_geni_master *mas = spi_master_get_devdata(spi);
-       struct geni_se *se = &mas->se;
 
-       geni_se_select_mode(se, GENI_SE_FIFO);
        ret = setup_fifo_params(spi_msg->spi, spi);
        if (ret)
                dev_err(mas->dev, "Couldn't select mode %d\n", ret);
@@ -283,7 +308,7 @@ static int spi_geni_init(struct spi_geni_master *mas)
         * Hardware programming guide suggests to configure
         * RX FIFO RFR level to fifo_depth-2.
         */
-       geni_se_init(se, 0x0, mas->tx_fifo_depth - 2);
+       geni_se_init(se, mas->tx_fifo_depth / 2, mas->tx_fifo_depth - 2);
        /* Transmit an entire FIFO worth of data per IRQ */
        mas->tx_wm = 1;
        ver = geni_se_get_qup_hw_version(se);
@@ -295,6 +320,8 @@ static int spi_geni_init(struct spi_geni_master *mas)
        else
                mas->oversampling = 1;
 
+       geni_se_select_mode(se, GENI_SE_FIFO);
+
        pm_runtime_put(mas->dev);
        return 0;
 }
@@ -306,6 +333,22 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
        u32 m_cmd = 0;
        u32 spi_tx_cfg, len;
        struct geni_se *se = &mas->se;
+       int ret;
+
+       /*
+        * Ensure that our interrupt handler isn't still running from some
+        * prior command before we start messing with the hardware behind
+        * its back.  We don't need to _keep_ the lock here since we're only
+        * worried about racing with out interrupt handler.  The SPI core
+        * already handles making sure that we're not trying to do two
+        * transfers at once or setting a chip select and doing a transfer
+        * concurrently.
+        *
+        * NOTE: we actually _can't_ hold the lock here because possibly we
+        * might call clk_set_rate() which needs to be able to sleep.
+        */
+       spin_lock_irq(&mas->lock);
+       spin_unlock_irq(&mas->lock);
 
        spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG);
        if (xfer->bits_per_word != mas->cur_bits_per_word) {
@@ -314,38 +357,12 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
        }
 
        /* Speed and bits per word can be overridden per transfer */
-       if (xfer->speed_hz != mas->cur_speed_hz) {
-               int ret;
-               u32 clk_sel, m_clk_cfg;
-               unsigned int idx, div;
-
-               ret = get_spi_clk_cfg(xfer->speed_hz, mas, &idx, &div);
-               if (ret) {
-                       dev_err(mas->dev, "Err setting clks:%d\n", ret);
-                       return;
-               }
-               /*
-                * SPI core clock gets configured with the requested frequency
-                * or the frequency closer to the requested frequency.
-                * For that reason requested frequency is stored in the
-                * cur_speed_hz and referred in the consecutive transfer instead
-                * of calling clk_get_rate() API.
-                */
-               mas->cur_speed_hz = xfer->speed_hz;
-               clk_sel = idx & CLK_SEL_MSK;
-               m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN;
-               writel(clk_sel, se->base + SE_GENI_CLK_SEL);
-               writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG);
-       }
+       ret = geni_spi_set_clock_and_bw(mas, xfer->speed_hz);
+       if (ret)
+               return;
 
        mas->tx_rem_bytes = 0;
        mas->rx_rem_bytes = 0;
-       if (xfer->tx_buf && xfer->rx_buf)
-               m_cmd = SPI_FULL_DUPLEX;
-       else if (xfer->tx_buf)
-               m_cmd = SPI_TX_ONLY;
-       else if (xfer->rx_buf)
-               m_cmd = SPI_RX_ONLY;
 
        spi_tx_cfg &= ~CS_TOGGLE;
 
@@ -356,17 +373,24 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
        len &= TRANS_LEN_MSK;
 
        mas->cur_xfer = xfer;
-       if (m_cmd & SPI_TX_ONLY) {
+       if (xfer->tx_buf) {
+               m_cmd |= SPI_TX_ONLY;
                mas->tx_rem_bytes = xfer->len;
                writel(len, se->base + SE_SPI_TX_TRANS_LEN);
        }
 
-       if (m_cmd & SPI_RX_ONLY) {
+       if (xfer->rx_buf) {
+               m_cmd |= SPI_RX_ONLY;
                writel(len, se->base + SE_SPI_RX_TRANS_LEN);
                mas->rx_rem_bytes = xfer->len;
        }
        writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG);
-       mas->cur_mcmd = CMD_XFER;
+
+       /*
+        * Lock around right before we start the transfer since our
+        * interrupt could come in at any time now.
+        */
+       spin_lock_irq(&mas->lock);
        geni_se_setup_m_cmd(se, m_cmd, FRAGMENTATION);
 
        /*
@@ -376,6 +400,7 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
         */
        if (m_cmd & SPI_TX_ONLY)
                writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG);
+       spin_unlock_irq(&mas->lock);
 }
 
 static int spi_geni_transfer_one(struct spi_master *spi,
@@ -477,13 +502,17 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
        struct spi_geni_master *mas = spi_master_get_devdata(spi);
        struct geni_se *se = &mas->se;
        u32 m_irq;
-       unsigned long flags;
 
-       if (mas->cur_mcmd == CMD_NONE)
+       m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
+       if (!m_irq)
                return IRQ_NONE;
 
-       spin_lock_irqsave(&mas->lock, flags);
-       m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
+       if (m_irq & (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |
+                    M_RX_FIFO_RD_ERR_EN | M_RX_FIFO_WR_ERR_EN |
+                    M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN))
+               dev_warn(mas->dev, "Unexpected IRQ err status %#010x\n", m_irq);
+
+       spin_lock(&mas->lock);
 
        if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN))
                geni_spi_handle_rx(mas);
@@ -492,39 +521,57 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
                geni_spi_handle_tx(mas);
 
        if (m_irq & M_CMD_DONE_EN) {
-               if (mas->cur_mcmd == CMD_XFER)
+               if (mas->cur_xfer) {
                        spi_finalize_current_transfer(spi);
-               else if (mas->cur_mcmd == CMD_CS)
-                       complete(&mas->xfer_done);
-               mas->cur_mcmd = CMD_NONE;
-               /*
-                * If this happens, then a CMD_DONE came before all the Tx
-                * buffer bytes were sent out. This is unusual, log this
-                * condition and disable the WM interrupt to prevent the
-                * system from stalling due an interrupt storm.
-                * If this happens when all Rx bytes haven't been received, log
-                * the condition.
-                * The only known time this can happen is if bits_per_word != 8
-                * and some registers that expect xfer lengths in num spi_words
-                * weren't written correctly.
-                */
-               if (mas->tx_rem_bytes) {
-                       writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
-                       dev_err(mas->dev, "Premature done. tx_rem = %d bpw%d\n",
-                               mas->tx_rem_bytes, mas->cur_bits_per_word);
+                       mas->cur_xfer = NULL;
+                       /*
+                        * If this happens, then a CMD_DONE came before all the
+                        * Tx buffer bytes were sent out. This is unusual, log
+                        * this condition and disable the WM interrupt to
+                        * prevent the system from stalling due an interrupt
+                        * storm.
+                        *
+                        * If this happens when all Rx bytes haven't been
+                        * received, log the condition. The only known time
+                        * this can happen is if bits_per_word != 8 and some
+                        * registers that expect xfer lengths in num spi_words
+                        * weren't written correctly.
+                        */
+                       if (mas->tx_rem_bytes) {
+                               writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
+                               dev_err(mas->dev, "Premature done. tx_rem = %d bpw%d\n",
+                                       mas->tx_rem_bytes, mas->cur_bits_per_word);
+                       }
+                       if (mas->rx_rem_bytes)
+                               dev_err(mas->dev, "Premature done. rx_rem = %d bpw%d\n",
+                                       mas->rx_rem_bytes, mas->cur_bits_per_word);
+               } else {
+                       complete(&mas->cs_done);
                }
-               if (mas->rx_rem_bytes)
-                       dev_err(mas->dev, "Premature done. rx_rem = %d bpw%d\n",
-                               mas->rx_rem_bytes, mas->cur_bits_per_word);
        }
 
-       if ((m_irq & M_CMD_CANCEL_EN) || (m_irq & M_CMD_ABORT_EN)) {
-               mas->cur_mcmd = CMD_NONE;
-               complete(&mas->xfer_done);
-       }
+       if (m_irq & M_CMD_CANCEL_EN)
+               complete(&mas->cancel_done);
+       if (m_irq & M_CMD_ABORT_EN)
+               complete(&mas->abort_done);
 
+       /*
+        * It's safe or a good idea to Ack all of our our interrupts at the
+        * end of the function. Specifically:
+        * - M_CMD_DONE_EN / M_RX_FIFO_LAST_EN: Edge triggered interrupts and
+        *   clearing Acks. Clearing at the end relies on nobody else having
+        *   started a new transfer yet or else we could be clearing _their_
+        *   done bit, but everyone grabs the spinlock before starting a new
+        *   transfer.
+        * - M_RX_FIFO_WATERMARK_EN / M_TX_FIFO_WATERMARK_EN: These appear
+        *   to be "latched level" interrupts so it's important to clear them
+        *   _after_ you've handled the condition and always safe to do so
+        *   since they'll re-assert if they're still happening.
+        */
        writel(m_irq, se->base + SE_GENI_M_IRQ_CLEAR);
-       spin_unlock_irqrestore(&mas->lock, flags);
+
+       spin_unlock(&mas->lock);
+
        return IRQ_HANDLED;
 }
 
@@ -561,6 +608,17 @@ static int spi_geni_probe(struct platform_device *pdev)
        mas->se.wrapper = dev_get_drvdata(dev->parent);
        mas->se.base = base;
        mas->se.clk = clk;
+       mas->se.opp_table = dev_pm_opp_set_clkname(&pdev->dev, "se");
+       if (IS_ERR(mas->se.opp_table))
+               return PTR_ERR(mas->se.opp_table);
+       /* OPP table is optional */
+       ret = dev_pm_opp_of_add_table(&pdev->dev);
+       if (!ret) {
+               mas->se.has_opp_table = true;
+       } else if (ret != -ENODEV) {
+               dev_err(&pdev->dev, "invalid OPP table in device tree\n");
+               return ret;
+       }
 
        spi->bus_num = -1;
        spi->dev.of_node = dev->of_node;
@@ -574,10 +632,25 @@ static int spi_geni_probe(struct platform_device *pdev)
        spi->handle_err = handle_fifo_timeout;
        spi->set_cs = spi_geni_set_cs;
 
-       init_completion(&mas->xfer_done);
+       init_completion(&mas->cs_done);
+       init_completion(&mas->cancel_done);
+       init_completion(&mas->abort_done);
        spin_lock_init(&mas->lock);
+       pm_runtime_use_autosuspend(&pdev->dev);
+       pm_runtime_set_autosuspend_delay(&pdev->dev, 250);
        pm_runtime_enable(dev);
 
+       ret = geni_icc_get(&mas->se, NULL);
+       if (ret)
+               goto spi_geni_probe_runtime_disable;
+       /* Set the bus quota to a reasonable value for register access */
+       mas->se.icc_paths[GENI_TO_CORE].avg_bw = Bps_to_icc(CORE_2X_50_MHZ);
+       mas->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
+
+       ret = geni_icc_set_bw(&mas->se);
+       if (ret)
+               goto spi_geni_probe_runtime_disable;
+
        ret = spi_geni_init(mas);
        if (ret)
                goto spi_geni_probe_runtime_disable;
@@ -596,6 +669,9 @@ spi_geni_probe_free_irq:
 spi_geni_probe_runtime_disable:
        pm_runtime_disable(dev);
        spi_master_put(spi);
+       if (mas->se.has_opp_table)
+               dev_pm_opp_of_remove_table(&pdev->dev);
+       dev_pm_opp_put_clkname(mas->se.opp_table);
        return ret;
 }
 
@@ -609,6 +685,9 @@ static int spi_geni_remove(struct platform_device *pdev)
 
        free_irq(mas->irq, spi);
        pm_runtime_disable(&pdev->dev);
+       if (mas->se.has_opp_table)
+               dev_pm_opp_of_remove_table(&pdev->dev);
+       dev_pm_opp_put_clkname(mas->se.opp_table);
        return 0;
 }
 
@@ -616,16 +695,33 @@ static int __maybe_unused spi_geni_runtime_suspend(struct device *dev)
 {
        struct spi_master *spi = dev_get_drvdata(dev);
        struct spi_geni_master *mas = spi_master_get_devdata(spi);
+       int ret;
 
-       return geni_se_resources_off(&mas->se);
+       /* Drop the performance state vote */
+       dev_pm_opp_set_rate(dev, 0);
+
+       ret = geni_se_resources_off(&mas->se);
+       if (ret)
+               return ret;
+
+       return geni_icc_disable(&mas->se);
 }
 
 static int __maybe_unused spi_geni_runtime_resume(struct device *dev)
 {
        struct spi_master *spi = dev_get_drvdata(dev);
        struct spi_geni_master *mas = spi_master_get_devdata(spi);
+       int ret;
+
+       ret = geni_icc_enable(&mas->se);
+       if (ret)
+               return ret;
+
+       ret = geni_se_resources_on(&mas->se);
+       if (ret)
+               return ret;
 
-       return geni_se_resources_on(&mas->se);
+       return dev_pm_opp_set_rate(mas->dev, mas->cur_sclk_hz);
 }
 
 static int __maybe_unused spi_geni_suspend(struct device *dev)