linux/drivers/firmware/qcom_scm.c
<<
>>
Prefs
   1/*
   2 * Qualcomm SCM driver
   3 *
   4 * Copyright (c) 2010,2015, The Linux Foundation. All rights reserved.
   5 * Copyright (C) 2015 Linaro Ltd.
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 and
   9 * only version 2 as published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 */
  17#include <linux/platform_device.h>
  18#include <linux/init.h>
  19#include <linux/cpumask.h>
  20#include <linux/export.h>
  21#include <linux/dma-mapping.h>
  22#include <linux/types.h>
  23#include <linux/qcom_scm.h>
  24#include <linux/of.h>
  25#include <linux/of_platform.h>
  26#include <linux/clk.h>
  27#include <linux/reset-controller.h>
  28
  29#include "qcom_scm.h"
  30
  31#define SCM_HAS_CORE_CLK        BIT(0)
  32#define SCM_HAS_IFACE_CLK       BIT(1)
  33#define SCM_HAS_BUS_CLK         BIT(2)
  34
  35struct qcom_scm {
  36        struct device *dev;
  37        struct clk *core_clk;
  38        struct clk *iface_clk;
  39        struct clk *bus_clk;
  40        struct reset_controller_dev reset;
  41};
  42
  43static struct qcom_scm *__scm;
  44
  45static int qcom_scm_clk_enable(void)
  46{
  47        int ret;
  48
  49        ret = clk_prepare_enable(__scm->core_clk);
  50        if (ret)
  51                goto bail;
  52
  53        ret = clk_prepare_enable(__scm->iface_clk);
  54        if (ret)
  55                goto disable_core;
  56
  57        ret = clk_prepare_enable(__scm->bus_clk);
  58        if (ret)
  59                goto disable_iface;
  60
  61        return 0;
  62
  63disable_iface:
  64        clk_disable_unprepare(__scm->iface_clk);
  65disable_core:
  66        clk_disable_unprepare(__scm->core_clk);
  67bail:
  68        return ret;
  69}
  70
  71static void qcom_scm_clk_disable(void)
  72{
  73        clk_disable_unprepare(__scm->core_clk);
  74        clk_disable_unprepare(__scm->iface_clk);
  75        clk_disable_unprepare(__scm->bus_clk);
  76}
  77
  78/**
  79 * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
  80 * @entry: Entry point function for the cpus
  81 * @cpus: The cpumask of cpus that will use the entry point
  82 *
  83 * Set the cold boot address of the cpus. Any cpu outside the supported
  84 * range would be removed from the cpu present mask.
  85 */
  86int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
  87{
  88        return __qcom_scm_set_cold_boot_addr(entry, cpus);
  89}
  90EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr);
  91
  92/**
  93 * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus
  94 * @entry: Entry point function for the cpus
  95 * @cpus: The cpumask of cpus that will use the entry point
  96 *
  97 * Set the Linux entry point for the SCM to transfer control to when coming
  98 * out of a power down. CPU power down may be executed on cpuidle or hotplug.
  99 */
 100int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
 101{
 102        return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus);
 103}
 104EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr);
 105
 106/**
 107 * qcom_scm_cpu_power_down() - Power down the cpu
 108 * @flags - Flags to flush cache
 109 *
 110 * This is an end point to power down cpu. If there was a pending interrupt,
 111 * the control would return from this function, otherwise, the cpu jumps to the
 112 * warm boot entry point set for this cpu upon reset.
 113 */
 114void qcom_scm_cpu_power_down(u32 flags)
 115{
 116        __qcom_scm_cpu_power_down(flags);
 117}
 118EXPORT_SYMBOL(qcom_scm_cpu_power_down);
 119
 120/**
 121 * qcom_scm_hdcp_available() - Check if secure environment supports HDCP.
 122 *
 123 * Return true if HDCP is supported, false if not.
 124 */
 125bool qcom_scm_hdcp_available(void)
 126{
 127        int ret = qcom_scm_clk_enable();
 128
 129        if (ret)
 130                return ret;
 131
 132        ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP,
 133                                                QCOM_SCM_CMD_HDCP);
 134
 135        qcom_scm_clk_disable();
 136
 137        return ret > 0 ? true : false;
 138}
 139EXPORT_SYMBOL(qcom_scm_hdcp_available);
 140
 141/**
 142 * qcom_scm_hdcp_req() - Send HDCP request.
 143 * @req: HDCP request array
 144 * @req_cnt: HDCP request array count
 145 * @resp: response buffer passed to SCM
 146 *
 147 * Write HDCP register(s) through SCM.
 148 */
 149int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
 150{
 151        int ret = qcom_scm_clk_enable();
 152
 153        if (ret)
 154                return ret;
 155
 156        ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp);
 157        qcom_scm_clk_disable();
 158        return ret;
 159}
 160EXPORT_SYMBOL(qcom_scm_hdcp_req);
 161
 162/**
 163 * qcom_scm_pas_supported() - Check if the peripheral authentication service is
 164 *                            available for the given peripherial
 165 * @peripheral: peripheral id
 166 *
 167 * Returns true if PAS is supported for this peripheral, otherwise false.
 168 */
 169bool qcom_scm_pas_supported(u32 peripheral)
 170{
 171        int ret;
 172
 173        ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
 174                                           QCOM_SCM_PAS_IS_SUPPORTED_CMD);
 175        if (ret <= 0)
 176                return false;
 177
 178        return __qcom_scm_pas_supported(__scm->dev, peripheral);
 179}
 180EXPORT_SYMBOL(qcom_scm_pas_supported);
 181
 182/**
 183 * qcom_scm_pas_init_image() - Initialize peripheral authentication service
 184 *                             state machine for a given peripheral, using the
 185 *                             metadata
 186 * @peripheral: peripheral id
 187 * @metadata:   pointer to memory containing ELF header, program header table
 188 *              and optional blob of data used for authenticating the metadata
 189 *              and the rest of the firmware
 190 * @size:       size of the metadata
 191 *
 192 * Returns 0 on success.
 193 */
 194int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
 195{
 196        dma_addr_t mdata_phys;
 197        void *mdata_buf;
 198        int ret;
 199
 200        /*
 201         * During the scm call memory protection will be enabled for the meta
 202         * data blob, so make sure it's physically contiguous, 4K aligned and
 203         * non-cachable to avoid XPU violations.
 204         */
 205        mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
 206                                       GFP_KERNEL);
 207        if (!mdata_buf) {
 208                dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
 209                return -ENOMEM;
 210        }
 211        memcpy(mdata_buf, metadata, size);
 212
 213        ret = qcom_scm_clk_enable();
 214        if (ret)
 215                goto free_metadata;
 216
 217        ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys);
 218
 219        qcom_scm_clk_disable();
 220
 221free_metadata:
 222        dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
 223
 224        return ret;
 225}
 226EXPORT_SYMBOL(qcom_scm_pas_init_image);
 227
 228/**
 229 * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
 230 *                            for firmware loading
 231 * @peripheral: peripheral id
 232 * @addr:       start address of memory area to prepare
 233 * @size:       size of the memory area to prepare
 234 *
 235 * Returns 0 on success.
 236 */
 237int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
 238{
 239        int ret;
 240
 241        ret = qcom_scm_clk_enable();
 242        if (ret)
 243                return ret;
 244
 245        ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size);
 246        qcom_scm_clk_disable();
 247
 248        return ret;
 249}
 250EXPORT_SYMBOL(qcom_scm_pas_mem_setup);
 251
 252/**
 253 * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
 254 *                                 and reset the remote processor
 255 * @peripheral: peripheral id
 256 *
 257 * Return 0 on success.
 258 */
 259int qcom_scm_pas_auth_and_reset(u32 peripheral)
 260{
 261        int ret;
 262
 263        ret = qcom_scm_clk_enable();
 264        if (ret)
 265                return ret;
 266
 267        ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral);
 268        qcom_scm_clk_disable();
 269
 270        return ret;
 271}
 272EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset);
 273
 274/**
 275 * qcom_scm_pas_shutdown() - Shut down the remote processor
 276 * @peripheral: peripheral id
 277 *
 278 * Returns 0 on success.
 279 */
 280int qcom_scm_pas_shutdown(u32 peripheral)
 281{
 282        int ret;
 283
 284        ret = qcom_scm_clk_enable();
 285        if (ret)
 286                return ret;
 287
 288        ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral);
 289        qcom_scm_clk_disable();
 290
 291        return ret;
 292}
 293EXPORT_SYMBOL(qcom_scm_pas_shutdown);
 294
 295static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev,
 296                                     unsigned long idx)
 297{
 298        if (idx != 0)
 299                return -EINVAL;
 300
 301        return __qcom_scm_pas_mss_reset(__scm->dev, 1);
 302}
 303
 304static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev,
 305                                       unsigned long idx)
 306{
 307        if (idx != 0)
 308                return -EINVAL;
 309
 310        return __qcom_scm_pas_mss_reset(__scm->dev, 0);
 311}
 312
 313static const struct reset_control_ops qcom_scm_pas_reset_ops = {
 314        .assert = qcom_scm_pas_reset_assert,
 315        .deassert = qcom_scm_pas_reset_deassert,
 316};
 317
 318int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare)
 319{
 320        return __qcom_scm_restore_sec_cfg(__scm->dev, device_id, spare);
 321}
 322EXPORT_SYMBOL(qcom_scm_restore_sec_cfg);
 323
 324int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size)
 325{
 326        return __qcom_scm_iommu_secure_ptbl_size(__scm->dev, spare, size);
 327}
 328EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_size);
 329
 330int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare)
 331{
 332        return __qcom_scm_iommu_secure_ptbl_init(__scm->dev, addr, size, spare);
 333}
 334EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init);
 335
 336/**
 337 * qcom_scm_is_available() - Checks if SCM is available
 338 */
 339bool qcom_scm_is_available(void)
 340{
 341        return !!__scm;
 342}
 343EXPORT_SYMBOL(qcom_scm_is_available);
 344
 345int qcom_scm_set_remote_state(u32 state, u32 id)
 346{
 347        return __qcom_scm_set_remote_state(__scm->dev, state, id);
 348}
 349EXPORT_SYMBOL(qcom_scm_set_remote_state);
 350
 351static int qcom_scm_probe(struct platform_device *pdev)
 352{
 353        struct qcom_scm *scm;
 354        unsigned long clks;
 355        int ret;
 356
 357        scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
 358        if (!scm)
 359                return -ENOMEM;
 360
 361        clks = (unsigned long)of_device_get_match_data(&pdev->dev);
 362        if (clks & SCM_HAS_CORE_CLK) {
 363                scm->core_clk = devm_clk_get(&pdev->dev, "core");
 364                if (IS_ERR(scm->core_clk)) {
 365                        if (PTR_ERR(scm->core_clk) != -EPROBE_DEFER)
 366                                dev_err(&pdev->dev,
 367                                        "failed to acquire core clk\n");
 368                        return PTR_ERR(scm->core_clk);
 369                }
 370        }
 371
 372        if (clks & SCM_HAS_IFACE_CLK) {
 373                scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
 374                if (IS_ERR(scm->iface_clk)) {
 375                        if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER)
 376                                dev_err(&pdev->dev,
 377                                        "failed to acquire iface clk\n");
 378                        return PTR_ERR(scm->iface_clk);
 379                }
 380        }
 381
 382        if (clks & SCM_HAS_BUS_CLK) {
 383                scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
 384                if (IS_ERR(scm->bus_clk)) {
 385                        if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER)
 386                                dev_err(&pdev->dev,
 387                                        "failed to acquire bus clk\n");
 388                        return PTR_ERR(scm->bus_clk);
 389                }
 390        }
 391
 392        scm->reset.ops = &qcom_scm_pas_reset_ops;
 393        scm->reset.nr_resets = 1;
 394        scm->reset.of_node = pdev->dev.of_node;
 395        ret = devm_reset_controller_register(&pdev->dev, &scm->reset);
 396        if (ret)
 397                return ret;
 398
 399        /* vote for max clk rate for highest performance */
 400        ret = clk_set_rate(scm->core_clk, INT_MAX);
 401        if (ret)
 402                return ret;
 403
 404        __scm = scm;
 405        __scm->dev = &pdev->dev;
 406
 407        __qcom_scm_init();
 408
 409        return 0;
 410}
 411
 412static const struct of_device_id qcom_scm_dt_match[] = {
 413        { .compatible = "qcom,scm-apq8064",
 414          /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
 415        },
 416        { .compatible = "qcom,scm-msm8660",
 417          .data = (void *) SCM_HAS_CORE_CLK,
 418        },
 419        { .compatible = "qcom,scm-msm8960",
 420          .data = (void *) SCM_HAS_CORE_CLK,
 421        },
 422        { .compatible = "qcom,scm-msm8996",
 423          .data = NULL, /* no clocks */
 424        },
 425        { .compatible = "qcom,scm",
 426          .data = (void *)(SCM_HAS_CORE_CLK
 427                           | SCM_HAS_IFACE_CLK
 428                           | SCM_HAS_BUS_CLK),
 429        },
 430        {}
 431};
 432
 433static struct platform_driver qcom_scm_driver = {
 434        .driver = {
 435                .name   = "qcom_scm",
 436                .of_match_table = qcom_scm_dt_match,
 437        },
 438        .probe = qcom_scm_probe,
 439};
 440
 441static int __init qcom_scm_init(void)
 442{
 443        struct device_node *np, *fw_np;
 444        int ret;
 445
 446        fw_np = of_find_node_by_name(NULL, "firmware");
 447
 448        if (!fw_np)
 449                return -ENODEV;
 450
 451        np = of_find_matching_node(fw_np, qcom_scm_dt_match);
 452
 453        if (!np) {
 454                of_node_put(fw_np);
 455                return -ENODEV;
 456        }
 457
 458        of_node_put(np);
 459
 460        ret = of_platform_populate(fw_np, qcom_scm_dt_match, NULL, NULL);
 461
 462        of_node_put(fw_np);
 463
 464        if (ret)
 465                return ret;
 466
 467        return platform_driver_register(&qcom_scm_driver);
 468}
 469subsys_initcall(qcom_scm_init);
 470