root/drivers/remoteproc/qcom_q6v5_pas.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. adsp_load
  2. adsp_start
  3. qcom_pas_handover
  4. adsp_stop
  5. adsp_da_to_va
  6. adsp_init_clock
  7. adsp_init_regulator
  8. adsp_alloc_memory_region
  9. adsp_probe
  10. adsp_remove

   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 
  29 struct 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 
  40 struct 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 
  70 static 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 
  80 static 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 
 119 disable_px_supply:
 120         regulator_disable(adsp->px_supply);
 121 disable_cx_supply:
 122         regulator_disable(adsp->cx_supply);
 123 disable_aggre2_clk:
 124         clk_disable_unprepare(adsp->aggre2_clk);
 125 disable_xo_clk:
 126         clk_disable_unprepare(adsp->xo);
 127 
 128         return ret;
 129 }
 130 
 131 static 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 
 141 static 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 
 162 static 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 
 174 static 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 
 182 static 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 
 208 static 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 
 220 static 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 
 248 static 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 
 317 free_rproc:
 318         rproc_free(rproc);
 319 
 320         return ret;
 321 }
 322 
 323 static 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 
 338 static 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 
 348 static 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 
 358 static 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 
 368 static 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 
 377 static 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 };
 388 MODULE_DEVICE_TABLE(of, adsp_of_match);
 389 
 390 static 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 
 399 module_platform_driver(adsp_driver);
 400 MODULE_DESCRIPTION("Qualcomm Hexagon v5 Peripheral Authentication Service driver");
 401 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */