diff options
-rw-r--r-- | drivers/remoteproc/qcom_wcnss.c | 109 |
1 files changed, 93 insertions, 16 deletions
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index e2573f79a137..f95854255c70 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -17,6 +17,8 @@ #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> #include <linux/qcom_scm.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> @@ -51,12 +53,15 @@ #define WCNSS_PMU_XO_MODE_19p2 0 #define WCNSS_PMU_XO_MODE_48 3 +#define WCNSS_MAX_PDS 2 + struct wcnss_data { size_t pmu_offset; size_t spare_offset; + const char *pd_names[WCNSS_MAX_PDS]; const struct wcnss_vreg_info *vregs; - size_t num_vregs; + size_t num_vregs, num_pd_vregs; }; struct qcom_wcnss { @@ -80,6 +85,8 @@ struct qcom_wcnss { struct mutex iris_lock; struct qcom_iris *iris; + struct device *pds[WCNSS_MAX_PDS]; + size_t num_pds; struct regulator_bulk_data *vregs; size_t num_vregs; @@ -111,24 +118,28 @@ static const struct wcnss_data pronto_v1_data = { .pmu_offset = 0x1004, .spare_offset = 0x1088, + .pd_names = { "mx", "cx" }, .vregs = (struct wcnss_vreg_info[]) { { "vddmx", 950000, 1150000, 0 }, { "vddcx", .super_turbo = true}, { "vddpx", 1800000, 1800000, 0 }, }, - .num_vregs = 3, + .num_pd_vregs = 2, + .num_vregs = 1, }; static const struct wcnss_data pronto_v2_data = { .pmu_offset = 0x1004, .spare_offset = 0x1088, + .pd_names = { "mx", "cx" }, .vregs = (struct wcnss_vreg_info[]) { { "vddmx", 1287500, 1287500, 0 }, { "vddcx", .super_turbo = true }, { "vddpx", 1800000, 1800000, 0 }, }, - .num_vregs = 3, + .num_pd_vregs = 2, + .num_vregs = 1, }; void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss, @@ -219,7 +230,7 @@ static void wcnss_configure_iris(struct qcom_wcnss *wcnss) static int wcnss_start(struct rproc *rproc) { struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; - int ret; + int ret, i; mutex_lock(&wcnss->iris_lock); if (!wcnss->iris) { @@ -228,9 +239,18 @@ static int wcnss_start(struct rproc *rproc) goto release_iris_lock; } + for (i = 0; i < wcnss->num_pds; i++) { + dev_pm_genpd_set_performance_state(wcnss->pds[i], INT_MAX); + ret = pm_runtime_get_sync(wcnss->pds[i]); + if (ret < 0) { + pm_runtime_put_noidle(wcnss->pds[i]); + goto disable_pds; + } + } + ret = regulator_bulk_enable(wcnss->num_vregs, wcnss->vregs); if (ret) - goto release_iris_lock; + goto disable_pds; ret = qcom_iris_enable(wcnss->iris); if (ret) @@ -262,6 +282,11 @@ disable_iris: qcom_iris_disable(wcnss->iris); disable_regulators: regulator_bulk_disable(wcnss->num_vregs, wcnss->vregs); +disable_pds: + for (i--; i >= 0; i--) { + pm_runtime_put(wcnss->pds[i]); + dev_pm_genpd_set_performance_state(wcnss->pds[i], 0); + } release_iris_lock: mutex_unlock(&wcnss->iris_lock); @@ -371,14 +396,54 @@ static irqreturn_t wcnss_stop_ack_interrupt(int irq, void *dev) return IRQ_HANDLED; } +static int wcnss_init_pds(struct qcom_wcnss *wcnss, + const char * const pd_names[WCNSS_MAX_PDS]) +{ + int i, ret; + + for (i = 0; i < WCNSS_MAX_PDS; i++) { + if (!pd_names[i]) + break; + + wcnss->pds[i] = dev_pm_domain_attach_by_name(wcnss->dev, pd_names[i]); + if (IS_ERR_OR_NULL(wcnss->pds[i])) { + ret = PTR_ERR(wcnss->pds[i]) ? : -ENODATA; + for (i--; i >= 0; i--) + dev_pm_domain_detach(wcnss->pds[i], false); + return ret; + } + } + wcnss->num_pds = i; + + return 0; +} + +static void wcnss_release_pds(struct qcom_wcnss *wcnss) +{ + int i; + + for (i = 0; i < wcnss->num_pds; i++) + dev_pm_domain_detach(wcnss->pds[i], false); +} + static int wcnss_init_regulators(struct qcom_wcnss *wcnss, const struct wcnss_vreg_info *info, - int num_vregs) + int num_vregs, int num_pd_vregs) { struct regulator_bulk_data *bulk; int ret; int i; + /* + * If attaching the power domains suceeded we can skip requesting + * the regulators for the power domains. For old device trees we need to + * reserve extra space to manage them through the regulator interface. + */ + if (wcnss->num_pds) + info += num_pd_vregs; + else + num_vregs += num_pd_vregs; + bulk = devm_kcalloc(wcnss->dev, num_vregs, sizeof(struct regulator_bulk_data), GFP_KERNEL); @@ -514,33 +579,42 @@ static int wcnss_probe(struct platform_device *pdev) wcnss->pmu_cfg = mmio + data->pmu_offset; wcnss->spare_out = mmio + data->spare_offset; - ret = wcnss_init_regulators(wcnss, data->vregs, data->num_vregs); - if (ret) + /* + * We might need to fallback to regulators instead of power domains + * for old device trees. Don't report an error in that case. + */ + ret = wcnss_init_pds(wcnss, data->pd_names); + if (ret && (ret != -ENODATA || !data->num_pd_vregs)) goto free_rproc; + ret = wcnss_init_regulators(wcnss, data->vregs, data->num_vregs, + data->num_pd_vregs); + if (ret) + goto detach_pds; + ret = wcnss_request_irq(wcnss, pdev, "wdog", false, wcnss_wdog_interrupt); if (ret < 0) - goto free_rproc; + goto detach_pds; wcnss->wdog_irq = ret; ret = wcnss_request_irq(wcnss, pdev, "fatal", false, wcnss_fatal_interrupt); if (ret < 0) - goto free_rproc; + goto detach_pds; wcnss->fatal_irq = ret; ret = wcnss_request_irq(wcnss, pdev, "ready", true, wcnss_ready_interrupt); if (ret < 0) - goto free_rproc; + goto detach_pds; wcnss->ready_irq = ret; ret = wcnss_request_irq(wcnss, pdev, "handover", true, wcnss_handover_interrupt); if (ret < 0) - goto free_rproc; + goto detach_pds; wcnss->handover_irq = ret; ret = wcnss_request_irq(wcnss, pdev, "stop-ack", true, wcnss_stop_ack_interrupt); if (ret < 0) - goto free_rproc; + goto detach_pds; wcnss->stop_ack_irq = ret; if (wcnss->stop_ack_irq) { @@ -548,7 +622,7 @@ static int wcnss_probe(struct platform_device *pdev) &wcnss->stop_bit); if (IS_ERR(wcnss->state)) { ret = PTR_ERR(wcnss->state); - goto free_rproc; + goto detach_pds; } } @@ -556,15 +630,17 @@ static int wcnss_probe(struct platform_device *pdev) wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID); if (IS_ERR(wcnss->sysmon)) { ret = PTR_ERR(wcnss->sysmon); - goto free_rproc; + goto detach_pds; } ret = rproc_add(rproc); if (ret) - goto free_rproc; + goto detach_pds; return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); +detach_pds: + wcnss_release_pds(wcnss); free_rproc: rproc_free(rproc); @@ -582,6 +658,7 @@ static int wcnss_remove(struct platform_device *pdev) qcom_remove_sysmon_subdev(wcnss->sysmon); qcom_remove_smd_subdev(wcnss->rproc, &wcnss->smd_subdev); + wcnss_release_pds(wcnss); rproc_free(wcnss->rproc); return 0; |