[12/16] spi: bcm63xx-hsspi: Add clock gate disable option support

Message ID 20230106200809.330769-13-william.zhang@broadcom.com
State New
Headers
Series spi: bcm63xx-hsspi: driver and doc updates |

Commit Message

William Zhang Jan. 6, 2023, 8:08 p.m. UTC
  Some SPI device such as Broadcom ISI based voice daughtercard
requires SPI clock running even when chip select is deasserted. By
default the controller turn off or gate the clock when cs is not active
to save power.

This change adds an option to support such device and keep the clock
running when flag brcm,no-clk-gate is present in the SPI device node or
when the SPI device driver clear the GATE_CLK_SSOFF flag in the device
controller data field.

Signed-off-by: William Zhang <william.zhang@broadcom.com>
---

 drivers/spi/spi-bcm63xx-hsspi.c | 46 +++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)
  

Patch

diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index 58f2b495c13c..be4ca01f332a 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -23,6 +23,13 @@ 
 #include <linux/reset.h>
 #include <linux/pm_runtime.h>
 
+/* Broadcom Legacy SPI device driver flags */
+#define SPIDEV_CONTROLLER_STATE_SET		BIT(31)
+#define SPIDEV_CONTROLLER_STATE_GATE_CLK_SSOFF	BIT(29)
+
+#define spidev_ctrl_data(spi)			\
+	((u32)((uintptr_t)(spi)->controller_data))
+
 #define HSSPI_GLOBAL_CTRL_REG			0x0
 #define GLOBAL_CTRL_CS_POLARITY_SHIFT		0
 #define GLOBAL_CTRL_CS_POLARITY_MASK		0x000000ff
@@ -120,11 +127,40 @@  struct bcm63xx_hsspi {
 static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs,
 				  struct spi_device *spi, int hz);
 
+static inline int bcm63xx_hsspi_dev_no_clk_gate(struct spi_device *spi)
+{
+	u32 ctrl_data = 0;
+
+	/* check spi device dn first */
+	if (of_property_read_bool(spi->dev.of_node, "brcm,no-clk-gate"))
+		return 1;
+
+	/* check spi dev controller data for legacy device support */
+	ctrl_data = spidev_ctrl_data(spi);
+	return ((ctrl_data & SPIDEV_CONTROLLER_STATE_SET) &&
+		!(ctrl_data & SPIDEV_CONTROLLER_STATE_GATE_CLK_SSOFF));
+}
+
 static size_t bcm63xx_hsspi_max_message_size(struct spi_device *spi)
 {
 	return HSSPI_BUFFER_LEN - HSSPI_OPCODE_LEN;
 }
 
+static void bcm63xx_hsspi_restore_clk_gate(struct bcm63xx_hsspi *bs,
+				       struct spi_device *spi)
+{
+	u32 reg = 0;
+
+	/* check if clk gate setting was previously turned off */
+	if (bcm63xx_hsspi_dev_no_clk_gate(spi)) {
+		mutex_lock(&bs->bus_mutex);
+		reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
+		reg |= GLOBAL_CTRL_CLK_GATE_SSOFF;
+		__raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
+		mutex_unlock(&bs->bus_mutex);
+	}
+}
+
 static int bcm63xx_hsspi_wait_cmd(struct bcm63xx_hsspi *bs)
 {
 	unsigned long limit;
@@ -354,6 +390,12 @@  static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs,
 	reg &= ~GLOBAL_CTRL_CLK_POLARITY;
 	if (spi->mode & SPI_CPOL)
 		reg |= GLOBAL_CTRL_CLK_POLARITY;
+
+	if (bcm63xx_hsspi_dev_no_clk_gate(spi))
+		reg &= ~GLOBAL_CTRL_CLK_GATE_SSOFF;
+	else
+		reg |= GLOBAL_CTRL_CLK_GATE_SSOFF;
+
 	__raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
 	mutex_unlock(&bs->bus_mutex);
 }
@@ -485,6 +527,7 @@  static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
 	if (bcm63xx_check_msg_prependable(master, msg, &t_prepend)) {
 		status = bcm63xx_hsspi_do_prepend_txrx(spi, &t_prepend);
 		msg->actual_length += (t_prepend.len + bs->prepend_cnt);
+		bcm63xx_hsspi_restore_clk_gate(bs, spi);
 		goto msg_done;
 	}
 
@@ -543,6 +586,9 @@  static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
 			bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
 	}
 
+	/* restore the default clk gate setting in case spidev turn it off */
+	bcm63xx_hsspi_restore_clk_gate(bs, spi);
+
 msg_done:
 	msg->status = status;
 	spi_finalize_current_message(master);