@@ -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);
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(+)