Add Clock stop feature support using runtime PM.
Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- drivers/soundwire/qcom.c | 103 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 7 deletions(-)
diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index d90eba6a1e88..0cf8c5c724e2 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -10,6 +10,7 @@ #include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_device.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/slimbus.h> @@ -19,6 +20,8 @@ #include <sound/soc.h> #include "bus.h"
+#define SWRM_COMP_SW_RESET (0x008) +#define SWRM_COMP_STATUS (0x014) #define SWRM_COMP_HW_VERSION 0x00 #define SWRM_COMP_CFG_ADDR 0x04 #define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1) @@ -408,6 +411,9 @@ static int qcom_swrm_enumerate(struct sdw_bus *bus) } } } + pm_runtime_get_sync(ctrl->dev); + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev);
complete(&ctrl->enumeration); return 0; @@ -421,6 +427,7 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) u8 devnum = 0; int ret = IRQ_HANDLED;
+ clk_prepare_enable(swrm->hclk); swrm->reg_read(swrm, SWRM_INTERRUPT_STATUS, &intr_sts); intr_sts_masked = intr_sts & swrm->intr_mask;
@@ -529,6 +536,7 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) intr_sts_masked = intr_sts & swrm->intr_mask; } while (intr_sts_masked);
+ clk_disable_unprepare(swrm->hclk); return ret; }
@@ -587,6 +595,8 @@ static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus, struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); int ret, i, len;
+ pm_runtime_get_sync(ctrl->dev); + if (msg->flags == SDW_MSG_FLAG_READ) { for (i = 0; i < msg->len;) { if ((msg->len - i) < QCOM_SWRM_MAX_RD_LEN) @@ -598,7 +608,7 @@ static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus, msg->addr + i, len, &msg->buf[i]); if (ret) - return ret; + goto done;
i = i + len; } @@ -607,12 +617,20 @@ static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus, ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->buf[i], msg->dev_num, msg->addr + i); - if (ret) - return SDW_CMD_IGNORED; + if (ret) { + ret = SDW_CMD_IGNORED; + goto done; + } } }
+ pm_runtime_put_autosuspend(ctrl->dev); + pm_runtime_mark_last_busy(ctrl->dev); return SDW_CMD_OK; +done: + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); + return ret; }
static int qcom_swrm_pre_bank_switch(struct sdw_bus *bus) @@ -620,13 +638,19 @@ static int qcom_swrm_pre_bank_switch(struct sdw_bus *bus) u32 reg = SWRM_MCP_FRAME_CTRL_BANK_ADDR(bus->params.next_bank); struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); u32 val; + int ret;
+ pm_runtime_get_sync(ctrl->dev); ctrl->reg_read(ctrl, reg, &val);
u32p_replace_bits(&val, ctrl->cols_index, SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK); u32p_replace_bits(&val, ctrl->rows_index, SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK);
- return ctrl->reg_write(ctrl, reg, val); + ret = ctrl->reg_write(ctrl, reg, val); + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); + + return ret; }
static int qcom_swrm_port_params(struct sdw_bus *bus, @@ -634,13 +658,18 @@ static int qcom_swrm_port_params(struct sdw_bus *bus, unsigned int bank) { struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); + int ret = 0; + pm_runtime_get_sync(ctrl->dev);
if (p_params->bps != SWR_INVALID_PARAM) - return ctrl->reg_write(ctrl, + ret = ctrl->reg_write(ctrl, SWRM_DP_BLOCK_CTRL_1(p_params->num), p_params->bps - 1); + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev);
- return 0; + + return ret; }
static int qcom_swrm_transport_params(struct sdw_bus *bus, @@ -651,6 +680,7 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus, u32 value; int reg = SWRM_DP_PORT_CTRL_BANK((params->port_num), bank); int ret; + pm_runtime_get_sync(ctrl->dev);
value = params->offset1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT; value |= params->offset2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT; @@ -685,6 +715,9 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus, reg = SWRM_DP_BLOCK_CTRL3_BANK(params->port_num, bank); ret = ctrl->reg_write(ctrl, reg, params->blk_pkg_mode); } + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); +
return ret; } @@ -696,6 +729,9 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus, u32 reg = SWRM_DP_PORT_CTRL_BANK(enable_ch->port_num, bank); struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); u32 val; + int ret; + + pm_runtime_get_sync(ctrl->dev);
ctrl->reg_read(ctrl, reg, &val);
@@ -704,7 +740,11 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus, else val &= ~(0xff << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT);
- return ctrl->reg_write(ctrl, reg, val); + ret = ctrl->reg_write(ctrl, reg, val); + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); + + return ret; }
static const struct sdw_master_port_ops qcom_swrm_port_ops = { @@ -1194,6 +1234,13 @@ static int qcom_swrm_probe(struct platform_device *pdev) (ctrl->version >> 24) & 0xff, (ctrl->version >> 16) & 0xff, ctrl->version & 0xffff);
+ pm_runtime_set_autosuspend_delay(dev, 30000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0;
err_master_add: @@ -1214,6 +1261,47 @@ static int qcom_swrm_remove(struct platform_device *pdev) return 0; }
+static int swrm_runtime_resume(struct device *dev) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); + + reinit_completion(&ctrl->enumeration); + clk_prepare_enable(ctrl->hclk); + ctrl->reg_write(ctrl, SWRM_COMP_SW_RESET, 0x01); + qcom_swrm_get_device_status(ctrl); + sdw_handle_slave_status(&ctrl->bus, ctrl->status); + qcom_swrm_init(ctrl); + wait_for_completion_timeout(&ctrl->enumeration, + msecs_to_jiffies(TIMEOUT_MS)); + usleep_range(100, 105); + + pm_runtime_mark_last_busy(dev); + + return 0; +} + +static int __maybe_unused swrm_runtime_suspend(struct device *dev) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); + + /* Mask bus clash interrupt */ + ctrl->intr_mask &= ~SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET; + ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, ctrl->intr_mask); + ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, ctrl->intr_mask); + /* clock stop sequence */ + qcom_swrm_cmd_fifo_wr_cmd(ctrl, 0x2, 0xF, SDW_SCP_CTRL); + + clk_disable_unprepare(ctrl->hclk); + + usleep_range(100, 105); + + return 0; +} + +static const struct dev_pm_ops swrm_dev_pm_ops = { + SET_RUNTIME_PM_OPS(swrm_runtime_suspend, swrm_runtime_resume, NULL) +}; + static const struct of_device_id qcom_swrm_of_match[] = { { .compatible = "qcom,soundwire-v1.3.0", .data = &swrm_v1_3_data }, { .compatible = "qcom,soundwire-v1.5.1", .data = &swrm_v1_5_data }, @@ -1228,6 +1316,7 @@ static struct platform_driver qcom_swrm_driver = { .driver = { .name = "qcom-soundwire", .of_match_table = qcom_swrm_of_match, + .pm = &swrm_dev_pm_ops, } }; module_platform_driver(qcom_swrm_driver);