alistair23-linux/drivers/remoteproc/qcom_q6v5_pas.c
Rishabh Bhatnagar a781e5aa59 remoteproc: core: Prevent system suspend during remoteproc recovery
The system might go into suspend during recovery of any remoteproc.
This will interrupt the recovery process in between increasing the
recovery time. Make the platform device as wakeup capable and
use pm_stay_wake/pm_relax APIs to avoid system from going into
suspend during recovery.

Signed-off-by: Siddharth Gupta <sidgup@codeaurora.org>
Signed-off-by: Rishabh Bhatnagar <rishabhb@codeaurora.org>
Acked-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Link: https://lore.kernel.org/r/1588183482-21146-1-git-send-email-rishabhb@codeaurora.org
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2020-05-12 12:11:41 -07:00

728 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Qualcomm ADSP/SLPI Peripheral Image Loader for MSM8974 and MSM8996
*
* Copyright (C) 2016 Linaro Ltd
* Copyright (C) 2014 Sony Mobile Communications AB
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#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>
#include <linux/soc/qcom/mdt_loader.h>
#include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/smem_state.h>
#include "qcom_common.h"
#include "qcom_q6v5.h"
#include "remoteproc_internal.h"
struct adsp_data {
int crash_reason_smem;
const char *firmware_name;
int pas_id;
bool has_aggre2_clk;
bool auto_boot;
char **active_pd_names;
char **proxy_pd_names;
const char *ssr_name;
const char *sysmon_name;
int ssctl_id;
};
struct qcom_adsp {
struct device *dev;
struct rproc *rproc;
struct qcom_q6v5 q6v5;
struct clk *xo;
struct clk *aggre2_clk;
struct regulator *cx_supply;
struct regulator *px_supply;
struct device *active_pds[1];
struct device *proxy_pds[3];
int active_pd_count;
int proxy_pd_count;
int pas_id;
int crash_reason_smem;
bool has_aggre2_clk;
struct completion start_done;
struct completion stop_done;
phys_addr_t mem_phys;
phys_addr_t mem_reloc;
void *mem_region;
size_t mem_size;
struct qcom_rproc_glink glink_subdev;
struct qcom_rproc_subdev smd_subdev;
struct qcom_rproc_ssr ssr_subdev;
struct qcom_sysmon *sysmon;
};
static int adsp_pds_enable(struct qcom_adsp *adsp, struct device **pds,
size_t pd_count)
{
int ret;
int i;
for (i = 0; i < pd_count; i++) {
dev_pm_genpd_set_performance_state(pds[i], INT_MAX);
ret = pm_runtime_get_sync(pds[i]);
if (ret < 0)
goto unroll_pd_votes;
}
return 0;
unroll_pd_votes:
for (i--; i >= 0; i--) {
dev_pm_genpd_set_performance_state(pds[i], 0);
pm_runtime_put(pds[i]);
}
return ret;
};
static void adsp_pds_disable(struct qcom_adsp *adsp, struct device **pds,
size_t pd_count)
{
int i;
for (i = 0; i < pd_count; i++) {
dev_pm_genpd_set_performance_state(pds[i], 0);
pm_runtime_put(pds[i]);
}
}
static int adsp_load(struct rproc *rproc, const struct firmware *fw)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
adsp->mem_region, adsp->mem_phys, adsp->mem_size,
&adsp->mem_reloc);
}
static int adsp_start(struct rproc *rproc)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
int ret;
qcom_q6v5_prepare(&adsp->q6v5);
ret = adsp_pds_enable(adsp, adsp->active_pds, adsp->active_pd_count);
if (ret < 0)
goto disable_irqs;
ret = adsp_pds_enable(adsp, adsp->proxy_pds, adsp->proxy_pd_count);
if (ret < 0)
goto disable_active_pds;
ret = clk_prepare_enable(adsp->xo);
if (ret)
goto disable_proxy_pds;
ret = clk_prepare_enable(adsp->aggre2_clk);
if (ret)
goto disable_xo_clk;
ret = regulator_enable(adsp->cx_supply);
if (ret)
goto disable_aggre2_clk;
ret = regulator_enable(adsp->px_supply);
if (ret)
goto disable_cx_supply;
ret = qcom_scm_pas_auth_and_reset(adsp->pas_id);
if (ret) {
dev_err(adsp->dev,
"failed to authenticate image and release reset\n");
goto disable_px_supply;
}
ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000));
if (ret == -ETIMEDOUT) {
dev_err(adsp->dev, "start timed out\n");
qcom_scm_pas_shutdown(adsp->pas_id);
goto disable_px_supply;
}
return 0;
disable_px_supply:
regulator_disable(adsp->px_supply);
disable_cx_supply:
regulator_disable(adsp->cx_supply);
disable_aggre2_clk:
clk_disable_unprepare(adsp->aggre2_clk);
disable_xo_clk:
clk_disable_unprepare(adsp->xo);
disable_proxy_pds:
adsp_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count);
disable_active_pds:
adsp_pds_disable(adsp, adsp->active_pds, adsp->active_pd_count);
disable_irqs:
qcom_q6v5_unprepare(&adsp->q6v5);
return ret;
}
static void qcom_pas_handover(struct qcom_q6v5 *q6v5)
{
struct qcom_adsp *adsp = container_of(q6v5, struct qcom_adsp, q6v5);
regulator_disable(adsp->px_supply);
regulator_disable(adsp->cx_supply);
clk_disable_unprepare(adsp->aggre2_clk);
clk_disable_unprepare(adsp->xo);
adsp_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count);
}
static int adsp_stop(struct rproc *rproc)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
int handover;
int ret;
ret = qcom_q6v5_request_stop(&adsp->q6v5);
if (ret == -ETIMEDOUT)
dev_err(adsp->dev, "timed out on wait\n");
ret = qcom_scm_pas_shutdown(adsp->pas_id);
if (ret)
dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
adsp_pds_disable(adsp, adsp->active_pds, adsp->active_pd_count);
handover = qcom_q6v5_unprepare(&adsp->q6v5);
if (handover)
qcom_pas_handover(&adsp->q6v5);
return ret;
}
static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
int offset;
offset = da - adsp->mem_reloc;
if (offset < 0 || offset + len > adsp->mem_size)
return NULL;
return adsp->mem_region + offset;
}
static unsigned long adsp_panic(struct rproc *rproc)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
return qcom_q6v5_panic(&adsp->q6v5);
}
static const struct rproc_ops adsp_ops = {
.start = adsp_start,
.stop = adsp_stop,
.da_to_va = adsp_da_to_va,
.parse_fw = qcom_register_dump_segments,
.load = adsp_load,
.panic = adsp_panic,
};
static int adsp_init_clock(struct qcom_adsp *adsp)
{
int ret;
adsp->xo = devm_clk_get(adsp->dev, "xo");
if (IS_ERR(adsp->xo)) {
ret = PTR_ERR(adsp->xo);
if (ret != -EPROBE_DEFER)
dev_err(adsp->dev, "failed to get xo clock");
return ret;
}
if (adsp->has_aggre2_clk) {
adsp->aggre2_clk = devm_clk_get(adsp->dev, "aggre2");
if (IS_ERR(adsp->aggre2_clk)) {
ret = PTR_ERR(adsp->aggre2_clk);
if (ret != -EPROBE_DEFER)
dev_err(adsp->dev,
"failed to get aggre2 clock");
return ret;
}
}
return 0;
}
static int adsp_init_regulator(struct qcom_adsp *adsp)
{
adsp->cx_supply = devm_regulator_get(adsp->dev, "cx");
if (IS_ERR(adsp->cx_supply))
return PTR_ERR(adsp->cx_supply);
regulator_set_load(adsp->cx_supply, 100000);
adsp->px_supply = devm_regulator_get(adsp->dev, "px");
return PTR_ERR_OR_ZERO(adsp->px_supply);
}
static int adsp_pds_attach(struct device *dev, struct device **devs,
char **pd_names)
{
size_t num_pds = 0;
int ret;
int i;
if (!pd_names)
return 0;
/* Handle single power domain */
if (dev->pm_domain) {
devs[0] = dev;
pm_runtime_enable(dev);
return 1;
}
while (pd_names[num_pds])
num_pds++;
for (i = 0; i < num_pds; i++) {
devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]);
if (IS_ERR_OR_NULL(devs[i])) {
ret = PTR_ERR(devs[i]) ? : -ENODATA;
goto unroll_attach;
}
}
return num_pds;
unroll_attach:
for (i--; i >= 0; i--)
dev_pm_domain_detach(devs[i], false);
return ret;
};
static void adsp_pds_detach(struct qcom_adsp *adsp, struct device **pds,
size_t pd_count)
{
struct device *dev = adsp->dev;
int i;
/* Handle single power domain */
if (dev->pm_domain && pd_count) {
pm_runtime_disable(dev);
return;
}
for (i = 0; i < pd_count; i++)
dev_pm_domain_detach(pds[i], false);
}
static int adsp_alloc_memory_region(struct qcom_adsp *adsp)
{
struct device_node *node;
struct resource r;
int ret;
node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0);
if (!node) {
dev_err(adsp->dev, "no memory-region specified\n");
return -EINVAL;
}
ret = of_address_to_resource(node, 0, &r);
if (ret)
return ret;
adsp->mem_phys = adsp->mem_reloc = r.start;
adsp->mem_size = resource_size(&r);
adsp->mem_region = devm_ioremap_wc(adsp->dev, adsp->mem_phys, adsp->mem_size);
if (!adsp->mem_region) {
dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n",
&r.start, adsp->mem_size);
return -EBUSY;
}
return 0;
}
static int adsp_probe(struct platform_device *pdev)
{
const struct adsp_data *desc;
struct qcom_adsp *adsp;
struct rproc *rproc;
const char *fw_name;
int ret;
desc = of_device_get_match_data(&pdev->dev);
if (!desc)
return -EINVAL;
if (!qcom_scm_is_available())
return -EPROBE_DEFER;
fw_name = desc->firmware_name;
ret = of_property_read_string(pdev->dev.of_node, "firmware-name",
&fw_name);
if (ret < 0 && ret != -EINVAL)
return ret;
rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops,
fw_name, sizeof(*adsp));
if (!rproc) {
dev_err(&pdev->dev, "unable to allocate remoteproc\n");
return -ENOMEM;
}
rproc->auto_boot = desc->auto_boot;
rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
adsp = (struct qcom_adsp *)rproc->priv;
adsp->dev = &pdev->dev;
adsp->rproc = rproc;
adsp->pas_id = desc->pas_id;
adsp->has_aggre2_clk = desc->has_aggre2_clk;
platform_set_drvdata(pdev, adsp);
device_wakeup_enable(adsp->dev);
ret = adsp_alloc_memory_region(adsp);
if (ret)
goto free_rproc;
ret = adsp_init_clock(adsp);
if (ret)
goto free_rproc;
ret = adsp_init_regulator(adsp);
if (ret)
goto free_rproc;
ret = adsp_pds_attach(&pdev->dev, adsp->active_pds,
desc->active_pd_names);
if (ret < 0)
goto free_rproc;
adsp->active_pd_count = ret;
ret = adsp_pds_attach(&pdev->dev, adsp->proxy_pds,
desc->proxy_pd_names);
if (ret < 0)
goto detach_active_pds;
adsp->proxy_pd_count = ret;
ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem,
qcom_pas_handover);
if (ret)
goto detach_proxy_pds;
qcom_add_glink_subdev(rproc, &adsp->glink_subdev, desc->ssr_name);
qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
adsp->sysmon = qcom_add_sysmon_subdev(rproc,
desc->sysmon_name,
desc->ssctl_id);
if (IS_ERR(adsp->sysmon)) {
ret = PTR_ERR(adsp->sysmon);
goto detach_proxy_pds;
}
ret = rproc_add(rproc);
if (ret)
goto detach_proxy_pds;
return 0;
detach_proxy_pds:
adsp_pds_detach(adsp, adsp->proxy_pds, adsp->proxy_pd_count);
detach_active_pds:
adsp_pds_detach(adsp, adsp->active_pds, adsp->active_pd_count);
free_rproc:
rproc_free(rproc);
return ret;
}
static int adsp_remove(struct platform_device *pdev)
{
struct qcom_adsp *adsp = platform_get_drvdata(pdev);
rproc_del(adsp->rproc);
qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev);
qcom_remove_sysmon_subdev(adsp->sysmon);
qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
rproc_free(adsp->rproc);
return 0;
}
static const struct adsp_data adsp_resource_init = {
.crash_reason_smem = 423,
.firmware_name = "adsp.mdt",
.pas_id = 1,
.has_aggre2_clk = false,
.auto_boot = true,
.ssr_name = "lpass",
.sysmon_name = "adsp",
.ssctl_id = 0x14,
};
static const struct adsp_data sm8150_adsp_resource = {
.crash_reason_smem = 423,
.firmware_name = "adsp.mdt",
.pas_id = 1,
.has_aggre2_clk = false,
.auto_boot = true,
.active_pd_names = (char*[]){
"load_state",
NULL
},
.proxy_pd_names = (char*[]){
"cx",
NULL
},
.ssr_name = "lpass",
.sysmon_name = "adsp",
.ssctl_id = 0x14,
};
static const struct adsp_data sm8250_adsp_resource = {
.crash_reason_smem = 423,
.firmware_name = "adsp.mdt",
.pas_id = 1,
.has_aggre2_clk = false,
.auto_boot = true,
.active_pd_names = (char*[]){
"load_state",
NULL
},
.proxy_pd_names = (char*[]){
"lcx",
"lmx",
NULL
},
.ssr_name = "lpass",
.sysmon_name = "adsp",
.ssctl_id = 0x14,
};
static const struct adsp_data msm8998_adsp_resource = {
.crash_reason_smem = 423,
.firmware_name = "adsp.mdt",
.pas_id = 1,
.has_aggre2_clk = false,
.auto_boot = true,
.proxy_pd_names = (char*[]){
"cx",
NULL
},
.ssr_name = "lpass",
.sysmon_name = "adsp",
.ssctl_id = 0x14,
};
static const struct adsp_data cdsp_resource_init = {
.crash_reason_smem = 601,
.firmware_name = "cdsp.mdt",
.pas_id = 18,
.has_aggre2_clk = false,
.auto_boot = true,
.ssr_name = "cdsp",
.sysmon_name = "cdsp",
.ssctl_id = 0x17,
};
static const struct adsp_data sm8150_cdsp_resource = {
.crash_reason_smem = 601,
.firmware_name = "cdsp.mdt",
.pas_id = 18,
.has_aggre2_clk = false,
.auto_boot = true,
.active_pd_names = (char*[]){
"load_state",
NULL
},
.proxy_pd_names = (char*[]){
"cx",
NULL
},
.ssr_name = "cdsp",
.sysmon_name = "cdsp",
.ssctl_id = 0x17,
};
static const struct adsp_data sm8250_cdsp_resource = {
.crash_reason_smem = 601,
.firmware_name = "cdsp.mdt",
.pas_id = 18,
.has_aggre2_clk = false,
.auto_boot = true,
.active_pd_names = (char*[]){
"load_state",
NULL
},
.proxy_pd_names = (char*[]){
"cx",
NULL
},
.ssr_name = "cdsp",
.sysmon_name = "cdsp",
.ssctl_id = 0x17,
};
static const struct adsp_data mpss_resource_init = {
.crash_reason_smem = 421,
.firmware_name = "modem.mdt",
.pas_id = 4,
.has_aggre2_clk = false,
.auto_boot = false,
.active_pd_names = (char*[]){
"load_state",
NULL
},
.proxy_pd_names = (char*[]){
"cx",
"mss",
NULL
},
.ssr_name = "mpss",
.sysmon_name = "modem",
.ssctl_id = 0x12,
};
static const struct adsp_data slpi_resource_init = {
.crash_reason_smem = 424,
.firmware_name = "slpi.mdt",
.pas_id = 12,
.has_aggre2_clk = true,
.auto_boot = true,
.ssr_name = "dsps",
.sysmon_name = "slpi",
.ssctl_id = 0x16,
};
static const struct adsp_data sm8150_slpi_resource = {
.crash_reason_smem = 424,
.firmware_name = "slpi.mdt",
.pas_id = 12,
.has_aggre2_clk = false,
.auto_boot = true,
.active_pd_names = (char*[]){
"load_state",
NULL
},
.proxy_pd_names = (char*[]){
"lcx",
"lmx",
NULL
},
.ssr_name = "dsps",
.sysmon_name = "slpi",
.ssctl_id = 0x16,
};
static const struct adsp_data sm8250_slpi_resource = {
.crash_reason_smem = 424,
.firmware_name = "slpi.mdt",
.pas_id = 12,
.has_aggre2_clk = false,
.auto_boot = true,
.active_pd_names = (char*[]){
"load_state",
NULL
},
.proxy_pd_names = (char*[]){
"lcx",
"lmx",
NULL
},
.ssr_name = "dsps",
.sysmon_name = "slpi",
.ssctl_id = 0x16,
};
static const struct adsp_data msm8998_slpi_resource = {
.crash_reason_smem = 424,
.firmware_name = "slpi.mdt",
.pas_id = 12,
.has_aggre2_clk = true,
.auto_boot = true,
.proxy_pd_names = (char*[]){
"ssc_cx",
NULL
},
.ssr_name = "dsps",
.sysmon_name = "slpi",
.ssctl_id = 0x16,
};
static const struct adsp_data wcss_resource_init = {
.crash_reason_smem = 421,
.firmware_name = "wcnss.mdt",
.pas_id = 6,
.auto_boot = true,
.ssr_name = "mpss",
.sysmon_name = "wcnss",
.ssctl_id = 0x12,
};
static const struct of_device_id adsp_of_match[] = {
{ .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init},
{ .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init},
{ .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init},
{ .compatible = "qcom,msm8998-adsp-pas", .data = &msm8998_adsp_resource},
{ .compatible = "qcom,msm8998-slpi-pas", .data = &msm8998_slpi_resource},
{ .compatible = "qcom,qcs404-adsp-pas", .data = &adsp_resource_init },
{ .compatible = "qcom,qcs404-cdsp-pas", .data = &cdsp_resource_init },
{ .compatible = "qcom,qcs404-wcss-pas", .data = &wcss_resource_init },
{ .compatible = "qcom,sc7180-mpss-pas", .data = &mpss_resource_init},
{ .compatible = "qcom,sdm845-adsp-pas", .data = &adsp_resource_init},
{ .compatible = "qcom,sdm845-cdsp-pas", .data = &cdsp_resource_init},
{ .compatible = "qcom,sm8150-adsp-pas", .data = &sm8150_adsp_resource},
{ .compatible = "qcom,sm8150-cdsp-pas", .data = &sm8150_cdsp_resource},
{ .compatible = "qcom,sm8150-mpss-pas", .data = &mpss_resource_init},
{ .compatible = "qcom,sm8150-slpi-pas", .data = &sm8150_slpi_resource},
{ .compatible = "qcom,sm8250-adsp-pas", .data = &sm8250_adsp_resource},
{ .compatible = "qcom,sm8250-cdsp-pas", .data = &sm8250_cdsp_resource},
{ .compatible = "qcom,sm8250-slpi-pas", .data = &sm8250_slpi_resource},
{ },
};
MODULE_DEVICE_TABLE(of, adsp_of_match);
static struct platform_driver adsp_driver = {
.probe = adsp_probe,
.remove = adsp_remove,
.driver = {
.name = "qcom_q6v5_pas",
.of_match_table = adsp_of_match,
},
};
module_platform_driver(adsp_driver);
MODULE_DESCRIPTION("Qualcomm Hexagon v5 Peripheral Authentication Service driver");
MODULE_LICENSE("GPL v2");