Qualcomm driver updates for v5.6 * SCM major refactoring and cleanup * Properly flag active only power domains as active only * Add SC7180 and SM8150 RPMH power domains * Return EPROBE_DEFER from QMI if packet family is not yet available * tag 'qcom-drivers-for-5.6' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: (27 commits) firmware: qcom_scm: Dynamically support SMCCC and legacy conventions firmware: qcom_scm: Remove thin wrappers firmware: qcom_scm: Order functions, definitions by service/command firmware: qcom_scm-32: Add device argument to atomic calls firmware: qcom_scm-32: Create common legacy atomic call firmware: qcom_scm-32: Move SMCCC register filling to qcom_scm_call firmware: qcom_scm-32: Use qcom_scm_desc in non-atomic calls firmware: qcom_scm-32: Add funcnum IDs firmware: qcom_scm-32: Use SMC arch wrappers firmware: qcom_scm-64: Improve SMC convention detection firmware: qcom_scm-64: Move SMC register filling to qcom_scm_call_smccc firmware: qcom_scm-64: Add SCM results struct firmware: qcom_scm-64: Move svc/cmd/owner into qcom_scm_desc firmware: qcom_scm-64: Make SMC macros less magical firmware: qcom_scm: Remove unused qcom_scm_get_version firmware: qcom_scm: Apply consistent naming scheme to command IDs firmware: qcom_scm: Rename macros and structures soc: qcom: rpmhpd: Set 'active_only' for active only power domains firmware: scm: Add stubs for OCMEM and restore_sec_cfg_available dt-bindings: power: rpmpd: Convert rpmpd bindings to yaml ...
Unified · Split
| @@ -47,7 +47,7 @@ examples: · 46 unmodified lines | |||
| 47 | - | | 47 | - | |
| 48 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 48 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 49 | 49 | ||
| 50 | cache-controller@1100000 { | 50 | system-cache-controller@1100000 { |
| 51 | compatible = "qcom,sdm845-llcc"; | 51 | compatible = "qcom,sdm845-llcc"; |
| 52 | reg = <0x1100000 0x200000>, <0x1300000 0x50000> ; | 52 | reg = <0x1100000 0x200000>, <0x1300000 0x50000> ; |
| 53 | reg-names = "llcc_base", "llcc_broadcast_base"; | 53 | reg-names = "llcc_base", "llcc_broadcast_base"; |
| @@ -1,148 +0,0 @@ | |||
| 1 | Qualcomm RPM/RPMh Power domains | ||
| 2 | |||
| 3 | For RPM/RPMh Power domains, we communicate a performance state to RPM/RPMh | ||
| 4 | which then translates it into a corresponding voltage on a rail | ||
| 5 | |||
| 6 | Required Properties: | ||
| 7 | - compatible: Should be one of the following | ||
| 8 | * qcom,msm8976-rpmpd: RPM Power domain for the msm8976 family of SoC | ||
| 9 | * qcom,msm8996-rpmpd: RPM Power domain for the msm8996 family of SoC | ||
| 10 | * qcom,msm8998-rpmpd: RPM Power domain for the msm8998 family of SoC | ||
| 11 | * qcom,qcs404-rpmpd: RPM Power domain for the qcs404 family of SoC | ||
| 12 | * qcom,sdm845-rpmhpd: RPMh Power domain for the sdm845 family of SoC | ||
| 13 | - #power-domain-cells: number of cells in Power domain specifier | ||
| 14 | must be 1. | ||
| 15 | - operating-points-v2: Phandle to the OPP table for the Power domain. | ||
| 16 | Refer to Documentation/devicetree/bindings/power/power_domain.txt | ||
| 17 | and Documentation/devicetree/bindings/opp/opp.txt for more details | ||
| 18 | |||
| 19 | Refer to <dt-bindings/power/qcom-rpmpd.h> for the level values for | ||
| 20 | various OPPs for different platforms as well as Power domain indexes | ||
| 21 | |||
| 22 | Example: rpmh power domain controller and OPP table | ||
| 23 | |||
| 24 | #include <dt-bindings/power/qcom-rpmhpd.h> | ||
| 25 | |||
| 26 | opp-level values specified in the OPP tables for RPMh power domains | ||
| 27 | should use the RPMH_REGULATOR_LEVEL_* constants from | ||
| 28 | <dt-bindings/power/qcom-rpmhpd.h> | ||
| 29 | |||
| 30 | rpmhpd: power-controller { | ||
| 31 | compatible = "qcom,sdm845-rpmhpd"; | ||
| 32 | #power-domain-cells = <1>; | ||
| 33 | operating-points-v2 = <&rpmhpd_opp_table>; | ||
| 34 | |||
| 35 | rpmhpd_opp_table: opp-table { | ||
| 36 | compatible = "operating-points-v2"; | ||
| 37 | |||
| 38 | rpmhpd_opp_ret: opp1 { | ||
| 39 | opp-level = <RPMH_REGULATOR_LEVEL_RETENTION>; | ||
| 40 | }; | ||
| 41 | |||
| 42 | rpmhpd_opp_min_svs: opp2 { | ||
| 43 | opp-level = <RPMH_REGULATOR_LEVEL_MIN_SVS>; | ||
| 44 | }; | ||
| 45 | |||
| 46 | rpmhpd_opp_low_svs: opp3 { | ||
| 47 | opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>; | ||
| 48 | }; | ||
| 49 | |||
| 50 | rpmhpd_opp_svs: opp4 { | ||
| 51 | opp-level = <RPMH_REGULATOR_LEVEL_SVS>; | ||
| 52 | }; | ||
| 53 | |||
| 54 | rpmhpd_opp_svs_l1: opp5 { | ||
| 55 | opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>; | ||
| 56 | }; | ||
| 57 | |||
| 58 | rpmhpd_opp_nom: opp6 { | ||
| 59 | opp-level = <RPMH_REGULATOR_LEVEL_NOM>; | ||
| 60 | }; | ||
| 61 | |||
| 62 | rpmhpd_opp_nom_l1: opp7 { | ||
| 63 | opp-level = <RPMH_REGULATOR_LEVEL_NOM_L1>; | ||
| 64 | }; | ||
| 65 | |||
| 66 | rpmhpd_opp_nom_l2: opp8 { | ||
| 67 | opp-level = <RPMH_REGULATOR_LEVEL_NOM_L2>; | ||
| 68 | }; | ||
| 69 | |||
| 70 | rpmhpd_opp_turbo: opp9 { | ||
| 71 | opp-level = <RPMH_REGULATOR_LEVEL_TURBO>; | ||
| 72 | }; | ||
| 73 | |||
| 74 | rpmhpd_opp_turbo_l1: opp10 { | ||
| 75 | opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L1>; | ||
| 76 | }; | ||
| 77 | }; | ||
| 78 | }; | ||
| 79 | |||
| 80 | Example: rpm power domain controller and OPP table | ||
| 81 | |||
| 82 | rpmpd: power-controller { | ||
| 83 | compatible = "qcom,msm8996-rpmpd"; | ||
| 84 | #power-domain-cells = <1>; | ||
| 85 | operating-points-v2 = <&rpmpd_opp_table>; | ||
| 86 | |||
| 87 | rpmpd_opp_table: opp-table { | ||
| 88 | compatible = "operating-points-v2"; | ||
| 89 | |||
| 90 | rpmpd_opp_low: opp1 { | ||
| 91 | opp-level = <1>; | ||
| 92 | }; | ||
| 93 | |||
| 94 | rpmpd_opp_ret: opp2 { | ||
| 95 | opp-level = <2>; | ||
| 96 | }; | ||
| 97 | |||
| 98 | rpmpd_opp_svs: opp3 { | ||
| 99 | opp-level = <3>; | ||
| 100 | }; | ||
| 101 | |||
| 102 | rpmpd_opp_normal: opp4 { | ||
| 103 | opp-level = <4>; | ||
| 104 | }; | ||
| 105 | |||
| 106 | rpmpd_opp_high: opp5 { | ||
| 107 | opp-level = <5>; | ||
| 108 | }; | ||
| 109 | |||
| 110 | rpmpd_opp_turbo: opp6 { | ||
| 111 | opp-level = <6>; | ||
| 112 | }; | ||
| 113 | }; | ||
| 114 | }; | ||
| 115 | |||
| 116 | Example: Client/Consumer device using OPP table | ||
| 117 | |||
| 118 | leaky-device0@12350000 { | ||
| 119 | compatible = "foo,i-leak-current"; | ||
| 120 | reg = <0x12350000 0x1000>; | ||
| 121 | power-domains = <&rpmhpd SDM845_MX>; | ||
| 122 | operating-points-v2 = <&leaky_opp_table>; | ||
| 123 | }; | ||
| 124 | |||
| 125 | |||
| 126 | leaky_opp_table: opp-table { | ||
| 127 | compatible = "operating-points-v2"; | ||
| 128 | |||
| 129 | opp1 { | ||
| 130 | opp-hz = /bits/ 64 <144000>; | ||
| 131 | required-opps = <&rpmhpd_opp_low>; | ||
| 132 | }; | ||
| 133 | |||
| 134 | opp2 { | ||
| 135 | opp-hz = /bits/ 64 <400000>; | ||
| 136 | required-opps = <&rpmhpd_opp_ret>; | ||
| 137 | }; | ||
| 138 | |||
| 139 | opp3 { | ||
| 140 | opp-hz = /bits/ 64 <20000000>; | ||
| 141 | required-opps = <&rpmpd_opp_svs>; | ||
| 142 | }; | ||
| 143 | |||
| 144 | opp4 { | ||
| 145 | opp-hz = /bits/ 64 <25000000>; | ||
| 146 | required-opps = <&rpmpd_opp_normal>; | ||
| 147 | }; | ||
| 148 | }; | ||
| @@ -0,0 +1,170 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/power/qcom,rpmpd.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Qualcomm RPM/RPMh Power domains | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Rajendra Nayak <rnayak@codeaurora.org> | ||
| 11 | |||
| 12 | description: | ||
| 13 | For RPM/RPMh Power domains, we communicate a performance state to RPM/RPMh | ||
| 14 | which then translates it into a corresponding voltage on a rail. | ||
| 15 | |||
| 16 | properties: | ||
| 17 | compatible: | ||
| 18 | enum: | ||
| 19 | - qcom,msm8976-rpmpd | ||
| 20 | - qcom,msm8996-rpmpd | ||
| 21 | - qcom,msm8998-rpmpd | ||
| 22 | - qcom,qcs404-rpmpd | ||
| 23 | - qcom,sc7180-rpmhpd | ||
| 24 | - qcom,sdm845-rpmhpd | ||
| 25 | - qcom,sm8150-rpmhpd | ||
| 26 | |||
| 27 | '#power-domain-cells': | ||
| 28 | const: 1 | ||
| 29 | |||
| 30 | operating-points-v2: true | ||
| 31 | |||
| 32 | opp-table: | ||
| 33 | type: object | ||
| 34 | |||
| 35 | required: | ||
| 36 | - compatible | ||
| 37 | - '#power-domain-cells' | ||
| 38 | - operating-points-v2 | ||
| 39 | |||
| 40 | additionalProperties: false | ||
| 41 | |||
| 42 | examples: | ||
| 43 | - | | ||
| 44 | |||
| 45 | // Example 1 (rpmh power domain controller and OPP table): | ||
| 46 | |||
| 47 | #include <dt-bindings/power/qcom-rpmpd.h> | ||
| 48 | |||
| 49 | rpmhpd: power-controller { | ||
| 50 | compatible = "qcom,sdm845-rpmhpd"; | ||
| 51 | #power-domain-cells = <1>; | ||
| 52 | operating-points-v2 = <&rpmhpd_opp_table>; | ||
| 53 | |||
| 54 | rpmhpd_opp_table: opp-table { | ||
| 55 | compatible = "operating-points-v2"; | ||
| 56 | |||
| 57 | rpmhpd_opp_ret: opp1 { | ||
| 58 | opp-level = <RPMH_REGULATOR_LEVEL_RETENTION>; | ||
| 59 | }; | ||
| 60 | |||
| 61 | rpmhpd_opp_min_svs: opp2 { | ||
| 62 | opp-level = <RPMH_REGULATOR_LEVEL_MIN_SVS>; | ||
| 63 | }; | ||
| 64 | |||
| 65 | rpmhpd_opp_low_svs: opp3 { | ||
| 66 | opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>; | ||
| 67 | }; | ||
| 68 | |||
| 69 | rpmhpd_opp_svs: opp4 { | ||
| 70 | opp-level = <RPMH_REGULATOR_LEVEL_SVS>; | ||
| 71 | }; | ||
| 72 | |||
| 73 | rpmhpd_opp_svs_l1: opp5 { | ||
| 74 | opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>; | ||
| 75 | }; | ||
| 76 | |||
| 77 | rpmhpd_opp_nom: opp6 { | ||
| 78 | opp-level = <RPMH_REGULATOR_LEVEL_NOM>; | ||
| 79 | }; | ||
| 80 | |||
| 81 | rpmhpd_opp_nom_l1: opp7 { | ||
| 82 | opp-level = <RPMH_REGULATOR_LEVEL_NOM_L1>; | ||
| 83 | }; | ||
| 84 | |||
| 85 | rpmhpd_opp_nom_l2: opp8 { | ||
| 86 | opp-level = <RPMH_REGULATOR_LEVEL_NOM_L2>; | ||
| 87 | }; | ||
| 88 | |||
| 89 | rpmhpd_opp_turbo: opp9 { | ||
| 90 | opp-level = <RPMH_REGULATOR_LEVEL_TURBO>; | ||
| 91 | }; | ||
| 92 | |||
| 93 | rpmhpd_opp_turbo_l1: opp10 { | ||
| 94 | opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L1>; | ||
| 95 | }; | ||
| 96 | }; | ||
| 97 | }; | ||
| 98 | |||
| 99 | - | | ||
| 100 | |||
| 101 | // Example 2 (rpm power domain controller and OPP table): | ||
| 102 | |||
| 103 | rpmpd: power-controller { | ||
| 104 | compatible = "qcom,msm8996-rpmpd"; | ||
| 105 | #power-domain-cells = <1>; | ||
| 106 | operating-points-v2 = <&rpmpd_opp_table>; | ||
| 107 | |||
| 108 | rpmpd_opp_table: opp-table { | ||
| 109 | compatible = "operating-points-v2"; | ||
| 110 | |||
| 111 | rpmpd_opp_low: opp1 { | ||
| 112 | opp-level = <1>; | ||
| 113 | }; | ||
| 114 | |||
| 115 | rpmpd_opp_ret: opp2 { | ||
| 116 | opp-level = <2>; | ||
| 117 | }; | ||
| 118 | |||
| 119 | rpmpd_opp_svs: opp3 { | ||
| 120 | opp-level = <3>; | ||
| 121 | }; | ||
| 122 | |||
| 123 | rpmpd_opp_normal: opp4 { | ||
| 124 | opp-level = <4>; | ||
| 125 | }; | ||
| 126 | |||
| 127 | rpmpd_opp_high: opp5 { | ||
| 128 | opp-level = <5>; | ||
| 129 | }; | ||
| 130 | |||
| 131 | rpmpd_opp_turbo: opp6 { | ||
| 132 | opp-level = <6>; | ||
| 133 | }; | ||
| 134 | }; | ||
| 135 | }; | ||
| 136 | |||
| 137 | - | | ||
| 138 | |||
| 139 | // Example 3 (Client/Consumer device using OPP table): | ||
| 140 | |||
| 141 | leaky-device0@12350000 { | ||
| 142 | compatible = "foo,i-leak-current"; | ||
| 143 | reg = <0x12350000 0x1000>; | ||
| 144 | power-domains = <&rpmhpd 0>; | ||
| 145 | operating-points-v2 = <&leaky_opp_table>; | ||
| 146 | }; | ||
| 147 | |||
| 148 | leaky_opp_table: opp-table { | ||
| 149 | compatible = "operating-points-v2"; | ||
| 150 | opp1 { | ||
| 151 | opp-hz = /bits/ 64 <144000>; | ||
| 152 | required-opps = <&rpmhpd_opp_low>; | ||
| 153 | }; | ||
| 154 | |||
| 155 | opp2 { | ||
| 156 | opp-hz = /bits/ 64 <400000>; | ||
| 157 | required-opps = <&rpmhpd_opp_ret>; | ||
| 158 | }; | ||
| 159 | |||
| 160 | opp3 { | ||
| 161 | opp-hz = /bits/ 64 <20000000>; | ||
| 162 | required-opps = <&rpmpd_opp_svs>; | ||
| 163 | }; | ||
| 164 | |||
| 165 | opp4 { | ||
| 166 | opp-hz = /bits/ 64 <25000000>; | ||
| 167 | required-opps = <&rpmpd_opp_normal>; | ||
| 168 | }; | ||
| 169 | }; | ||
| 170 | ... | ||
| @@ -239,14 +239,6 @@ config QCOM_SCM · 238 unmodified lines | |||
| 239 | depends on ARM || ARM64 | 239 | depends on ARM || ARM64 |
| 240 | select RESET_CONTROLLER | 240 | select RESET_CONTROLLER |
| 241 | 241 | ||
| 242 | config QCOM_SCM_32 | ||
| 243 | def_bool y | ||
| 244 | depends on QCOM_SCM && ARM | ||
| 245 | |||
| 246 | config QCOM_SCM_64 | ||
| 247 | def_bool y | ||
| 248 | depends on QCOM_SCM && ARM64 | ||
| 249 | |||
| 250 | config QCOM_SCM_DOWNLOAD_MODE_DEFAULT | 242 | config QCOM_SCM_DOWNLOAD_MODE_DEFAULT |
| 251 | bool "Qualcomm download mode enabled by default" | 243 | bool "Qualcomm download mode enabled by default" |
| 252 | depends on QCOM_SCM | 244 | depends on QCOM_SCM |
| @@ -17,10 +17,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o · 16 unmodified lines | |||
| 17 | obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o | 17 | obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o |
| 18 | obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o | 18 | obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o |
| 19 | obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o | 19 | obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o |
| 20 | obj-$(CONFIG_QCOM_SCM) += qcom_scm.o | 20 | obj-$(CONFIG_QCOM_SCM) += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o |
| 21 | obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o | ||
| 22 | obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o | ||
| 23 | CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a | ||
| 24 | obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o | 21 | obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o |
| 25 | obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o | 22 | obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o |
| 26 | obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o | 23 | obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o |
| @@ -1,671 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* Copyright (c) 2010,2015, The Linux Foundation. All rights reserved. | ||
| 3 | * Copyright (C) 2015 Linaro Ltd. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/slab.h> | ||
| 7 | #include <linux/io.h> | ||
| 8 | #include <linux/module.h> | ||
| 9 | #include <linux/mutex.h> | ||
| 10 | #include <linux/errno.h> | ||
| 11 | #include <linux/err.h> | ||
| 12 | #include <linux/qcom_scm.h> | ||
| 13 | #include <linux/dma-mapping.h> | ||
| 14 | |||
| 15 | #include "qcom_scm.h" | ||
| 16 | |||
| 17 | #define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00 | ||
| 18 | #define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 | ||
| 19 | #define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 | ||
| 20 | #define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 | ||
| 21 | |||
| 22 | #define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 | ||
| 23 | #define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 | ||
| 24 | #define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 | ||
| 25 | #define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 | ||
| 26 | |||
| 27 | struct qcom_scm_entry { | ||
| 28 | int flag; | ||
| 29 | void *entry; | ||
| 30 | }; | ||
| 31 | |||
| 32 | static struct qcom_scm_entry qcom_scm_wb[] = { | ||
| 33 | { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 }, | ||
| 34 | { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 }, | ||
| 35 | { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 }, | ||
| 36 | { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 }, | ||
| 37 | }; | ||
| 38 | |||
| 39 | static DEFINE_MUTEX(qcom_scm_lock); | ||
| 40 | |||
| 41 | /** | ||
| 42 | * struct qcom_scm_command - one SCM command buffer | ||
| 43 | * @len: total available memory for command and response | ||
| 44 | * @buf_offset: start of command buffer | ||
| 45 | * @resp_hdr_offset: start of response buffer | ||
| 46 | * @id: command to be executed | ||
| 47 | * @buf: buffer returned from qcom_scm_get_command_buffer() | ||
| 48 | * | ||
| 49 | * An SCM command is laid out in memory as follows: | ||
| 50 | * | ||
| 51 | * ------------------- <--- struct qcom_scm_command | ||
| 52 | * | command header | | ||
| 53 | * ------------------- <--- qcom_scm_get_command_buffer() | ||
| 54 | * | command buffer | | ||
| 55 | * ------------------- <--- struct qcom_scm_response and | ||
| 56 | * | response header | qcom_scm_command_to_response() | ||
| 57 | * ------------------- <--- qcom_scm_get_response_buffer() | ||
| 58 | * | response buffer | | ||
| 59 | * ------------------- | ||
| 60 | * | ||
| 61 | * There can be arbitrary padding between the headers and buffers so | ||
| 62 | * you should always use the appropriate qcom_scm_get_*_buffer() routines | ||
| 63 | * to access the buffers in a safe manner. | ||
| 64 | */ | ||
| 65 | struct qcom_scm_command { | ||
| 66 | __le32 len; | ||
| 67 | __le32 buf_offset; | ||
| 68 | __le32 resp_hdr_offset; | ||
| 69 | __le32 id; | ||
| 70 | __le32 buf[0]; | ||
| 71 | }; | ||
| 72 | |||
| 73 | /** | ||
| 74 | * struct qcom_scm_response - one SCM response buffer | ||
| 75 | * @len: total available memory for response | ||
| 76 | * @buf_offset: start of response data relative to start of qcom_scm_response | ||
| 77 | * @is_complete: indicates if the command has finished processing | ||
| 78 | */ | ||
| 79 | struct qcom_scm_response { | ||
| 80 | __le32 len; | ||
| 81 | __le32 buf_offset; | ||
| 82 | __le32 is_complete; | ||
| 83 | }; | ||
| 84 | |||
| 85 | /** | ||
| 86 | * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response | ||
| 87 | * @cmd: command | ||
| 88 | * | ||
| 89 | * Returns a pointer to a response for a command. | ||
| 90 | */ | ||
| 91 | static inline struct qcom_scm_response *qcom_scm_command_to_response( | ||
| 92 | const struct qcom_scm_command *cmd) | ||
| 93 | { | ||
| 94 | return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); | ||
| 95 | } | ||
| 96 | |||
| 97 | /** | ||
| 98 | * qcom_scm_get_command_buffer() - Get a pointer to a command buffer | ||
| 99 | * @cmd: command | ||
| 100 | * | ||
| 101 | * Returns a pointer to the command buffer of a command. | ||
| 102 | */ | ||
| 103 | static inline void *qcom_scm_get_command_buffer(const struct qcom_scm_command *cmd) | ||
| 104 | { | ||
| 105 | return (void *)cmd->buf; | ||
| 106 | } | ||
| 107 | |||
| 108 | /** | ||
| 109 | * qcom_scm_get_response_buffer() - Get a pointer to a response buffer | ||
| 110 | * @rsp: response | ||
| 111 | * | ||
| 112 | * Returns a pointer to a response buffer of a response. | ||
| 113 | */ | ||
| 114 | static inline void *qcom_scm_get_response_buffer(const struct qcom_scm_response *rsp) | ||
| 115 | { | ||
| 116 | return (void *)rsp + le32_to_cpu(rsp->buf_offset); | ||
| 117 | } | ||
| 118 | |||
| 119 | static u32 smc(u32 cmd_addr) | ||
| 120 | { | ||
| 121 | int context_id; | ||
| 122 | register u32 r0 asm("r0") = 1; | ||
| 123 | register u32 r1 asm("r1") = (u32)&context_id; | ||
| 124 | register u32 r2 asm("r2") = cmd_addr; | ||
| 125 | do { | ||
| 126 | asm volatile( | ||
| 127 | __asmeq("%0", "r0") | ||
| 128 | __asmeq("%1", "r0") | ||
| 129 | __asmeq("%2", "r1") | ||
| 130 | __asmeq("%3", "r2") | ||
| 131 | #ifdef REQUIRES_SEC | ||
| 132 | ".arch_extension sec\n" | ||
| 133 | #endif | ||
| 134 | "smc #0 @ switch to secure world\n" | ||
| 135 | : "=r" (r0) | ||
| 136 | : "r" (r0), "r" (r1), "r" (r2) | ||
| 137 | : "r3", "r12"); | ||
| 138 | } while (r0 == QCOM_SCM_INTERRUPTED); | ||
| 139 | |||
| 140 | return r0; | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * qcom_scm_call() - Send an SCM command | ||
| 145 | * @dev: struct device | ||
| 146 | * @svc_id: service identifier | ||
| 147 | * @cmd_id: command identifier | ||
| 148 | * @cmd_buf: command buffer | ||
| 149 | * @cmd_len: length of the command buffer | ||
| 150 | * @resp_buf: response buffer | ||
| 151 | * @resp_len: length of the response buffer | ||
| 152 | * | ||
| 153 | * Sends a command to the SCM and waits for the command to finish processing. | ||
| 154 | * | ||
| 155 | * A note on cache maintenance: | ||
| 156 | * Note that any buffers that are expected to be accessed by the secure world | ||
| 157 | * must be flushed before invoking qcom_scm_call and invalidated in the cache | ||
| 158 | * immediately after qcom_scm_call returns. Cache maintenance on the command | ||
| 159 | * and response buffers is taken care of by qcom_scm_call; however, callers are | ||
| 160 | * responsible for any other cached buffers passed over to the secure world. | ||
| 161 | */ | ||
| 162 | static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, | ||
| 163 | const void *cmd_buf, size_t cmd_len, void *resp_buf, | ||
| 164 | size_t resp_len) | ||
| 165 | { | ||
| 166 | int ret; | ||
| 167 | struct qcom_scm_command *cmd; | ||
| 168 | struct qcom_scm_response *rsp; | ||
| 169 | size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len; | ||
| 170 | dma_addr_t cmd_phys; | ||
| 171 | |||
| 172 | cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); | ||
| 173 | if (!cmd) | ||
| 174 | return -ENOMEM; | ||
| 175 | |||
| 176 | cmd->len = cpu_to_le32(alloc_len); | ||
| 177 | cmd->buf_offset = cpu_to_le32(sizeof(*cmd)); | ||
| 178 | cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len); | ||
| 179 | |||
| 180 | cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); | ||
| 181 | if (cmd_buf) | ||
| 182 | memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); | ||
| 183 | |||
| 184 | rsp = qcom_scm_command_to_response(cmd); | ||
| 185 | |||
| 186 | cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); | ||
| 187 | if (dma_mapping_error(dev, cmd_phys)) { | ||
| 188 | kfree(cmd); | ||
| 189 | return -ENOMEM; | ||
| 190 | } | ||
| 191 | |||
| 192 | mutex_lock(&qcom_scm_lock); | ||
| 193 | ret = smc(cmd_phys); | ||
| 194 | if (ret < 0) | ||
| 195 | ret = qcom_scm_remap_error(ret); | ||
| 196 | mutex_unlock(&qcom_scm_lock); | ||
| 197 | if (ret) | ||
| 198 | goto out; | ||
| 199 | |||
| 200 | do { | ||
| 201 | dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, | ||
| 202 | sizeof(*rsp), DMA_FROM_DEVICE); | ||
| 203 | } while (!rsp->is_complete); | ||
| 204 | |||
| 205 | if (resp_buf) { | ||
| 206 | dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + | ||
| 207 | le32_to_cpu(rsp->buf_offset), | ||
| 208 | resp_len, DMA_FROM_DEVICE); | ||
| 209 | memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), | ||
| 210 | resp_len); | ||
| 211 | } | ||
| 212 | out: | ||
| 213 | dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); | ||
| 214 | kfree(cmd); | ||
| 215 | return ret; | ||
| 216 | } | ||
| 217 | |||
| 218 | #define SCM_CLASS_REGISTER (0x2 << 8) | ||
| 219 | #define SCM_MASK_IRQS BIT(5) | ||
| 220 | #define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \ | ||
| 221 | SCM_CLASS_REGISTER | \ | ||
| 222 | SCM_MASK_IRQS | \ | ||
| 223 | (n & 0xf)) | ||
| 224 | |||
| 225 | /** | ||
| 226 | * qcom_scm_call_atomic1() - Send an atomic SCM command with one argument | ||
| 227 | * @svc_id: service identifier | ||
| 228 | * @cmd_id: command identifier | ||
| 229 | * @arg1: first argument | ||
| 230 | * | ||
| 231 | * This shall only be used with commands that are guaranteed to be | ||
| 232 | * uninterruptable, atomic and SMP safe. | ||
| 233 | */ | ||
| 234 | static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) | ||
| 235 | { | ||
| 236 | int context_id; | ||
| 237 | |||
| 238 | register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1); | ||
| 239 | register u32 r1 asm("r1") = (u32)&context_id; | ||
| 240 | register u32 r2 asm("r2") = arg1; | ||
| 241 | |||
| 242 | asm volatile( | ||
| 243 | __asmeq("%0", "r0") | ||
| 244 | __asmeq("%1", "r0") | ||
| 245 | __asmeq("%2", "r1") | ||
| 246 | __asmeq("%3", "r2") | ||
| 247 | #ifdef REQUIRES_SEC | ||
| 248 | ".arch_extension sec\n" | ||
| 249 | #endif | ||
| 250 | "smc #0 @ switch to secure world\n" | ||
| 251 | : "=r" (r0) | ||
| 252 | : "r" (r0), "r" (r1), "r" (r2) | ||
| 253 | : "r3", "r12"); | ||
| 254 | return r0; | ||
| 255 | } | ||
| 256 | |||
| 257 | /** | ||
| 258 | * qcom_scm_call_atomic2() - Send an atomic SCM command with two arguments | ||
| 259 | * @svc_id: service identifier | ||
| 260 | * @cmd_id: command identifier | ||
| 261 | * @arg1: first argument | ||
| 262 | * @arg2: second argument | ||
| 263 | * | ||
| 264 | * This shall only be used with commands that are guaranteed to be | ||
| 265 | * uninterruptable, atomic and SMP safe. | ||
| 266 | */ | ||
| 267 | static s32 qcom_scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2) | ||
| 268 | { | ||
| 269 | int context_id; | ||
| 270 | |||
| 271 | register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 2); | ||
| 272 | register u32 r1 asm("r1") = (u32)&context_id; | ||
| 273 | register u32 r2 asm("r2") = arg1; | ||
| 274 | register u32 r3 asm("r3") = arg2; | ||
| 275 | |||
| 276 | asm volatile( | ||
| 277 | __asmeq("%0", "r0") | ||
| 278 | __asmeq("%1", "r0") | ||
| 279 | __asmeq("%2", "r1") | ||
| 280 | __asmeq("%3", "r2") | ||
| 281 | __asmeq("%4", "r3") | ||
| 282 | #ifdef REQUIRES_SEC | ||
| 283 | ".arch_extension sec\n" | ||
| 284 | #endif | ||
| 285 | "smc #0 @ switch to secure world\n" | ||
| 286 | : "=r" (r0) | ||
| 287 | : "r" (r0), "r" (r1), "r" (r2), "r" (r3) | ||
| 288 | : "r12"); | ||
| 289 | return r0; | ||
| 290 | } | ||
| 291 | |||
| 292 | u32 qcom_scm_get_version(void) | ||
| 293 | { | ||
| 294 | int context_id; | ||
| 295 | static u32 version = -1; | ||
| 296 | register u32 r0 asm("r0"); | ||
| 297 | register u32 r1 asm("r1"); | ||
| 298 | |||
| 299 | if (version != -1) | ||
| 300 | return version; | ||
| 301 | |||
| 302 | mutex_lock(&qcom_scm_lock); | ||
| 303 | |||
| 304 | r0 = 0x1 << 8; | ||
| 305 | r1 = (u32)&context_id; | ||
| 306 | do { | ||
| 307 | asm volatile( | ||
| 308 | __asmeq("%0", "r0") | ||
| 309 | __asmeq("%1", "r1") | ||
| 310 | __asmeq("%2", "r0") | ||
| 311 | __asmeq("%3", "r1") | ||
| 312 | #ifdef REQUIRES_SEC | ||
| 313 | ".arch_extension sec\n" | ||
| 314 | #endif | ||
| 315 | "smc #0 @ switch to secure world\n" | ||
| 316 | : "=r" (r0), "=r" (r1) | ||
| 317 | : "r" (r0), "r" (r1) | ||
| 318 | : "r2", "r3", "r12"); | ||
| 319 | } while (r0 == QCOM_SCM_INTERRUPTED); | ||
| 320 | |||
| 321 | version = r1; | ||
| 322 | mutex_unlock(&qcom_scm_lock); | ||
| 323 | |||
| 324 | return version; | ||
| 325 | } | ||
| 326 | EXPORT_SYMBOL(qcom_scm_get_version); | ||
| 327 | |||
| 328 | /** | ||
| 329 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus | ||
| 330 | * @entry: Entry point function for the cpus | ||
| 331 | * @cpus: The cpumask of cpus that will use the entry point | ||
| 332 | * | ||
| 333 | * Set the cold boot address of the cpus. Any cpu outside the supported | ||
| 334 | * range would be removed from the cpu present mask. | ||
| 335 | */ | ||
| 336 | int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) | ||
| 337 | { | ||
| 338 | int flags = 0; | ||
| 339 | int cpu; | ||
| 340 | int scm_cb_flags[] = { | ||
| 341 | QCOM_SCM_FLAG_COLDBOOT_CPU0, | ||
| 342 | QCOM_SCM_FLAG_COLDBOOT_CPU1, | ||
| 343 | QCOM_SCM_FLAG_COLDBOOT_CPU2, | ||
| 344 | QCOM_SCM_FLAG_COLDBOOT_CPU3, | ||
| 345 | }; | ||
| 346 | |||
| 347 | if (!cpus || (cpus && cpumask_empty(cpus))) | ||
| 348 | return -EINVAL; | ||
| 349 | |||
| 350 | for_each_cpu(cpu, cpus) { | ||
| 351 | if (cpu < ARRAY_SIZE(scm_cb_flags)) | ||
| 352 | flags |= scm_cb_flags[cpu]; | ||
| 353 | else | ||
| 354 | set_cpu_present(cpu, false); | ||
| 355 | } | ||
| 356 | |||
| 357 | return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, | ||
| 358 | flags, virt_to_phys(entry)); | ||
| 359 | } | ||
| 360 | |||
| 361 | /** | ||
| 362 | * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus | ||
| 363 | * @entry: Entry point function for the cpus | ||
| 364 | * @cpus: The cpumask of cpus that will use the entry point | ||
| 365 | * | ||
| 366 | * Set the Linux entry point for the SCM to transfer control to when coming | ||
| 367 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. | ||
| 368 | */ | ||
| 369 | int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, | ||
| 370 | const cpumask_t *cpus) | ||
| 371 | { | ||
| 372 | int ret; | ||
| 373 | int flags = 0; | ||
| 374 | int cpu; | ||
| 375 | struct { | ||
| 376 | __le32 flags; | ||
| 377 | __le32 addr; | ||
| 378 | } cmd; | ||
| 379 | |||
| 380 | /* | ||
| 381 | * Reassign only if we are switching from hotplug entry point | ||
| 382 | * to cpuidle entry point or vice versa. | ||
| 383 | */ | ||
| 384 | for_each_cpu(cpu, cpus) { | ||
| 385 | if (entry == qcom_scm_wb[cpu].entry) | ||
| 386 | continue; | ||
| 387 | flags |= qcom_scm_wb[cpu].flag; | ||
| 388 | } | ||
| 389 | |||
| 390 | /* No change in entry function */ | ||
| 391 | if (!flags) | ||
| 392 | return 0; | ||
| 393 | |||
| 394 | cmd.addr = cpu_to_le32(virt_to_phys(entry)); | ||
| 395 | cmd.flags = cpu_to_le32(flags); | ||
| 396 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, | ||
| 397 | &cmd, sizeof(cmd), NULL, 0); | ||
| 398 | if (!ret) { | ||
| 399 | for_each_cpu(cpu, cpus) | ||
| 400 | qcom_scm_wb[cpu].entry = entry; | ||
| 401 | } | ||
| 402 | |||
| 403 | return ret; | ||
| 404 | } | ||
| 405 | |||
| 406 | /** | ||
| 407 | * qcom_scm_cpu_power_down() - Power down the cpu | ||
| 408 | * @flags - Flags to flush cache | ||
| 409 | * | ||
| 410 | * This is an end point to power down cpu. If there was a pending interrupt, | ||
| 411 | * the control would return from this function, otherwise, the cpu jumps to the | ||
| 412 | * warm boot entry point set for this cpu upon reset. | ||
| 413 | */ | ||
| 414 | void __qcom_scm_cpu_power_down(u32 flags) | ||
| 415 | { | ||
| 416 | qcom_scm_call_atomic1(QCOM_SCM_SVC_BOOT, QCOM_SCM_CMD_TERMINATE_PC, | ||
| 417 | flags & QCOM_SCM_FLUSH_FLAG_MASK); | ||
| 418 | } | ||
| 419 | |||
| 420 | int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id) | ||
| 421 | { | ||
| 422 | int ret; | ||
| 423 | __le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id); | ||
| 424 | __le32 ret_val = 0; | ||
| 425 | |||
| 426 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, | ||
| 427 | &svc_cmd, sizeof(svc_cmd), &ret_val, | ||
| 428 | sizeof(ret_val)); | ||
| 429 | if (ret) | ||
| 430 | return ret; | ||
| 431 | |||
| 432 | return le32_to_cpu(ret_val); | ||
| 433 | } | ||
| 434 | |||
| 435 | int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, | ||
| 436 | u32 req_cnt, u32 *resp) | ||
| 437 | { | ||
| 438 | if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) | ||
| 439 | return -ERANGE; | ||
| 440 | |||
| 441 | return qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, | ||
| 442 | req, req_cnt * sizeof(*req), resp, sizeof(*resp)); | ||
| 443 | } | ||
| 444 | |||
| 445 | int __qcom_scm_ocmem_lock(struct device *dev, u32 id, u32 offset, u32 size, | ||
| 446 | u32 mode) | ||
| 447 | { | ||
| 448 | struct ocmem_tz_lock { | ||
| 449 | __le32 id; | ||
| 450 | __le32 offset; | ||
| 451 | __le32 size; | ||
| 452 | __le32 mode; | ||
| 453 | } request; | ||
| 454 | |||
| 455 | request.id = cpu_to_le32(id); | ||
| 456 | request.offset = cpu_to_le32(offset); | ||
| 457 | request.size = cpu_to_le32(size); | ||
| 458 | request.mode = cpu_to_le32(mode); | ||
| 459 | |||
| 460 | return qcom_scm_call(dev, QCOM_SCM_OCMEM_SVC, QCOM_SCM_OCMEM_LOCK_CMD, | ||
| 461 | &request, sizeof(request), NULL, 0); | ||
| 462 | } | ||
| 463 | |||
| 464 | int __qcom_scm_ocmem_unlock(struct device *dev, u32 id, u32 offset, u32 size) | ||
| 465 | { | ||
| 466 | struct ocmem_tz_unlock { | ||
| 467 | __le32 id; | ||
| 468 | __le32 offset; | ||
| 469 | __le32 size; | ||
| 470 | } request; | ||
| 471 | |||
| 472 | request.id = cpu_to_le32(id); | ||
| 473 | request.offset = cpu_to_le32(offset); | ||
| 474 | request.size = cpu_to_le32(size); | ||
| 475 | |||
| 476 | return qcom_scm_call(dev, QCOM_SCM_OCMEM_SVC, QCOM_SCM_OCMEM_UNLOCK_CMD, | ||
| 477 | &request, sizeof(request), NULL, 0); | ||
| 478 | } | ||
| 479 | |||
| 480 | void __qcom_scm_init(void) | ||
| 481 | { | ||
| 482 | } | ||
| 483 | |||
| 484 | bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) | ||
| 485 | { | ||
| 486 | __le32 out; | ||
| 487 | __le32 in; | ||
| 488 | int ret; | ||
| 489 | |||
| 490 | in = cpu_to_le32(peripheral); | ||
| 491 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
| 492 | QCOM_SCM_PAS_IS_SUPPORTED_CMD, | ||
| 493 | &in, sizeof(in), | ||
| 494 | &out, sizeof(out)); | ||
| 495 | |||
| 496 | return ret ? false : !!out; | ||
| 497 | } | ||
| 498 | |||
| 499 | int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, | ||
| 500 | dma_addr_t metadata_phys) | ||
| 501 | { | ||
| 502 | __le32 scm_ret; | ||
| 503 | int ret; | ||
| 504 | struct { | ||
| 505 | __le32 proc; | ||
| 506 | __le32 image_addr; | ||
| 507 | } request; | ||
| 508 | |||
| 509 | request.proc = cpu_to_le32(peripheral); | ||
| 510 | request.image_addr = cpu_to_le32(metadata_phys); | ||
| 511 | |||
| 512 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
| 513 | QCOM_SCM_PAS_INIT_IMAGE_CMD, | ||
| 514 | &request, sizeof(request), | ||
| 515 | &scm_ret, sizeof(scm_ret)); | ||
| 516 | |||
| 517 | return ret ? : le32_to_cpu(scm_ret); | ||
| 518 | } | ||
| 519 | |||
| 520 | int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, | ||
| 521 | phys_addr_t addr, phys_addr_t size) | ||
| 522 | { | ||
| 523 | __le32 scm_ret; | ||
| 524 | int ret; | ||
| 525 | struct { | ||
| 526 | __le32 proc; | ||
| 527 | __le32 addr; | ||
| 528 | __le32 len; | ||
| 529 | } request; | ||
| 530 | |||
| 531 | request.proc = cpu_to_le32(peripheral); | ||
| 532 | request.addr = cpu_to_le32(addr); | ||
| 533 | request.len = cpu_to_le32(size); | ||
| 534 | |||
| 535 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
| 536 | QCOM_SCM_PAS_MEM_SETUP_CMD, | ||
| 537 | &request, sizeof(request), | ||
| 538 | &scm_ret, sizeof(scm_ret)); | ||
| 539 | |||
| 540 | return ret ? : le32_to_cpu(scm_ret); | ||
| 541 | } | ||
| 542 | |||
| 543 | int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) | ||
| 544 | { | ||
| 545 | __le32 out; | ||
| 546 | __le32 in; | ||
| 547 | int ret; | ||
| 548 | |||
| 549 | in = cpu_to_le32(peripheral); | ||
| 550 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
| 551 | QCOM_SCM_PAS_AUTH_AND_RESET_CMD, | ||
| 552 | &in, sizeof(in), | ||
| 553 | &out, sizeof(out)); | ||
| 554 | |||
| 555 | return ret ? : le32_to_cpu(out); | ||
| 556 | } | ||
| 557 | |||
| 558 | int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) | ||
| 559 | { | ||
| 560 | __le32 out; | ||
| 561 | __le32 in; | ||
| 562 | int ret; | ||
| 563 | |||
| 564 | in = cpu_to_le32(peripheral); | ||
| 565 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
| 566 | QCOM_SCM_PAS_SHUTDOWN_CMD, | ||
| 567 | &in, sizeof(in), | ||
| 568 | &out, sizeof(out)); | ||
| 569 | |||
| 570 | return ret ? : le32_to_cpu(out); | ||
| 571 | } | ||
| 572 | |||
| 573 | int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) | ||
| 574 | { | ||
| 575 | __le32 out; | ||
| 576 | __le32 in = cpu_to_le32(reset); | ||
| 577 | int ret; | ||
| 578 | |||
| 579 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, | ||
| 580 | &in, sizeof(in), | ||
| 581 | &out, sizeof(out)); | ||
| 582 | |||
| 583 | return ret ? : le32_to_cpu(out); | ||
| 584 | } | ||
| 585 | |||
| 586 | int __qcom_scm_set_dload_mode(struct device *dev, bool enable) | ||
| 587 | { | ||
| 588 | return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_DLOAD_MODE, | ||
| 589 | enable ? QCOM_SCM_SET_DLOAD_MODE : 0, 0); | ||
| 590 | } | ||
| 591 | |||
| 592 | int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) | ||
| 593 | { | ||
| 594 | struct { | ||
| 595 | __le32 state; | ||
| 596 | __le32 id; | ||
| 597 | } req; | ||
| 598 | __le32 scm_ret = 0; | ||
| 599 | int ret; | ||
| 600 | |||
| 601 | req.state = cpu_to_le32(state); | ||
| 602 | req.id = cpu_to_le32(id); | ||
| 603 | |||
| 604 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_REMOTE_STATE, | ||
| 605 | &req, sizeof(req), &scm_ret, sizeof(scm_ret)); | ||
| 606 | |||
| 607 | return ret ? : le32_to_cpu(scm_ret); | ||
| 608 | } | ||
| 609 | |||
| 610 | int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, | ||
| 611 | size_t mem_sz, phys_addr_t src, size_t src_sz, | ||
| 612 | phys_addr_t dest, size_t dest_sz) | ||
| 613 | { | ||
| 614 | return -ENODEV; | ||
| 615 | } | ||
| 616 | |||
| 617 | int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, | ||
| 618 | u32 spare) | ||
| 619 | { | ||
| 620 | struct msm_scm_sec_cfg { | ||
| 621 | __le32 id; | ||
| 622 | __le32 ctx_bank_num; | ||
| 623 | } cfg; | ||
| 624 | int ret, scm_ret = 0; | ||
| 625 | |||
| 626 | cfg.id = cpu_to_le32(device_id); | ||
| 627 | cfg.ctx_bank_num = cpu_to_le32(spare); | ||
| 628 | |||
| 629 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, QCOM_SCM_RESTORE_SEC_CFG, | ||
| 630 | &cfg, sizeof(cfg), &scm_ret, sizeof(scm_ret)); | ||
| 631 | |||
| 632 | if (ret || scm_ret) | ||
| 633 | return ret ? ret : -EINVAL; | ||
| 634 | |||
| 635 | return 0; | ||
| 636 | } | ||
| 637 | |||
| 638 | int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, | ||
| 639 | size_t *size) | ||
| 640 | { | ||
| 641 | return -ENODEV; | ||
| 642 | } | ||
| 643 | |||
| 644 | int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, | ||
| 645 | u32 spare) | ||
| 646 | { | ||
| 647 | return -ENODEV; | ||
| 648 | } | ||
| 649 | |||
| 650 | int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, | ||
| 651 | unsigned int *val) | ||
| 652 | { | ||
| 653 | int ret; | ||
| 654 | |||
| 655 | ret = qcom_scm_call_atomic1(QCOM_SCM_SVC_IO, QCOM_SCM_IO_READ, addr); | ||
| 656 | if (ret >= 0) | ||
| 657 | *val = ret; | ||
| 658 | |||
| 659 | return ret < 0 ? ret : 0; | ||
| 660 | } | ||
| 661 | |||
| 662 | int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) | ||
| 663 | { | ||
| 664 | return qcom_scm_call_atomic2(QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE, | ||
| 665 | addr, val); | ||
| 666 | } | ||
| 667 | |||
| 668 | int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool enable) | ||
| 669 | { | ||
| 670 | return -ENODEV; | ||
| 671 | } | ||
| @@ -1,579 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* Copyright (c) 2015, The Linux Foundation. All rights reserved. | ||
| 3 | */ | ||
| 4 | |||
| 5 | #include <linux/io.h> | ||
| 6 | #include <linux/errno.h> | ||
| 7 | #include <linux/delay.h> | ||
| 8 | #include <linux/mutex.h> | ||
| 9 | #include <linux/slab.h> | ||
| 10 | #include <linux/types.h> | ||
| 11 | #include <linux/qcom_scm.h> | ||
| 12 | #include <linux/arm-smccc.h> | ||
| 13 | #include <linux/dma-mapping.h> | ||
| 14 | |||
| 15 | #include "qcom_scm.h" | ||
| 16 | |||
| 17 | #define QCOM_SCM_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) | ||
| 18 | |||
| 19 | #define MAX_QCOM_SCM_ARGS 10 | ||
| 20 | #define MAX_QCOM_SCM_RETS 3 | ||
| 21 | |||
| 22 | enum qcom_scm_arg_types { | ||
| 23 | QCOM_SCM_VAL, | ||
| 24 | QCOM_SCM_RO, | ||
| 25 | QCOM_SCM_RW, | ||
| 26 | QCOM_SCM_BUFVAL, | ||
| 27 | }; | ||
| 28 | |||
| 29 | #define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ | ||
| 30 | (((a) & 0x3) << 4) | \ | ||
| 31 | (((b) & 0x3) << 6) | \ | ||
| 32 | (((c) & 0x3) << 8) | \ | ||
| 33 | (((d) & 0x3) << 10) | \ | ||
| 34 | (((e) & 0x3) << 12) | \ | ||
| 35 | (((f) & 0x3) << 14) | \ | ||
| 36 | (((g) & 0x3) << 16) | \ | ||
| 37 | (((h) & 0x3) << 18) | \ | ||
| 38 | (((i) & 0x3) << 20) | \ | ||
| 39 | (((j) & 0x3) << 22) | \ | ||
| 40 | ((num) & 0xf)) | ||
| 41 | |||
| 42 | #define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | ||
| 43 | |||
| 44 | /** | ||
| 45 | * struct qcom_scm_desc | ||
| 46 | * @arginfo: Metadata describing the arguments in args[] | ||
| 47 | * @args: The array of arguments for the secure syscall | ||
| 48 | * @res: The values returned by the secure syscall | ||
| 49 | */ | ||
| 50 | struct qcom_scm_desc { | ||
| 51 | u32 arginfo; | ||
| 52 | u64 args[MAX_QCOM_SCM_ARGS]; | ||
| 53 | }; | ||
| 54 | |||
| 55 | static u64 qcom_smccc_convention = -1; | ||
| 56 | static DEFINE_MUTEX(qcom_scm_lock); | ||
| 57 | |||
| 58 | #define QCOM_SCM_EBUSY_WAIT_MS 30 | ||
| 59 | #define QCOM_SCM_EBUSY_MAX_RETRY 20 | ||
| 60 | |||
| 61 | #define N_EXT_QCOM_SCM_ARGS 7 | ||
| 62 | #define FIRST_EXT_ARG_IDX 3 | ||
| 63 | #define N_REGISTER_ARGS (MAX_QCOM_SCM_ARGS - N_EXT_QCOM_SCM_ARGS + 1) | ||
| 64 | |||
| 65 | static void __qcom_scm_call_do(const struct qcom_scm_desc *desc, | ||
| 66 | struct arm_smccc_res *res, u32 fn_id, | ||
| 67 | u64 x5, u32 type) | ||
| 68 | { | ||
| 69 | u64 cmd; | ||
| 70 | struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 }; | ||
| 71 | |||
| 72 | cmd = ARM_SMCCC_CALL_VAL(type, qcom_smccc_convention, | ||
| 73 | ARM_SMCCC_OWNER_SIP, fn_id); | ||
| 74 | |||
| 75 | quirk.state.a6 = 0; | ||
| 76 | |||
| 77 | do { | ||
| 78 | arm_smccc_smc_quirk(cmd, desc->arginfo, desc->args[0], | ||
| 79 | desc->args[1], desc->args[2], x5, | ||
| 80 | quirk.state.a6, 0, res, &quirk); | ||
| 81 | |||
| 82 | if (res->a0 == QCOM_SCM_INTERRUPTED) | ||
| 83 | cmd = res->a0; | ||
| 84 | |||
| 85 | } while (res->a0 == QCOM_SCM_INTERRUPTED); | ||
| 86 | } | ||
| 87 | |||
| 88 | static void qcom_scm_call_do(const struct qcom_scm_desc *desc, | ||
| 89 | struct arm_smccc_res *res, u32 fn_id, | ||
| 90 | u64 x5, bool atomic) | ||
| 91 | { | ||
| 92 | int retry_count = 0; | ||
| 93 | |||
| 94 | if (atomic) { | ||
| 95 | __qcom_scm_call_do(desc, res, fn_id, x5, ARM_SMCCC_FAST_CALL); | ||
| 96 | return; | ||
| 97 | } | ||
| 98 | |||
| 99 | do { | ||
| 100 | mutex_lock(&qcom_scm_lock); | ||
| 101 | |||
| 102 | __qcom_scm_call_do(desc, res, fn_id, x5, | ||
| 103 | ARM_SMCCC_STD_CALL); | ||
| 104 | |||
| 105 | mutex_unlock(&qcom_scm_lock); | ||
| 106 | |||
| 107 | if (res->a0 == QCOM_SCM_V2_EBUSY) { | ||
| 108 | if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) | ||
| 109 | break; | ||
| 110 | msleep(QCOM_SCM_EBUSY_WAIT_MS); | ||
| 111 | } | ||
| 112 | } while (res->a0 == QCOM_SCM_V2_EBUSY); | ||
| 113 | } | ||
| 114 | |||
| 115 | static int ___qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, | ||
| 116 | const struct qcom_scm_desc *desc, | ||
| 117 | struct arm_smccc_res *res, bool atomic) | ||
| 118 | { | ||
| 119 | int arglen = desc->arginfo & 0xf; | ||
| 120 | int i; | ||
| 121 | u32 fn_id = QCOM_SCM_FNID(svc_id, cmd_id); | ||
| 122 | u64 x5 = desc->args[FIRST_EXT_ARG_IDX]; | ||
| 123 | dma_addr_t args_phys = 0; | ||
| 124 | void *args_virt = NULL; | ||
| 125 | size_t alloc_len; | ||
| 126 | gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; | ||
| 127 | |||
| 128 | if (unlikely(arglen > N_REGISTER_ARGS)) { | ||
| 129 | alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64); | ||
| 130 | args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); | ||
| 131 | |||
| 132 | if (!args_virt) | ||
| 133 | return -ENOMEM; | ||
| 134 | |||
| 135 | if (qcom_smccc_convention == ARM_SMCCC_SMC_32) { | ||
| 136 | __le32 *args = args_virt; | ||
| 137 | |||
| 138 | for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++) | ||
| 139 | args[i] = cpu_to_le32(desc->args[i + | ||
| 140 | FIRST_EXT_ARG_IDX]); | ||
| 141 | } else { | ||
| 142 | __le64 *args = args_virt; | ||
| 143 | |||
| 144 | for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++) | ||
| 145 | args[i] = cpu_to_le64(desc->args[i + | ||
| 146 | FIRST_EXT_ARG_IDX]); | ||
| 147 | } | ||
| 148 | |||
| 149 | args_phys = dma_map_single(dev, args_virt, alloc_len, | ||
| 150 | DMA_TO_DEVICE); | ||
| 151 | |||
| 152 | if (dma_mapping_error(dev, args_phys)) { | ||
| 153 | kfree(args_virt); | ||
| 154 | return -ENOMEM; | ||
| 155 | } | ||
| 156 | |||
| 157 | x5 = args_phys; | ||
| 158 | } | ||
| 159 | |||
| 160 | qcom_scm_call_do(desc, res, fn_id, x5, atomic); | ||
| 161 | |||
| 162 | if (args_virt) { | ||
| 163 | dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); | ||
| 164 | kfree(args_virt); | ||
| 165 | } | ||
| 166 | |||
| 167 | if ((long)res->a0 < 0) | ||
| 168 | return qcom_scm_remap_error(res->a0); | ||
| 169 | |||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * qcom_scm_call() - Invoke a syscall in the secure world | ||
| 175 | * @dev: device | ||
| 176 | * @svc_id: service identifier | ||
| 177 | * @cmd_id: command identifier | ||
| 178 | * @desc: Descriptor structure containing arguments and return values | ||
| 179 | * | ||
| 180 | * Sends a command to the SCM and waits for the command to finish processing. | ||
| 181 | * This should *only* be called in pre-emptible context. | ||
| 182 | */ | ||
| 183 | static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, | ||
| 184 | const struct qcom_scm_desc *desc, | ||
| 185 | struct arm_smccc_res *res) | ||
| 186 | { | ||
| 187 | might_sleep(); | ||
| 188 | return ___qcom_scm_call(dev, svc_id, cmd_id, desc, res, false); | ||
| 189 | } | ||
| 190 | |||
| 191 | /** | ||
| 192 | * qcom_scm_call_atomic() - atomic variation of qcom_scm_call() | ||
| 193 | * @dev: device | ||
| 194 | * @svc_id: service identifier | ||
| 195 | * @cmd_id: command identifier | ||
| 196 | * @desc: Descriptor structure containing arguments and return values | ||
| 197 | * @res: Structure containing results from SMC/HVC call | ||
| 198 | * | ||
| 199 | * Sends a command to the SCM and waits for the command to finish processing. | ||
| 200 | * This can be called in atomic context. | ||
| 201 | */ | ||
| 202 | static int qcom_scm_call_atomic(struct device *dev, u32 svc_id, u32 cmd_id, | ||
| 203 | const struct qcom_scm_desc *desc, | ||
| 204 | struct arm_smccc_res *res) | ||
| 205 | { | ||
| 206 | return ___qcom_scm_call(dev, svc_id, cmd_id, desc, res, true); | ||
| 207 | } | ||
| 208 | |||
| 209 | /** | ||
| 210 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus | ||
| 211 | * @entry: Entry point function for the cpus | ||
| 212 | * @cpus: The cpumask of cpus that will use the entry point | ||
| 213 | * | ||
| 214 | * Set the cold boot address of the cpus. Any cpu outside the supported | ||
| 215 | * range would be removed from the cpu present mask. | ||
| 216 | */ | ||
| 217 | int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) | ||
| 218 | { | ||
| 219 | return -ENOTSUPP; | ||
| 220 | } | ||
| 221 | |||
| 222 | /** | ||
| 223 | * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus | ||
| 224 | * @dev: Device pointer | ||
| 225 | * @entry: Entry point function for the cpus | ||
| 226 | * @cpus: The cpumask of cpus that will use the entry point | ||
| 227 | * | ||
| 228 | * Set the Linux entry point for the SCM to transfer control to when coming | ||
| 229 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. | ||
| 230 | */ | ||
| 231 | int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, | ||
| 232 | const cpumask_t *cpus) | ||
| 233 | { | ||
| 234 | return -ENOTSUPP; | ||
| 235 | } | ||
| 236 | |||
| 237 | /** | ||
| 238 | * qcom_scm_cpu_power_down() - Power down the cpu | ||
| 239 | * @flags - Flags to flush cache | ||
| 240 | * | ||
| 241 | * This is an end point to power down cpu. If there was a pending interrupt, | ||
| 242 | * the control would return from this function, otherwise, the cpu jumps to the | ||
| 243 | * warm boot entry point set for this cpu upon reset. | ||
| 244 | */ | ||
| 245 | void __qcom_scm_cpu_power_down(u32 flags) | ||
| 246 | { | ||
| 247 | } | ||
| 248 | |||
| 249 | int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id) | ||
| 250 | { | ||
| 251 | int ret; | ||
| 252 | struct qcom_scm_desc desc = {0}; | ||
| 253 | struct arm_smccc_res res; | ||
| 254 | |||
| 255 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
| 256 | desc.args[0] = QCOM_SCM_FNID(svc_id, cmd_id) | | ||
| 257 | (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); | ||
| 258 | |||
| 259 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, | ||
| 260 | &desc, &res); | ||
| 261 | |||
| 262 | return ret ? : res.a1; | ||
| 263 | } | ||
| 264 | |||
| 265 | int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, | ||
| 266 | u32 req_cnt, u32 *resp) | ||
| 267 | { | ||
| 268 | int ret; | ||
| 269 | struct qcom_scm_desc desc = {0}; | ||
| 270 | struct arm_smccc_res res; | ||
| 271 | |||
| 272 | if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) | ||
| 273 | return -ERANGE; | ||
| 274 | |||
| 275 | desc.args[0] = req[0].addr; | ||
| 276 | desc.args[1] = req[0].val; | ||
| 277 | desc.args[2] = req[1].addr; | ||
| 278 | desc.args[3] = req[1].val; | ||
| 279 | desc.args[4] = req[2].addr; | ||
| 280 | desc.args[5] = req[2].val; | ||
| 281 | desc.args[6] = req[3].addr; | ||
| 282 | desc.args[7] = req[3].val; | ||
| 283 | desc.args[8] = req[4].addr; | ||
| 284 | desc.args[9] = req[4].val; | ||
| 285 | desc.arginfo = QCOM_SCM_ARGS(10); | ||
| 286 | |||
| 287 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, &desc, | ||
| 288 | &res); | ||
| 289 | *resp = res.a1; | ||
| 290 | |||
| 291 | return ret; | ||
| 292 | } | ||
| 293 | |||
| 294 | int __qcom_scm_ocmem_lock(struct device *dev, uint32_t id, uint32_t offset, | ||
| 295 | uint32_t size, uint32_t mode) | ||
| 296 | { | ||
| 297 | return -ENOTSUPP; | ||
| 298 | } | ||
| 299 | |||
| 300 | int __qcom_scm_ocmem_unlock(struct device *dev, uint32_t id, uint32_t offset, | ||
| 301 | uint32_t size) | ||
| 302 | { | ||
| 303 | return -ENOTSUPP; | ||
| 304 | } | ||
| 305 | |||
| 306 | void __qcom_scm_init(void) | ||
| 307 | { | ||
| 308 | u64 cmd; | ||
| 309 | struct arm_smccc_res res; | ||
| 310 | u32 function = QCOM_SCM_FNID(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD); | ||
| 311 | |||
| 312 | /* First try a SMC64 call */ | ||
| 313 | cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, | ||
| 314 | ARM_SMCCC_OWNER_SIP, function); | ||
| 315 | |||
| 316 | arm_smccc_smc(cmd, QCOM_SCM_ARGS(1), cmd & (~BIT(ARM_SMCCC_TYPE_SHIFT)), | ||
| 317 | 0, 0, 0, 0, 0, &res); | ||
| 318 | |||
| 319 | if (!res.a0 && res.a1) | ||
| 320 | qcom_smccc_convention = ARM_SMCCC_SMC_64; | ||
| 321 | else | ||
| 322 | qcom_smccc_convention = ARM_SMCCC_SMC_32; | ||
| 323 | } | ||
| 324 | |||
| 325 | bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) | ||
| 326 | { | ||
| 327 | int ret; | ||
| 328 | struct qcom_scm_desc desc = {0}; | ||
| 329 | struct arm_smccc_res res; | ||
| 330 | |||
| 331 | desc.args[0] = peripheral; | ||
| 332 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
| 333 | |||
| 334 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
| 335 | QCOM_SCM_PAS_IS_SUPPORTED_CMD, | ||
| 336 | &desc, &res); | ||
| 337 | |||
| 338 | return ret ? false : !!res.a1; | ||
| 339 | } | ||
| 340 | |||
| 341 | int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, | ||
| 342 | dma_addr_t metadata_phys) | ||
| 343 | { | ||
| 344 | int ret; | ||
| 345 | struct qcom_scm_desc desc = {0}; | ||
| 346 | struct arm_smccc_res res; | ||
| 347 | |||
| 348 | desc.args[0] = peripheral; | ||
| 349 | desc.args[1] = metadata_phys; | ||
| 350 | desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); | ||
| 351 | |||
| 352 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD, | ||
| 353 | &desc, &res); | ||
| 354 | |||
| 355 | return ret ? : res.a1; | ||
| 356 | } | ||
| 357 | |||
| 358 | int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, | ||
| 359 | phys_addr_t addr, phys_addr_t size) | ||
| 360 | { | ||
| 361 | int ret; | ||
| 362 | struct qcom_scm_desc desc = {0}; | ||
| 363 | struct arm_smccc_res res; | ||
| 364 | |||
| 365 | desc.args[0] = peripheral; | ||
| 366 | desc.args[1] = addr; | ||
| 367 | desc.args[2] = size; | ||
| 368 | desc.arginfo = QCOM_SCM_ARGS(3); | ||
| 369 | |||
| 370 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MEM_SETUP_CMD, | ||
| 371 | &desc, &res); | ||
| 372 | |||
| 373 | return ret ? : res.a1; | ||
| 374 | } | ||
| 375 | |||
| 376 | int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) | ||
| 377 | { | ||
| 378 | int ret; | ||
| 379 | struct qcom_scm_desc desc = {0}; | ||
| 380 | struct arm_smccc_res res; | ||
| 381 | |||
| 382 | desc.args[0] = peripheral; | ||
| 383 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
| 384 | |||
| 385 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
| 386 | QCOM_SCM_PAS_AUTH_AND_RESET_CMD, | ||
| 387 | &desc, &res); | ||
| 388 | |||
| 389 | return ret ? : res.a1; | ||
| 390 | } | ||
| 391 | |||
| 392 | int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) | ||
| 393 | { | ||
| 394 | int ret; | ||
| 395 | struct qcom_scm_desc desc = {0}; | ||
| 396 | struct arm_smccc_res res; | ||
| 397 | |||
| 398 | desc.args[0] = peripheral; | ||
| 399 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
| 400 | |||
| 401 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_SHUTDOWN_CMD, | ||
| 402 | &desc, &res); | ||
| 403 | |||
| 404 | return ret ? : res.a1; | ||
| 405 | } | ||
| 406 | |||
| 407 | int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) | ||
| 408 | { | ||
| 409 | struct qcom_scm_desc desc = {0}; | ||
| 410 | struct arm_smccc_res res; | ||
| 411 | int ret; | ||
| 412 | |||
| 413 | desc.args[0] = reset; | ||
| 414 | desc.args[1] = 0; | ||
| 415 | desc.arginfo = QCOM_SCM_ARGS(2); | ||
| 416 | |||
| 417 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, &desc, | ||
| 418 | &res); | ||
| 419 | |||
| 420 | return ret ? : res.a1; | ||
| 421 | } | ||
| 422 | |||
| 423 | int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) | ||
| 424 | { | ||
| 425 | struct qcom_scm_desc desc = {0}; | ||
| 426 | struct arm_smccc_res res; | ||
| 427 | int ret; | ||
| 428 | |||
| 429 | desc.args[0] = state; | ||
| 430 | desc.args[1] = id; | ||
| 431 | desc.arginfo = QCOM_SCM_ARGS(2); | ||
| 432 | |||
| 433 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_REMOTE_STATE, | ||
| 434 | &desc, &res); | ||
| 435 | |||
| 436 | return ret ? : res.a1; | ||
| 437 | } | ||
| 438 | |||
| 439 | int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, | ||
| 440 | size_t mem_sz, phys_addr_t src, size_t src_sz, | ||
| 441 | phys_addr_t dest, size_t dest_sz) | ||
| 442 | { | ||
| 443 | int ret; | ||
| 444 | struct qcom_scm_desc desc = {0}; | ||
| 445 | struct arm_smccc_res res; | ||
| 446 | |||
| 447 | desc.args[0] = mem_region; | ||
| 448 | desc.args[1] = mem_sz; | ||
| 449 | desc.args[2] = src; | ||
| 450 | desc.args[3] = src_sz; | ||
| 451 | desc.args[4] = dest; | ||
| 452 | desc.args[5] = dest_sz; | ||
| 453 | desc.args[6] = 0; | ||
| 454 | |||
| 455 | desc.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, | ||
| 456 | QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, | ||
| 457 | QCOM_SCM_VAL, QCOM_SCM_VAL); | ||
| 458 | |||
| 459 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, | ||
| 460 | QCOM_MEM_PROT_ASSIGN_ID, | ||
| 461 | &desc, &res); | ||
| 462 | |||
| 463 | return ret ? : res.a1; | ||
| 464 | } | ||
| 465 | |||
| 466 | int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) | ||
| 467 | { | ||
| 468 | struct qcom_scm_desc desc = {0}; | ||
| 469 | struct arm_smccc_res res; | ||
| 470 | int ret; | ||
| 471 | |||
| 472 | desc.args[0] = device_id; | ||
| 473 | desc.args[1] = spare; | ||
| 474 | desc.arginfo = QCOM_SCM_ARGS(2); | ||
| 475 | |||
| 476 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, QCOM_SCM_RESTORE_SEC_CFG, | ||
| 477 | &desc, &res); | ||
| 478 | |||
| 479 | return ret ? : res.a1; | ||
| 480 | } | ||
| 481 | |||
| 482 | int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, | ||
| 483 | size_t *size) | ||
| 484 | { | ||
| 485 | struct qcom_scm_desc desc = {0}; | ||
| 486 | struct arm_smccc_res res; | ||
| 487 | int ret; | ||
| 488 | |||
| 489 | desc.args[0] = spare; | ||
| 490 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
| 491 | |||
| 492 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, | ||
| 493 | QCOM_SCM_IOMMU_SECURE_PTBL_SIZE, &desc, &res); | ||
| 494 | |||
| 495 | if (size) | ||
| 496 | *size = res.a1; | ||
| 497 | |||
| 498 | return ret ? : res.a2; | ||
| 499 | } | ||
| 500 | |||
| 501 | int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, | ||
| 502 | u32 spare) | ||
| 503 | { | ||
| 504 | struct qcom_scm_desc desc = {0}; | ||
| 505 | struct arm_smccc_res res; | ||
| 506 | int ret; | ||
| 507 | |||
| 508 | desc.args[0] = addr; | ||
| 509 | desc.args[1] = size; | ||
| 510 | desc.args[2] = spare; | ||
| 511 | desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL, | ||
| 512 | QCOM_SCM_VAL); | ||
| 513 | |||
| 514 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, | ||
| 515 | QCOM_SCM_IOMMU_SECURE_PTBL_INIT, &desc, &res); | ||
| 516 | |||
| 517 | /* the pg table has been initialized already, ignore the error */ | ||
| 518 | if (ret == -EPERM) | ||
| 519 | ret = 0; | ||
| 520 | |||
| 521 | return ret; | ||
| 522 | } | ||
| 523 | |||
| 524 | int __qcom_scm_set_dload_mode(struct device *dev, bool enable) | ||
| 525 | { | ||
| 526 | struct qcom_scm_desc desc = {0}; | ||
| 527 | struct arm_smccc_res res; | ||
| 528 | |||
| 529 | desc.args[0] = QCOM_SCM_SET_DLOAD_MODE; | ||
| 530 | desc.args[1] = enable ? QCOM_SCM_SET_DLOAD_MODE : 0; | ||
| 531 | desc.arginfo = QCOM_SCM_ARGS(2); | ||
| 532 | |||
| 533 | return qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_DLOAD_MODE, | ||
| 534 | &desc, &res); | ||
| 535 | } | ||
| 536 | |||
| 537 | int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, | ||
| 538 | unsigned int *val) | ||
| 539 | { | ||
| 540 | struct qcom_scm_desc desc = {0}; | ||
| 541 | struct arm_smccc_res res; | ||
| 542 | int ret; | ||
| 543 | |||
| 544 | desc.args[0] = addr; | ||
| 545 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
| 546 | |||
| 547 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_IO, QCOM_SCM_IO_READ, | ||
| 548 | &desc, &res); | ||
| 549 | if (ret >= 0) | ||
| 550 | *val = res.a1; | ||
| 551 | |||
| 552 | return ret < 0 ? ret : 0; | ||
| 553 | } | ||
| 554 | |||
| 555 | int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) | ||
| 556 | { | ||
| 557 | struct qcom_scm_desc desc = {0}; | ||
| 558 | struct arm_smccc_res res; | ||
| 559 | |||
| 560 | desc.args[0] = addr; | ||
| 561 | desc.args[1] = val; | ||
| 562 | desc.arginfo = QCOM_SCM_ARGS(2); | ||
| 563 | |||
| 564 | return qcom_scm_call(dev, QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE, | ||
| 565 | &desc, &res); | ||
| 566 | } | ||
| 567 | |||
| 568 | int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool en) | ||
| 569 | { | ||
| 570 | struct qcom_scm_desc desc = {0}; | ||
| 571 | struct arm_smccc_res res; | ||
| 572 | |||
| 573 | desc.args[0] = QCOM_SCM_CONFIG_ERRATA1_CLIENT_ALL; | ||
| 574 | desc.args[1] = en; | ||
| 575 | desc.arginfo = QCOM_SCM_ARGS(2); | ||
| 576 | |||
| 577 | return qcom_scm_call_atomic(dev, QCOM_SCM_SVC_SMMU_PROGRAM, | ||
| 578 | QCOM_SCM_CONFIG_ERRATA1, &desc, &res); | ||
| 579 | } | ||
| @@ -0,0 +1,242 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. | ||
| 3 | * Copyright (C) 2015 Linaro Ltd. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/slab.h> | ||
| 7 | #include <linux/io.h> | ||
| 8 | #include <linux/module.h> | ||
| 9 | #include <linux/mutex.h> | ||
| 10 | #include <linux/errno.h> | ||
| 11 | #include <linux/err.h> | ||
| 12 | #include <linux/qcom_scm.h> | ||
| 13 | #include <linux/arm-smccc.h> | ||
| 14 | #include <linux/dma-mapping.h> | ||
| 15 | |||
| 16 | #include "qcom_scm.h" | ||
| 17 | |||
| 18 | static DEFINE_MUTEX(qcom_scm_lock); | ||
| 19 | |||
| 20 | |||
| 21 | /** | ||
| 22 | * struct arm_smccc_args | ||
| 23 | * @args: The array of values used in registers in smc instruction | ||
| 24 | */ | ||
| 25 | struct arm_smccc_args { | ||
| 26 | unsigned long args[8]; | ||
| 27 | }; | ||
| 28 | |||
| 29 | |||
| 30 | /** | ||
| 31 | * struct scm_legacy_command - one SCM command buffer | ||
| 32 | * @len: total available memory for command and response | ||
| 33 | * @buf_offset: start of command buffer | ||
| 34 | * @resp_hdr_offset: start of response buffer | ||
| 35 | * @id: command to be executed | ||
| 36 | * @buf: buffer returned from scm_legacy_get_command_buffer() | ||
| 37 | * | ||
| 38 | * An SCM command is laid out in memory as follows: | ||
| 39 | * | ||
| 40 | * ------------------- <--- struct scm_legacy_command | ||
| 41 | * | command header | | ||
| 42 | * ------------------- <--- scm_legacy_get_command_buffer() | ||
| 43 | * | command buffer | | ||
| 44 | * ------------------- <--- struct scm_legacy_response and | ||
| 45 | * | response header | scm_legacy_command_to_response() | ||
| 46 | * ------------------- <--- scm_legacy_get_response_buffer() | ||
| 47 | * | response buffer | | ||
| 48 | * ------------------- | ||
| 49 | * | ||
| 50 | * There can be arbitrary padding between the headers and buffers so | ||
| 51 | * you should always use the appropriate scm_legacy_get_*_buffer() routines | ||
| 52 | * to access the buffers in a safe manner. | ||
| 53 | */ | ||
| 54 | struct scm_legacy_command { | ||
| 55 | __le32 len; | ||
| 56 | __le32 buf_offset; | ||
| 57 | __le32 resp_hdr_offset; | ||
| 58 | __le32 id; | ||
| 59 | __le32 buf[0]; | ||
| 60 | }; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * struct scm_legacy_response - one SCM response buffer | ||
| 64 | * @len: total available memory for response | ||
| 65 | * @buf_offset: start of response data relative to start of scm_legacy_response | ||
| 66 | * @is_complete: indicates if the command has finished processing | ||
| 67 | */ | ||
| 68 | struct scm_legacy_response { | ||
| 69 | __le32 len; | ||
| 70 | __le32 buf_offset; | ||
| 71 | __le32 is_complete; | ||
| 72 | }; | ||
| 73 | |||
| 74 | /** | ||
| 75 | * scm_legacy_command_to_response() - Get a pointer to a scm_legacy_response | ||
| 76 | * @cmd: command | ||
| 77 | * | ||
| 78 | * Returns a pointer to a response for a command. | ||
| 79 | */ | ||
| 80 | static inline struct scm_legacy_response *scm_legacy_command_to_response( | ||
| 81 | const struct scm_legacy_command *cmd) | ||
| 82 | { | ||
| 83 | return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); | ||
| 84 | } | ||
| 85 | |||
| 86 | /** | ||
| 87 | * scm_legacy_get_command_buffer() - Get a pointer to a command buffer | ||
| 88 | * @cmd: command | ||
| 89 | * | ||
| 90 | * Returns a pointer to the command buffer of a command. | ||
| 91 | */ | ||
| 92 | static inline void *scm_legacy_get_command_buffer( | ||
| 93 | const struct scm_legacy_command *cmd) | ||
| 94 | { | ||
| 95 | return (void *)cmd->buf; | ||
| 96 | } | ||
| 97 | |||
| 98 | /** | ||
| 99 | * scm_legacy_get_response_buffer() - Get a pointer to a response buffer | ||
| 100 | * @rsp: response | ||
| 101 | * | ||
| 102 | * Returns a pointer to a response buffer of a response. | ||
| 103 | */ | ||
| 104 | static inline void *scm_legacy_get_response_buffer( | ||
| 105 | const struct scm_legacy_response *rsp) | ||
| 106 | { | ||
| 107 | return (void *)rsp + le32_to_cpu(rsp->buf_offset); | ||
| 108 | } | ||
| 109 | |||
| 110 | static void __scm_legacy_do(const struct arm_smccc_args *smc, | ||
| 111 | struct arm_smccc_res *res) | ||
| 112 | { | ||
| 113 | do { | ||
| 114 | arm_smccc_smc(smc->args[0], smc->args[1], smc->args[2], | ||
| 115 | smc->args[3], smc->args[4], smc->args[5], | ||
| 116 | smc->args[6], smc->args[7], res); | ||
| 117 | } while (res->a0 == QCOM_SCM_INTERRUPTED); | ||
| 118 | } | ||
| 119 | |||
| 120 | /** | ||
| 121 | * qcom_scm_call() - Sends a command to the SCM and waits for the command to | ||
| 122 | * finish processing. | ||
| 123 | * | ||
| 124 | * A note on cache maintenance: | ||
| 125 | * Note that any buffers that are expected to be accessed by the secure world | ||
| 126 | * must be flushed before invoking qcom_scm_call and invalidated in the cache | ||
| 127 | * immediately after qcom_scm_call returns. Cache maintenance on the command | ||
| 128 | * and response buffers is taken care of by qcom_scm_call; however, callers are | ||
| 129 | * responsible for any other cached buffers passed over to the secure world. | ||
| 130 | */ | ||
| 131 | int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, | ||
| 132 | struct qcom_scm_res *res) | ||
| 133 | { | ||
| 134 | u8 arglen = desc->arginfo & 0xf; | ||
| 135 | int ret = 0, context_id; | ||
| 136 | unsigned int i; | ||
| 137 | struct scm_legacy_command *cmd; | ||
| 138 | struct scm_legacy_response *rsp; | ||
| 139 | struct arm_smccc_args smc = {0}; | ||
| 140 | struct arm_smccc_res smc_res; | ||
| 141 | const size_t cmd_len = arglen * sizeof(__le32); | ||
| 142 | const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof(__le32); | ||
| 143 | size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len; | ||
| 144 | dma_addr_t cmd_phys; | ||
| 145 | __le32 *arg_buf; | ||
| 146 | const __le32 *res_buf; | ||
| 147 | |||
| 148 | cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); | ||
| 149 | if (!cmd) | ||
| 150 | return -ENOMEM; | ||
| 151 | |||
| 152 | cmd->len = cpu_to_le32(alloc_len); | ||
| 153 | cmd->buf_offset = cpu_to_le32(sizeof(*cmd)); | ||
| 154 | cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len); | ||
| 155 | cmd->id = cpu_to_le32(SCM_LEGACY_FNID(desc->svc, desc->cmd)); | ||
| 156 | |||
| 157 | arg_buf = scm_legacy_get_command_buffer(cmd); | ||
| 158 | for (i = 0; i < arglen; i++) | ||
| 159 | arg_buf[i] = cpu_to_le32(desc->args[i]); | ||
| 160 | |||
| 161 | rsp = scm_legacy_command_to_response(cmd); | ||
| 162 | |||
| 163 | cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); | ||
| 164 | if (dma_mapping_error(dev, cmd_phys)) { | ||
| 165 | kfree(cmd); | ||
| 166 | return -ENOMEM; | ||
| 167 | } | ||
| 168 | |||
| 169 | smc.args[0] = 1; | ||
| 170 | smc.args[1] = (unsigned long)&context_id; | ||
| 171 | smc.args[2] = cmd_phys; | ||
| 172 | |||
| 173 | mutex_lock(&qcom_scm_lock); | ||
| 174 | __scm_legacy_do(&smc, &smc_res); | ||
| 175 | if (smc_res.a0) | ||
| 176 | ret = qcom_scm_remap_error(smc_res.a0); | ||
| 177 | mutex_unlock(&qcom_scm_lock); | ||
| 178 | if (ret) | ||
| 179 | goto out; | ||
| 180 | |||
| 181 | do { | ||
| 182 | dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, | ||
| 183 | sizeof(*rsp), DMA_FROM_DEVICE); | ||
| 184 | } while (!rsp->is_complete); | ||
| 185 | |||
| 186 | dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + | ||
| 187 | le32_to_cpu(rsp->buf_offset), | ||
| 188 | resp_len, DMA_FROM_DEVICE); | ||
| 189 | |||
| 190 | if (res) { | ||
| 191 | res_buf = scm_legacy_get_response_buffer(rsp); | ||
| 192 | for (i = 0; i < MAX_QCOM_SCM_RETS; i++) | ||
| 193 | res->result[i] = le32_to_cpu(res_buf[i]); | ||
| 194 | } | ||
| 195 | out: | ||
| 196 | dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); | ||
| 197 | kfree(cmd); | ||
| 198 | return ret; | ||
| 199 | } | ||
| 200 | |||
| 201 | #define SCM_LEGACY_ATOMIC_N_REG_ARGS 5 | ||
| 202 | #define SCM_LEGACY_ATOMIC_FIRST_REG_IDX 2 | ||
| 203 | #define SCM_LEGACY_CLASS_REGISTER (0x2 << 8) | ||
| 204 | #define SCM_LEGACY_MASK_IRQS BIT(5) | ||
| 205 | #define SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \ | ||
| 206 | ((SCM_LEGACY_FNID(svc, cmd) << 12) | \ | ||
| 207 | SCM_LEGACY_CLASS_REGISTER | \ | ||
| 208 | SCM_LEGACY_MASK_IRQS | \ | ||
| 209 | (n & 0xf)) | ||
| 210 | |||
| 211 | /** | ||
| 212 | * qcom_scm_call_atomic() - Send an atomic SCM command with up to 5 arguments | ||
| 213 | * and 3 return values | ||
| 214 | * @desc: SCM call descriptor containing arguments | ||
| 215 | * @res: SCM call return values | ||
| 216 | * | ||
| 217 | * This shall only be used with commands that are guaranteed to be | ||
| 218 | * uninterruptable, atomic and SMP safe. | ||
| 219 | */ | ||
| 220 | int scm_legacy_call_atomic(struct device *unused, | ||
| 221 | const struct qcom_scm_desc *desc, | ||
| 222 | struct qcom_scm_res *res) | ||
| 223 | { | ||
| 224 | int context_id; | ||
| 225 | struct arm_smccc_res smc_res; | ||
| 226 | size_t arglen = desc->arginfo & 0xf; | ||
| 227 | |||
| 228 | BUG_ON(arglen > SCM_LEGACY_ATOMIC_N_REG_ARGS); | ||
| 229 | |||
| 230 | arm_smccc_smc(SCM_LEGACY_ATOMIC_ID(desc->svc, desc->cmd, arglen), | ||
| 231 | (unsigned long)&context_id, | ||
| 232 | desc->args[0], desc->args[1], desc->args[2], | ||
| 233 | desc->args[3], desc->args[4], 0, &smc_res); | ||
| 234 | |||
| 235 | if (res) { | ||
| 236 | res->result[0] = smc_res.a1; | ||
| 237 | res->result[1] = smc_res.a2; | ||
| 238 | res->result[2] = smc_res.a3; | ||
| 239 | } | ||
| 240 | |||
| 241 | return smc_res.a0; | ||
| 242 | } | ||
| @@ -0,0 +1,151 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved. | ||
| 3 | */ | ||
| 4 | |||
| 5 | #include <linux/io.h> | ||
| 6 | #include <linux/errno.h> | ||
| 7 | #include <linux/delay.h> | ||
| 8 | #include <linux/mutex.h> | ||
| 9 | #include <linux/slab.h> | ||
| 10 | #include <linux/types.h> | ||
| 11 | #include <linux/qcom_scm.h> | ||
| 12 | #include <linux/arm-smccc.h> | ||
| 13 | #include <linux/dma-mapping.h> | ||
| 14 | |||
| 15 | #include "qcom_scm.h" | ||
| 16 | |||
| 17 | /** | ||
| 18 | * struct arm_smccc_args | ||
| 19 | * @args: The array of values used in registers in smc instruction | ||
| 20 | */ | ||
| 21 | struct arm_smccc_args { | ||
| 22 | unsigned long args[8]; | ||
| 23 | }; | ||
| 24 | |||
| 25 | static DEFINE_MUTEX(qcom_scm_lock); | ||
| 26 | |||
| 27 | #define QCOM_SCM_EBUSY_WAIT_MS 30 | ||
| 28 | #define QCOM_SCM_EBUSY_MAX_RETRY 20 | ||
| 29 | |||
| 30 | #define SCM_SMC_N_REG_ARGS 4 | ||
| 31 | #define SCM_SMC_FIRST_EXT_IDX (SCM_SMC_N_REG_ARGS - 1) | ||
| 32 | #define SCM_SMC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SCM_SMC_N_REG_ARGS + 1) | ||
| 33 | #define SCM_SMC_FIRST_REG_IDX 2 | ||
| 34 | #define SCM_SMC_LAST_REG_IDX (SCM_SMC_FIRST_REG_IDX + SCM_SMC_N_REG_ARGS - 1) | ||
| 35 | |||
| 36 | static void __scm_smc_do_quirk(const struct arm_smccc_args *smc, | ||
| 37 | struct arm_smccc_res *res) | ||
| 38 | { | ||
| 39 | unsigned long a0 = smc->args[0]; | ||
| 40 | struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 }; | ||
| 41 | |||
| 42 | quirk.state.a6 = 0; | ||
| 43 | |||
| 44 | do { | ||
| 45 | arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2], | ||
| 46 | smc->args[3], smc->args[4], smc->args[5], | ||
| 47 | quirk.state.a6, smc->args[7], res, &quirk); | ||
| 48 | |||
| 49 | if (res->a0 == QCOM_SCM_INTERRUPTED) | ||
| 50 | a0 = res->a0; | ||
| 51 | |||
| 52 | } while (res->a0 == QCOM_SCM_INTERRUPTED); | ||
| 53 | } | ||
| 54 | |||
| 55 | static void __scm_smc_do(const struct arm_smccc_args *smc, | ||
| 56 | struct arm_smccc_res *res, bool atomic) | ||
| 57 | { | ||
| 58 | int retry_count = 0; | ||
| 59 | |||
| 60 | if (atomic) { | ||
| 61 | __scm_smc_do_quirk(smc, res); | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | |||
| 65 | do { | ||
| 66 | mutex_lock(&qcom_scm_lock); | ||
| 67 | |||
| 68 | __scm_smc_do_quirk(smc, res); | ||
| 69 | |||
| 70 | mutex_unlock(&qcom_scm_lock); | ||
| 71 | |||
| 72 | if (res->a0 == QCOM_SCM_V2_EBUSY) { | ||
| 73 | if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) | ||
| 74 | break; | ||
| 75 | msleep(QCOM_SCM_EBUSY_WAIT_MS); | ||
| 76 | } | ||
| 77 | } while (res->a0 == QCOM_SCM_V2_EBUSY); | ||
| 78 | } | ||
| 79 | |||
| 80 | int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, | ||
| 81 | struct qcom_scm_res *res, bool atomic) | ||
| 82 | { | ||
| 83 | int arglen = desc->arginfo & 0xf; | ||
| 84 | int i; | ||
| 85 | dma_addr_t args_phys = 0; | ||
| 86 | void *args_virt = NULL; | ||
| 87 | size_t alloc_len; | ||
| 88 | gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; | ||
| 89 | u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL; | ||
| 90 | u32 qcom_smccc_convention = | ||
| 91 | (qcom_scm_convention == SMC_CONVENTION_ARM_32) ? | ||
| 92 | ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64; | ||
| 93 | struct arm_smccc_res smc_res; | ||
| 94 | struct arm_smccc_args smc = {0}; | ||
| 95 | |||
| 96 | smc.args[0] = ARM_SMCCC_CALL_VAL( | ||
| 97 | smccc_call_type, | ||
| 98 | qcom_smccc_convention, | ||
| 99 | desc->owner, | ||
| 100 | SCM_SMC_FNID(desc->svc, desc->cmd)); | ||
| 101 | smc.args[1] = desc->arginfo; | ||
| 102 | for (i = 0; i < SCM_SMC_N_REG_ARGS; i++) | ||
| 103 | smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i]; | ||
| 104 | |||
| 105 | if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) { | ||
| 106 | alloc_len = SCM_SMC_N_EXT_ARGS * sizeof(u64); | ||
| 107 | args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); | ||
| 108 | |||
| 109 | if (!args_virt) | ||
| 110 | return -ENOMEM; | ||
| 111 | |||
| 112 | if (qcom_smccc_convention == ARM_SMCCC_SMC_32) { | ||
| 113 | __le32 *args = args_virt; | ||
| 114 | |||
| 115 | for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++) | ||
| 116 | args[i] = cpu_to_le32(desc->args[i + | ||
| 117 | SCM_SMC_FIRST_EXT_IDX]); | ||
| 118 | } else { | ||
| 119 | __le64 *args = args_virt; | ||
| 120 | |||
| 121 | for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++) | ||
| 122 | args[i] = cpu_to_le64(desc->args[i + | ||
| 123 | SCM_SMC_FIRST_EXT_IDX]); | ||
| 124 | } | ||
| 125 | |||
| 126 | args_phys = dma_map_single(dev, args_virt, alloc_len, | ||
| 127 | DMA_TO_DEVICE); | ||
| 128 | |||
| 129 | if (dma_mapping_error(dev, args_phys)) { | ||
| 130 | kfree(args_virt); | ||
| 131 | return -ENOMEM; | ||
| 132 | } | ||
| 133 | |||
| 134 | smc.args[SCM_SMC_LAST_REG_IDX] = args_phys; | ||
| 135 | } | ||
| 136 | |||
| 137 | __scm_smc_do(&smc, &smc_res, atomic); | ||
| 138 | |||
| 139 | if (args_virt) { | ||
| 140 | dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); | ||
| 141 | kfree(args_virt); | ||
| 142 | } | ||
| 143 | |||
| 144 | if (res) { | ||
| 145 | res->result[0] = smc_res.a1; | ||
| 146 | res->result[1] = smc_res.a2; | ||
| 147 | res->result[2] = smc_res.a3; | ||
| 148 | } | ||
| 149 | |||
| 150 | return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0; | ||
| 151 | } | ||
| @@ -1,8 +1,5 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* | 2 | /* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. |
| 3 | * Qualcomm SCM driver | ||
| 4 | * | ||
| 5 | * Copyright (c) 2010,2015, The Linux Foundation. All rights reserved. | ||
| 6 | * Copyright (C) 2015 Linaro Ltd. | 3 | * Copyright (C) 2015 Linaro Ltd. |
| 7 | */ | 4 | */ |
| 8 | #include <linux/platform_device.h> | 5 | #include <linux/platform_device.h> |
| @@ -19,6 +16,7 @@ · 10 unmodified lines | |||
| 19 | #include <linux/of_platform.h> | 16 | #include <linux/of_platform.h> |
| 20 | #include <linux/clk.h> | 17 | #include <linux/clk.h> |
| 21 | #include <linux/reset-controller.h> | 18 | #include <linux/reset-controller.h> |
| 19 | #include <linux/arm-smccc.h> | ||
| 22 | 20 | ||
| 23 | #include "qcom_scm.h" | 21 | #include "qcom_scm.h" |
| 24 | 22 | ||
| @@ -52,6 +50,35 @@ struct qcom_scm_mem_map_info { · 27 unmodified lines | |||
| 52 | __le64 mem_size; | 50 | __le64 mem_size; |
| 53 | }; | 51 | }; |
| 54 | 52 | ||
| 53 | #define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00 | ||
| 54 | #define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 | ||
| 55 | #define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 | ||
| 56 | #define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 | ||
| 57 | |||
| 58 | #define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 | ||
| 59 | #define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 | ||
| 60 | #define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 | ||
| 61 | #define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 | ||
| 62 | |||
| 63 | struct qcom_scm_wb_entry { | ||
| 64 | int flag; | ||
| 65 | void *entry; | ||
| 66 | }; | ||
| 67 | |||
| 68 | static struct qcom_scm_wb_entry qcom_scm_wb[] = { | ||
| 69 | { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU0 }, | ||
| 70 | { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU1 }, | ||
| 71 | { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU2 }, | ||
| 72 | { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 }, | ||
| 73 | }; | ||
| 74 | |||
| 75 | static const char *qcom_scm_convention_names[] = { | ||
| 76 | [SMC_CONVENTION_UNKNOWN] = "unknown", | ||
| 77 | [SMC_CONVENTION_ARM_32] = "smc arm 32", | ||
| 78 | [SMC_CONVENTION_ARM_64] = "smc arm 64", | ||
| 79 | [SMC_CONVENTION_LEGACY] = "smc legacy", | ||
| 80 | }; | ||
| 81 | |||
| 55 | static struct qcom_scm *__scm; | 82 | static struct qcom_scm *__scm; |
| 56 | 83 | ||
| 57 | static int qcom_scm_clk_enable(void) | 84 | static int qcom_scm_clk_enable(void) |
| @@ -87,149 +114,308 @@ static void qcom_scm_clk_disable(void) · 29 unmodified lines | |||
| 87 | clk_disable_unprepare(__scm->bus_clk); | 114 | clk_disable_unprepare(__scm->bus_clk); |
| 88 | } | 115 | } |
| 89 | 116 | ||
| 90 | /** | 117 | static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, |
| 91 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus | 118 | u32 cmd_id); |
| 92 | * @entry: Entry point function for the cpus | 119 | |
| 93 | * @cpus: The cpumask of cpus that will use the entry point | 120 | enum qcom_scm_convention qcom_scm_convention; |
| 94 | * | 121 | static bool has_queried __read_mostly; |
| 95 | * Set the cold boot address of the cpus. Any cpu outside the supported | 122 | static DEFINE_SPINLOCK(query_lock); |
| 96 | * range would be removed from the cpu present mask. | 123 | |
| 97 | */ | 124 | static void __query_convention(void) |
| 98 | int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) | ||
| 99 | { | 125 | { |
| 100 | return __qcom_scm_set_cold_boot_addr(entry, cpus); | 126 | unsigned long flags; |
| 127 | struct qcom_scm_desc desc = { | ||
| 128 | .svc = QCOM_SCM_SVC_INFO, | ||
| 129 | .cmd = QCOM_SCM_INFO_IS_CALL_AVAIL, | ||
| 130 | .args[0] = SCM_SMC_FNID(QCOM_SCM_SVC_INFO, | ||
| 131 | QCOM_SCM_INFO_IS_CALL_AVAIL) | | ||
| 132 | (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT), | ||
| 133 | .arginfo = QCOM_SCM_ARGS(1), | ||
| 134 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 135 | }; | ||
| 136 | struct qcom_scm_res res; | ||
| 137 | int ret; | ||
| 138 | |||
| 139 | spin_lock_irqsave(&query_lock, flags); | ||
| 140 | if (has_queried) | ||
| 141 | goto out; | ||
| 142 | |||
| 143 | qcom_scm_convention = SMC_CONVENTION_ARM_64; | ||
| 144 | // Device isn't required as there is only one argument - no device | ||
| 145 | // needed to dma_map_single to secure world | ||
| 146 | ret = scm_smc_call(NULL, &desc, &res, true); | ||
| 147 | if (!ret && res.result[0] == 1) | ||
| 148 | goto out; | ||
| 149 | |||
| 150 | qcom_scm_convention = SMC_CONVENTION_ARM_32; | ||
| 151 | ret = scm_smc_call(NULL, &desc, &res, true); | ||
| 152 | if (!ret && res.result[0] == 1) | ||
| 153 | goto out; | ||
| 154 | |||
| 155 | qcom_scm_convention = SMC_CONVENTION_LEGACY; | ||
| 156 | out: | ||
| 157 | has_queried = true; | ||
| 158 | spin_unlock_irqrestore(&query_lock, flags); | ||
| 159 | pr_info("qcom_scm: convention: %s\n", | ||
| 160 | qcom_scm_convention_names[qcom_scm_convention]); | ||
| 101 | } | 161 | } |
| 102 | EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); | ||
| 103 | 162 | ||
| 104 | /** | 163 | static inline enum qcom_scm_convention __get_convention(void) |
| 105 | * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus | ||
| 106 | * @entry: Entry point function for the cpus | ||
| 107 | * @cpus: The cpumask of cpus that will use the entry point | ||
| 108 | * | ||
| 109 | * Set the Linux entry point for the SCM to transfer control to when coming | ||
| 110 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. | ||
| 111 | */ | ||
| 112 | int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) | ||
| 113 | { | 164 | { |
| 114 | return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus); | 165 | if (unlikely(!has_queried)) |
| 166 | __query_convention(); | ||
| 167 | return qcom_scm_convention; | ||
| 115 | } | 168 | } |
| 116 | EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); | ||
| 117 | 169 | ||
| 118 | /** | 170 | /** |
| 119 | * qcom_scm_cpu_power_down() - Power down the cpu | 171 | * qcom_scm_call() - Invoke a syscall in the secure world |
| 120 | * @flags - Flags to flush cache | 172 | * @dev: device |
| 173 | * @svc_id: service identifier | ||
| 174 | * @cmd_id: command identifier | ||
| 175 | * @desc: Descriptor structure containing arguments and return values | ||
| 121 | * | 176 | * |
| 122 | * This is an end point to power down cpu. If there was a pending interrupt, | 177 | * Sends a command to the SCM and waits for the command to finish processing. |
| 123 | * the control would return from this function, otherwise, the cpu jumps to the | 178 | * This should *only* be called in pre-emptible context. |
| 124 | * warm boot entry point set for this cpu upon reset. | ||
| 125 | */ | 179 | */ |
| 126 | void qcom_scm_cpu_power_down(u32 flags) | 180 | static int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc, |
| 181 | struct qcom_scm_res *res) | ||
| 127 | { | 182 | { |
| 128 | __qcom_scm_cpu_power_down(flags); | 183 | might_sleep(); |
| 184 | switch (__get_convention()) { | ||
| 185 | case SMC_CONVENTION_ARM_32: | ||
| 186 | case SMC_CONVENTION_ARM_64: | ||
| 187 | return scm_smc_call(dev, desc, res, false); | ||
| 188 | case SMC_CONVENTION_LEGACY: | ||
| 189 | return scm_legacy_call(dev, desc, res); | ||
| 190 | default: | ||
| 191 | pr_err("Unknown current SCM calling convention.\n"); | ||
| 192 | return -EINVAL; | ||
| 193 | } | ||
| 129 | } | 194 | } |
| 130 | EXPORT_SYMBOL(qcom_scm_cpu_power_down); | ||
| 131 | 195 | ||
| 132 | /** | 196 | /** |
| 133 | * qcom_scm_hdcp_available() - Check if secure environment supports HDCP. | 197 | * qcom_scm_call_atomic() - atomic variation of qcom_scm_call() |
| 198 | * @dev: device | ||
| 199 | * @svc_id: service identifier | ||
| 200 | * @cmd_id: command identifier | ||
| 201 | * @desc: Descriptor structure containing arguments and return values | ||
| 202 | * @res: Structure containing results from SMC/HVC call | ||
| 134 | * | 203 | * |
| 135 | * Return true if HDCP is supported, false if not. | 204 | * Sends a command to the SCM and waits for the command to finish processing. |
| 205 | * This can be called in atomic context. | ||
| 136 | */ | 206 | */ |
| 137 | bool qcom_scm_hdcp_available(void) | 207 | static int qcom_scm_call_atomic(struct device *dev, |
| 208 | const struct qcom_scm_desc *desc, | ||
| 209 | struct qcom_scm_res *res) | ||
| 138 | { | 210 | { |
| 139 | int ret = qcom_scm_clk_enable(); | 211 | switch (__get_convention()) { |
| 140 | 212 | case SMC_CONVENTION_ARM_32: | |
| 141 | if (ret) | 213 | case SMC_CONVENTION_ARM_64: |
| 142 | return ret; | 214 | return scm_smc_call(dev, desc, res, true); |
| 215 | case SMC_CONVENTION_LEGACY: | ||
| 216 | return scm_legacy_call_atomic(dev, desc, res); | ||
| 217 | default: | ||
| 218 | pr_err("Unknown current SCM calling convention.\n"); | ||
| 219 | return -EINVAL; | ||
| 220 | } | ||
| 221 | } | ||
| 143 | 222 | ||
| 144 | ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP, | 223 | static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, |
| 145 | QCOM_SCM_CMD_HDCP); | 224 | u32 cmd_id) |
| 225 | { | ||
| 226 | int ret; | ||
| 227 | struct qcom_scm_desc desc = { | ||
| 228 | .svc = QCOM_SCM_SVC_INFO, | ||
| 229 | .cmd = QCOM_SCM_INFO_IS_CALL_AVAIL, | ||
| 230 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 231 | }; | ||
| 232 | struct qcom_scm_res res; | ||
| 233 | |||
| 234 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
| 235 | switch (__get_convention()) { | ||
| 236 | case SMC_CONVENTION_ARM_32: | ||
| 237 | case SMC_CONVENTION_ARM_64: | ||
| 238 | desc.args[0] = SCM_SMC_FNID(svc_id, cmd_id) | | ||
| 239 | (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); | ||
| 240 | break; | ||
| 241 | case SMC_CONVENTION_LEGACY: | ||
| 242 | desc.args[0] = SCM_LEGACY_FNID(svc_id, cmd_id); | ||
| 243 | break; | ||
| 244 | default: | ||
| 245 | pr_err("Unknown SMC convention being used\n"); | ||
| 246 | return -EINVAL; | ||
| 247 | } | ||
| 146 | 248 | ||
| 147 | qcom_scm_clk_disable(); | 249 | ret = qcom_scm_call(dev, &desc, &res); |
| 148 | 250 | ||
| 149 | return ret > 0 ? true : false; | 251 | return ret ? : res.result[0]; |
| 150 | } | 252 | } |
| 151 | EXPORT_SYMBOL(qcom_scm_hdcp_available); | ||
| 152 | 253 | ||
| 153 | /** | 254 | /** |
| 154 | * qcom_scm_hdcp_req() - Send HDCP request. | 255 | * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus |
| 155 | * @req: HDCP request array | 256 | * @entry: Entry point function for the cpus |
| 156 | * @req_cnt: HDCP request array count | 257 | * @cpus: The cpumask of cpus that will use the entry point |
| 157 | * @resp: response buffer passed to SCM | ||
| 158 | * | 258 | * |
| 159 | * Write HDCP register(s) through SCM. | 259 | * Set the Linux entry point for the SCM to transfer control to when coming |
| 260 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. | ||
| 160 | */ | 261 | */ |
| 161 | int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) | 262 | int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) |
| 162 | { | 263 | { |
| 163 | int ret = qcom_scm_clk_enable(); | 264 | int ret; |
| 265 | int flags = 0; | ||
| 266 | int cpu; | ||
| 267 | struct qcom_scm_desc desc = { | ||
| 268 | .svc = QCOM_SCM_SVC_BOOT, | ||
| 269 | .cmd = QCOM_SCM_BOOT_SET_ADDR, | ||
| 270 | .arginfo = QCOM_SCM_ARGS(2), | ||
| 271 | }; | ||
| 164 | 272 | ||
| 165 | if (ret) | 273 | /* |
| 166 | return ret; | 274 | * Reassign only if we are switching from hotplug entry point |
| 275 | * to cpuidle entry point or vice versa. | ||
| 276 | */ | ||
| 277 | for_each_cpu(cpu, cpus) { | ||
| 278 | if (entry == qcom_scm_wb[cpu].entry) | ||
| 279 | continue; | ||
| 280 | flags |= qcom_scm_wb[cpu].flag; | ||
| 281 | } | ||
| 282 | |||
| 283 | /* No change in entry function */ | ||
| 284 | if (!flags) | ||
| 285 | return 0; | ||
| 286 | |||
| 287 | desc.args[0] = flags; | ||
| 288 | desc.args[1] = virt_to_phys(entry); | ||
| 289 | |||
| 290 | ret = qcom_scm_call(__scm->dev, &desc, NULL); | ||
| 291 | if (!ret) { | ||
| 292 | for_each_cpu(cpu, cpus) | ||
| 293 | qcom_scm_wb[cpu].entry = entry; | ||
| 294 | } | ||
| 167 | 295 | ||
| 168 | ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp); | ||
| 169 | qcom_scm_clk_disable(); | ||
| 170 | return ret; | 296 | return ret; |
| 171 | } | 297 | } |
| 172 | EXPORT_SYMBOL(qcom_scm_hdcp_req); | 298 | EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); |
| 173 | 299 | ||
| 174 | /** | 300 | /** |
| 175 | * qcom_scm_pas_supported() - Check if the peripheral authentication service is | 301 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus |
| 176 | * available for the given peripherial | 302 | * @entry: Entry point function for the cpus |
| 177 | * @peripheral: peripheral id | 303 | * @cpus: The cpumask of cpus that will use the entry point |
| 178 | * | 304 | * |
| 179 | * Returns true if PAS is supported for this peripheral, otherwise false. | 305 | * Set the cold boot address of the cpus. Any cpu outside the supported |
| 306 | * range would be removed from the cpu present mask. | ||
| 180 | */ | 307 | */ |
| 181 | bool qcom_scm_pas_supported(u32 peripheral) | 308 | int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) |
| 182 | { | 309 | { |
| 183 | int ret; | 310 | int flags = 0; |
| 311 | int cpu; | ||
| 312 | int scm_cb_flags[] = { | ||
| 313 | QCOM_SCM_FLAG_COLDBOOT_CPU0, | ||
| 314 | QCOM_SCM_FLAG_COLDBOOT_CPU1, | ||
| 315 | QCOM_SCM_FLAG_COLDBOOT_CPU2, | ||
| 316 | QCOM_SCM_FLAG_COLDBOOT_CPU3, | ||
| 317 | }; | ||
| 318 | struct qcom_scm_desc desc = { | ||
| 319 | .svc = QCOM_SCM_SVC_BOOT, | ||
| 320 | .cmd = QCOM_SCM_BOOT_SET_ADDR, | ||
| 321 | .arginfo = QCOM_SCM_ARGS(2), | ||
| 322 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 323 | }; | ||
| 324 | |||
| 325 | if (!cpus || (cpus && cpumask_empty(cpus))) | ||
| 326 | return -EINVAL; | ||
| 184 | 327 | ||
| 185 | ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, | 328 | for_each_cpu(cpu, cpus) { |
| 186 | QCOM_SCM_PAS_IS_SUPPORTED_CMD); | 329 | if (cpu < ARRAY_SIZE(scm_cb_flags)) |
| 187 | if (ret <= 0) | 330 | flags |= scm_cb_flags[cpu]; |
| 188 | return false; | 331 | else |
| 332 | set_cpu_present(cpu, false); | ||
| 333 | } | ||
| 334 | |||
| 335 | desc.args[0] = flags; | ||
| 336 | desc.args[1] = virt_to_phys(entry); | ||
| 189 | 337 | ||
| 190 | return __qcom_scm_pas_supported(__scm->dev, peripheral); | 338 | return qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL); |
| 191 | } | 339 | } |
| 192 | EXPORT_SYMBOL(qcom_scm_pas_supported); | 340 | EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); |
| 193 | 341 | ||
| 194 | /** | 342 | /** |
| 195 | * qcom_scm_ocmem_lock_available() - is OCMEM lock/unlock interface available | 343 | * qcom_scm_cpu_power_down() - Power down the cpu |
| 344 | * @flags - Flags to flush cache | ||
| 345 | * | ||
| 346 | * This is an end point to power down cpu. If there was a pending interrupt, | ||
| 347 | * the control would return from this function, otherwise, the cpu jumps to the | ||
| 348 | * warm boot entry point set for this cpu upon reset. | ||
| 196 | */ | 349 | */ |
| 197 | bool qcom_scm_ocmem_lock_available(void) | 350 | void qcom_scm_cpu_power_down(u32 flags) |
| 198 | { | 351 | { |
| 199 | return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_OCMEM_SVC, | 352 | struct qcom_scm_desc desc = { |
| 200 | QCOM_SCM_OCMEM_LOCK_CMD); | 353 | .svc = QCOM_SCM_SVC_BOOT, |
| 354 | .cmd = QCOM_SCM_BOOT_TERMINATE_PC, | ||
| 355 | .args[0] = flags & QCOM_SCM_FLUSH_FLAG_MASK, | ||
| 356 | .arginfo = QCOM_SCM_ARGS(1), | ||
| 357 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 358 | }; | ||
| 359 | |||
| 360 | qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL); | ||
| 201 | } | 361 | } |
| 202 | EXPORT_SYMBOL(qcom_scm_ocmem_lock_available); | 362 | EXPORT_SYMBOL(qcom_scm_cpu_power_down); |
| 203 | 363 | ||
| 204 | /** | 364 | int qcom_scm_set_remote_state(u32 state, u32 id) |
| 205 | * qcom_scm_ocmem_lock() - call OCMEM lock interface to assign an OCMEM | ||
| 206 | * region to the specified initiator | ||
| 207 | * | ||
| 208 | * @id: tz initiator id | ||
| 209 | * @offset: OCMEM offset | ||
| 210 | * @size: OCMEM size | ||
| 211 | * @mode: access mode (WIDE/NARROW) | ||
| 212 | */ | ||
| 213 | int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size, | ||
| 214 | u32 mode) | ||
| 215 | { | 365 | { |
| 216 | return __qcom_scm_ocmem_lock(__scm->dev, id, offset, size, mode); | 366 | struct qcom_scm_desc desc = { |
| 367 | .svc = QCOM_SCM_SVC_BOOT, | ||
| 368 | .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, | ||
| 369 | .arginfo = QCOM_SCM_ARGS(2), | ||
| 370 | .args[0] = state, | ||
| 371 | .args[1] = id, | ||
| 372 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 373 | }; | ||
| 374 | struct qcom_scm_res res; | ||
| 375 | int ret; | ||
| 376 | |||
| 377 | ret = qcom_scm_call(__scm->dev, &desc, &res); | ||
| 378 | |||
| 379 | return ret ? : res.result[0]; | ||
| 217 | } | 380 | } |
| 218 | EXPORT_SYMBOL(qcom_scm_ocmem_lock); | 381 | EXPORT_SYMBOL(qcom_scm_set_remote_state); |
| 219 | 382 | ||
| 220 | /** | 383 | static int __qcom_scm_set_dload_mode(struct device *dev, bool enable) |
| 221 | * qcom_scm_ocmem_unlock() - call OCMEM unlock interface to release an OCMEM | ||
| 222 | * region from the specified initiator | ||
| 223 | * | ||
| 224 | * @id: tz initiator id | ||
| 225 | * @offset: OCMEM offset | ||
| 226 | * @size: OCMEM size | ||
| 227 | */ | ||
| 228 | int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size) | ||
| 229 | { | 384 | { |
| 230 | return __qcom_scm_ocmem_unlock(__scm->dev, id, offset, size); | 385 | struct qcom_scm_desc desc = { |
| 386 | .svc = QCOM_SCM_SVC_BOOT, | ||
| 387 | .cmd = QCOM_SCM_BOOT_SET_DLOAD_MODE, | ||
| 388 | .arginfo = QCOM_SCM_ARGS(2), | ||
| 389 | .args[0] = QCOM_SCM_BOOT_SET_DLOAD_MODE, | ||
| 390 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 391 | }; | ||
| 392 | |||
| 393 | desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0; | ||
| 394 | |||
| 395 | return qcom_scm_call(__scm->dev, &desc, NULL); | ||
| 396 | } | ||
| 397 | |||
| 398 | static void qcom_scm_set_download_mode(bool enable) | ||
| 399 | { | ||
| 400 | bool avail; | ||
| 401 | int ret = 0; | ||
| 402 | |||
| 403 | avail = __qcom_scm_is_call_available(__scm->dev, | ||
| 404 | QCOM_SCM_SVC_BOOT, | ||
| 405 | QCOM_SCM_BOOT_SET_DLOAD_MODE); | ||
| 406 | if (avail) { | ||
| 407 | ret = __qcom_scm_set_dload_mode(__scm->dev, enable); | ||
| 408 | } else if (__scm->dload_mode_addr) { | ||
| 409 | ret = qcom_scm_io_writel(__scm->dload_mode_addr, | ||
| 410 | enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0); | ||
| 411 | } else { | ||
| 412 | dev_err(__scm->dev, | ||
| 413 | "No available mechanism for setting download mode\n"); | ||
| 414 | } | ||
| 415 | |||
| 416 | if (ret) | ||
| 417 | dev_err(__scm->dev, "failed to set download mode: %d\n", ret); | ||
| 231 | } | 418 | } |
| 232 | EXPORT_SYMBOL(qcom_scm_ocmem_unlock); | ||
| 233 | 419 | ||
| 234 | /** | 420 | /** |
| 235 | * qcom_scm_pas_init_image() - Initialize peripheral authentication service | 421 | * qcom_scm_pas_init_image() - Initialize peripheral authentication service |
| @@ -248,6 +434,14 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size) · 12 unmodified lines | |||
| 248 | dma_addr_t mdata_phys; | 434 | dma_addr_t mdata_phys; |
| 249 | void *mdata_buf; | 435 | void *mdata_buf; |
| 250 | int ret; | 436 | int ret; |
| 437 | struct qcom_scm_desc desc = { | ||
| 438 | .svc = QCOM_SCM_SVC_PIL, | ||
| 439 | .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, | ||
| 440 | .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW), | ||
| 441 | .args[0] = peripheral, | ||
| 442 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 443 | }; | ||
| 444 | struct qcom_scm_res res; | ||
| 251 | 445 | ||
| 252 | /* | 446 | /* |
| 253 | * During the scm call memory protection will be enabled for the meta | 447 | * During the scm call memory protection will be enabled for the meta |
| @@ -266,14 +460,16 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size) · 12 unmodified lines | |||
| 266 | if (ret) | 460 | if (ret) |
| 267 | goto free_metadata; | 461 | goto free_metadata; |
| 268 | 462 | ||
| 269 | ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys); | 463 | desc.args[1] = mdata_phys; |
| 464 | |||
| 465 | ret = qcom_scm_call(__scm->dev, &desc, &res); | ||
| 270 | 466 | ||
| 271 | qcom_scm_clk_disable(); | 467 | qcom_scm_clk_disable(); |
| 272 | 468 | ||
| 273 | free_metadata: | 469 | free_metadata: |
| 274 | dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); | 470 | dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); |
| 275 | 471 | ||
| 276 | return ret; | 472 | return ret ? : res.result[0]; |
| 277 | } | 473 | } |
| 278 | EXPORT_SYMBOL(qcom_scm_pas_init_image); | 474 | EXPORT_SYMBOL(qcom_scm_pas_init_image); |
| 279 | 475 | ||
| @@ -289,15 +485,25 @@ EXPORT_SYMBOL(qcom_scm_pas_init_image); · 9 unmodified lines | |||
| 289 | int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) | 485 | int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) |
| 290 | { | 486 | { |
| 291 | int ret; | 487 | int ret; |
| 488 | struct qcom_scm_desc desc = { | ||
| 489 | .svc = QCOM_SCM_SVC_PIL, | ||
| 490 | .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, | ||
| 491 | .arginfo = QCOM_SCM_ARGS(3), | ||
| 492 | .args[0] = peripheral, | ||
| 493 | .args[1] = addr, | ||
| 494 | .args[2] = size, | ||
| 495 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 496 | }; | ||
| 497 | struct qcom_scm_res res; | ||
| 292 | 498 | ||
| 293 | ret = qcom_scm_clk_enable(); | 499 | ret = qcom_scm_clk_enable(); |
| 294 | if (ret) | 500 | if (ret) |
| 295 | return ret; | 501 | return ret; |
| 296 | 502 | ||
| 297 | ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size); | 503 | ret = qcom_scm_call(__scm->dev, &desc, &res); |
| 298 | qcom_scm_clk_disable(); | 504 | qcom_scm_clk_disable(); |
| 299 | 505 | ||
| 300 | return ret; | 506 | return ret ? : res.result[0]; |
| 301 | } | 507 | } |
| 302 | EXPORT_SYMBOL(qcom_scm_pas_mem_setup); | 508 | EXPORT_SYMBOL(qcom_scm_pas_mem_setup); |
| 303 | 509 | ||
| @@ -311,15 +517,23 @@ EXPORT_SYMBOL(qcom_scm_pas_mem_setup); · 7 unmodified lines | |||
| 311 | int qcom_scm_pas_auth_and_reset(u32 peripheral) | 517 | int qcom_scm_pas_auth_and_reset(u32 peripheral) |
| 312 | { | 518 | { |
| 313 | int ret; | 519 | int ret; |
| 520 | struct qcom_scm_desc desc = { | ||
| 521 | .svc = QCOM_SCM_SVC_PIL, | ||
| 522 | .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, | ||
| 523 | .arginfo = QCOM_SCM_ARGS(1), | ||
| 524 | .args[0] = peripheral, | ||
| 525 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 526 | }; | ||
| 527 | struct qcom_scm_res res; | ||
| 314 | 528 | ||
| 315 | ret = qcom_scm_clk_enable(); | 529 | ret = qcom_scm_clk_enable(); |
| 316 | if (ret) | 530 | if (ret) |
| 317 | return ret; | 531 | return ret; |
| 318 | 532 | ||
| 319 | ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral); | 533 | ret = qcom_scm_call(__scm->dev, &desc, &res); |
| 320 | qcom_scm_clk_disable(); | 534 | qcom_scm_clk_disable(); |
| 321 | 535 | ||
| 322 | return ret; | 536 | return ret ? : res.result[0]; |
| 323 | } | 537 | } |
| 324 | EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset); | 538 | EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset); |
| 325 | 539 | ||
| @@ -332,18 +546,75 @@ EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset); · 6 unmodified lines | |||
| 332 | int qcom_scm_pas_shutdown(u32 peripheral) | 546 | int qcom_scm_pas_shutdown(u32 peripheral) |
| 333 | { | 547 | { |
| 334 | int ret; | 548 | int ret; |
| 549 | struct qcom_scm_desc desc = { | ||
| 550 | .svc = QCOM_SCM_SVC_PIL, | ||
| 551 | .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, | ||
| 552 | .arginfo = QCOM_SCM_ARGS(1), | ||
| 553 | .args[0] = peripheral, | ||
| 554 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 555 | }; | ||
| 556 | struct qcom_scm_res res; | ||
| 335 | 557 | ||
| 336 | ret = qcom_scm_clk_enable(); | 558 | ret = qcom_scm_clk_enable(); |
| 337 | if (ret) | 559 | if (ret) |
| 338 | return ret; | 560 | return ret; |
| 339 | 561 | ||
| 340 | ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral); | 562 | ret = qcom_scm_call(__scm->dev, &desc, &res); |
| 563 | |||
| 341 | qcom_scm_clk_disable(); | 564 | qcom_scm_clk_disable(); |
| 342 | 565 | ||
| 343 | return ret; | 566 | return ret ? : res.result[0]; |
| 344 | } | 567 | } |
| 345 | EXPORT_SYMBOL(qcom_scm_pas_shutdown); | 568 | EXPORT_SYMBOL(qcom_scm_pas_shutdown); |
| 346 | 569 | ||
| 570 | /** | ||
| 571 | * qcom_scm_pas_supported() - Check if the peripheral authentication service is | ||
| 572 | * available for the given peripherial | ||
| 573 | * @peripheral: peripheral id | ||
| 574 | * | ||
| 575 | * Returns true if PAS is supported for this peripheral, otherwise false. | ||
| 576 | */ | ||
| 577 | bool qcom_scm_pas_supported(u32 peripheral) | ||
| 578 | { | ||
| 579 | int ret; | ||
| 580 | struct qcom_scm_desc desc = { | ||
| 581 | .svc = QCOM_SCM_SVC_PIL, | ||
| 582 | .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, | ||
| 583 | .arginfo = QCOM_SCM_ARGS(1), | ||
| 584 | .args[0] = peripheral, | ||
| 585 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 586 | }; | ||
| 587 | struct qcom_scm_res res; | ||
| 588 | |||
| 589 | ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, | ||
| 590 | QCOM_SCM_PIL_PAS_IS_SUPPORTED); | ||
| 591 | if (ret <= 0) | ||
| 592 | return false; | ||
| 593 | |||
| 594 | ret = qcom_scm_call(__scm->dev, &desc, &res); | ||
| 595 | |||
| 596 | return ret ? false : !!res.result[0]; | ||
| 597 | } | ||
| 598 | EXPORT_SYMBOL(qcom_scm_pas_supported); | ||
| 599 | |||
| 600 | static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) | ||
| 601 | { | ||
| 602 | struct qcom_scm_desc desc = { | ||
| 603 | .svc = QCOM_SCM_SVC_PIL, | ||
| 604 | .cmd = QCOM_SCM_PIL_PAS_MSS_RESET, | ||
| 605 | .arginfo = QCOM_SCM_ARGS(2), | ||
| 606 | .args[0] = reset, | ||
| 607 | .args[1] = 0, | ||
| 608 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 609 | }; | ||
| 610 | struct qcom_scm_res res; | ||
| 611 | int ret; | ||
| 612 | |||
| 613 | ret = qcom_scm_call(__scm->dev, &desc, &res); | ||
| 614 | |||
| 615 | return ret ? : res.result[0]; | ||
| 616 | } | ||
| 617 | |||
| 347 | static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev, | 618 | static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev, |
| 348 | unsigned long idx) | 619 | unsigned long idx) |
| 349 | { | 620 | { |
| @@ -367,6 +638,43 @@ static const struct reset_control_ops qcom_scm_pas_reset_ops = { · 17 unmodified lines | |||
| 367 | .deassert = qcom_scm_pas_reset_deassert, | 638 | .deassert = qcom_scm_pas_reset_deassert, |
| 368 | }; | 639 | }; |
| 369 | 640 | ||
| 641 | int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) | ||
| 642 | { | ||
| 643 | struct qcom_scm_desc desc = { | ||
| 644 | .svc = QCOM_SCM_SVC_IO, | ||
| 645 | .cmd = QCOM_SCM_IO_READ, | ||
| 646 | .arginfo = QCOM_SCM_ARGS(1), | ||
| 647 | .args[0] = addr, | ||
| 648 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 649 | }; | ||
| 650 | struct qcom_scm_res res; | ||
| 651 | int ret; | ||
| 652 | |||
| 653 | |||
| 654 | ret = qcom_scm_call(__scm->dev, &desc, &res); | ||
| 655 | if (ret >= 0) | ||
| 656 | *val = res.result[0]; | ||
| 657 | |||
| 658 | return ret < 0 ? ret : 0; | ||
| 659 | } | ||
| 660 | EXPORT_SYMBOL(qcom_scm_io_readl); | ||
| 661 | |||
| 662 | int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) | ||
| 663 | { | ||
| 664 | struct qcom_scm_desc desc = { | ||
| 665 | .svc = QCOM_SCM_SVC_IO, | ||
| 666 | .cmd = QCOM_SCM_IO_WRITE, | ||
| 667 | .arginfo = QCOM_SCM_ARGS(2), | ||
| 668 | .args[0] = addr, | ||
| 669 | .args[1] = val, | ||
| 670 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 671 | }; | ||
| 672 | |||
| 673 | |||
| 674 | return qcom_scm_call(__scm->dev, &desc, NULL); | ||
| 675 | } | ||
| 676 | EXPORT_SYMBOL(qcom_scm_io_writel); | ||
| 677 | |||
| 370 | /** | 678 | /** |
| 371 | * qcom_scm_restore_sec_cfg_available() - Check if secure environment | 679 | * qcom_scm_restore_sec_cfg_available() - Check if secure environment |
| 372 | * supports restore security config interface. | 680 | * supports restore security config interface. |
| @@ -376,108 +684,106 @@ static const struct reset_control_ops qcom_scm_pas_reset_ops = { · 3 unmodified lines | |||
| 376 | bool qcom_scm_restore_sec_cfg_available(void) | 684 | bool qcom_scm_restore_sec_cfg_available(void) |
| 377 | { | 685 | { |
| 378 | return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP, | 686 | return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP, |
| 379 | QCOM_SCM_RESTORE_SEC_CFG); | 687 | QCOM_SCM_MP_RESTORE_SEC_CFG); |
| 380 | } | 688 | } |
| 381 | EXPORT_SYMBOL(qcom_scm_restore_sec_cfg_available); | 689 | EXPORT_SYMBOL(qcom_scm_restore_sec_cfg_available); |
| 382 | 690 | ||
| 383 | int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) | 691 | int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) |
| 384 | { | 692 | { |
| 385 | return __qcom_scm_restore_sec_cfg(__scm->dev, device_id, spare); | 693 | struct qcom_scm_desc desc = { |
| 386 | } | 694 | .svc = QCOM_SCM_SVC_MP, |
| 387 | EXPORT_SYMBOL(qcom_scm_restore_sec_cfg); | 695 | .cmd = QCOM_SCM_MP_RESTORE_SEC_CFG, |
| 388 | 696 | .arginfo = QCOM_SCM_ARGS(2), | |
| 389 | int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) | 697 | .args[0] = device_id, |
| 390 | { | 698 | .args[1] = spare, |
| 391 | return __qcom_scm_iommu_secure_ptbl_size(__scm->dev, spare, size); | 699 | .owner = ARM_SMCCC_OWNER_SIP, |
| 392 | } | 700 | }; |
| 393 | EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_size); | 701 | struct qcom_scm_res res; |
| 394 | 702 | int ret; | |
| 395 | int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) | ||
| 396 | { | ||
| 397 | return __qcom_scm_iommu_secure_ptbl_init(__scm->dev, addr, size, spare); | ||
| 398 | } | ||
| 399 | EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init); | ||
| 400 | 703 | ||
| 401 | int qcom_scm_qsmmu500_wait_safe_toggle(bool en) | 704 | ret = qcom_scm_call(__scm->dev, &desc, &res); |
| 402 | { | ||
| 403 | return __qcom_scm_qsmmu500_wait_safe_toggle(__scm->dev, en); | ||
| 404 | } | ||
| 405 | EXPORT_SYMBOL(qcom_scm_qsmmu500_wait_safe_toggle); | ||
| 406 | 705 | ||
| 407 | int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) | 706 | return ret ? : res.result[0]; |
| 408 | { | ||
| 409 | return __qcom_scm_io_readl(__scm->dev, addr, val); | ||
| 410 | } | 707 | } |
| 411 | EXPORT_SYMBOL(qcom_scm_io_readl); | 708 | EXPORT_SYMBOL(qcom_scm_restore_sec_cfg); |
| 412 | 709 | ||
| 413 | int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) | 710 | int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) |
| 414 | { | 711 | { |
| 415 | return __qcom_scm_io_writel(__scm->dev, addr, val); | 712 | struct qcom_scm_desc desc = { |
| 416 | } | 713 | .svc = QCOM_SCM_SVC_MP, |
| 417 | EXPORT_SYMBOL(qcom_scm_io_writel); | 714 | .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE, |
| 715 | .arginfo = QCOM_SCM_ARGS(1), | ||
| 716 | .args[0] = spare, | ||
| 717 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 718 | }; | ||
| 719 | struct qcom_scm_res res; | ||
| 720 | int ret; | ||
| 418 | 721 | ||
| 419 | static void qcom_scm_set_download_mode(bool enable) | 722 | ret = qcom_scm_call(__scm->dev, &desc, &res); |
| 420 | { | ||
| 421 | bool avail; | ||
| 422 | int ret = 0; | ||
| 423 | 723 | ||
| 424 | avail = __qcom_scm_is_call_available(__scm->dev, | 724 | if (size) |
| 425 | QCOM_SCM_SVC_BOOT, | 725 | *size = res.result[0]; |
| 426 | QCOM_SCM_SET_DLOAD_MODE); | ||
| 427 | if (avail) { | ||
| 428 | ret = __qcom_scm_set_dload_mode(__scm->dev, enable); | ||
| 429 | } else if (__scm->dload_mode_addr) { | ||
| 430 | ret = __qcom_scm_io_writel(__scm->dev, __scm->dload_mode_addr, | ||
| 431 | enable ? QCOM_SCM_SET_DLOAD_MODE : 0); | ||
| 432 | } else { | ||
| 433 | dev_err(__scm->dev, | ||
| 434 | "No available mechanism for setting download mode\n"); | ||
| 435 | } | ||
| 436 | 726 | ||
| 437 | if (ret) | 727 | return ret ? : res.result[1]; |
| 438 | dev_err(__scm->dev, "failed to set download mode: %d\n", ret); | ||
| 439 | } | 728 | } |
| 729 | EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_size); | ||
| 440 | 730 | ||
| 441 | static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) | 731 | int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) |
| 442 | { | 732 | { |
| 443 | struct device_node *tcsr; | 733 | struct qcom_scm_desc desc = { |
| 444 | struct device_node *np = dev->of_node; | 734 | .svc = QCOM_SCM_SVC_MP, |
| 445 | struct resource res; | 735 | .cmd = QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT, |
| 446 | u32 offset; | 736 | .arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL, |
| 737 | QCOM_SCM_VAL), | ||
| 738 | .args[0] = addr, | ||
| 739 | .args[1] = size, | ||
| 740 | .args[2] = spare, | ||
| 741 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 742 | }; | ||
| 447 | int ret; | 743 | int ret; |
| 448 | 744 | ||
| 449 | tcsr = of_parse_phandle(np, "qcom,dload-mode", 0); | 745 | desc.args[0] = addr; |
| 450 | if (!tcsr) | 746 | desc.args[1] = size; |
| 451 | return 0; | 747 | desc.args[2] = spare; |
| 452 | 748 | desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_RW, QCOM_SCM_VAL, | |
| 453 | ret = of_address_to_resource(tcsr, 0, &res); | 749 | QCOM_SCM_VAL); |
| 454 | of_node_put(tcsr); | ||
| 455 | if (ret) | ||
| 456 | return ret; | ||
| 457 | |||
| 458 | ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset); | ||
| 459 | if (ret < 0) | ||
| 460 | return ret; | ||
| 461 | 750 | ||
| 462 | *addr = res.start + offset; | 751 | ret = qcom_scm_call(__scm->dev, &desc, NULL); |
| 463 | 752 | ||
| 464 | return 0; | 753 | /* the pg table has been initialized already, ignore the error */ |
| 465 | } | 754 | if (ret == -EPERM) |
| 755 | ret = 0; | ||
| 466 | 756 | ||
| 467 | /** | 757 | return ret; |
| 468 | * qcom_scm_is_available() - Checks if SCM is available | ||
| 469 | */ | ||
| 470 | bool qcom_scm_is_available(void) | ||
| 471 | { | ||
| 472 | return !!__scm; | ||
| 473 | } | 758 | } |
| 474 | EXPORT_SYMBOL(qcom_scm_is_available); | 759 | EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init); |
| 475 | 760 | ||
| 476 | int qcom_scm_set_remote_state(u32 state, u32 id) | 761 | static int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, |
| 762 | size_t mem_sz, phys_addr_t src, size_t src_sz, | ||
| 763 | phys_addr_t dest, size_t dest_sz) | ||
| 477 | { | 764 | { |
| 478 | return __qcom_scm_set_remote_state(__scm->dev, state, id); | 765 | int ret; |
| 766 | struct qcom_scm_desc desc = { | ||
| 767 | .svc = QCOM_SCM_SVC_MP, | ||
| 768 | .cmd = QCOM_SCM_MP_ASSIGN, | ||
| 769 | .arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, | ||
| 770 | QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, | ||
| 771 | QCOM_SCM_VAL, QCOM_SCM_VAL), | ||
| 772 | .args[0] = mem_region, | ||
| 773 | .args[1] = mem_sz, | ||
| 774 | .args[2] = src, | ||
| 775 | .args[3] = src_sz, | ||
| 776 | .args[4] = dest, | ||
| 777 | .args[5] = dest_sz, | ||
| 778 | .args[6] = 0, | ||
| 779 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 780 | }; | ||
| 781 | struct qcom_scm_res res; | ||
| 782 | |||
| 783 | ret = qcom_scm_call(dev, &desc, &res); | ||
| 784 | |||
| 785 | return ret ? : res.result[0]; | ||
| 479 | } | 786 | } |
| 480 | EXPORT_SYMBOL(qcom_scm_set_remote_state); | ||
| 481 | 787 | ||
| 482 | /** | 788 | /** |
| 483 | * qcom_scm_assign_mem() - Make a secure call to reassign memory ownership | 789 | * qcom_scm_assign_mem() - Make a secure call to reassign memory ownership |
| @@ -561,6 +867,184 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, · 77 unmodified lines | |||
| 561 | } | 867 | } |
| 562 | EXPORT_SYMBOL(qcom_scm_assign_mem); | 868 | EXPORT_SYMBOL(qcom_scm_assign_mem); |
| 563 | 869 | ||
| 870 | /** | ||
| 871 | * qcom_scm_ocmem_lock_available() - is OCMEM lock/unlock interface available | ||
| 872 | */ | ||
| 873 | bool qcom_scm_ocmem_lock_available(void) | ||
| 874 | { | ||
| 875 | return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_OCMEM, | ||
| 876 | QCOM_SCM_OCMEM_LOCK_CMD); | ||
| 877 | } | ||
| 878 | EXPORT_SYMBOL(qcom_scm_ocmem_lock_available); | ||
| 879 | |||
| 880 | /** | ||
| 881 | * qcom_scm_ocmem_lock() - call OCMEM lock interface to assign an OCMEM | ||
| 882 | * region to the specified initiator | ||
| 883 | * | ||
| 884 | * @id: tz initiator id | ||
| 885 | * @offset: OCMEM offset | ||
| 886 | * @size: OCMEM size | ||
| 887 | * @mode: access mode (WIDE/NARROW) | ||
| 888 | */ | ||
| 889 | int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size, | ||
| 890 | u32 mode) | ||
| 891 | { | ||
| 892 | struct qcom_scm_desc desc = { | ||
| 893 | .svc = QCOM_SCM_SVC_OCMEM, | ||
| 894 | .cmd = QCOM_SCM_OCMEM_LOCK_CMD, | ||
| 895 | .args[0] = id, | ||
| 896 | .args[1] = offset, | ||
| 897 | .args[2] = size, | ||
| 898 | .args[3] = mode, | ||
| 899 | .arginfo = QCOM_SCM_ARGS(4), | ||
| 900 | }; | ||
| 901 | |||
| 902 | return qcom_scm_call(__scm->dev, &desc, NULL); | ||
| 903 | } | ||
| 904 | EXPORT_SYMBOL(qcom_scm_ocmem_lock); | ||
| 905 | |||
| 906 | /** | ||
| 907 | * qcom_scm_ocmem_unlock() - call OCMEM unlock interface to release an OCMEM | ||
| 908 | * region from the specified initiator | ||
| 909 | * | ||
| 910 | * @id: tz initiator id | ||
| 911 | * @offset: OCMEM offset | ||
| 912 | * @size: OCMEM size | ||
| 913 | */ | ||
| 914 | int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size) | ||
| 915 | { | ||
| 916 | struct qcom_scm_desc desc = { | ||
| 917 | .svc = QCOM_SCM_SVC_OCMEM, | ||
| 918 | .cmd = QCOM_SCM_OCMEM_UNLOCK_CMD, | ||
| 919 | .args[0] = id, | ||
| 920 | .args[1] = offset, | ||
| 921 | .args[2] = size, | ||
| 922 | .arginfo = QCOM_SCM_ARGS(3), | ||
| 923 | }; | ||
| 924 | |||
| 925 | return qcom_scm_call(__scm->dev, &desc, NULL); | ||
| 926 | } | ||
| 927 | EXPORT_SYMBOL(qcom_scm_ocmem_unlock); | ||
| 928 | |||
| 929 | /** | ||
| 930 | * qcom_scm_hdcp_available() - Check if secure environment supports HDCP. | ||
| 931 | * | ||
| 932 | * Return true if HDCP is supported, false if not. | ||
| 933 | */ | ||
| 934 | bool qcom_scm_hdcp_available(void) | ||
| 935 | { | ||
| 936 | int ret = qcom_scm_clk_enable(); | ||
| 937 | |||
| 938 | if (ret) | ||
| 939 | return ret; | ||
| 940 | |||
| 941 | ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP, | ||
| 942 | QCOM_SCM_HDCP_INVOKE); | ||
| 943 | |||
| 944 | qcom_scm_clk_disable(); | ||
| 945 | |||
| 946 | return ret > 0 ? true : false; | ||
| 947 | } | ||
| 948 | EXPORT_SYMBOL(qcom_scm_hdcp_available); | ||
| 949 | |||
| 950 | /** | ||
| 951 | * qcom_scm_hdcp_req() - Send HDCP request. | ||
| 952 | * @req: HDCP request array | ||
| 953 | * @req_cnt: HDCP request array count | ||
| 954 | * @resp: response buffer passed to SCM | ||
| 955 | * | ||
| 956 | * Write HDCP register(s) through SCM. | ||
| 957 | */ | ||
| 958 | int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) | ||
| 959 | { | ||
| 960 | int ret; | ||
| 961 | struct qcom_scm_desc desc = { | ||
| 962 | .svc = QCOM_SCM_SVC_HDCP, | ||
| 963 | .cmd = QCOM_SCM_HDCP_INVOKE, | ||
| 964 | .arginfo = QCOM_SCM_ARGS(10), | ||
| 965 | .args = { | ||
| 966 | req[0].addr, | ||
| 967 | req[0].val, | ||
| 968 | req[1].addr, | ||
| 969 | req[1].val, | ||
| 970 | req[2].addr, | ||
| 971 | req[2].val, | ||
| 972 | req[3].addr, | ||
| 973 | req[3].val, | ||
| 974 | req[4].addr, | ||
| 975 | req[4].val | ||
| 976 | }, | ||
| 977 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 978 | }; | ||
| 979 | struct qcom_scm_res res; | ||
| 980 | |||
| 981 | if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) | ||
| 982 | return -ERANGE; | ||
| 983 | |||
| 984 | ret = qcom_scm_clk_enable(); | ||
| 985 | if (ret) | ||
| 986 | return ret; | ||
| 987 | |||
| 988 | ret = qcom_scm_call(__scm->dev, &desc, &res); | ||
| 989 | *resp = res.result[0]; | ||
| 990 | |||
| 991 | qcom_scm_clk_disable(); | ||
| 992 | |||
| 993 | return ret; | ||
| 994 | } | ||
| 995 | EXPORT_SYMBOL(qcom_scm_hdcp_req); | ||
| 996 | |||
| 997 | int qcom_scm_qsmmu500_wait_safe_toggle(bool en) | ||
| 998 | { | ||
| 999 | struct qcom_scm_desc desc = { | ||
| 1000 | .svc = QCOM_SCM_SVC_SMMU_PROGRAM, | ||
| 1001 | .cmd = QCOM_SCM_SMMU_CONFIG_ERRATA1, | ||
| 1002 | .arginfo = QCOM_SCM_ARGS(2), | ||
| 1003 | .args[0] = QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL, | ||
| 1004 | .args[1] = en, | ||
| 1005 | .owner = ARM_SMCCC_OWNER_SIP, | ||
| 1006 | }; | ||
| 1007 | |||
| 1008 | |||
| 1009 | return qcom_scm_call_atomic(__scm->dev, &desc, NULL); | ||
| 1010 | } | ||
| 1011 | EXPORT_SYMBOL(qcom_scm_qsmmu500_wait_safe_toggle); | ||
| 1012 | |||
| 1013 | static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) | ||
| 1014 | { | ||
| 1015 | struct device_node *tcsr; | ||
| 1016 | struct device_node *np = dev->of_node; | ||
| 1017 | struct resource res; | ||
| 1018 | u32 offset; | ||
| 1019 | int ret; | ||
| 1020 | |||
| 1021 | tcsr = of_parse_phandle(np, "qcom,dload-mode", 0); | ||
| 1022 | if (!tcsr) | ||
| 1023 | return 0; | ||
| 1024 | |||
| 1025 | ret = of_address_to_resource(tcsr, 0, &res); | ||
| 1026 | of_node_put(tcsr); | ||
| 1027 | if (ret) | ||
| 1028 | return ret; | ||
| 1029 | |||
| 1030 | ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset); | ||
| 1031 | if (ret < 0) | ||
| 1032 | return ret; | ||
| 1033 | |||
| 1034 | *addr = res.start + offset; | ||
| 1035 | |||
| 1036 | return 0; | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | /** | ||
| 1040 | * qcom_scm_is_available() - Checks if SCM is available | ||
| 1041 | */ | ||
| 1042 | bool qcom_scm_is_available(void) | ||
| 1043 | { | ||
| 1044 | return !!__scm; | ||
| 1045 | } | ||
| 1046 | EXPORT_SYMBOL(qcom_scm_is_available); | ||
| 1047 | |||
| 564 | static int qcom_scm_probe(struct platform_device *pdev) | 1048 | static int qcom_scm_probe(struct platform_device *pdev) |
| 565 | { | 1049 | { |
| 566 | struct qcom_scm *scm; | 1050 | struct qcom_scm *scm; |
| @@ -631,7 +1115,7 @@ static int qcom_scm_probe(struct platform_device *pdev) · 64 unmodified lines | |||
| 631 | __scm = scm; | 1115 | __scm = scm; |
| 632 | __scm->dev = &pdev->dev; | 1116 | __scm->dev = &pdev->dev; |
| 633 | 1117 | ||
| 634 | __qcom_scm_init(); | 1118 | __query_convention(); |
| 635 | 1119 | ||
| 636 | /* | 1120 | /* |
| 637 | * If requested enable "download mode", from this point on warmboot | 1121 | * If requested enable "download mode", from this point on warmboot |
| @@ -1,71 +1,116 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | /* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. | 2 | /* Copyright (c) 2010-2015,2019 The Linux Foundation. All rights reserved. |
| 3 | */ | 3 | */ |
| 4 | #ifndef __QCOM_SCM_INT_H | 4 | #ifndef __QCOM_SCM_INT_H |
| 5 | #define __QCOM_SCM_INT_H | 5 | #define __QCOM_SCM_INT_H |
| 6 | 6 | ||
| 7 | #define QCOM_SCM_SVC_BOOT 0x1 | 7 | enum qcom_scm_convention { |
| 8 | #define QCOM_SCM_BOOT_ADDR 0x1 | 8 | SMC_CONVENTION_UNKNOWN, |
| 9 | #define QCOM_SCM_SET_DLOAD_MODE 0x10 | 9 | SMC_CONVENTION_LEGACY, |
| 10 | #define QCOM_SCM_BOOT_ADDR_MC 0x11 | 10 | SMC_CONVENTION_ARM_32, |
| 11 | #define QCOM_SCM_SET_REMOTE_STATE 0xa | 11 | SMC_CONVENTION_ARM_64, |
| 12 | extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id); | 12 | }; |
| 13 | extern int __qcom_scm_set_dload_mode(struct device *dev, bool enable); | 13 | |
| 14 | 14 | extern enum qcom_scm_convention qcom_scm_convention; | |
| 15 | #define QCOM_SCM_FLAG_HLOS 0x01 | 15 | |
| 16 | #define QCOM_SCM_FLAG_COLDBOOT_MC 0x02 | 16 | #define MAX_QCOM_SCM_ARGS 10 |
| 17 | #define QCOM_SCM_FLAG_WARMBOOT_MC 0x04 | 17 | #define MAX_QCOM_SCM_RETS 3 |
| 18 | extern int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, | 18 | |
| 19 | const cpumask_t *cpus); | 19 | enum qcom_scm_arg_types { |
| 20 | extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); | 20 | QCOM_SCM_VAL, |
| 21 | 21 | QCOM_SCM_RO, | |
| 22 | #define QCOM_SCM_CMD_TERMINATE_PC 0x2 | 22 | QCOM_SCM_RW, |
| 23 | QCOM_SCM_BUFVAL, | ||
| 24 | }; | ||
| 25 | |||
| 26 | #define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ | ||
| 27 | (((a) & 0x3) << 4) | \ | ||
| 28 | (((b) & 0x3) << 6) | \ | ||
| 29 | (((c) & 0x3) << 8) | \ | ||
| 30 | (((d) & 0x3) << 10) | \ | ||
| 31 | (((e) & 0x3) << 12) | \ | ||
| 32 | (((f) & 0x3) << 14) | \ | ||
| 33 | (((g) & 0x3) << 16) | \ | ||
| 34 | (((h) & 0x3) << 18) | \ | ||
| 35 | (((i) & 0x3) << 20) | \ | ||
| 36 | (((j) & 0x3) << 22) | \ | ||
| 37 | ((num) & 0xf)) | ||
| 38 | |||
| 39 | #define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | ||
| 40 | |||
| 41 | |||
| 42 | /** | ||
| 43 | * struct qcom_scm_desc | ||
| 44 | * @arginfo: Metadata describing the arguments in args[] | ||
| 45 | * @args: The array of arguments for the secure syscall | ||
| 46 | */ | ||
| 47 | struct qcom_scm_desc { | ||
| 48 | u32 svc; | ||
| 49 | u32 cmd; | ||
| 50 | u32 arginfo; | ||
| 51 | u64 args[MAX_QCOM_SCM_ARGS]; | ||
| 52 | u32 owner; | ||
| 53 | }; | ||
| 54 | |||
| 55 | /** | ||
| 56 | * struct qcom_scm_res | ||
| 57 | * @result: The values returned by the secure syscall | ||
| 58 | */ | ||
| 59 | struct qcom_scm_res { | ||
| 60 | u64 result[MAX_QCOM_SCM_RETS]; | ||
| 61 | }; | ||
| 62 | |||
| 63 | #define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) | ||
| 64 | extern int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, | ||
| 65 | struct qcom_scm_res *res, bool atomic); | ||
| 66 | |||
| 67 | #define SCM_LEGACY_FNID(s, c) (((s) << 10) | ((c) & 0x3ff)) | ||
| 68 | extern int scm_legacy_call_atomic(struct device *dev, | ||
| 69 | const struct qcom_scm_desc *desc, | ||
| 70 | struct qcom_scm_res *res); | ||
| 71 | extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, | ||
| 72 | struct qcom_scm_res *res); | ||
| 73 | |||
| 74 | #define QCOM_SCM_SVC_BOOT 0x01 | ||
| 75 | #define QCOM_SCM_BOOT_SET_ADDR 0x01 | ||
| 76 | #define QCOM_SCM_BOOT_TERMINATE_PC 0x02 | ||
| 77 | #define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10 | ||
| 78 | #define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a | ||
| 23 | #define QCOM_SCM_FLUSH_FLAG_MASK 0x3 | 79 | #define QCOM_SCM_FLUSH_FLAG_MASK 0x3 |
| 24 | #define QCOM_SCM_CMD_CORE_HOTPLUGGED 0x10 | ||
| 25 | extern void __qcom_scm_cpu_power_down(u32 flags); | ||
| 26 | 80 | ||
| 27 | #define QCOM_SCM_SVC_IO 0x5 | 81 | #define QCOM_SCM_SVC_PIL 0x02 |
| 28 | #define QCOM_SCM_IO_READ 0x1 | 82 | #define QCOM_SCM_PIL_PAS_INIT_IMAGE 0x01 |
| 29 | #define QCOM_SCM_IO_WRITE 0x2 | 83 | #define QCOM_SCM_PIL_PAS_MEM_SETUP 0x02 |
| 30 | extern int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, unsigned int *val); | 84 | #define QCOM_SCM_PIL_PAS_AUTH_AND_RESET 0x05 |
| 31 | extern int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val); | 85 | #define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06 |
| 86 | #define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07 | ||
| 87 | #define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a | ||
| 88 | |||
| 89 | #define QCOM_SCM_SVC_IO 0x05 | ||
| 90 | #define QCOM_SCM_IO_READ 0x01 | ||
| 91 | #define QCOM_SCM_IO_WRITE 0x02 | ||
| 92 | |||
| 93 | #define QCOM_SCM_SVC_INFO 0x06 | ||
| 94 | #define QCOM_SCM_INFO_IS_CALL_AVAIL 0x01 | ||
| 32 | 95 | ||
| 33 | #define QCOM_SCM_SVC_INFO 0x6 | 96 | #define QCOM_SCM_SVC_MP 0x0c |
| 34 | #define QCOM_IS_CALL_AVAIL_CMD 0x1 | 97 | #define QCOM_SCM_MP_RESTORE_SEC_CFG 0x02 |
| 35 | extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, | 98 | #define QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE 0x03 |
| 36 | u32 cmd_id); | 99 | #define QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT 0x04 |
| 100 | #define QCOM_SCM_MP_ASSIGN 0x16 | ||
| 101 | |||
| 102 | #define QCOM_SCM_SVC_OCMEM 0x0f | ||
| 103 | #define QCOM_SCM_OCMEM_LOCK_CMD 0x01 | ||
| 104 | #define QCOM_SCM_OCMEM_UNLOCK_CMD 0x02 | ||
| 37 | 105 | ||
| 38 | #define QCOM_SCM_SVC_HDCP 0x11 | 106 | #define QCOM_SCM_SVC_HDCP 0x11 |
| 39 | #define QCOM_SCM_CMD_HDCP 0x01 | 107 | #define QCOM_SCM_HDCP_INVOKE 0x01 |
| 40 | extern int __qcom_scm_hdcp_req(struct device *dev, | ||
| 41 | struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp); | ||
| 42 | 108 | ||
| 43 | extern void __qcom_scm_init(void); | 109 | #define QCOM_SCM_SVC_SMMU_PROGRAM 0x15 |
| 110 | #define QCOM_SCM_SMMU_CONFIG_ERRATA1 0x03 | ||
| 111 | #define QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL 0x02 | ||
| 44 | 112 | ||
| 45 | #define QCOM_SCM_OCMEM_SVC 0xf | 113 | extern void __qcom_scm_init(void); |
| 46 | #define QCOM_SCM_OCMEM_LOCK_CMD 0x1 | ||
| 47 | #define QCOM_SCM_OCMEM_UNLOCK_CMD 0x2 | ||
| 48 | |||
| 49 | extern int __qcom_scm_ocmem_lock(struct device *dev, u32 id, u32 offset, | ||
| 50 | u32 size, u32 mode); | ||
| 51 | extern int __qcom_scm_ocmem_unlock(struct device *dev, u32 id, u32 offset, | ||
| 52 | u32 size); | ||
| 53 | |||
| 54 | #define QCOM_SCM_SVC_PIL 0x2 | ||
| 55 | #define QCOM_SCM_PAS_INIT_IMAGE_CMD 0x1 | ||
| 56 | #define QCOM_SCM_PAS_MEM_SETUP_CMD 0x2 | ||
| 57 | #define QCOM_SCM_PAS_AUTH_AND_RESET_CMD 0x5 | ||
| 58 | #define QCOM_SCM_PAS_SHUTDOWN_CMD 0x6 | ||
| 59 | #define QCOM_SCM_PAS_IS_SUPPORTED_CMD 0x7 | ||
| 60 | #define QCOM_SCM_PAS_MSS_RESET 0xa | ||
| 61 | extern bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral); | ||
| 62 | extern int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, | ||
| 63 | dma_addr_t metadata_phys); | ||
| 64 | extern int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, | ||
| 65 | phys_addr_t addr, phys_addr_t size); | ||
| 66 | extern int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral); | ||
| 67 | extern int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral); | ||
| 68 | extern int __qcom_scm_pas_mss_reset(struct device *dev, bool reset); | ||
| 69 | 114 | ||
| 70 | /* common error codes */ | 115 | /* common error codes */ |
| 71 | #define QCOM_SCM_V2_EBUSY -12 | 116 | #define QCOM_SCM_V2_EBUSY -12 |
| @@ -94,25 +139,4 @@ static inline int qcom_scm_remap_error(int err) · 22 unmodified lines | |||
| 94 | return -EINVAL; | 139 | return -EINVAL; |
| 95 | } | 140 | } |
| 96 | 141 | ||
| 97 | #define QCOM_SCM_SVC_MP 0xc | ||
| 98 | #define QCOM_SCM_RESTORE_SEC_CFG 2 | ||
| 99 | extern int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, | ||
| 100 | u32 spare); | ||
| 101 | #define QCOM_SCM_IOMMU_SECURE_PTBL_SIZE 3 | ||
| 102 | #define QCOM_SCM_IOMMU_SECURE_PTBL_INIT 4 | ||
| 103 | #define QCOM_SCM_SVC_SMMU_PROGRAM 0x15 | ||
| 104 | #define QCOM_SCM_CONFIG_ERRATA1 0x3 | ||
| 105 | #define QCOM_SCM_CONFIG_ERRATA1_CLIENT_ALL 0x2 | ||
| 106 | extern int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, | ||
| 107 | size_t *size); | ||
| 108 | extern int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, | ||
| 109 | u32 size, u32 spare); | ||
| 110 | extern int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, | ||
| 111 | bool enable); | ||
| 112 | #define QCOM_MEM_PROT_ASSIGN_ID 0x16 | ||
| 113 | extern int __qcom_scm_assign_mem(struct device *dev, | ||
| 114 | phys_addr_t mem_region, size_t mem_sz, | ||
| 115 | phys_addr_t src, size_t src_sz, | ||
| 116 | phys_addr_t dest, size_t dest_sz); | ||
| 117 | |||
| 118 | #endif | 142 | #endif |
| @@ -45,13 +45,13 @@ config QCOM_GLINK_SSR · 44 unmodified lines | |||
| 45 | neighboring subsystems going up or down. | 45 | neighboring subsystems going up or down. |
| 46 | 46 | ||
| 47 | config QCOM_GSBI | 47 | config QCOM_GSBI |
| 48 | tristate "QCOM General Serial Bus Interface" | 48 | tristate "QCOM General Serial Bus Interface" |
| 49 | depends on ARCH_QCOM || COMPILE_TEST | 49 | depends on ARCH_QCOM || COMPILE_TEST |
| 50 | select MFD_SYSCON | 50 | select MFD_SYSCON |
| 51 | help | 51 | help |
| 52 | Say y here to enable GSBI support. The GSBI provides control | 52 | Say y here to enable GSBI support. The GSBI provides control |
| 53 | functions for connecting the underlying serial UART, SPI, and I2C | 53 | functions for connecting the underlying serial UART, SPI, and I2C |
| 54 | devices to the output pins. | 54 | devices to the output pins. |
| 55 | 55 | ||
| 56 | config QCOM_LLCC | 56 | config QCOM_LLCC |
| 57 | tristate "Qualcomm Technologies, Inc. LLCC driver" | 57 | tristate "Qualcomm Technologies, Inc. LLCC driver" |
| @@ -71,10 +71,10 @@ config QCOM_OCMEM · 13 unmodified lines | |||
| 71 | depends on ARCH_QCOM | 71 | depends on ARCH_QCOM |
| 72 | select QCOM_SCM | 72 | select QCOM_SCM |
| 73 | help | 73 | help |
| 74 | The On Chip Memory (OCMEM) allocator allows various clients to | 74 | The On Chip Memory (OCMEM) allocator allows various clients to |
| 75 | allocate memory from OCMEM based on performance, latency and power | 75 | allocate memory from OCMEM based on performance, latency and power |
| 76 | requirements. This is typically used by the GPU, camera/video, and | 76 | requirements. This is typically used by the GPU, camera/video, and |
| 77 | audio components on some Snapdragon SoCs. | 77 | audio components on some Snapdragon SoCs. |
| 78 | 78 | ||
| 79 | config QCOM_PM | 79 | config QCOM_PM |
| 80 | bool "Qualcomm Power Management" | 80 | bool "Qualcomm Power Management" |
| @@ -198,8 +198,8 @@ config QCOM_APR · 117 unmodified lines | |||
| 198 | depends on ARCH_QCOM || COMPILE_TEST | 198 | depends on ARCH_QCOM || COMPILE_TEST |
| 199 | depends on RPMSG | 199 | depends on RPMSG |
| 200 | help | 200 | help |
| 201 | Enable APR IPC protocol support between | 201 | Enable APR IPC protocol support between |
| 202 | application processor and QDSP6. APR is | 202 | application processor and QDSP6. APR is |
| 203 | used by audio driver to configure QDSP6 | 203 | used by audio driver to configure QDSP6 |
| 204 | ASM, ADM and AFE modules. | 204 | ASM, ADM and AFE modules. |
| 205 | endmenu | 205 | endmenu |
| @@ -655,8 +655,12 @@ int qmi_handle_init(struct qmi_handle *qmi, size_t recv_buf_size, · 654 unmodified lines | |||
| 655 | 655 | ||
| 656 | qmi->sock = qmi_sock_create(qmi, &qmi->sq); | 656 | qmi->sock = qmi_sock_create(qmi, &qmi->sq); |
| 657 | if (IS_ERR(qmi->sock)) { | 657 | if (IS_ERR(qmi->sock)) { |
| 658 | pr_err("failed to create QMI socket\n"); | 658 | if (PTR_ERR(qmi->sock) == -EAFNOSUPPORT) { |
| 659 | ret = PTR_ERR(qmi->sock); | 659 | ret = -EPROBE_DEFER; |
| 660 | } else { | ||
| 661 | pr_err("failed to create QMI socket\n"); | ||
| 662 | ret = PTR_ERR(qmi->sock); | ||
| 663 | } | ||
| 660 | goto err_destroy_wq; | 664 | goto err_destroy_wq; |
| 661 | } | 665 | } |
| 662 | 666 | ||
| @@ -93,6 +93,7 @@ static struct rpmhpd sdm845_mx = { · 92 unmodified lines | |||
| 93 | 93 | ||
| 94 | static struct rpmhpd sdm845_mx_ao = { | 94 | static struct rpmhpd sdm845_mx_ao = { |
| 95 | .pd = { .name = "mx_ao", }, | 95 | .pd = { .name = "mx_ao", }, |
| 96 | .active_only = true, | ||
| 96 | .peer = &sdm845_mx, | 97 | .peer = &sdm845_mx, |
| 97 | .res_name = "mx.lvl", | 98 | .res_name = "mx.lvl", |
| 98 | }; | 99 | }; |
| @@ -107,6 +108,7 @@ static struct rpmhpd sdm845_cx = { · 8 unmodified lines | |||
| 107 | 108 | ||
| 108 | static struct rpmhpd sdm845_cx_ao = { | 109 | static struct rpmhpd sdm845_cx_ao = { |
| 109 | .pd = { .name = "cx_ao", }, | 110 | .pd = { .name = "cx_ao", }, |
| 111 | .active_only = true, | ||
| 110 | .peer = &sdm845_cx, | 112 | .peer = &sdm845_cx, |
| 111 | .parent = &sdm845_mx_ao.pd, | 113 | .parent = &sdm845_mx_ao.pd, |
| 112 | .res_name = "cx.lvl", | 114 | .res_name = "cx.lvl", |
| @@ -129,8 +131,62 @@ static const struct rpmhpd_desc sdm845_desc = { · 16 unmodified lines | |||
| 129 | .num_pds = ARRAY_SIZE(sdm845_rpmhpds), | 131 | .num_pds = ARRAY_SIZE(sdm845_rpmhpds), |
| 130 | }; | 132 | }; |
| 131 | 133 | ||
| 134 | /* SM8150 RPMH powerdomains */ | ||
| 135 | |||
| 136 | static struct rpmhpd sm8150_mmcx_ao; | ||
| 137 | static struct rpmhpd sm8150_mmcx = { | ||
| 138 | .pd = { .name = "mmcx", }, | ||
| 139 | .peer = &sm8150_mmcx_ao, | ||
| 140 | .res_name = "mmcx.lvl", | ||
| 141 | }; | ||
| 142 | |||
| 143 | static struct rpmhpd sm8150_mmcx_ao = { | ||
| 144 | .pd = { .name = "mmcx_ao", }, | ||
| 145 | .active_only = true, | ||
| 146 | .peer = &sm8150_mmcx, | ||
| 147 | .res_name = "mmcx.lvl", | ||
| 148 | }; | ||
| 149 | |||
| 150 | static struct rpmhpd *sm8150_rpmhpds[] = { | ||
| 151 | [SM8150_MSS] = &sdm845_mss, | ||
| 152 | [SM8150_EBI] = &sdm845_ebi, | ||
| 153 | [SM8150_LMX] = &sdm845_lmx, | ||
| 154 | [SM8150_LCX] = &sdm845_lcx, | ||
| 155 | [SM8150_GFX] = &sdm845_gfx, | ||
| 156 | [SM8150_MX] = &sdm845_mx, | ||
| 157 | [SM8150_MX_AO] = &sdm845_mx_ao, | ||
| 158 | [SM8150_CX] = &sdm845_cx, | ||
| 159 | [SM8150_CX_AO] = &sdm845_cx_ao, | ||
| 160 | [SM8150_MMCX] = &sm8150_mmcx, | ||
| 161 | [SM8150_MMCX_AO] = &sm8150_mmcx_ao, | ||
| 162 | }; | ||
| 163 | |||
| 164 | static const struct rpmhpd_desc sm8150_desc = { | ||
| 165 | .rpmhpds = sm8150_rpmhpds, | ||
| 166 | .num_pds = ARRAY_SIZE(sm8150_rpmhpds), | ||
| 167 | }; | ||
| 168 | |||
| 169 | /* SC7180 RPMH powerdomains */ | ||
| 170 | static struct rpmhpd *sc7180_rpmhpds[] = { | ||
| 171 | [SC7180_CX] = &sdm845_cx, | ||
| 172 | [SC7180_CX_AO] = &sdm845_cx_ao, | ||
| 173 | [SC7180_GFX] = &sdm845_gfx, | ||
| 174 | [SC7180_MX] = &sdm845_mx, | ||
| 175 | [SC7180_MX_AO] = &sdm845_mx_ao, | ||
| 176 | [SC7180_LMX] = &sdm845_lmx, | ||
| 177 | [SC7180_LCX] = &sdm845_lcx, | ||
| 178 | [SC7180_MSS] = &sdm845_mss, | ||
| 179 | }; | ||
| 180 | |||
| 181 | static const struct rpmhpd_desc sc7180_desc = { | ||
| 182 | .rpmhpds = sc7180_rpmhpds, | ||
| 183 | .num_pds = ARRAY_SIZE(sc7180_rpmhpds), | ||
| 184 | }; | ||
| 185 | |||
| 132 | static const struct of_device_id rpmhpd_match_table[] = { | 186 | static const struct of_device_id rpmhpd_match_table[] = { |
| 187 | { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, | ||
| 133 | { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, | 188 | { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, |
| 189 | { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, | ||
| 134 | { } | 190 | { } |
| 135 | }; | 191 | }; |
| 136 | 192 | ||
| @@ -15,12 +15,36 @@ · 14 unmodified lines | |||
| 15 | #define SDM845_GFX 7 | 15 | #define SDM845_GFX 7 |
| 16 | #define SDM845_MSS 8 | 16 | #define SDM845_MSS 8 |
| 17 | 17 | ||
| 18 | /* SM8150 Power Domain Indexes */ | ||
| 19 | #define SM8150_MSS 0 | ||
| 20 | #define SM8150_EBI 1 | ||
| 21 | #define SM8150_LMX 2 | ||
| 22 | #define SM8150_LCX 3 | ||
| 23 | #define SM8150_GFX 4 | ||
| 24 | #define SM8150_MX 5 | ||
| 25 | #define SM8150_MX_AO 6 | ||
| 26 | #define SM8150_CX 7 | ||
| 27 | #define SM8150_CX_AO 8 | ||
| 28 | #define SM8150_MMCX 9 | ||
| 29 | #define SM8150_MMCX_AO 10 | ||
| 30 | |||
| 31 | /* SC7180 Power Domain Indexes */ | ||
| 32 | #define SC7180_CX 0 | ||
| 33 | #define SC7180_CX_AO 1 | ||
| 34 | #define SC7180_GFX 2 | ||
| 35 | #define SC7180_MX 3 | ||
| 36 | #define SC7180_MX_AO 4 | ||
| 37 | #define SC7180_LMX 5 | ||
| 38 | #define SC7180_LCX 6 | ||
| 39 | #define SC7180_MSS 7 | ||
| 40 | |||
| 18 | /* SDM845 Power Domain performance levels */ | 41 | /* SDM845 Power Domain performance levels */ |
| 19 | #define RPMH_REGULATOR_LEVEL_RETENTION 16 | 42 | #define RPMH_REGULATOR_LEVEL_RETENTION 16 |
| 20 | #define RPMH_REGULATOR_LEVEL_MIN_SVS 48 | 43 | #define RPMH_REGULATOR_LEVEL_MIN_SVS 48 |
| 21 | #define RPMH_REGULATOR_LEVEL_LOW_SVS 64 | 44 | #define RPMH_REGULATOR_LEVEL_LOW_SVS 64 |
| 22 | #define RPMH_REGULATOR_LEVEL_SVS 128 | 45 | #define RPMH_REGULATOR_LEVEL_SVS 128 |
| 23 | #define RPMH_REGULATOR_LEVEL_SVS_L1 192 | 46 | #define RPMH_REGULATOR_LEVEL_SVS_L1 192 |
| 47 | #define RPMH_REGULATOR_LEVEL_SVS_L2 224 | ||
| 24 | #define RPMH_REGULATOR_LEVEL_NOM 256 | 48 | #define RPMH_REGULATOR_LEVEL_NOM 256 |
| 25 | #define RPMH_REGULATOR_LEVEL_NOM_L1 320 | 49 | #define RPMH_REGULATOR_LEVEL_NOM_L1 320 |
| 26 | #define RPMH_REGULATOR_LEVEL_NOM_L2 336 | 50 | #define RPMH_REGULATOR_LEVEL_NOM_L2 336 |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | /* Copyright (c) 2010-2015, 2018, The Linux Foundation. All rights reserved. | 2 | /* Copyright (c) 2010-2015, 2018-2019 The Linux Foundation. All rights reserved. |
| 3 | * Copyright (C) 2015 Linaro Ltd. | 3 | * Copyright (C) 2015 Linaro Ltd. |
| 4 | */ | 4 | */ |
| 5 | #ifndef __QCOM_SCM_H | 5 | #ifndef __QCOM_SCM_H |
| @@ -55,77 +55,94 @@ enum qcom_scm_sec_dev_id { · 49 unmodified lines | |||
| 55 | #define QCOM_SCM_PERM_RWX (QCOM_SCM_PERM_RW | QCOM_SCM_PERM_EXEC) | 55 | #define QCOM_SCM_PERM_RWX (QCOM_SCM_PERM_RW | QCOM_SCM_PERM_EXEC) |
| 56 | 56 | ||
| 57 | #if IS_ENABLED(CONFIG_QCOM_SCM) | 57 | #if IS_ENABLED(CONFIG_QCOM_SCM) |
| 58 | extern bool qcom_scm_is_available(void); | ||
| 59 | |||
| 58 | extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); | 60 | extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); |
| 59 | extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); | 61 | extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); |
| 60 | extern bool qcom_scm_is_available(void); | 62 | extern void qcom_scm_cpu_power_down(u32 flags); |
| 61 | extern bool qcom_scm_hdcp_available(void); | 63 | extern int qcom_scm_set_remote_state(u32 state, u32 id); |
| 62 | extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, | 64 | |
| 63 | u32 *resp); | ||
| 64 | extern bool qcom_scm_ocmem_lock_available(void); | ||
| 65 | extern int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, | ||
| 66 | u32 size, u32 mode); | ||
| 67 | extern int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, | ||
| 68 | u32 size); | ||
| 69 | extern bool qcom_scm_pas_supported(u32 peripheral); | ||
| 70 | extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, | 65 | extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, |
| 71 | size_t size); | 66 | size_t size); |
| 72 | extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, | 67 | extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, |
| 73 | phys_addr_t size); | 68 | phys_addr_t size); |
| 74 | extern int qcom_scm_pas_auth_and_reset(u32 peripheral); | 69 | extern int qcom_scm_pas_auth_and_reset(u32 peripheral); |
| 75 | extern int qcom_scm_pas_shutdown(u32 peripheral); | 70 | extern int qcom_scm_pas_shutdown(u32 peripheral); |
| 76 | extern int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, | 71 | extern bool qcom_scm_pas_supported(u32 peripheral); |
| 77 | unsigned int *src, | 72 | |
| 78 | const struct qcom_scm_vmperm *newvm, | 73 | extern int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); |
| 79 | unsigned int dest_cnt); | 74 | extern int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); |
| 80 | extern void qcom_scm_cpu_power_down(u32 flags); | 75 | |
| 81 | extern u32 qcom_scm_get_version(void); | ||
| 82 | extern int qcom_scm_set_remote_state(u32 state, u32 id); | ||
| 83 | extern bool qcom_scm_restore_sec_cfg_available(void); | 76 | extern bool qcom_scm_restore_sec_cfg_available(void); |
| 84 | extern int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); | 77 | extern int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); |
| 85 | extern int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size); | 78 | extern int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size); |
| 86 | extern int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare); | 79 | extern int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare); |
| 80 | extern int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, | ||
| 81 | unsigned int *src, | ||
| 82 | const struct qcom_scm_vmperm *newvm, | ||
| 83 | unsigned int dest_cnt); | ||
| 84 | |||
| 85 | extern bool qcom_scm_ocmem_lock_available(void); | ||
| 86 | extern int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, | ||
| 87 | u32 size, u32 mode); | ||
| 88 | extern int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, | ||
| 89 | u32 size); | ||
| 90 | |||
| 91 | extern bool qcom_scm_hdcp_available(void); | ||
| 92 | extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, | ||
| 93 | u32 *resp); | ||
| 94 | |||
| 87 | extern int qcom_scm_qsmmu500_wait_safe_toggle(bool en); | 95 | extern int qcom_scm_qsmmu500_wait_safe_toggle(bool en); |
| 88 | extern int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); | ||
| 89 | extern int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); | ||
| 90 | #else | 96 | #else |
| 91 | 97 | ||
| 92 | #include <linux/errno.h> | 98 | #include <linux/errno.h> |
| 93 | 99 | ||
| 94 | static inline | ||
| 95 | int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) | ||
| 96 | { | ||
| 97 | return -ENODEV; | ||
| 98 | } | ||
| 99 | static inline | ||
| 100 | int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) | ||
| 101 | { | ||
| 102 | return -ENODEV; | ||
| 103 | } | ||
| 104 | static inline bool qcom_scm_is_available(void) { return false; } | 100 | static inline bool qcom_scm_is_available(void) { return false; } |
| 105 | static inline bool qcom_scm_hdcp_available(void) { return false; } | 101 | |
| 106 | static inline int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, | 102 | static inline int qcom_scm_set_cold_boot_addr(void *entry, |
| 107 | u32 *resp) { return -ENODEV; } | 103 | const cpumask_t *cpus) { return -ENODEV; } |
| 108 | static inline bool qcom_scm_pas_supported(u32 peripheral) { return false; } | 104 | static inline int qcom_scm_set_warm_boot_addr(void *entry, |
| 105 | const cpumask_t *cpus) { return -ENODEV; } | ||
| 106 | static inline void qcom_scm_cpu_power_down(u32 flags) {} | ||
| 107 | static inline u32 qcom_scm_set_remote_state(u32 state,u32 id) | ||
| 108 | { return -ENODEV; } | ||
| 109 | |||
| 109 | static inline int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, | 110 | static inline int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, |
| 110 | size_t size) { return -ENODEV; } | 111 | size_t size) { return -ENODEV; } |
| 111 | static inline int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, | 112 | static inline int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, |
| 112 | phys_addr_t size) { return -ENODEV; } | 113 | phys_addr_t size) { return -ENODEV; } |
| 113 | static inline int | 114 | static inline int qcom_scm_pas_auth_and_reset(u32 peripheral) |
| 114 | qcom_scm_pas_auth_and_reset(u32 peripheral) { return -ENODEV; } | 115 | { return -ENODEV; } |
| 115 | static inline int qcom_scm_pas_shutdown(u32 peripheral) { return -ENODEV; } | 116 | static inline int qcom_scm_pas_shutdown(u32 peripheral) { return -ENODEV; } |
| 117 | static inline bool qcom_scm_pas_supported(u32 peripheral) { return false; } | ||
| 118 | |||
| 119 | static inline int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) | ||
| 120 | { return -ENODEV; } | ||
| 121 | static inline int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) | ||
| 122 | { return -ENODEV; } | ||
| 123 | |||
| 124 | static inline bool qcom_scm_restore_sec_cfg_available(void) { return false; } | ||
| 125 | static inline int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) | ||
| 126 | { return -ENODEV; } | ||
| 127 | static inline int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) | ||
| 128 | { return -ENODEV; } | ||
| 129 | static inline int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) | ||
| 130 | { return -ENODEV; } | ||
| 116 | static inline int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, | 131 | static inline int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, |
| 117 | unsigned int *src, | 132 | unsigned int *src, const struct qcom_scm_vmperm *newvm, |
| 118 | const struct qcom_scm_vmperm *newvm, | 133 | unsigned int dest_cnt) { return -ENODEV; } |
| 119 | unsigned int dest_cnt) { return -ENODEV; } | 134 | |
| 120 | static inline void qcom_scm_cpu_power_down(u32 flags) {} | 135 | static inline bool qcom_scm_ocmem_lock_available(void) { return false; } |
| 121 | static inline u32 qcom_scm_get_version(void) { return 0; } | 136 | static inline int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, |
| 122 | static inline u32 | 137 | u32 size, u32 mode) { return -ENODEV; } |
| 123 | qcom_scm_set_remote_state(u32 state,u32 id) { return -ENODEV; } | 138 | static inline int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, |
| 124 | static inline int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) { return -ENODEV; } | 139 | u32 offset, u32 size) { return -ENODEV; } |
| 125 | static inline int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) { return -ENODEV; } | 140 | |
| 126 | static inline int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) { return -ENODEV; } | 141 | static inline bool qcom_scm_hdcp_available(void) { return false; } |
| 127 | static inline int qcom_scm_qsmmu500_wait_safe_toggle(bool en) { return -ENODEV; } | 142 | static inline int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, |
| 128 | static inline int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) { return -ENODEV; } | 143 | u32 *resp) { return -ENODEV; } |
| 129 | static inline int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) { return -ENODEV; } | 144 | |
| 145 | static inline int qcom_scm_qsmmu500_wait_safe_toggle(bool en) | ||
| 146 | { return -ENODEV; } | ||
| 130 | #endif | 147 | #endif |
| 131 | #endif | 148 | #endif |