linux/drivers/remoteproc/qcom_q6v5_pas.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Qualcomm ADSP/SLPI Peripheral Image Loader for MSM8974 and MSM8996
   4 *
   5 * Copyright (C) 2016 Linaro Ltd
   6 * Copyright (C) 2014 Sony Mobile Communications AB
   7 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
   8 */
   9
  10#include <linux/clk.h>
  11#include <linux/firmware.h>
  12#include <linux/interrupt.h>
  13#include <linux/kernel.h>
  14#include <linux/module.h>
  15#include <linux/of_address.h>
  16#include <linux/of_device.h>
  17#include <linux/platform_device.h>
  18#include <linux/qcom_scm.h>
  19#include <linux/regulator/consumer.h>
  20#include <linux/remoteproc.h>
  21#include <linux/soc/qcom/mdt_loader.h>
  22#include <linux/soc/qcom/smem.h>
  23#include <linux/soc/qcom/smem_state.h>
  24
  25#include "qcom_common.h"
  26#include "qcom_q6v5.h"
  27#include "remoteproc_internal.h"
  28
  29struct adsp_data {
  30        int crash_reason_smem;
  31        const char *firmware_name;
  32        int pas_id;
  33        bool has_aggre2_clk;
  34
  35        const char *ssr_name;
  36        const char *sysmon_name;
  37        int ssctl_id;
  38};
  39
  40struct qcom_adsp {
  41        struct device *dev;
  42        struct rproc *rproc;
  43
  44        struct qcom_q6v5 q6v5;
  45
  46        struct clk *xo;
  47        struct clk *aggre2_clk;
  48
  49        struct regulator *cx_supply;
  50        struct regulator *px_supply;
  51
  52        int pas_id;
  53        int crash_reason_smem;
  54        bool has_aggre2_clk;
  55
  56        struct completion start_done;
  57        struct completion stop_done;
  58
  59        phys_addr_t mem_phys;
  60        phys_addr_t mem_reloc;
  61        void *mem_region;
  62        size_t mem_size;
  63
  64        struct qcom_rproc_glink glink_subdev;
  65        struct qcom_rproc_subdev smd_subdev;
  66        struct qcom_rproc_ssr ssr_subdev;
  67        struct qcom_sysmon *sysmon;
  68};
  69
  70static int adsp_load(struct rproc *rproc, const struct firmware *fw)
  71{
  72        struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
  73
  74        return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
  75                             adsp->mem_region, adsp->mem_phys, adsp->mem_size,
  76                             &adsp->mem_reloc);
  77
  78}
  79
  80static int adsp_start(struct rproc *rproc)
  81{
  82        struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
  83        int ret;
  84
  85        qcom_q6v5_prepare(&adsp->q6v5);
  86
  87        ret = clk_prepare_enable(adsp->xo);
  88        if (ret)
  89                return ret;
  90
  91        ret = clk_prepare_enable(adsp->aggre2_clk);
  92        if (ret)
  93                goto disable_xo_clk;
  94
  95        ret = regulator_enable(adsp->cx_supply);
  96        if (ret)
  97                goto disable_aggre2_clk;
  98
  99        ret = regulator_enable(adsp->px_supply);
 100        if (ret)
 101                goto disable_cx_supply;
 102
 103        ret = qcom_scm_pas_auth_and_reset(adsp->pas_id);
 104        if (ret) {
 105                dev_err(adsp->dev,
 106                        "failed to authenticate image and release reset\n");
 107                goto disable_px_supply;
 108        }
 109
 110        ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000));
 111        if (ret == -ETIMEDOUT) {
 112                dev_err(adsp->dev, "start timed out\n");
 113                qcom_scm_pas_shutdown(adsp->pas_id);
 114                goto disable_px_supply;
 115        }
 116
 117        return 0;
 118
 119disable_px_supply:
 120        regulator_disable(adsp->px_supply);
 121disable_cx_supply:
 122        regulator_disable(adsp->cx_supply);
 123disable_aggre2_clk:
 124        clk_disable_unprepare(adsp->aggre2_clk);
 125disable_xo_clk:
 126        clk_disable_unprepare(adsp->xo);
 127
 128        return ret;
 129}
 130
 131static void qcom_pas_handover(struct qcom_q6v5 *q6v5)
 132{
 133        struct qcom_adsp *adsp = container_of(q6v5, struct qcom_adsp, q6v5);
 134
 135        regulator_disable(adsp->px_supply);
 136        regulator_disable(adsp->cx_supply);
 137        clk_disable_unprepare(adsp->aggre2_clk);
 138        clk_disable_unprepare(adsp->xo);
 139}
 140
 141static int adsp_stop(struct rproc *rproc)
 142{
 143        struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
 144        int handover;
 145        int ret;
 146
 147        ret = qcom_q6v5_request_stop(&adsp->q6v5);
 148        if (ret == -ETIMEDOUT)
 149                dev_err(adsp->dev, "timed out on wait\n");
 150
 151        ret = qcom_scm_pas_shutdown(adsp->pas_id);
 152        if (ret)
 153                dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
 154
 155        handover = qcom_q6v5_unprepare(&adsp->q6v5);
 156        if (handover)
 157                qcom_pas_handover(&adsp->q6v5);
 158
 159        return ret;
 160}
 161
 162static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
 163{
 164        struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
 165        int offset;
 166
 167        offset = da - adsp->mem_reloc;
 168        if (offset < 0 || offset + len > adsp->mem_size)
 169                return NULL;
 170
 171        return adsp->mem_region + offset;
 172}
 173
 174static const struct rproc_ops adsp_ops = {
 175        .start = adsp_start,
 176        .stop = adsp_stop,
 177        .da_to_va = adsp_da_to_va,
 178        .parse_fw = qcom_register_dump_segments,
 179        .load = adsp_load,
 180};
 181
 182static int adsp_init_clock(struct qcom_adsp *adsp)
 183{
 184        int ret;
 185
 186        adsp->xo = devm_clk_get(adsp->dev, "xo");
 187        if (IS_ERR(adsp->xo)) {
 188                ret = PTR_ERR(adsp->xo);
 189                if (ret != -EPROBE_DEFER)
 190                        dev_err(adsp->dev, "failed to get xo clock");
 191                return ret;
 192        }
 193
 194        if (adsp->has_aggre2_clk) {
 195                adsp->aggre2_clk = devm_clk_get(adsp->dev, "aggre2");
 196                if (IS_ERR(adsp->aggre2_clk)) {
 197                        ret = PTR_ERR(adsp->aggre2_clk);
 198                        if (ret != -EPROBE_DEFER)
 199                                dev_err(adsp->dev,
 200                                        "failed to get aggre2 clock");
 201                        return ret;
 202                }
 203        }
 204
 205        return 0;
 206}
 207
 208static int adsp_init_regulator(struct qcom_adsp *adsp)
 209{
 210        adsp->cx_supply = devm_regulator_get(adsp->dev, "cx");
 211        if (IS_ERR(adsp->cx_supply))
 212                return PTR_ERR(adsp->cx_supply);
 213
 214        regulator_set_load(adsp->cx_supply, 100000);
 215
 216        adsp->px_supply = devm_regulator_get(adsp->dev, "px");
 217        return PTR_ERR_OR_ZERO(adsp->px_supply);
 218}
 219
 220static int adsp_alloc_memory_region(struct qcom_adsp *adsp)
 221{
 222        struct device_node *node;
 223        struct resource r;
 224        int ret;
 225
 226        node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0);
 227        if (!node) {
 228                dev_err(adsp->dev, "no memory-region specified\n");
 229                return -EINVAL;
 230        }
 231
 232        ret = of_address_to_resource(node, 0, &r);
 233        if (ret)
 234                return ret;
 235
 236        adsp->mem_phys = adsp->mem_reloc = r.start;
 237        adsp->mem_size = resource_size(&r);
 238        adsp->mem_region = devm_ioremap_wc(adsp->dev, adsp->mem_phys, adsp->mem_size);
 239        if (!adsp->mem_region) {
 240                dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n",
 241                        &r.start, adsp->mem_size);
 242                return -EBUSY;
 243        }
 244
 245        return 0;
 246}
 247
 248static int adsp_probe(struct platform_device *pdev)
 249{
 250        const struct adsp_data *desc;
 251        struct qcom_adsp *adsp;
 252        struct rproc *rproc;
 253        const char *fw_name;
 254        int ret;
 255
 256        desc = of_device_get_match_data(&pdev->dev);
 257        if (!desc)
 258                return -EINVAL;
 259
 260        if (!qcom_scm_is_available())
 261                return -EPROBE_DEFER;
 262
 263        fw_name = desc->firmware_name;
 264        ret = of_property_read_string(pdev->dev.of_node, "firmware-name",
 265                                      &fw_name);
 266        if (ret < 0 && ret != -EINVAL)
 267                return ret;
 268
 269        rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops,
 270                            fw_name, sizeof(*adsp));
 271        if (!rproc) {
 272                dev_err(&pdev->dev, "unable to allocate remoteproc\n");
 273                return -ENOMEM;
 274        }
 275
 276        adsp = (struct qcom_adsp *)rproc->priv;
 277        adsp->dev = &pdev->dev;
 278        adsp->rproc = rproc;
 279        adsp->pas_id = desc->pas_id;
 280        adsp->has_aggre2_clk = desc->has_aggre2_clk;
 281        platform_set_drvdata(pdev, adsp);
 282
 283        ret = adsp_alloc_memory_region(adsp);
 284        if (ret)
 285                goto free_rproc;
 286
 287        ret = adsp_init_clock(adsp);
 288        if (ret)
 289                goto free_rproc;
 290
 291        ret = adsp_init_regulator(adsp);
 292        if (ret)
 293                goto free_rproc;
 294
 295        ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem,
 296                             qcom_pas_handover);
 297        if (ret)
 298                goto free_rproc;
 299
 300        qcom_add_glink_subdev(rproc, &adsp->glink_subdev);
 301        qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
 302        qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
 303        adsp->sysmon = qcom_add_sysmon_subdev(rproc,
 304                                              desc->sysmon_name,
 305                                              desc->ssctl_id);
 306        if (IS_ERR(adsp->sysmon)) {
 307                ret = PTR_ERR(adsp->sysmon);
 308                goto free_rproc;
 309        }
 310
 311        ret = rproc_add(rproc);
 312        if (ret)
 313                goto free_rproc;
 314
 315        return 0;
 316
 317free_rproc:
 318        rproc_free(rproc);
 319
 320        return ret;
 321}
 322
 323static int adsp_remove(struct platform_device *pdev)
 324{
 325        struct qcom_adsp *adsp = platform_get_drvdata(pdev);
 326
 327        rproc_del(adsp->rproc);
 328
 329        qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev);
 330        qcom_remove_sysmon_subdev(adsp->sysmon);
 331        qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
 332        qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
 333        rproc_free(adsp->rproc);
 334
 335        return 0;
 336}
 337
 338static const struct adsp_data adsp_resource_init = {
 339                .crash_reason_smem = 423,
 340                .firmware_name = "adsp.mdt",
 341                .pas_id = 1,
 342                .has_aggre2_clk = false,
 343                .ssr_name = "lpass",
 344                .sysmon_name = "adsp",
 345                .ssctl_id = 0x14,
 346};
 347
 348static const struct adsp_data cdsp_resource_init = {
 349        .crash_reason_smem = 601,
 350        .firmware_name = "cdsp.mdt",
 351        .pas_id = 18,
 352        .has_aggre2_clk = false,
 353        .ssr_name = "cdsp",
 354        .sysmon_name = "cdsp",
 355        .ssctl_id = 0x17,
 356};
 357
 358static const struct adsp_data slpi_resource_init = {
 359                .crash_reason_smem = 424,
 360                .firmware_name = "slpi.mdt",
 361                .pas_id = 12,
 362                .has_aggre2_clk = true,
 363                .ssr_name = "dsps",
 364                .sysmon_name = "slpi",
 365                .ssctl_id = 0x16,
 366};
 367
 368static const struct adsp_data wcss_resource_init = {
 369        .crash_reason_smem = 421,
 370        .firmware_name = "wcnss.mdt",
 371        .pas_id = 6,
 372        .ssr_name = "mpss",
 373        .sysmon_name = "wcnss",
 374        .ssctl_id = 0x12,
 375};
 376
 377static const struct of_device_id adsp_of_match[] = {
 378        { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init},
 379        { .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init},
 380        { .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init},
 381        { .compatible = "qcom,qcs404-adsp-pas", .data = &adsp_resource_init },
 382        { .compatible = "qcom,qcs404-cdsp-pas", .data = &cdsp_resource_init },
 383        { .compatible = "qcom,qcs404-wcss-pas", .data = &wcss_resource_init },
 384        { .compatible = "qcom,sdm845-adsp-pas", .data = &adsp_resource_init},
 385        { .compatible = "qcom,sdm845-cdsp-pas", .data = &cdsp_resource_init},
 386        { },
 387};
 388MODULE_DEVICE_TABLE(of, adsp_of_match);
 389
 390static struct platform_driver adsp_driver = {
 391        .probe = adsp_probe,
 392        .remove = adsp_remove,
 393        .driver = {
 394                .name = "qcom_q6v5_pas",
 395                .of_match_table = adsp_of_match,
 396        },
 397};
 398
 399module_platform_driver(adsp_driver);
 400MODULE_DESCRIPTION("Qualcomm Hexagon v5 Peripheral Authentication Service driver");
 401MODULE_LICENSE("GPL v2");
 402