summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/debugfs-driver-dcc127
-rw-r--r--Documentation/devicetree/bindings/firmware/qcom,scm.yaml64
-rw-r--r--Documentation/devicetree/bindings/mfd/qcom-rpm.txt283
-rw-r--r--Documentation/devicetree/bindings/power/qcom,rpmpd.yaml2
-rw-r--r--Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml6
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml5
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml44
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,msm8976-ramp-controller.yaml36
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,rpm.yaml101
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml1
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/firmware/qcom_scm-smc.c86
-rw-r--r--drivers/firmware/qcom_scm.c90
-rw-r--r--drivers/firmware/qcom_scm.h8
-rw-r--r--drivers/soc/qcom/Kconfig17
-rw-r--r--drivers/soc/qcom/Makefile2
-rw-r--r--drivers/soc/qcom/apr.c3
-rw-r--r--drivers/soc/qcom/cpr.c6
-rw-r--r--drivers/soc/qcom/dcc.c1300
-rw-r--r--drivers/soc/qcom/ramp_controller.c343
-rw-r--r--drivers/soc/qcom/rmtfs_mem.c29
-rw-r--r--drivers/soc/qcom/rpmhpd.c34
-rw-r--r--drivers/soc/qcom/rpmpd.c18
-rw-r--r--drivers/soc/qcom/socinfo.c96
-rw-r--r--include/dt-bindings/arm/qcom,ids.h75
-rw-r--r--include/dt-bindings/firmware/qcom,scm.h16
-rw-r--r--include/dt-bindings/power/qcom-rpmpd.h29
-rw-r--r--include/linux/qcom_scm.h6
-rw-r--r--include/linux/soc/qcom/apr.h2
-rw-r--r--sound/soc/qcom/qdsp6/q6core.c4
30 files changed, 2489 insertions, 352 deletions
diff --git a/Documentation/ABI/testing/debugfs-driver-dcc b/Documentation/ABI/testing/debugfs-driver-dcc
new file mode 100644
index 000000000000..27ed5919d21b
--- /dev/null
+++ b/Documentation/ABI/testing/debugfs-driver-dcc
@@ -0,0 +1,127 @@
+What: /sys/kernel/debug/dcc/.../ready
+Date: December 2022
+Contact: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
+Description:
+ This file is used to check the status of the dcc
+ hardware if it's ready to receive user configurations.
+ A 'Y' here indicates dcc is ready.
+
+What: /sys/kernel/debug/dcc/.../trigger
+Date: December 2022
+Contact: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
+Description:
+ This is the debugfs interface for manual software
+ triggers. The trigger can be invoked by writing '1'
+ to the file.
+
+What: /sys/kernel/debug/dcc/.../config_reset
+Date: December 2022
+Contact: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
+Description:
+ This file is used to reset the configuration of
+ a dcc driver to the default configuration. When '1'
+ is written to the file, all the previous addresses
+ stored in the driver gets removed and users need to
+ reconfigure addresses again.
+
+What: /sys/kernel/debug/dcc/.../[list-number]/config
+Date: December 2022
+Contact: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
+Description:
+ This stores the addresses of the registers which
+ can be read in case of a hardware crash or manual
+ software triggers. The input addresses type
+ can be one of following dcc instructions: read,
+ write, read-write, and loop type. The lists need to
+ be configured sequentially and not in a overlapping
+ manner; e.g. users can jump to list x only after
+ list y is configured and enabled. The input format for
+ each type is as follows:
+
+ i) Read instruction
+
+ ::
+
+ echo R <addr> <n> <bus> >/sys/kernel/debug/dcc/../[list-number]/config
+
+ where:
+
+ <addr>
+ The address to be read.
+
+ <n>
+ The addresses word count, starting from address <1>.
+ Each word is 32 bits (4 bytes). If omitted, defaulted
+ to 1.
+
+ <bus type>
+ The bus type, which can be either 'apb' or 'ahb'.
+ The default is 'ahb' if leaved out.
+
+ ii) Write instruction
+
+ ::
+
+ echo W <addr> <n> <bus type> > /sys/kernel/debug/dcc/../[list-number]/config
+
+ where:
+
+ <addr>
+ The address to be written.
+
+ <n>
+ The value to be written at <addr>.
+
+ <bus type>
+ The bus type, which can be either 'apb' or 'ahb'.
+
+ iii) Read-write instruction
+
+ ::
+
+ echo RW <addr> <n> <mask> > /sys/kernel/debug/dcc/../[list-number]/config
+
+ where:
+
+ <addr>
+ The address to be read and written.
+
+ <n>
+ The value to be written at <addr>.
+
+ <mask>
+ The value mask.
+
+ iv) Loop instruction
+
+ ::
+
+ echo L <loop count> <address count> <address>... > /sys/kernel/debug/dcc/../[list-number]/config
+
+ where:
+
+ <loop count>
+ Number of iterations
+
+ <address count>
+ total number of addresses to be written
+
+ <address>
+ Space-separated list of addresses.
+
+What: /sys/kernel/debug/dcc/.../[list-number]/enable
+Date: December 2022
+Contact: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
+Description:
+ This debugfs interface is used for enabling the
+ the dcc hardware. A file named "enable" is in the
+ directory list number where users can enable/disable
+ the specific list by writing boolean (1 or 0) to the
+ file.
+
+ On enabling the dcc, all the addresses specified
+ by the user for the corresponding list is written
+ into dcc sram which is read by the dcc hardware
+ on manual or crash induced triggers. Lists must
+ be configured and enabled sequentially, e.g. list
+ 2 can only be enabled when list 1 have so.
diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
index 25688571ee7c..4193492ba73e 100644
--- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
+++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
@@ -38,6 +38,7 @@ properties:
- qcom,scm-msm8994
- qcom,scm-msm8996
- qcom,scm-msm8998
+ - qcom,scm-qdu1000
- qcom,scm-sc7180
- qcom,scm-sc7280
- qcom,scm-sc8280xp
@@ -73,6 +74,12 @@ properties:
'#reset-cells':
const: 1
+ interrupts:
+ description:
+ The wait-queue interrupt that firmware raises as part of handshake
+ protocol to handle sleeping SCM calls.
+ maxItems: 1
+
qcom,dload-mode:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
@@ -82,6 +89,32 @@ properties:
description: TCSR hardware block
allOf:
+ # Clocks
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,scm-apq8064
+ - qcom,scm-apq8084
+ - qcom,scm-mdm9607
+ - qcom,scm-msm8226
+ - qcom,scm-msm8660
+ - qcom,scm-msm8916
+ - qcom,scm-msm8953
+ - qcom,scm-msm8960
+ - qcom,scm-msm8974
+ - qcom,scm-msm8976
+ - qcom,scm-sm6375
+ then:
+ required:
+ - clocks
+ - clock-names
+ else:
+ properties:
+ clock-names: false
+ clocks: false
+
- if:
properties:
compatible:
@@ -100,10 +133,6 @@ allOf:
clocks:
maxItems: 1
- required:
- - clocks
- - clock-names
-
- if:
properties:
compatible:
@@ -111,6 +140,7 @@ allOf:
enum:
- qcom,scm-apq8084
- qcom,scm-mdm9607
+ - qcom,scm-msm8226
- qcom,scm-msm8916
- qcom,scm-msm8953
- qcom,scm-msm8974
@@ -127,9 +157,29 @@ allOf:
minItems: 3
maxItems: 3
- required:
- - clocks
- - clock-names
+ # Interconnects
+ - if:
+ not:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,scm-sm8450
+ then:
+ properties:
+ interconnects: false
+
+ # Interrupts
+ - if:
+ not:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,scm-sm8450
+ then:
+ properties:
+ interrupts: false
required:
- compatible
diff --git a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
deleted file mode 100644
index b823b8625243..000000000000
--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
+++ /dev/null
@@ -1,283 +0,0 @@
-Qualcomm Resource Power Manager (RPM)
-
-This driver is used to interface with the Resource Power Manager (RPM) found in
-various Qualcomm platforms. The RPM allows each component in the system to vote
-for state of the system resources, such as clocks, regulators and bus
-frequencies.
-
-- compatible:
- Usage: required
- Value type: <string>
- Definition: must be one of:
- "qcom,rpm-apq8064"
- "qcom,rpm-msm8660"
- "qcom,rpm-msm8960"
- "qcom,rpm-ipq8064"
- "qcom,rpm-mdm9615"
-
-- reg:
- Usage: required
- Value type: <prop-encoded-array>
- Definition: base address and size of the RPM's message ram
-
-- interrupts:
- Usage: required
- Value type: <prop-encoded-array>
- Definition: three entries specifying the RPM's:
- 1. acknowledgement interrupt
- 2. error interrupt
- 3. wakeup interrupt
-
-- interrupt-names:
- Usage: required
- Value type: <string-array>
- Definition: must be the three strings "ack", "err" and "wakeup", in order
-
-- qcom,ipc:
- Usage: required
- Value type: <prop-encoded-array>
-
- Definition: three entries specifying the outgoing ipc bit used for
- signaling the RPM:
- - phandle to a syscon node representing the apcs registers
- - u32 representing offset to the register within the syscon
- - u32 representing the ipc bit within the register
-
-
-= SUBNODES
-
-The RPM exposes resources to its subnodes. The below bindings specify the set
-of valid subnodes that can operate on these resources.
-
-== Regulators
-
-Regulator nodes are identified by their compatible:
-
-- compatible:
- Usage: required
- Value type: <string>
- Definition: must be one of:
- "qcom,rpm-pm8058-regulators"
- "qcom,rpm-pm8901-regulators"
- "qcom,rpm-pm8921-regulators"
- "qcom,rpm-pm8018-regulators"
- "qcom,rpm-smb208-regulators"
-
-- vdd_l0_l1_lvs-supply:
-- vdd_l2_l11_l12-supply:
-- vdd_l3_l4_l5-supply:
-- vdd_l6_l7-supply:
-- vdd_l8-supply:
-- vdd_l9-supply:
-- vdd_l10-supply:
-- vdd_l13_l16-supply:
-- vdd_l14_l15-supply:
-- vdd_l17_l18-supply:
-- vdd_l19_l20-supply:
-- vdd_l21-supply:
-- vdd_l22-supply:
-- vdd_l23_l24_l25-supply:
-- vdd_ncp-supply:
-- vdd_s0-supply:
-- vdd_s1-supply:
-- vdd_s2-supply:
-- vdd_s3-supply:
-- vdd_s4-supply:
- Usage: optional (pm8058 only)
- Value type: <phandle>
- Definition: reference to regulator supplying the input pin, as
- described in the data sheet
-
-- lvs0_in-supply:
-- lvs1_in-supply:
-- lvs2_in-supply:
-- lvs3_in-supply:
-- mvs_in-supply:
-- vdd_l0-supply:
-- vdd_l1-supply:
-- vdd_l2-supply:
-- vdd_l3-supply:
-- vdd_l4-supply:
-- vdd_l5-supply:
-- vdd_l6-supply:
-- vdd_s0-supply:
-- vdd_s1-supply:
-- vdd_s2-supply:
-- vdd_s3-supply:
-- vdd_s4-supply:
- Usage: optional (pm8901 only)
- Value type: <phandle>
- Definition: reference to regulator supplying the input pin, as
- described in the data sheet
-
-- vdd_l1_l2_l12_l18-supply:
-- vdd_l3_l15_l17-supply:
-- vdd_l4_l14-supply:
-- vdd_l5_l8_l16-supply:
-- vdd_l6_l7-supply:
-- vdd_l9_l11-supply:
-- vdd_l10_l22-supply:
-- vdd_l21_l23_l29-supply:
-- vdd_l24-supply:
-- vdd_l25-supply:
-- vdd_l26-supply:
-- vdd_l27-supply:
-- vdd_l28-supply:
-- vdd_ncp-supply:
-- vdd_s1-supply:
-- vdd_s2-supply:
-- vdd_s4-supply:
-- vdd_s5-supply:
-- vdd_s6-supply:
-- vdd_s7-supply:
-- vdd_s8-supply:
-- vin_5vs-supply:
-- vin_lvs1_3_6-supply:
-- vin_lvs2-supply:
-- vin_lvs4_5_7-supply:
- Usage: optional (pm8921 only)
- Value type: <phandle>
- Definition: reference to regulator supplying the input pin, as
- described in the data sheet
-
-- vin_lvs1-supply:
-- vdd_l7-supply:
-- vdd_l8-supply:
-- vdd_l9_l10_l11_l12-supply:
- Usage: optional (pm8018 only)
- Value type: <phandle>
- Definition: reference to regulator supplying the input pin, as
- described in the data sheet
-
-The regulator node houses sub-nodes for each regulator within the device. Each
-sub-node is identified using the node's name, with valid values listed for each
-of the pmics below.
-
-pm8058:
- l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15,
- l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, s0, s1, s2, s3, s4,
- lvs0, lvs1, ncp
-
-pm8901:
- l0, l1, l2, l3, l4, l5, l6, s0, s1, s2, s3, s4, lvs0, lvs1, lvs2, lvs3,
- mvs
-
-pm8921:
- s1, s2, s3, s4, s7, s8, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11,
- l12, l14, l15, l16, l17, l18, l21, l22, l23, l24, l25, l26, l27, l28,
- l29, lvs1, lvs2, lvs3, lvs4, lvs5, lvs6, lvs7, usb-switch, hdmi-switch,
- ncp
-
-pm8018:
- s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11,
- l12, l14, lvs1
-
-smb208:
- s1a, s1b, s2a, s2b
-
-The content of each sub-node is defined by the standard binding for regulators -
-see regulator.txt - with additional custom properties described below:
-
-=== Switch-mode Power Supply regulator custom properties
-
-- bias-pull-down:
- Usage: optional
- Value type: <empty>
- Definition: enable pull down of the regulator when inactive
-
-- qcom,switch-mode-frequency:
- Usage: required
- Value type: <u32>
- Definition: Frequency (Hz) of the switch-mode power supply;
- must be one of:
- 19200000, 9600000, 6400000, 4800000, 3840000, 3200000,
- 2740000, 2400000, 2130000, 1920000, 1750000, 1600000,
- 1480000, 1370000, 1280000, 1200000
-
-- qcom,force-mode:
- Usage: optional (default if no other qcom,force-mode is specified)
- Value type: <u32>
- Definition: indicates that the regulator should be forced to a
- particular mode, valid values are:
- QCOM_RPM_FORCE_MODE_NONE - do not force any mode
- QCOM_RPM_FORCE_MODE_LPM - force into low power mode
- QCOM_RPM_FORCE_MODE_HPM - force into high power mode
- QCOM_RPM_FORCE_MODE_AUTO - allow regulator to automatically
- select its own mode based on
- realtime current draw, only for:
- pm8921 smps and ftsmps
-
-- qcom,power-mode-hysteretic:
- Usage: optional
- Value type: <empty>
- Definition: select that the power supply should operate in hysteretic
- mode, instead of the default pwm mode
-
-=== Low-dropout regulator custom properties
-
-- bias-pull-down:
- Usage: optional
- Value type: <empty>
- Definition: enable pull down of the regulator when inactive
-
-- qcom,force-mode:
- Usage: optional
- Value type: <u32>
- Definition: indicates that the regulator should not be forced to any
- particular mode, valid values are:
- QCOM_RPM_FORCE_MODE_NONE - do not force any mode
- QCOM_RPM_FORCE_MODE_LPM - force into low power mode
- QCOM_RPM_FORCE_MODE_HPM - force into high power mode
- QCOM_RPM_FORCE_MODE_BYPASS - set regulator to use bypass
- mode, i.e. to act as a switch
- and not regulate, only for:
- pm8921 pldo, nldo and nldo1200
-
-=== Negative Charge Pump custom properties
-
-- qcom,switch-mode-frequency:
- Usage: required
- Value type: <u32>
- Definition: Frequency (Hz) of the switch mode power supply;
- must be one of:
- 19200000, 9600000, 6400000, 4800000, 3840000, 3200000,
- 2740000, 2400000, 2130000, 1920000, 1750000, 1600000,
- 1480000, 1370000, 1280000, 1200000
-
-= EXAMPLE
-
- #include <dt-bindings/mfd/qcom-rpm.h>
-
- rpm@108000 {
- compatible = "qcom,rpm-msm8960";
- reg = <0x108000 0x1000>;
- qcom,ipc = <&apcs 0x8 2>;
-
- interrupts = <0 19 0>, <0 21 0>, <0 22 0>;
- interrupt-names = "ack", "err", "wakeup";
-
- regulators {
- compatible = "qcom,rpm-pm8921-regulators";
- vdd_l1_l2_l12_l18-supply = <&pm8921_s4>;
-
- s1 {
- regulator-min-microvolt = <1225000>;
- regulator-max-microvolt = <1225000>;
-
- bias-pull-down;
-
- qcom,switch-mode-frequency = <3200000>;
- };
-
- pm8921_s4: s4 {
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
-
- qcom,switch-mode-frequency = <1600000>;
- bias-pull-down;
-
- qcom,force-mode = <QCOM_RPM_FORCE_MODE_AUTO>;
- };
- };
- };
-
diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
index 633d49884019..afad3135ed67 100644
--- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
+++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
@@ -30,6 +30,7 @@ properties:
- qcom,qcs404-rpmpd
- qcom,qdu1000-rpmhpd
- qcom,sa8540p-rpmhpd
+ - qcom,sa8775p-rpmhpd
- qcom,sdm660-rpmpd
- qcom,sc7180-rpmhpd
- qcom,sc7280-rpmhpd
@@ -39,7 +40,6 @@ properties:
- qcom,sdm845-rpmhpd
- qcom,sdx55-rpmhpd
- qcom,sdx65-rpmhpd
- - qcom,sm4250-rpmpd
- qcom,sm6115-rpmpd
- qcom,sm6125-rpmpd
- qcom,sm6350-rpmhpd
diff --git a/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml b/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml
index 2998f1c8f0db..08eb10c25821 100644
--- a/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml
+++ b/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml
@@ -27,9 +27,11 @@ properties:
identifier of the client to use this region for buffers
qcom,vmid:
- $ref: /schemas/types.yaml#/definitions/uint32
+ $ref: /schemas/types.yaml#/definitions/uint32-array
description: >
- vmid of the remote processor, to set up memory protection
+ Array of vmids of the remote processors, to set up memory protection
+ minItems: 1
+ maxItems: 2
required:
- qcom,client-id
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml
index 290555426c39..bdf482db32aa 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml
@@ -39,8 +39,8 @@ properties:
qcom,protection-domain:
$ref: /schemas/types.yaml#/definitions/string-array
description: |
- Protection domain service name and path for APR service
- possible values are::
+ Protection domain service name and path for APR service (if supported).
+ Possible values are::
"avs/audio", "msm/adsp/audio_pd".
"kernel/elf_loader", "msm/modem/wlan_pd".
"tms/servreg", "msm/adsp/audio_pd".
@@ -49,6 +49,5 @@ properties:
required:
- reg
- - qcom,protection-domain
additionalProperties: true
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml
new file mode 100644
index 000000000000..ce7e20dd22c9
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/qcom/qcom,dcc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Data Capture and Compare
+
+maintainers:
+ - Souradeep Chowdhury <quic_schowdhu@quicinc.com>
+
+description: |
+ DCC (Data Capture and Compare) is a DMA engine which is used to save
+ configuration data or system memory contents during catastrophic failure
+ or SW trigger. DCC is used to capture and store data for debugging purpose
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - qcom,sm8150-dcc
+ - qcom,sc7280-dcc
+ - qcom,sc7180-dcc
+ - qcom,sdm845-dcc
+ - const: qcom,dcc
+
+ reg:
+ items:
+ - description: DCC base
+ - description: DCC RAM base
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ dma@10a2000{
+ compatible = "qcom,sm8150-dcc", "qcom,dcc";
+ reg = <0x010a2000 0x1000>,
+ <0x010ad000 0x2000>;
+ };
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,msm8976-ramp-controller.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,msm8976-ramp-controller.yaml
new file mode 100644
index 000000000000..aae9cf7b8caf
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,msm8976-ramp-controller.yaml
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/qcom/qcom,msm8976-ramp-controller.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Ramp Controller
+
+maintainers:
+ - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+description:
+ The Ramp Controller is used to program the sequence ID for pulse
+ swallowing, enable sequences and link Sequence IDs (SIDs) for the
+ CPU cores on some Qualcomm SoCs.
+
+properties:
+ compatible:
+ enum:
+ - qcom,msm8976-ramp-controller
+
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ cpu-power-controller@b014000 {
+ compatible = "qcom,msm8976-ramp-controller";
+ reg = <0x0b014000 0x68>;
+ };
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,rpm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,rpm.yaml
new file mode 100644
index 000000000000..b00be9e01206
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,rpm.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/qcom/qcom,rpm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Resource Power Manager (RPM)
+
+description:
+ This driver is used to interface with the Resource Power Manager (RPM) found
+ in various Qualcomm platforms. The RPM allows each component in the system
+ to vote for state of the system resources, such as clocks, regulators and bus
+ frequencies.
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - qcom,rpm-apq8064
+ - qcom,rpm-msm8660
+ - qcom,rpm-msm8960
+ - qcom,rpm-ipq8064
+ - qcom,rpm-mdm9615
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 3
+
+ interrupt-names:
+ items:
+ - const: ack
+ - const: err
+ - const: wakeup
+
+ qcom,ipc:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - items:
+ - description: phandle to a syscon node representing the APCS registers
+ - description: u32 representing offset to the register within the syscon
+ - description: u32 representing the ipc bit within the register
+ description:
+ Three entries specifying the outgoing ipc bit used for signaling the RPM.
+
+patternProperties:
+ "^regulators(-[01])?$":
+ type: object
+ $ref: /schemas/regulator/qcom,rpm-regulator.yaml#
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-names
+ - qcom,ipc
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/mfd/qcom-rpm.h>
+
+ rpm@108000 {
+ compatible = "qcom,rpm-msm8960";
+ reg = <0x108000 0x1000>;
+ qcom,ipc = <&apcs 0x8 2>;
+
+ interrupts = <GIC_SPI 19 IRQ_TYPE_NONE>, <GIC_SPI 21 IRQ_TYPE_NONE>, <GIC_SPI 22 IRQ_TYPE_NONE>;
+ interrupt-names = "ack", "err", "wakeup";
+
+ regulators {
+ compatible = "qcom,rpm-pm8921-regulators";
+ vdd_l1_l2_l12_l18-supply = <&pm8921_s4>;
+
+ s1 {
+ regulator-min-microvolt = <1225000>;
+ regulator-max-microvolt = <1225000>;
+
+ bias-pull-down;
+
+ qcom,switch-mode-frequency = <3200000>;
+ };
+
+ pm8921_s4: s4 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+
+ qcom,switch-mode-frequency = <1600000>;
+ bias-pull-down;
+
+ qcom,force-mode = <QCOM_RPM_FORCE_MODE_AUTO>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml
index 11c0f4dd797c..16fd67c0bd1f 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml
@@ -80,6 +80,7 @@ if:
enum:
- qcom,rpm-apq8084
- qcom,rpm-msm8916
+ - qcom,rpm-msm8936
- qcom,rpm-msm8974
- qcom,rpm-msm8976
- qcom,rpm-msm8953
diff --git a/MAINTAINERS b/MAINTAINERS
index 79e0a1c71bc2..5b49887c1dc7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5821,6 +5821,14 @@ W: http://lists.twibble.org/mailman/listinfo/dc395x/
F: Documentation/scsi/dc395x.rst
F: drivers/scsi/dc395x.*
+DCC QTI DRIVER
+M: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
+L: linux-arm-msm@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/debugfs-driver-dcc
+F: Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml
+F: drivers/soc/qcom/dcc.c
+
DCCP PROTOCOL
L: dccp@vger.kernel.org
S: Orphan
diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c
index d111833364ba..bb3235a64b8f 100644
--- a/drivers/firmware/qcom_scm-smc.c
+++ b/drivers/firmware/qcom_scm-smc.c
@@ -52,29 +52,97 @@ static void __scm_smc_do_quirk(const struct arm_smccc_args *smc,
} while (res->a0 == QCOM_SCM_INTERRUPTED);
}
-static void __scm_smc_do(const struct arm_smccc_args *smc,
- struct arm_smccc_res *res, bool atomic)
+static void fill_wq_resume_args(struct arm_smccc_args *resume, u32 smc_call_ctx)
{
- int retry_count = 0;
+ memset(resume->args, 0, sizeof(resume->args[0]) * ARRAY_SIZE(resume->args));
+
+ resume->args[0] = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL,
+ ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_SIP,
+ SCM_SMC_FNID(QCOM_SCM_SVC_WAITQ, QCOM_SCM_WAITQ_RESUME));
+
+ resume->args[1] = QCOM_SCM_ARGS(1);
+
+ resume->args[2] = smc_call_ctx;
+}
+
+int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending)
+{
+ int ret;
+ struct arm_smccc_res get_wq_res;
+ struct arm_smccc_args get_wq_ctx = {0};
+
+ get_wq_ctx.args[0] = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL,
+ ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_SIP,
+ SCM_SMC_FNID(QCOM_SCM_SVC_WAITQ, QCOM_SCM_WAITQ_GET_WQ_CTX));
+
+ /* Guaranteed to return only success or error, no WAITQ_* */
+ __scm_smc_do_quirk(&get_wq_ctx, &get_wq_res);
+ ret = get_wq_res.a0;
+ if (ret)
+ return ret;
+
+ *wq_ctx = get_wq_res.a1;
+ *flags = get_wq_res.a2;
+ *more_pending = get_wq_res.a3;
+
+ return 0;
+}
+
+static int __scm_smc_do_quirk_handle_waitq(struct device *dev, struct arm_smccc_args *waitq,
+ struct arm_smccc_res *res)
+{
+ int ret;
+ u32 wq_ctx, smc_call_ctx;
+ struct arm_smccc_args resume;
+ struct arm_smccc_args *smc = waitq;
+
+ do {
+ __scm_smc_do_quirk(smc, res);
+
+ if (res->a0 == QCOM_SCM_WAITQ_SLEEP) {
+ wq_ctx = res->a1;
+ smc_call_ctx = res->a2;
+
+ ret = qcom_scm_wait_for_wq_completion(wq_ctx);
+ if (ret)
+ return ret;
+
+ fill_wq_resume_args(&resume, smc_call_ctx);
+ smc = &resume;
+ }
+ } while (res->a0 == QCOM_SCM_WAITQ_SLEEP);
+
+ return 0;
+}
+
+static int __scm_smc_do(struct device *dev, struct arm_smccc_args *smc,
+ struct arm_smccc_res *res, bool atomic)
+{
+ int ret, retry_count = 0;
if (atomic) {
__scm_smc_do_quirk(smc, res);
- return;
+ return 0;
}
do {
mutex_lock(&qcom_scm_lock);
- __scm_smc_do_quirk(smc, res);
+ ret = __scm_smc_do_quirk_handle_waitq(dev, smc, res);
mutex_unlock(&qcom_scm_lock);
+ if (ret)
+ return ret;
+
if (res->a0 == QCOM_SCM_V2_EBUSY) {
if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
break;
msleep(QCOM_SCM_EBUSY_WAIT_MS);
}
} while (res->a0 == QCOM_SCM_V2_EBUSY);
+
+ return 0;
}
@@ -83,7 +151,7 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
struct qcom_scm_res *res, bool atomic)
{
int arglen = desc->arginfo & 0xf;
- int i;
+ int i, ret;
dma_addr_t args_phys = 0;
void *args_virt = NULL;
size_t alloc_len;
@@ -135,13 +203,17 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
smc.args[SCM_SMC_LAST_REG_IDX] = args_phys;
}
- __scm_smc_do(&smc, &smc_res, atomic);
+ /* ret error check follows after args_virt cleanup*/
+ ret = __scm_smc_do(dev, &smc, &smc_res, atomic);
if (args_virt) {
dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE);
kfree(args_virt);
}
+ if (ret)
+ return ret;
+
if (res) {
res->result[0] = smc_res.a1;
res->result[1] = smc_res.a2;
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index cdbfe54c8146..2000323722bf 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -4,6 +4,8 @@
*/
#include <linux/platform_device.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
#include <linux/cpumask.h>
#include <linux/export.h>
#include <linux/dma-mapping.h>
@@ -13,6 +15,7 @@
#include <linux/qcom_scm.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
#include <linux/reset-controller.h>
@@ -33,6 +36,7 @@ struct qcom_scm {
struct clk *iface_clk;
struct clk *bus_clk;
struct icc_path *path;
+ struct completion waitq_comp;
struct reset_controller_dev reset;
/* control access to the interconnect path */
@@ -63,6 +67,9 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
BIT(2), BIT(1), BIT(4), BIT(6)
};
+#define QCOM_SMC_WAITQ_FLAG_WAKE_ONE BIT(0)
+#define QCOM_SMC_WAITQ_FLAG_WAKE_ALL BIT(1)
+
static const char * const qcom_scm_convention_names[] = {
[SMC_CONVENTION_UNKNOWN] = "unknown",
[SMC_CONVENTION_ARM_32] = "smc arm 32",
@@ -1325,11 +1332,79 @@ bool qcom_scm_is_available(void)
}
EXPORT_SYMBOL(qcom_scm_is_available);
+static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx)
+{
+ /* FW currently only supports a single wq_ctx (zero).
+ * TODO: Update this logic to include dynamic allocation and lookup of
+ * completion structs when FW supports more wq_ctx values.
+ */
+ if (wq_ctx != 0) {
+ dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
+{
+ int ret;
+
+ ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
+ if (ret)
+ return ret;
+
+ wait_for_completion(&__scm->waitq_comp);
+
+ return 0;
+}
+
+static int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx)
+{
+ int ret;
+
+ ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
+ if (ret)
+ return ret;
+
+ complete(&__scm->waitq_comp);
+
+ return 0;
+}
+
+static irqreturn_t qcom_scm_irq_handler(int irq, void *data)
+{
+ int ret;
+ struct qcom_scm *scm = data;
+ u32 wq_ctx, flags, more_pending = 0;
+
+ do {
+ ret = scm_get_wq_ctx(&wq_ctx, &flags, &more_pending);
+ if (ret) {
+ dev_err(scm->dev, "GET_WQ_CTX SMC call failed: %d\n", ret);
+ goto out;
+ }
+
+ if (flags != QCOM_SMC_WAITQ_FLAG_WAKE_ONE &&
+ flags != QCOM_SMC_WAITQ_FLAG_WAKE_ALL) {
+ dev_err(scm->dev, "Invalid flags found for wq_ctx: %u\n", flags);
+ goto out;
+ }
+
+ ret = qcom_scm_waitq_wakeup(scm, wq_ctx);
+ if (ret)
+ goto out;
+ } while (more_pending);
+
+out:
+ return IRQ_HANDLED;
+}
+
static int qcom_scm_probe(struct platform_device *pdev)
{
struct qcom_scm *scm;
unsigned long clks;
- int ret;
+ int irq, ret;
scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
if (!scm)
@@ -1402,6 +1477,19 @@ static int qcom_scm_probe(struct platform_device *pdev)
__scm = scm;
__scm->dev = &pdev->dev;
+ init_completion(&__scm->waitq_comp);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ if (irq != -ENXIO)
+ return irq;
+ } else {
+ ret = devm_request_threaded_irq(__scm->dev, irq, NULL, qcom_scm_irq_handler,
+ IRQF_ONESHOT, "qcom-scm", __scm);
+ if (ret < 0)
+ return dev_err_probe(scm->dev, ret, "Failed to request qcom-scm irq\n");
+ }
+
__get_convention();
/*
diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h
index db3d08a01209..e6e512bd57d1 100644
--- a/drivers/firmware/qcom_scm.h
+++ b/drivers/firmware/qcom_scm.h
@@ -60,6 +60,9 @@ struct qcom_scm_res {
u64 result[MAX_QCOM_SCM_RETS];
};
+int qcom_scm_wait_for_wq_completion(u32 wq_ctx);
+int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending);
+
#define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))
extern int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
enum qcom_scm_convention qcom_convention,
@@ -129,6 +132,10 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
#define QCOM_SCM_SMMU_CONFIG_ERRATA1 0x03
#define QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL 0x02
+#define QCOM_SCM_SVC_WAITQ 0x24
+#define QCOM_SCM_WAITQ_RESUME 0x02
+#define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03
+
/* common error codes */
#define QCOM_SCM_V2_EBUSY -12
#define QCOM_SCM_ENOMEM -5
@@ -137,6 +144,7 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
#define QCOM_SCM_EINVAL_ARG -2
#define QCOM_SCM_ERROR -1
#define QCOM_SCM_INTERRUPTED 1
+#define QCOM_SCM_WAITQ_SLEEP 2
static inline int qcom_scm_remap_error(int err)
{
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index ae504c43d9e7..21c4ce2315ba 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -70,6 +70,14 @@ config QCOM_LLCC
SDM845. This provides interfaces to clients that use the LLCC.
Say yes here to enable LLCC slice driver.
+config QCOM_DCC
+ tristate "Qualcomm Technologies, Inc. Data Capture and Compare(DCC) engine driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ help
+ This option enables driver for Data Capture and Compare engine. DCC
+ driver provides interface to configure DCC block and read back
+ captured data from DCC's internal SRAM.
+
config QCOM_KRYO_L2_ACCESSORS
bool
depends on ARCH_QCOM && ARM64 || COMPILE_TEST
@@ -96,6 +104,15 @@ config QCOM_QMI_HELPERS
tristate
depends on NET
+config QCOM_RAMP_CTRL
+ tristate "Qualcomm Ramp Controller driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ help
+ The Ramp Controller is used to program the sequence ID for pulse
+ swallowing, enable sequence and link sequence IDs for the CPU
+ cores on some Qualcomm SoCs.
+ Say y here to enable support for the ramp controller.
+
config QCOM_RMTFS_MEM
tristate "Qualcomm Remote Filesystem memory driver"
depends on ARCH_QCOM
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index d66604aff2b0..3b92c6c8e0ec 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -4,12 +4,14 @@ obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o
obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
obj-$(CONFIG_QCOM_CPR) += cpr.o
+obj-$(CONFIG_QCOM_DCC) += dcc.o
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
obj-$(CONFIG_QCOM_OCMEM) += ocmem.o
obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o
obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o
qmi_helpers-y += qmi_encdec.o qmi_interface.o
+obj-$(CONFIG_QCOM_RAMP_CTRL) += ramp_controller.o
obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o
obj-$(CONFIG_QCOM_RPMH) += qcom_rpmh.o
qcom_rpmh-y += rpmh-rsc.o
diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c
index cd44f17dad3d..d51abb462ae5 100644
--- a/drivers/soc/qcom/apr.c
+++ b/drivers/soc/qcom/apr.c
@@ -461,9 +461,10 @@ static int apr_add_device(struct device *dev, struct device_node *np,
goto out;
}
+ /* Protection domain is optional, it does not exist on older platforms */
ret = of_property_read_string_index(np, "qcom,protection-domain",
1, &adev->service_path);
- if (ret < 0) {
+ if (ret < 0 && ret != -EINVAL) {
dev_err(dev, "Failed to read second value of qcom,protection-domain\n");
goto out;
}
diff --git a/drivers/soc/qcom/cpr.c b/drivers/soc/qcom/cpr.c
index e9b854ed1bdf..144ea68e0920 100644
--- a/drivers/soc/qcom/cpr.c
+++ b/drivers/soc/qcom/cpr.c
@@ -1708,12 +1708,16 @@ static int cpr_probe(struct platform_device *pdev)
ret = of_genpd_add_provider_simple(dev->of_node, &drv->pd);
if (ret)
- return ret;
+ goto err_remove_genpd;
platform_set_drvdata(pdev, drv);
cpr_debugfs_init(drv);
return 0;
+
+err_remove_genpd:
+ pm_genpd_remove(&drv->pd);
+ return ret;
}
static int cpr_remove(struct platform_device *pdev)
diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c
new file mode 100644
index 000000000000..5b50d638771d
--- /dev/null
+++ b/drivers/soc/qcom/dcc.c
@@ -0,0 +1,1300 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#define STATUS_READY_TIMEOUT 5000 /* microseconds */
+
+#define DCC_SRAM_NODE "dcc_sram"
+
+/* DCC registers */
+#define DCC_HW_INFO 0x04
+#define DCC_LL_NUM_INFO 0x10
+#define DCC_STATUS(vers) ((vers) == 1 ? 0x0c : 0x1c)
+#define DCC_LL_LOCK 0x00
+#define DCC_LL_CFG 0x04
+#define DCC_LL_BASE 0x08
+#define DCC_FD_BASE 0x0c
+#define DCC_LL_TIMEOUT 0x10
+#define DCC_LL_INT_ENABLE 0x18
+#define DCC_LL_INT_STATUS 0x1c
+#define DCC_LL_SW_TRIGGER 0x2c
+#define DCC_LL_BUS_ACCESS_STATUS 0x30
+
+/* Default value used if a bit 6 in the HW_INFO register is set. */
+#define DCC_FIX_LOOP_OFFSET 16
+
+/* Mask to find version info from HW_Info register */
+#define DCC_VER_INFO_MASK BIT(9)
+
+#define MAX_DCC_OFFSET GENMASK(9, 2)
+#define MAX_DCC_LEN GENMASK(6, 0)
+#define MAX_LOOP_CNT GENMASK(7, 0)
+#define MAX_LOOP_ADDR 10
+
+#define DCC_ADDR_DESCRIPTOR 0x00
+#define DCC_ADDR_LIMIT 27
+#define DCC_WORD_SIZE sizeof(u32)
+#define DCC_ADDR_RANGE_MASK GENMASK(31, 4)
+#define DCC_LOOP_DESCRIPTOR BIT(30)
+#define DCC_RD_MOD_WR_DESCRIPTOR BIT(31)
+#define DCC_LINK_DESCRIPTOR GENMASK(31, 30)
+#define DCC_STATUS_MASK GENMASK(1, 0)
+#define DCC_LOCK_MASK BIT(0)
+#define DCC_LOOP_OFFSET_MASK BIT(6)
+#define DCC_TRIGGER_MASK BIT(9)
+
+#define DCC_WRITE_MASK BIT(15)
+#define DCC_WRITE_OFF_MASK GENMASK(7, 0)
+#define DCC_WRITE_LEN_MASK GENMASK(14, 8)
+
+#define DCC_READ_IND 0x00
+#define DCC_WRITE_IND (BIT(28))
+
+#define DCC_AHB_IND 0x00
+#define DCC_APB_IND BIT(29)
+
+#define DCC_MAX_LINK_LIST 8
+
+#define DCC_VER_MASK2 GENMASK(5, 0)
+
+#define DCC_SRAM_WORD_LENGTH 4
+
+#define DCC_RD_MOD_WR_ADDR 0xc105e
+
+enum dcc_descriptor_type {
+ DCC_READ_TYPE,
+ DCC_LOOP_TYPE,
+ DCC_READ_WRITE_TYPE,
+ DCC_WRITE_TYPE
+};
+
+struct dcc_config_entry {
+ u32 base;
+ u32 offset;
+ u32 len;
+ u32 loop_cnt;
+ u32 write_val;
+ u32 mask;
+ bool apb_bus;
+ enum dcc_descriptor_type desc_type;
+ struct list_head list;
+};
+
+/**
+ * struct dcc_drvdata - configuration information related to a dcc device
+ * @base: Base Address of the dcc device
+ * @dev: The device attached to the driver data
+ * @mutex: Lock to protect access and manipulation of dcc_drvdata
+ * @ram_base: Base address for the SRAM dedicated for the dcc device
+ * @ram_size: Total size of the SRAM dedicated for the dcc device
+ * @ram_offset: Offset to the SRAM dedicated for dcc device
+ * @mem_map_ver: Memory map version of DCC hardware
+ * @ram_cfg: Used for address limit calculation for dcc
+ * @ram_start: Starting address of DCC SRAM
+ * @sram_dev: Miscellaneous device equivalent of dcc SRAM
+ * @cfg_head: Points to the head of the linked list of addresses
+ * @dbg_dir: The dcc debugfs directory under which all the debugfs files are placed
+ * @nr_link_list: Total number of linkedlists supported by the DCC configuration
+ * @loop_shift: Loop offset bits range for the addresses
+ * @enable_bitmap: Bitmap to capture the enabled status of each linked list of addresses
+ */
+struct dcc_drvdata {
+ void __iomem *base;
+ void __iomem *ram_base;
+ struct device *dev;
+ struct mutex mutex;
+ size_t ram_size;
+ size_t ram_offset;
+ int mem_map_ver;
+ unsigned int ram_cfg;
+ unsigned int ram_start;
+ struct miscdevice sram_dev;
+ struct list_head *cfg_head;
+ struct dentry *dbg_dir;
+ size_t nr_link_list;
+ u8 loop_shift;
+ unsigned long *enable_bitmap;
+};
+
+struct dcc_cfg_attr {
+ u32 addr;
+ u32 prev_addr;
+ u32 prev_off;
+ u32 link;
+ u32 sram_offset;
+};
+
+struct dcc_cfg_loop_attr {
+ u32 loop_cnt;
+ u32 loop_len;
+ u32 loop_off;
+ bool loop_start;
+};
+
+static inline u32 dcc_list_offset(int version)
+{
+ return version == 1 ? 0x1c : version == 2 ? 0x2c : 0x34;
+}
+
+static inline void dcc_list_writel(struct dcc_drvdata *drvdata,
+ u32 ll, u32 val, u32 off)
+{
+ u32 offset = dcc_list_offset(drvdata->mem_map_ver) + off;
+
+ writel(val, drvdata->base + ll * 0x80 + offset);
+}
+
+static inline u32 dcc_list_readl(struct dcc_drvdata *drvdata, u32 ll, u32 off)
+{
+ u32 offset = dcc_list_offset(drvdata->mem_map_ver) + off;
+
+ return readl(drvdata->base + ll * 0x80 + offset);
+}
+
+static void dcc_sram_write_auto(struct dcc_drvdata *drvdata,
+ u32 val, u32 *off)
+{
+ /* If the overflow condition is met increment the offset
+ * and return to indicate that overflow has occurred
+ */
+ if (unlikely(*off > drvdata->ram_size - 4)) {
+ *off += 4;
+ return;
+ }
+
+ writel(val, drvdata->ram_base + *off);
+
+ *off += 4;
+}
+
+static int dcc_sw_trigger(struct dcc_drvdata *drvdata)
+{
+ void __iomem *addr;
+ int ret = 0;
+ int i;
+ u32 status;
+ u32 ll_cfg;
+ u32 tmp_ll_cfg;
+ u32 val;
+
+ mutex_lock(&drvdata->mutex);
+
+ for (i = 0; i < drvdata->nr_link_list; i++) {
+ if (!test_bit(i, drvdata->enable_bitmap))
+ continue;
+ ll_cfg = dcc_list_readl(drvdata, i, DCC_LL_CFG);
+ tmp_ll_cfg = ll_cfg & ~DCC_TRIGGER_MASK;
+ dcc_list_writel(drvdata, tmp_ll_cfg, i, DCC_LL_CFG);
+ dcc_list_writel(drvdata, 1, i, DCC_LL_SW_TRIGGER);
+ dcc_list_writel(drvdata, ll_cfg, i, DCC_LL_CFG);
+ }
+
+ addr = drvdata->base + DCC_STATUS(drvdata->mem_map_ver);
+ if (readl_poll_timeout(addr, val, !FIELD_GET(DCC_STATUS_MASK, val),
+ 1, STATUS_READY_TIMEOUT)) {
+ dev_err(drvdata->dev, "DCC is busy after receiving sw trigger\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ for (i = 0; i < drvdata->nr_link_list; i++) {
+ if (!test_bit(i, drvdata->enable_bitmap))
+ continue;
+
+ status = dcc_list_readl(drvdata, i, DCC_LL_BUS_ACCESS_STATUS);
+ if (!status)
+ continue;
+
+ dev_err(drvdata->dev, "Read access error for list %d err: 0x%x\n",
+ i, status);
+ ll_cfg = dcc_list_readl(drvdata, i, DCC_LL_CFG);
+ tmp_ll_cfg = ll_cfg & ~DCC_TRIGGER_MASK;
+ dcc_list_writel(drvdata, tmp_ll_cfg, i, DCC_LL_CFG);
+ dcc_list_writel(drvdata, DCC_STATUS_MASK, i, DCC_LL_BUS_ACCESS_STATUS);
+ dcc_list_writel(drvdata, ll_cfg, i, DCC_LL_CFG);
+ ret = -ENODATA;
+ break;
+ }
+
+out_unlock:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+
+static void dcc_ll_cfg_reset_link(struct dcc_cfg_attr *cfg)
+{
+ cfg->addr = 0x00;
+ cfg->link = 0;
+ cfg->prev_off = 0;
+ cfg->prev_addr = cfg->addr;
+}
+
+static void dcc_emit_read_write(struct dcc_drvdata *drvdata,
+ struct dcc_config_entry *entry,
+ struct dcc_cfg_attr *cfg)
+{
+ if (cfg->link) {
+ /*
+ * write new offset = 1 to continue
+ * processing the list
+ */
+
+ dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset);
+
+ /* Reset link and prev_off */
+ dcc_ll_cfg_reset_link(cfg);
+ }
+
+ cfg->addr = DCC_RD_MOD_WR_DESCRIPTOR;
+ dcc_sram_write_auto(drvdata, cfg->addr, &cfg->sram_offset);
+
+ dcc_sram_write_auto(drvdata, entry->mask, &cfg->sram_offset);
+
+ dcc_sram_write_auto(drvdata, entry->write_val, &cfg->sram_offset);
+
+ cfg->addr = 0;
+}
+
+static void dcc_emit_loop(struct dcc_drvdata *drvdata, struct dcc_config_entry *entry,
+ struct dcc_cfg_attr *cfg,
+ struct dcc_cfg_loop_attr *cfg_loop,
+ u32 *total_len)
+{
+ int loop;
+
+ /* Check if we need to write link of prev entry */
+ if (cfg->link)
+ dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset);
+
+ if (cfg_loop->loop_start) {
+ loop = (cfg->sram_offset - cfg_loop->loop_off) / 4;
+ loop |= (cfg_loop->loop_cnt << drvdata->loop_shift) &
+ GENMASK(DCC_ADDR_LIMIT, drvdata->loop_shift);
+ loop |= DCC_LOOP_DESCRIPTOR;
+ *total_len += (*total_len - cfg_loop->loop_len) * cfg_loop->loop_cnt;
+
+ dcc_sram_write_auto(drvdata, loop, &cfg->sram_offset);
+
+ cfg_loop->loop_start = false;
+ cfg_loop->loop_len = 0;
+ cfg_loop->loop_off = 0;
+ } else {
+ cfg_loop->loop_start = true;
+ cfg_loop->loop_cnt = entry->loop_cnt - 1;
+ cfg_loop->loop_len = *total_len;
+ cfg_loop->loop_off = cfg->sram_offset;
+ }
+
+ /* Reset link and prev_off */
+ dcc_ll_cfg_reset_link(cfg);
+}
+
+static void dcc_emit_write(struct dcc_drvdata *drvdata,
+ struct dcc_config_entry *entry,
+ struct dcc_cfg_attr *cfg)
+{
+ u32 off;
+
+ if (cfg->link) {
+ /*
+ * write new offset = 1 to continue
+ * processing the list
+ */
+ dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset);
+
+ /* Reset link and prev_off */
+ cfg->addr = 0x00;
+ cfg->prev_off = 0;
+ cfg->prev_addr = cfg->addr;
+ }
+
+ off = entry->offset / 4;
+ /* write new offset-length pair to correct position */
+ cfg->link |= ((off & DCC_WRITE_OFF_MASK) | DCC_WRITE_MASK |
+ FIELD_PREP(DCC_WRITE_LEN_MASK, entry->len));
+ cfg->link |= DCC_LINK_DESCRIPTOR;
+
+ /* Address type */
+ cfg->addr = (entry->base >> 4) & GENMASK(DCC_ADDR_LIMIT, 0);
+ if (entry->apb_bus)
+ cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_WRITE_IND | DCC_APB_IND;
+ else
+ cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_WRITE_IND | DCC_AHB_IND;
+ dcc_sram_write_auto(drvdata, cfg->addr, &cfg->sram_offset);
+
+ dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset);
+
+ dcc_sram_write_auto(drvdata, entry->write_val, &cfg->sram_offset);
+
+ cfg->addr = 0x00;
+ cfg->link = 0;
+}
+
+static int dcc_emit_read(struct dcc_drvdata *drvdata,
+ struct dcc_config_entry *entry,
+ struct dcc_cfg_attr *cfg,
+ u32 *pos, u32 *total_len)
+{
+ u32 off;
+ u32 temp_off;
+
+ cfg->addr = (entry->base >> 4) & GENMASK(27, 0);
+
+ if (entry->apb_bus)
+ cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_READ_IND | DCC_APB_IND;
+ else
+ cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_READ_IND | DCC_AHB_IND;
+
+ off = entry->offset / 4;
+
+ *total_len += entry->len * 4;
+
+ if (!cfg->prev_addr || cfg->prev_addr != cfg->addr || cfg->prev_off > off) {
+ /* Check if we need to write prev link entry */
+ if (cfg->link)
+ dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset);
+ dev_dbg(drvdata->dev, "DCC: sram address 0x%x\n", cfg->sram_offset);
+
+ /* Write address */
+ dcc_sram_write_auto(drvdata, cfg->addr, &cfg->sram_offset);
+
+ /* Reset link and prev_off */
+ cfg->link = 0;
+ cfg->prev_off = 0;
+ }
+
+ if ((off - cfg->prev_off) > 0xff || entry->len > MAX_DCC_LEN) {
+ dev_err(drvdata->dev, "DCC: Programming error Base: 0x%x, offset 0x%x\n",
+ entry->base, entry->offset);
+ return -EINVAL;
+ }
+
+ if (cfg->link) {
+ /*
+ * link already has one offset-length so new
+ * offset-length needs to be placed at
+ * bits [29:15]
+ */
+ *pos = 15;
+
+ /* Clear bits [31:16] */
+ cfg->link &= GENMASK(14, 0);
+ } else {
+ /*
+ * link is empty, so new offset-length needs
+ * to be placed at bits [15:0]
+ */
+ *pos = 0;
+ cfg->link = 1 << 15;
+ }
+
+ /* write new offset-length pair to correct position */
+ temp_off = (off - cfg->prev_off) & GENMASK(7, 0);
+ cfg->link |= temp_off | ((entry->len << 8) & GENMASK(14, 8)) << *pos;
+
+ cfg->link |= DCC_LINK_DESCRIPTOR;
+
+ if (*pos) {
+ dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset);
+ cfg->link = 0;
+ }
+
+ cfg->prev_off = off + entry->len - 1;
+ cfg->prev_addr = cfg->addr;
+ return 0;
+}
+
+static int dcc_emit_config(struct dcc_drvdata *drvdata, unsigned int curr_list)
+{
+ int ret;
+ u32 total_len, pos;
+ struct dcc_config_entry *entry;
+ struct dcc_cfg_attr cfg;
+ struct dcc_cfg_loop_attr cfg_loop;
+
+ memset(&cfg, 0, sizeof(cfg));
+ memset(&cfg_loop, 0, sizeof(cfg_loop));
+ cfg.sram_offset = drvdata->ram_cfg * 4;
+ total_len = 0;
+
+ list_for_each_entry(entry, &drvdata->cfg_head[curr_list], list) {
+ switch (entry->desc_type) {
+ case DCC_READ_WRITE_TYPE:
+ dcc_emit_read_write(drvdata, entry, &cfg);
+ break;
+
+ case DCC_LOOP_TYPE:
+ dcc_emit_loop(drvdata, entry, &cfg, &cfg_loop, &total_len);
+ break;
+
+ case DCC_WRITE_TYPE:
+ dcc_emit_write(drvdata, entry, &cfg);
+ break;
+
+ case DCC_READ_TYPE:
+ ret = dcc_emit_read(drvdata, entry, &cfg, &pos, &total_len);
+ if (ret)
+ goto err;
+ break;
+ }
+ }
+
+ if (cfg.link)
+ dcc_sram_write_auto(drvdata, cfg.link, &cfg.sram_offset);
+
+ if (cfg_loop.loop_start) {
+ dev_err(drvdata->dev, "DCC: Programming error: Loop unterminated\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Handling special case of list ending with a rd_mod_wr */
+ if (cfg.addr == DCC_RD_MOD_WR_DESCRIPTOR) {
+ cfg.addr = (DCC_RD_MOD_WR_ADDR) & GENMASK(27, 0);
+ cfg.addr |= DCC_ADDR_DESCRIPTOR;
+ dcc_sram_write_auto(drvdata, cfg.addr, &cfg.sram_offset);
+ }
+
+ /* Setting zero to indicate end of the list */
+ cfg.link = DCC_LINK_DESCRIPTOR;
+ dcc_sram_write_auto(drvdata, cfg.link, &cfg.sram_offset);
+
+ /* Check if sram offset exceeds the ram size */
+ if (cfg.sram_offset > drvdata->ram_size)
+ goto overstep;
+
+ /* Update ram_cfg and check if the data will overstep */
+ drvdata->ram_cfg = (cfg.sram_offset + total_len) / 4;
+
+ if (cfg.sram_offset + total_len > drvdata->ram_size) {
+ cfg.sram_offset += total_len;
+ goto overstep;
+ }
+
+ drvdata->ram_start = cfg.sram_offset / 4;
+ return 0;
+overstep:
+ ret = -EINVAL;
+ memset_io(drvdata->ram_base, 0, drvdata->ram_size);
+
+err:
+ return ret;
+}
+
+static bool dcc_valid_list(struct dcc_drvdata *drvdata, unsigned int curr_list)
+{
+ u32 lock_reg;
+
+ if (list_empty(&drvdata->cfg_head[curr_list]))
+ return false;
+
+ if (test_bit(curr_list, drvdata->enable_bitmap)) {
+ dev_err(drvdata->dev, "List %d is already enabled\n", curr_list);
+ return false;
+ }
+
+ lock_reg = dcc_list_readl(drvdata, curr_list, DCC_LL_LOCK);
+ if (lock_reg & DCC_LOCK_MASK) {
+ dev_err(drvdata->dev, "List %d is already locked\n", curr_list);
+ return false;
+ }
+
+ return true;
+}
+
+static bool is_dcc_enabled(struct dcc_drvdata *drvdata)
+{
+ int list;
+
+ for (list = 0; list < drvdata->nr_link_list; list++)
+ if (test_bit(list, drvdata->enable_bitmap))
+ return true;
+
+ return false;
+}
+
+static int dcc_enable(struct dcc_drvdata *drvdata, unsigned int curr_list)
+{
+ int ret;
+ u32 ram_cfg_base;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (!dcc_valid_list(drvdata, curr_list)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ /* Fill dcc sram with the poison value.
+ * This helps in understanding bus
+ * hang from registers returning a zero
+ */
+ if (!is_dcc_enabled(drvdata))
+ memset_io(drvdata->ram_base, 0xde, drvdata->ram_size);
+
+ /* 1. Take ownership of the list */
+ dcc_list_writel(drvdata, DCC_LOCK_MASK, curr_list, DCC_LL_LOCK);
+
+ /* 2. Program linked-list in the SRAM */
+ ram_cfg_base = drvdata->ram_cfg;
+ ret = dcc_emit_config(drvdata, curr_list);
+ if (ret) {
+ dcc_list_writel(drvdata, 0, curr_list, DCC_LL_LOCK);
+ goto out_unlock;
+ }
+
+ /* 3. Program DCC_RAM_CFG reg */
+ dcc_list_writel(drvdata, ram_cfg_base +
+ drvdata->ram_offset / 4, curr_list, DCC_LL_BASE);
+ dcc_list_writel(drvdata, drvdata->ram_start +
+ drvdata->ram_offset / 4, curr_list, DCC_FD_BASE);
+ dcc_list_writel(drvdata, 0xFFF, curr_list, DCC_LL_TIMEOUT);
+
+ /* 4. Clears interrupt status register */
+ dcc_list_writel(drvdata, 0, curr_list, DCC_LL_INT_ENABLE);
+ dcc_list_writel(drvdata, (BIT(0) | BIT(1) | BIT(2)),
+ curr_list, DCC_LL_INT_STATUS);
+
+ set_bit(curr_list, drvdata->enable_bitmap);
+
+ /* 5. Configure trigger */
+ dcc_list_writel(drvdata, DCC_TRIGGER_MASK,
+ curr_list, DCC_LL_CFG);
+
+out_unlock:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+
+static void dcc_disable(struct dcc_drvdata *drvdata, int curr_list)
+{
+ mutex_lock(&drvdata->mutex);
+
+ if (!test_bit(curr_list, drvdata->enable_bitmap))
+ goto out_unlock;
+ dcc_list_writel(drvdata, 0, curr_list, DCC_LL_CFG);
+ dcc_list_writel(drvdata, 0, curr_list, DCC_LL_BASE);
+ dcc_list_writel(drvdata, 0, curr_list, DCC_FD_BASE);
+ dcc_list_writel(drvdata, 0, curr_list, DCC_LL_LOCK);
+ clear_bit(curr_list, drvdata->enable_bitmap);
+out_unlock:
+ mutex_unlock(&drvdata->mutex);
+}
+
+static u32 dcc_filp_curr_list(const struct file *filp)
+{
+ struct dentry *dentry = file_dentry(filp);
+ int curr_list, ret;
+
+ ret = kstrtoint(dentry->d_parent->d_name.name, 0, &curr_list);
+ if (ret)
+ return ret;
+
+ return curr_list;
+}
+
+static ssize_t enable_read(struct file *filp, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ char *buf;
+ struct dcc_drvdata *drvdata = filp->private_data;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (is_dcc_enabled(drvdata))
+ buf = "Y\n";
+ else
+ buf = "N\n";
+
+ mutex_unlock(&drvdata->mutex);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t enable_write(struct file *filp, const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0, curr_list;
+ bool val;
+ struct dcc_drvdata *drvdata = filp->private_data;
+
+ curr_list = dcc_filp_curr_list(filp);
+ if (curr_list < 0)
+ return curr_list;
+
+ ret = kstrtobool_from_user(userbuf, count, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val) {
+ ret = dcc_enable(drvdata, curr_list);
+ if (ret)
+ return ret;
+ } else {
+ dcc_disable(drvdata, curr_list);
+ }
+
+ return count;
+}
+
+static const struct file_operations enable_fops = {
+ .read = enable_read,
+ .write = enable_write,
+ .open = simple_open,
+ .llseek = generic_file_llseek,
+};
+
+static ssize_t trigger_write(struct file *filp,
+ const char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ int ret;
+ unsigned int val;
+ struct dcc_drvdata *drvdata = filp->private_data;
+
+ ret = kstrtouint_from_user(user_buf, count, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val != 1)
+ return -EINVAL;
+
+ ret = dcc_sw_trigger(drvdata);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations trigger_fops = {
+ .write = trigger_write,
+ .open = simple_open,
+ .llseek = generic_file_llseek,
+};
+
+static int dcc_config_add(struct dcc_drvdata *drvdata, unsigned int addr,
+ unsigned int len, bool apb_bus, int curr_list)
+{
+ int ret = 0;
+ struct dcc_config_entry *entry, *pentry;
+ unsigned int base, offset;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (!len || len > drvdata->ram_size / DCC_WORD_SIZE) {
+ dev_err(drvdata->dev, "DCC: Invalid length\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ base = addr & DCC_ADDR_RANGE_MASK;
+
+ if (!list_empty(&drvdata->cfg_head[curr_list])) {
+ pentry = list_last_entry(&drvdata->cfg_head[curr_list],
+ struct dcc_config_entry, list);
+
+ if (pentry->desc_type == DCC_READ_TYPE &&
+ addr >= (pentry->base + pentry->offset) &&
+ addr <= (pentry->base + pentry->offset + MAX_DCC_OFFSET)) {
+ /* Re-use base address from last entry */
+ base = pentry->base;
+
+ if ((pentry->len * 4 + pentry->base + pentry->offset)
+ == addr) {
+ len += pentry->len;
+
+ if (len > MAX_DCC_LEN)
+ pentry->len = MAX_DCC_LEN;
+ else
+ pentry->len = len;
+
+ addr = pentry->base + pentry->offset +
+ pentry->len * 4;
+ len -= pentry->len;
+ }
+ }
+ }
+
+ offset = addr - base;
+
+ while (len) {
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ entry->base = base;
+ entry->offset = offset;
+ entry->len = min_t(u32, len, MAX_DCC_LEN);
+ entry->desc_type = DCC_READ_TYPE;
+ entry->apb_bus = apb_bus;
+ INIT_LIST_HEAD(&entry->list);
+ list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]);
+
+ len -= entry->len;
+ offset += MAX_DCC_LEN * 4;
+ }
+
+out_unlock:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+
+static ssize_t dcc_config_add_read(struct dcc_drvdata *drvdata, char *buf, int curr_list)
+{
+ bool bus;
+ int len, nval;
+ unsigned int base;
+ char apb_bus[4];
+
+ nval = sscanf(buf, "%x %i %3s", &base, &len, apb_bus);
+ if (nval <= 0 || nval > 3)
+ return -EINVAL;
+
+ if (nval == 1) {
+ len = 1;
+ bus = false;
+ } else if (nval == 2) {
+ bus = false;
+ } else if (!strcmp("apb", apb_bus)) {
+ bus = true;
+ } else if (!strcmp("ahb", apb_bus)) {
+ bus = false;
+ } else {
+ return -EINVAL;
+ }
+
+ return dcc_config_add(drvdata, base, len, bus, curr_list);
+}
+
+static void dcc_config_reset(struct dcc_drvdata *drvdata)
+{
+ struct dcc_config_entry *entry, *temp;
+ int curr_list;
+
+ mutex_lock(&drvdata->mutex);
+
+ for (curr_list = 0; curr_list < drvdata->nr_link_list; curr_list++) {
+ list_for_each_entry_safe(entry, temp,
+ &drvdata->cfg_head[curr_list], list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ }
+ drvdata->ram_start = 0;
+ drvdata->ram_cfg = 0;
+ mutex_unlock(&drvdata->mutex);
+}
+
+static ssize_t config_reset_write(struct file *filp,
+ const char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned int val;
+ int ret;
+ struct dcc_drvdata *drvdata = filp->private_data;
+
+ ret = kstrtouint_from_user(user_buf, count, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val)
+ dcc_config_reset(drvdata);
+
+ return count;
+}
+
+static const struct file_operations config_reset_fops = {
+ .write = config_reset_write,
+ .open = simple_open,
+ .llseek = generic_file_llseek,
+};
+
+static ssize_t ready_read(struct file *filp, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ char *buf;
+ struct dcc_drvdata *drvdata = filp->private_data;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (!is_dcc_enabled(drvdata)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (!FIELD_GET(BIT(1), readl(drvdata->base + DCC_STATUS(drvdata->mem_map_ver))))
+ buf = "Y\n";
+ else
+ buf = "N\n";
+out_unlock:
+ mutex_unlock(&drvdata->mutex);
+
+ if (ret < 0)
+ return -EINVAL;
+ else
+ return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf) + 1);
+}
+
+static const struct file_operations ready_fops = {
+ .read = ready_read,
+ .open = simple_open,
+ .llseek = generic_file_llseek,
+};
+
+static int dcc_add_loop(struct dcc_drvdata *drvdata, unsigned long loop_cnt, int curr_list)
+{
+ struct dcc_config_entry *entry;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->loop_cnt = min_t(u32, loop_cnt, MAX_LOOP_CNT);
+ entry->desc_type = DCC_LOOP_TYPE;
+ INIT_LIST_HEAD(&entry->list);
+ list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]);
+
+ return 0;
+}
+
+static ssize_t dcc_config_add_loop(struct dcc_drvdata *drvdata, char *buf, int curr_list)
+{
+ int ret, i = 0;
+ char *token, *input;
+ char delim[2] = " ";
+ unsigned int val[MAX_LOOP_ADDR];
+
+ input = buf;
+
+ while ((token = strsep(&input, delim)) && i < MAX_LOOP_ADDR) {
+ ret = kstrtoint(token, 0, &val[i++]);
+ if (ret)
+ return ret;
+ }
+
+ if (token) {
+ dev_err(drvdata->dev, "Max limit %u of loop address exceeded",
+ MAX_LOOP_ADDR);
+ return -EINVAL;
+ }
+
+ if (val[1] < 1 || val[1] > 8)
+ return -EINVAL;
+
+ ret = dcc_add_loop(drvdata, val[0], curr_list);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < val[1]; i++)
+ dcc_config_add(drvdata, val[i + 2], 1, false, curr_list);
+
+ return dcc_add_loop(drvdata, 1, curr_list);
+}
+
+static int dcc_rd_mod_wr_add(struct dcc_drvdata *drvdata, unsigned int mask,
+ unsigned int val, int curr_list)
+{
+ int ret = 0;
+ struct dcc_config_entry *entry;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (list_empty(&drvdata->cfg_head[curr_list])) {
+ dev_err(drvdata->dev, "DCC: No read address programmed\n");
+ ret = -EPERM;
+ goto out_unlock;
+ }
+
+ entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ entry->desc_type = DCC_READ_WRITE_TYPE;
+ entry->mask = mask;
+ entry->write_val = val;
+ list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]);
+out_unlock:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+
+static ssize_t dcc_config_add_read_write(struct dcc_drvdata *drvdata, char *buf, int curr_list)
+{
+ int ret;
+ int nval;
+ unsigned int addr, mask, val;
+
+ nval = sscanf(buf, "%x %x %x", &addr, &mask, &val);
+
+ if (nval <= 1 || nval > 3)
+ return -EINVAL;
+
+ ret = dcc_config_add(drvdata, addr, 1, false, curr_list);
+ if (ret)
+ return ret;
+
+ return dcc_rd_mod_wr_add(drvdata, mask, val, curr_list);
+}
+
+static int dcc_add_write(struct dcc_drvdata *drvdata, unsigned int addr,
+ unsigned int write_val, int apb_bus, int curr_list)
+{
+ struct dcc_config_entry *entry;
+
+ entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->desc_type = DCC_WRITE_TYPE;
+ entry->base = addr & GENMASK(31, 4);
+ entry->offset = addr - entry->base;
+ entry->write_val = write_val;
+ entry->len = 1;
+ entry->apb_bus = apb_bus;
+ list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]);
+
+ return 0;
+}
+
+static ssize_t dcc_config_add_write(struct dcc_drvdata *drvdata, char *buf, int curr_list)
+{
+ bool bus;
+ int nval;
+ unsigned int addr, write_val;
+ char apb_bus[4];
+
+ nval = sscanf(buf, "%x %x %3s", &addr, &write_val, apb_bus);
+
+ if (nval <= 1 || nval > 3)
+ return -EINVAL;
+
+ if (nval == 2)
+ bus = true;
+
+ if (nval == 3) {
+ if (!strcmp("apb", apb_bus))
+ bus = true;
+ else if (!strcmp("ahb", apb_bus))
+ bus = false;
+ else
+ return -EINVAL;
+ }
+
+ return dcc_add_write(drvdata, addr, write_val, bus, curr_list);
+}
+
+static int config_show(struct seq_file *m, void *data)
+{
+ struct dcc_drvdata *drvdata = m->private;
+ struct dcc_config_entry *entry;
+ int index = 0, curr_list;
+
+ curr_list = dcc_filp_curr_list(m->file);
+ if (curr_list < 0)
+ return curr_list;
+
+ mutex_lock(&drvdata->mutex);
+
+ list_for_each_entry(entry, &drvdata->cfg_head[curr_list], list) {
+ index++;
+ switch (entry->desc_type) {
+ case DCC_READ_WRITE_TYPE:
+ seq_printf(m, "RW mask: 0x%x, val: 0x%x\n index: 0x%x\n",
+ entry->mask, entry->write_val, index);
+ break;
+ case DCC_LOOP_TYPE:
+ seq_printf(m, "L index: 0x%x Loop: %d\n", index, entry->loop_cnt);
+ break;
+ case DCC_WRITE_TYPE:
+ seq_printf(m, "W Base:0x%x, Offset: 0x%x, val: 0x%x, APB: %d\n, Index: 0x%x\n",
+ entry->base, entry->offset, entry->write_val, entry->apb_bus,
+ index);
+ break;
+ case DCC_READ_TYPE:
+ seq_printf(m, "R Base:0x%x, Offset: 0x%x, len: 0x%x, APB: %d\n, Index: 0x%x\n",
+ entry->base, entry->offset, entry->len, entry->apb_bus, index);
+ }
+ }
+ mutex_unlock(&drvdata->mutex);
+ return 0;
+}
+
+static int config_open(struct inode *inode, struct file *file)
+{
+ struct dcc_drvdata *drvdata = inode->i_private;
+
+ return single_open(file, config_show, drvdata);
+}
+
+static ssize_t config_write(struct file *filp,
+ const char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ int ret, curr_list;
+ char *token, buf[50];
+ char *bufp = buf;
+ char *delim = " ";
+ struct dcc_drvdata *drvdata = filp->private_data;
+
+ if (count > sizeof(buf) || count == 0)
+ return -EINVAL;
+
+ ret = copy_from_user(buf, user_buf, count);
+ if (ret)
+ return -EFAULT;
+
+ curr_list = dcc_filp_curr_list(filp);
+ if (curr_list < 0)
+ return curr_list;
+
+ if (buf[count - 1] == '\n')
+ buf[count - 1] = '\0';
+ else
+ return -EINVAL;
+
+ token = strsep(&bufp, delim);
+
+ if (!strcmp("R", token)) {
+ ret = dcc_config_add_read(drvdata, bufp, curr_list);
+ } else if (!strcmp("W", token)) {
+ ret = dcc_config_add_write(drvdata, bufp, curr_list);
+ } else if (!strcmp("RW", token)) {
+ ret = dcc_config_add_read_write(drvdata, bufp, curr_list);
+ } else if (!strcmp("L", token)) {
+ ret = dcc_config_add_loop(drvdata, bufp, curr_list);
+ } else {
+ dev_err(drvdata->dev, "%s is not a correct input\n", token);
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations config_fops = {
+ .open = config_open,
+ .read = seq_read,
+ .write = config_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void dcc_delete_debug_dir(struct dcc_drvdata *drvdata)
+{
+ debugfs_remove_recursive(drvdata->dbg_dir);
+};
+
+static void dcc_create_debug_dir(struct dcc_drvdata *drvdata)
+{
+ int i;
+ char list_num[10];
+ struct dentry *list;
+ struct device *dev = drvdata->dev;
+
+ drvdata->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
+ if (IS_ERR(drvdata->dbg_dir)) {
+ pr_err("can't create debugfs dir\n");
+ return;
+ }
+
+ for (i = 0; i <= drvdata->nr_link_list; i++) {
+ sprintf(list_num, "%d", i);
+ list = debugfs_create_dir(list_num, drvdata->dbg_dir);
+ debugfs_create_file("enable", 0600, list, drvdata, &enable_fops);
+ debugfs_create_file("config", 0600, list, drvdata, &config_fops);
+ }
+
+ debugfs_create_file("trigger", 0200, drvdata->dbg_dir, drvdata, &trigger_fops);
+ debugfs_create_file("ready", 0400, drvdata->dbg_dir, drvdata, &ready_fops);
+ debugfs_create_file("config_reset", 0200, drvdata->dbg_dir,
+ drvdata, &config_reset_fops);
+}
+
+static ssize_t dcc_sram_read(struct file *file, char __user *data,
+ size_t len, loff_t *ppos)
+{
+ struct dcc_drvdata *drvdata = container_of(file->private_data, struct dcc_drvdata, sram_dev);
+ unsigned char *buf;
+
+ /* EOF check */
+ if (*ppos >= drvdata->ram_size)
+ return 0;
+
+ if ((*ppos + len) > drvdata->ram_size)
+ len = (drvdata->ram_size - *ppos);
+
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy_fromio(buf, drvdata->ram_base + *ppos, len);
+
+ if (copy_to_user(data, buf, len)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ *ppos += len;
+
+ kfree(buf);
+
+ return len;
+}
+
+static const struct file_operations dcc_sram_fops = {
+ .owner = THIS_MODULE,
+ .read = dcc_sram_read,
+ .llseek = no_llseek,
+};
+
+static int dcc_sram_dev_init(struct dcc_drvdata *drvdata)
+{
+ drvdata->sram_dev.minor = MISC_DYNAMIC_MINOR;
+ drvdata->sram_dev.name = DCC_SRAM_NODE;
+ drvdata->sram_dev.fops = &dcc_sram_fops;
+
+ return misc_register(&drvdata->sram_dev);
+}
+
+static void dcc_sram_dev_exit(struct dcc_drvdata *drvdata)
+{
+ misc_deregister(&drvdata->sram_dev);
+}
+
+static int dcc_probe(struct platform_device *pdev)
+{
+ u32 val;
+ int ret = 0, i;
+ struct device *dev = &pdev->dev;
+ struct dcc_drvdata *drvdata;
+ struct resource *res;
+
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ drvdata->dev = &pdev->dev;
+ platform_set_drvdata(pdev, drvdata);
+
+ drvdata->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(drvdata->base))
+ return PTR_ERR(drvdata->base);
+
+ drvdata->ram_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
+ if (IS_ERR(drvdata->ram_base))
+ return PTR_ERR(drvdata->ram_base);
+
+ drvdata->ram_size = resource_size(res);
+
+ drvdata->ram_offset = (size_t)of_device_get_match_data(&pdev->dev);
+
+ val = readl(drvdata->base + DCC_HW_INFO);
+
+ if (FIELD_GET(DCC_VER_INFO_MASK, val)) {
+ drvdata->mem_map_ver = 3;
+ drvdata->nr_link_list = readl(drvdata->base + DCC_LL_NUM_INFO);
+ if (!drvdata->nr_link_list)
+ return -EINVAL;
+ } else if ((val & DCC_VER_MASK2) == DCC_VER_MASK2) {
+ drvdata->mem_map_ver = 2;
+ drvdata->nr_link_list = readl(drvdata->base + DCC_LL_NUM_INFO);
+ if (!drvdata->nr_link_list)
+ return -EINVAL;
+ } else {
+ drvdata->mem_map_ver = 1;
+ drvdata->nr_link_list = DCC_MAX_LINK_LIST;
+ }
+
+ /* Either set the fixed loop offset or calculate
+ * it from the total number of words in dcc_sram.
+ * Max consecutive addresses dcc can loop is
+ * equivalent to the words in dcc_sram.
+ */
+ if (val & DCC_LOOP_OFFSET_MASK)
+ drvdata->loop_shift = DCC_FIX_LOOP_OFFSET;
+ else
+ drvdata->loop_shift = get_bitmask_order((drvdata->ram_offset +
+ drvdata->ram_size) / DCC_SRAM_WORD_LENGTH - 1);
+
+ mutex_init(&drvdata->mutex);
+
+ drvdata->enable_bitmap = devm_kcalloc(dev, BITS_TO_LONGS(drvdata->nr_link_list),
+ sizeof(*drvdata->enable_bitmap), GFP_KERNEL);
+ if (!drvdata->enable_bitmap)
+ return -ENOMEM;
+
+ drvdata->cfg_head = devm_kcalloc(dev, drvdata->nr_link_list,
+ sizeof(*drvdata->cfg_head), GFP_KERNEL);
+ if (!drvdata->cfg_head)
+ return -ENOMEM;
+
+ for (i = 0; i < drvdata->nr_link_list; i++)
+ INIT_LIST_HEAD(&drvdata->cfg_head[i]);
+
+ ret = dcc_sram_dev_init(drvdata);
+ if (ret) {
+ dev_err(drvdata->dev, "DCC: sram node not registered.\n");
+ return ret;
+ }
+
+ dcc_create_debug_dir(drvdata);
+
+ return 0;
+}
+
+static int dcc_remove(struct platform_device *pdev)
+{
+ struct dcc_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ dcc_delete_debug_dir(drvdata);
+ dcc_sram_dev_exit(drvdata);
+ dcc_config_reset(drvdata);
+
+ return 0;
+}
+
+static const struct of_device_id dcc_match_table[] = {
+ { .compatible = "qcom,sc7180-dcc", .data = (void *)0x6000 },
+ { .compatible = "qcom,sc7280-dcc", .data = (void *)0x12000 },
+ { .compatible = "qcom,sdm845-dcc", .data = (void *)0x6000 },
+ { .compatible = "qcom,sm8150-dcc", .data = (void *)0x5000 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, dcc_match_table);
+
+static struct platform_driver dcc_driver = {
+ .probe = dcc_probe,
+ .remove = dcc_remove,
+ .driver = {
+ .name = "qcom-dcc",
+ .of_match_table = dcc_match_table,
+ },
+};
+module_platform_driver(dcc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. DCC driver");
diff --git a/drivers/soc/qcom/ramp_controller.c b/drivers/soc/qcom/ramp_controller.c
new file mode 100644
index 000000000000..dc74d2a19de2
--- /dev/null
+++ b/drivers/soc/qcom/ramp_controller.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm Ramp Controller driver
+ * Copyright (c) 2022, AngeloGioacchino Del Regno
+ * <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define RC_UPDATE_EN BIT(0)
+#define RC_ROOT_EN BIT(1)
+
+#define RC_REG_CFG_UPDATE 0x60
+#define RC_CFG_UPDATE_EN BIT(8)
+#define RC_CFG_ACK GENMASK(31, 16)
+
+#define RC_DCVS_CFG_SID 2
+#define RC_LINK_SID 3
+#define RC_LMH_SID 6
+#define RC_DFS_SID 14
+
+#define RC_UPDATE_TIMEOUT_US 500
+
+/**
+ * struct qcom_ramp_controller_desc - SoC specific parameters
+ * @cfg_dfs_sid: Dynamic Frequency Scaling SID configuration
+ * @cfg_link_sid: Link SID configuration
+ * @cfg_lmh_sid: Limits Management hardware SID configuration
+ * @cfg_ramp_en: Ramp Controller enable sequence
+ * @cfg_ramp_dis: Ramp Controller disable sequence
+ * @cmd_reg: Command register offset
+ * @num_dfs_sids: Number of DFS SIDs (max 8)
+ * @num_link_sids: Number of Link SIDs (max 3)
+ * @num_lmh_sids: Number of LMh SIDs (max 8)
+ * @num_ramp_en: Number of entries in enable sequence
+ * @num_ramp_dis: Number of entries in disable sequence
+ */
+struct qcom_ramp_controller_desc {
+ const struct reg_sequence *cfg_dfs_sid;
+ const struct reg_sequence *cfg_link_sid;
+ const struct reg_sequence *cfg_lmh_sid;
+ const struct reg_sequence *cfg_ramp_en;
+ const struct reg_sequence *cfg_ramp_dis;
+ u8 cmd_reg;
+ u8 num_dfs_sids;
+ u8 num_link_sids;
+ u8 num_lmh_sids;
+ u8 num_ramp_en;
+ u8 num_ramp_dis;
+};
+
+/**
+ * struct qcom_ramp_controller - Main driver structure
+ * @regmap: Regmap handle
+ * @desc: SoC specific parameters
+ */
+struct qcom_ramp_controller {
+ struct regmap *regmap;
+ const struct qcom_ramp_controller_desc *desc;
+};
+
+/**
+ * rc_wait_for_update() - Wait for Ramp Controller root update
+ * @qrc: Main driver structure
+ *
+ * Return: Zero for success or negative number for failure
+ */
+static int rc_wait_for_update(struct qcom_ramp_controller *qrc)
+{
+ const struct qcom_ramp_controller_desc *d = qrc->desc;
+ struct regmap *r = qrc->regmap;
+ u32 val;
+ int ret;
+
+ ret = regmap_set_bits(r, d->cmd_reg, RC_ROOT_EN);
+ if (ret)
+ return ret;
+
+ return regmap_read_poll_timeout(r, d->cmd_reg, val, !(val & RC_UPDATE_EN),
+ 1, RC_UPDATE_TIMEOUT_US);
+}
+
+/**
+ * rc_set_cfg_update() - Ramp Controller configuration update
+ * @qrc: Main driver structure
+ * @ce: Configuration entry to update
+ *
+ * Return: Zero for success or negative number for failure
+ */
+static int rc_set_cfg_update(struct qcom_ramp_controller *qrc, u8 ce)
+{
+ const struct qcom_ramp_controller_desc *d = qrc->desc;
+ struct regmap *r = qrc->regmap;
+ u32 ack, val;
+ int ret;
+
+ /* The ack bit is between bits 16-31 of RC_REG_CFG_UPDATE */
+ ack = FIELD_PREP(RC_CFG_ACK, BIT(ce));
+
+ /* Write the configuration type first... */
+ ret = regmap_set_bits(r, d->cmd_reg + RC_REG_CFG_UPDATE, ce);
+ if (ret)
+ return ret;
+
+ /* ...and after that, enable the update bit to sync the changes */
+ ret = regmap_set_bits(r, d->cmd_reg + RC_REG_CFG_UPDATE, RC_CFG_UPDATE_EN);
+ if (ret)
+ return ret;
+
+ /* Wait for the changes to go through */
+ ret = regmap_read_poll_timeout(r, d->cmd_reg + RC_REG_CFG_UPDATE, val,
+ val & ack, 1, RC_UPDATE_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ /*
+ * Configuration update success! The CFG_UPDATE register will not be
+ * cleared automatically upon applying the configuration, so we have
+ * to do that manually in order to leave the ramp controller in a
+ * predictable and clean state.
+ */
+ ret = regmap_write(r, d->cmd_reg + RC_REG_CFG_UPDATE, 0);
+ if (ret)
+ return ret;
+
+ /* Wait for the update bit cleared ack */
+ return regmap_read_poll_timeout(r, d->cmd_reg + RC_REG_CFG_UPDATE,
+ val, !(val & RC_CFG_ACK), 1,
+ RC_UPDATE_TIMEOUT_US);
+}
+
+/**
+ * rc_write_cfg - Send configuration sequence
+ * @qrc: Main driver structure
+ * @seq: Register sequence to send before asking for update
+ * @ce: Configuration SID
+ * @nsids: Total number of SIDs
+ *
+ * Returns: Zero for success or negative number for error
+ */
+static int rc_write_cfg(struct qcom_ramp_controller *qrc,
+ const struct reg_sequence *seq,
+ u16 ce, u8 nsids)
+{
+ int ret;
+ u8 i;
+
+ /* Check if, and wait until the ramp controller is ready */
+ ret = rc_wait_for_update(qrc);
+ if (ret)
+ return ret;
+
+ /* Write the sequence */
+ ret = regmap_multi_reg_write(qrc->regmap, seq, nsids);
+ if (ret)
+ return ret;
+
+ /* Pull the trigger: do config update starting from the last sid */
+ for (i = 0; i < nsids; i++) {
+ ret = rc_set_cfg_update(qrc, (u8)ce - i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * rc_ramp_ctrl_enable() - Enable Ramp up/down Control
+ * @qrc: Main driver structure
+ *
+ * Return: Zero for success or negative number for error
+ */
+static int rc_ramp_ctrl_enable(struct qcom_ramp_controller *qrc)
+{
+ const struct qcom_ramp_controller_desc *d = qrc->desc;
+ int i, ret;
+
+ for (i = 0; i < d->num_ramp_en; i++) {
+ ret = rc_write_cfg(qrc, &d->cfg_ramp_en[i], RC_DCVS_CFG_SID, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * qcom_ramp_controller_start() - Initialize and start the ramp controller
+ * @qrc: Main driver structure
+ *
+ * The Ramp Controller needs to be initialized by programming the relevant
+ * registers with SoC-specific configuration: once programming is done,
+ * the hardware will take care of the rest (no further handling required).
+ *
+ * Return: Zero for success or negative number for error
+ */
+static int qcom_ramp_controller_start(struct qcom_ramp_controller *qrc)
+{
+ const struct qcom_ramp_controller_desc *d = qrc->desc;
+ int ret;
+
+ /* Program LMH, DFS, Link SIDs */
+ ret = rc_write_cfg(qrc, d->cfg_lmh_sid, RC_LMH_SID, d->num_lmh_sids);
+ if (ret)
+ return ret;
+
+ ret = rc_write_cfg(qrc, d->cfg_dfs_sid, RC_DFS_SID, d->num_dfs_sids);
+ if (ret)
+ return ret;
+
+ ret = rc_write_cfg(qrc, d->cfg_link_sid, RC_LINK_SID, d->num_link_sids);
+ if (ret)
+ return ret;
+
+ /* Everything is ready! Enable the ramp up/down control */
+ return rc_ramp_ctrl_enable(qrc);
+}
+
+static const struct regmap_config qrc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x68,
+ .fast_io = true,
+};
+
+static const struct reg_sequence msm8976_cfg_dfs_sid[] = {
+ { 0x10, 0xfefebff7 },
+ { 0x14, 0xfdff7fef },
+ { 0x18, 0xfbffdefb },
+ { 0x1c, 0xb69b5555 },
+ { 0x20, 0x24929249 },
+ { 0x24, 0x49241112 },
+ { 0x28, 0x11112111 },
+ { 0x2c, 0x8102 }
+};
+
+static const struct reg_sequence msm8976_cfg_link_sid[] = {
+ { 0x40, 0xfc987 }
+};
+
+static const struct reg_sequence msm8976_cfg_lmh_sid[] = {
+ { 0x30, 0x77706db },
+ { 0x34, 0x5550249 },
+ { 0x38, 0x111 }
+};
+
+static const struct reg_sequence msm8976_cfg_ramp_en[] = {
+ { 0x50, 0x800 }, /* pre_en */
+ { 0x50, 0xc00 }, /* en */
+ { 0x50, 0x400 } /* post_en */
+};
+
+static const struct reg_sequence msm8976_cfg_ramp_dis[] = {
+ { 0x50, 0x0 }
+};
+
+static const struct qcom_ramp_controller_desc msm8976_rc_cfg = {
+ .cfg_dfs_sid = msm8976_cfg_dfs_sid,
+ .num_dfs_sids = ARRAY_SIZE(msm8976_cfg_dfs_sid),
+
+ .cfg_link_sid = msm8976_cfg_link_sid,
+ .num_link_sids = ARRAY_SIZE(msm8976_cfg_link_sid),
+
+ .cfg_lmh_sid = msm8976_cfg_lmh_sid,
+ .num_lmh_sids = ARRAY_SIZE(msm8976_cfg_lmh_sid),
+
+ .cfg_ramp_en = msm8976_cfg_ramp_en,
+ .num_ramp_en = ARRAY_SIZE(msm8976_cfg_ramp_en),
+
+ .cfg_ramp_dis = msm8976_cfg_ramp_dis,
+ .num_ramp_dis = ARRAY_SIZE(msm8976_cfg_ramp_dis),
+
+ .cmd_reg = 0x0,
+};
+
+static int qcom_ramp_controller_probe(struct platform_device *pdev)
+{
+ struct qcom_ramp_controller *qrc;
+ void __iomem *base;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ qrc = devm_kmalloc(&pdev->dev, sizeof(*qrc), GFP_KERNEL);
+ if (!qrc)
+ return -ENOMEM;
+
+ qrc->desc = device_get_match_data(&pdev->dev);
+ if (!qrc)
+ return -EINVAL;
+
+ qrc->regmap = devm_regmap_init_mmio(&pdev->dev, base, &qrc_regmap_config);
+ if (IS_ERR(qrc->regmap))
+ return PTR_ERR(qrc->regmap);
+
+ platform_set_drvdata(pdev, qrc);
+
+ return qcom_ramp_controller_start(qrc);
+}
+
+static int qcom_ramp_controller_remove(struct platform_device *pdev)
+{
+ struct qcom_ramp_controller *qrc = platform_get_drvdata(pdev);
+
+ return rc_write_cfg(qrc, qrc->desc->cfg_ramp_dis,
+ RC_DCVS_CFG_SID, qrc->desc->num_ramp_dis);
+}
+
+static const struct of_device_id qcom_ramp_controller_match_table[] = {
+ { .compatible = "qcom,msm8976-ramp-controller", .data = &msm8976_rc_cfg },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, qcom_ramp_controller_match_table);
+
+static struct platform_driver qcom_ramp_controller_driver = {
+ .driver = {
+ .name = "qcom-ramp-controller",
+ .of_match_table = qcom_ramp_controller_match_table,
+ .suppress_bind_attrs = true,
+ },
+ .probe = qcom_ramp_controller_probe,
+ .remove = qcom_ramp_controller_remove,
+};
+
+static int __init qcom_ramp_controller_init(void)
+{
+ return platform_driver_register(&qcom_ramp_controller_driver);
+}
+arch_initcall(qcom_ramp_controller_init);
+
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_DESCRIPTION("Qualcomm Ramp Controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c
index 0feaae357821..9d59ad509a5c 100644
--- a/drivers/soc/qcom/rmtfs_mem.c
+++ b/drivers/soc/qcom/rmtfs_mem.c
@@ -17,6 +17,7 @@
#include <linux/qcom_scm.h>
#define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1)
+#define NUM_MAX_VMIDS 2
static dev_t qcom_rmtfs_mem_major;
@@ -171,12 +172,12 @@ static void qcom_rmtfs_mem_release_device(struct device *dev)
static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
- struct qcom_scm_vmperm perms[2];
+ struct qcom_scm_vmperm perms[NUM_MAX_VMIDS + 1];
struct reserved_mem *rmem;
struct qcom_rmtfs_mem *rmtfs_mem;
u32 client_id;
- u32 vmid;
- int ret;
+ u32 num_vmids, vmid[NUM_MAX_VMIDS];
+ int ret, i;
rmem = of_reserved_mem_lookup(node);
if (!rmem) {
@@ -226,7 +227,18 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
goto put_device;
}
- ret = of_property_read_u32(node, "qcom,vmid", &vmid);
+ num_vmids = of_property_count_u32_elems(node, "qcom,vmid");
+ if (num_vmids < 0) {
+ dev_err(&pdev->dev, "failed to count qcom,vmid elements: %d\n", ret);
+ goto remove_cdev;
+ } else if (num_vmids > NUM_MAX_VMIDS) {
+ dev_warn(&pdev->dev,
+ "too many VMIDs (%d) specified! Only mapping first %d entries\n",
+ num_vmids, NUM_MAX_VMIDS);
+ num_vmids = NUM_MAX_VMIDS;
+ }
+
+ ret = of_property_read_u32_array(node, "qcom,vmid", vmid, num_vmids);
if (ret < 0 && ret != -EINVAL) {
dev_err(&pdev->dev, "failed to parse qcom,vmid\n");
goto remove_cdev;
@@ -238,12 +250,15 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
perms[0].vmid = QCOM_SCM_VMID_HLOS;
perms[0].perm = QCOM_SCM_PERM_RW;
- perms[1].vmid = vmid;
- perms[1].perm = QCOM_SCM_PERM_RW;
+
+ for (i = 0; i < num_vmids; i++) {
+ perms[i + 1].vmid = vmid[i];
+ perms[i + 1].perm = QCOM_SCM_PERM_RW;
+ }
rmtfs_mem->perms = BIT(QCOM_SCM_VMID_HLOS);
ret = qcom_scm_assign_mem(rmtfs_mem->addr, rmtfs_mem->size,
- &rmtfs_mem->perms, perms, 2);
+ &rmtfs_mem->perms, perms, num_vmids + 1);
if (ret < 0) {
dev_err(&pdev->dev, "assign memory failed\n");
goto remove_cdev;
diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c
index 4c2d2c296790..f20e2a49a669 100644
--- a/drivers/soc/qcom/rpmhpd.c
+++ b/drivers/soc/qcom/rpmhpd.c
@@ -187,6 +187,16 @@ static struct rpmhpd nsp = {
.res_name = "nsp.lvl",
};
+static struct rpmhpd nsp0 = {
+ .pd = { .name = "nsp0", },
+ .res_name = "nsp0.lvl",
+};
+
+static struct rpmhpd nsp1 = {
+ .pd = { .name = "nsp1", },
+ .res_name = "nsp1.lvl",
+};
+
static struct rpmhpd qphy = {
.pd = { .name = "qphy", },
.res_name = "qphy.lvl",
@@ -212,6 +222,29 @@ static const struct rpmhpd_desc sa8540p_desc = {
.num_pds = ARRAY_SIZE(sa8540p_rpmhpds),
};
+/* SA8775P RPMH power domains */
+static struct rpmhpd *sa8775p_rpmhpds[] = {
+ [SA8775P_CX] = &cx,
+ [SA8775P_CX_AO] = &cx_ao,
+ [SA8775P_EBI] = &ebi,
+ [SA8775P_GFX] = &gfx,
+ [SA8775P_LCX] = &lcx,
+ [SA8775P_LMX] = &lmx,
+ [SA8775P_MMCX] = &mmcx,
+ [SA8775P_MMCX_AO] = &mmcx_ao,
+ [SA8775P_MXC] = &mxc,
+ [SA8775P_MXC_AO] = &mxc_ao,
+ [SA8775P_MX] = &mx,
+ [SA8775P_MX_AO] = &mx_ao,
+ [SA8775P_NSP0] = &nsp0,
+ [SA8775P_NSP1] = &nsp1,
+};
+
+static const struct rpmhpd_desc sa8775p_desc = {
+ .rpmhpds = sa8775p_rpmhpds,
+ .num_pds = ARRAY_SIZE(sa8775p_rpmhpds),
+};
+
/* SDM670 RPMH powerdomains */
static struct rpmhpd *sdm670_rpmhpds[] = {
[SDM670_CX] = &cx_w_mx_parent,
@@ -487,6 +520,7 @@ static const struct rpmhpd_desc sc8280xp_desc = {
static const struct of_device_id rpmhpd_match_table[] = {
{ .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc },
{ .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc },
+ { .compatible = "qcom,sa8775p-rpmhpd", .data = &sa8775p_desc },
{ .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc },
{ .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc },
{ .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc },
diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c
index f0db6a10cf4e..337b1ad1cd3b 100644
--- a/drivers/soc/qcom/rpmpd.c
+++ b/drivers/soc/qcom/rpmpd.c
@@ -471,23 +471,6 @@ static const struct rpmpd_desc qcm2290_desc = {
.max_state = RPM_SMD_LEVEL_TURBO_NO_CPR,
};
-static struct rpmpd *sm4250_rpmpds[] = {
- [SM4250_VDDCX] = &sm6115_vddcx,
- [SM4250_VDDCX_AO] = &sm6115_vddcx_ao,
- [SM4250_VDDCX_VFL] = &sm6115_vddcx_vfl,
- [SM4250_VDDMX] = &sm6115_vddmx,
- [SM4250_VDDMX_AO] = &sm6115_vddmx_ao,
- [SM4250_VDDMX_VFL] = &sm6115_vddmx_vfl,
- [SM4250_VDD_LPI_CX] = &sm6115_vdd_lpi_cx,
- [SM4250_VDD_LPI_MX] = &sm6115_vdd_lpi_mx,
-};
-
-static const struct rpmpd_desc sm4250_desc = {
- .rpmpds = sm4250_rpmpds,
- .num_pds = ARRAY_SIZE(sm4250_rpmpds),
- .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR,
-};
-
static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc },
{ .compatible = "qcom,msm8226-rpmpd", .data = &msm8226_desc },
@@ -502,7 +485,6 @@ static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,qcm2290-rpmpd", .data = &qcm2290_desc },
{ .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc },
{ .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc },
- { .compatible = "qcom,sm4250-rpmpd", .data = &sm4250_desc },
{ .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc },
{ .compatible = "qcom,sm6125-rpmpd", .data = &sm6125_desc },
{ .compatible = "qcom,sm6375-rpmpd", .data = &sm6375_desc },
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index ebcbf9b9c18b..3b970a80f3aa 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -169,6 +169,11 @@ struct socinfo {
__le32 ndefective_parts_array_offset;
/* Version 15 */
__le32 nmodem_supported;
+ /* Version 16 */
+ __le32 feature_code;
+ __le32 pcode;
+ __le32 npartnamemap_offset;
+ __le32 nnum_partname_mapping;
};
#ifdef CONFIG_DEBUG_FS
@@ -189,6 +194,8 @@ struct socinfo_params {
u32 num_defective_parts;
u32 ndefective_parts_array_offset;
u32 nmodem_supported;
+ u32 feature_code;
+ u32 pcode;
};
struct smem_image_version {
@@ -214,44 +221,68 @@ struct soc_id {
};
static const struct soc_id soc_id[] = {
+ { qcom_board_id(MSM8260) },
+ { qcom_board_id(MSM8660) },
+ { qcom_board_id(APQ8060) },
{ qcom_board_id(MSM8960) },
{ qcom_board_id(APQ8064) },
+ { qcom_board_id(MSM8930) },
+ { qcom_board_id(MSM8630) },
+ { qcom_board_id(MSM8230) },
+ { qcom_board_id(APQ8030) },
+ { qcom_board_id(MSM8627) },
+ { qcom_board_id(MSM8227) },
{ qcom_board_id(MSM8660A) },
{ qcom_board_id(MSM8260A) },
{ qcom_board_id(APQ8060A) },
{ qcom_board_id(MSM8974) },
+ { qcom_board_id(MSM8225) },
+ { qcom_board_id(MSM8625) },
{ qcom_board_id(MPQ8064) },
{ qcom_board_id(MSM8960AB) },
{ qcom_board_id(APQ8060AB) },
{ qcom_board_id(MSM8260AB) },
{ qcom_board_id(MSM8660AB) },
+ { qcom_board_id(MSM8930AA) },
+ { qcom_board_id(MSM8630AA) },
+ { qcom_board_id(MSM8230AA) },
{ qcom_board_id(MSM8626) },
{ qcom_board_id(MSM8610) },
{ qcom_board_id(APQ8064AB) },
+ { qcom_board_id(MSM8930AB) },
+ { qcom_board_id(MSM8630AB) },
+ { qcom_board_id(MSM8230AB) },
+ { qcom_board_id(APQ8030AB) },
{ qcom_board_id(MSM8226) },
{ qcom_board_id(MSM8526) },
+ { qcom_board_id(APQ8030AA) },
{ qcom_board_id(MSM8110) },
{ qcom_board_id(MSM8210) },
{ qcom_board_id(MSM8810) },
{ qcom_board_id(MSM8212) },
{ qcom_board_id(MSM8612) },
{ qcom_board_id(MSM8112) },
+ { qcom_board_id(MSM8125) },
{ qcom_board_id(MSM8225Q) },
{ qcom_board_id(MSM8625Q) },
{ qcom_board_id(MSM8125Q) },
{ qcom_board_id(APQ8064AA) },
{ qcom_board_id(APQ8084) },
+ { qcom_board_id(MSM8130) },
+ { qcom_board_id(MSM8130AA) },
+ { qcom_board_id(MSM8130AB) },
+ { qcom_board_id(MSM8627AA) },
+ { qcom_board_id(MSM8227AA) },
{ qcom_board_id(APQ8074) },
{ qcom_board_id(MSM8274) },
{ qcom_board_id(MSM8674) },
+ { qcom_board_id(MDM9635) },
{ qcom_board_id_named(MSM8974PRO_AC, "MSM8974PRO-AC") },
{ qcom_board_id(MSM8126) },
{ qcom_board_id(APQ8026) },
{ qcom_board_id(MSM8926) },
{ qcom_board_id(MSM8326) },
{ qcom_board_id(MSM8916) },
- { qcom_board_id(MSM8956) },
- { qcom_board_id(MSM8976) },
{ qcom_board_id(MSM8994) },
{ qcom_board_id_named(APQ8074PRO_AA, "APQ8074PRO-AA") },
{ qcom_board_id_named(APQ8074PRO_AB, "APQ8074PRO-AB") },
@@ -273,32 +304,72 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(MSM8510) },
{ qcom_board_id(MSM8512) },
{ qcom_board_id(MSM8936) },
+ { qcom_board_id(MDM9640) },
{ qcom_board_id(MSM8939) },
{ qcom_board_id(APQ8036) },
{ qcom_board_id(APQ8039) },
+ { qcom_board_id(MSM8236) },
+ { qcom_board_id(MSM8636) },
+ { qcom_board_id(MSM8909) },
{ qcom_board_id(MSM8996) },
{ qcom_board_id(APQ8016) },
{ qcom_board_id(MSM8216) },
{ qcom_board_id(MSM8116) },
{ qcom_board_id(MSM8616) },
{ qcom_board_id(MSM8992) },
+ { qcom_board_id(APQ8092) },
{ qcom_board_id(APQ8094) },
+ { qcom_board_id(MSM8209) },
+ { qcom_board_id(MSM8208) },
+ { qcom_board_id(MDM9209) },
+ { qcom_board_id(MDM9309) },
+ { qcom_board_id(MDM9609) },
+ { qcom_board_id(MSM8239) },
+ { qcom_board_id(MSM8952) },
+ { qcom_board_id(APQ8009) },
+ { qcom_board_id(MSM8956) },
+ { qcom_board_id(MSM8929) },
+ { qcom_board_id(MSM8629) },
+ { qcom_board_id(MSM8229) },
+ { qcom_board_id(APQ8029) },
+ { qcom_board_id(APQ8056) },
+ { qcom_board_id(MSM8609) },
+ { qcom_board_id(APQ8076) },
+ { qcom_board_id(MSM8976) },
+ { qcom_board_id(MDM9650) },
+ { qcom_board_id(MDM9655) },
+ { qcom_board_id(MDM9250) },
+ { qcom_board_id(MDM9255) },
+ { qcom_board_id(MDM9350) },
+ { qcom_board_id(APQ8052) },
{ qcom_board_id(MDM9607) },
{ qcom_board_id(APQ8096) },
{ qcom_board_id(MSM8998) },
{ qcom_board_id(MSM8953) },
+ { qcom_board_id(MSM8937) },
+ { qcom_board_id(APQ8037) },
{ qcom_board_id(MDM8207) },
{ qcom_board_id(MDM9207) },
{ qcom_board_id(MDM9307) },
{ qcom_board_id(MDM9628) },
+ { qcom_board_id(MSM8909W) },
+ { qcom_board_id(APQ8009W) },
+ { qcom_board_id(MSM8996L) },
+ { qcom_board_id(MSM8917) },
{ qcom_board_id(APQ8053) },
{ qcom_board_id(MSM8996SG) },
+ { qcom_board_id(APQ8017) },
+ { qcom_board_id(MSM8217) },
+ { qcom_board_id(MSM8617) },
{ qcom_board_id(MSM8996AU) },
{ qcom_board_id(APQ8096AU) },
{ qcom_board_id(APQ8096SG) },
+ { qcom_board_id(MSM8940) },
+ { qcom_board_id(SDX201) },
{ qcom_board_id(SDM660) },
{ qcom_board_id(SDM630) },
{ qcom_board_id(APQ8098) },
+ { qcom_board_id(MSM8920) },
{ qcom_board_id(SDM845) },
{ qcom_board_id(MDM9206) },
{ qcom_board_id(IPQ8074) },
@@ -306,6 +377,8 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(SDM658) },
{ qcom_board_id(SDA658) },
{ qcom_board_id(SDA630) },
+ { qcom_board_id(MSM8905) },
+ { qcom_board_id(SDX202) },
{ qcom_board_id(SDM450) },
{ qcom_board_id(SM8150) },
{ qcom_board_id(SDA845) },
@@ -317,10 +390,15 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(SDM632) },
{ qcom_board_id(SDA632) },
{ qcom_board_id(SDA450) },
+ { qcom_board_id(SDM439) },
+ { qcom_board_id(SDM429) },
{ qcom_board_id(SM8250) },
{ qcom_board_id(SA8155) },
+ { qcom_board_id(SDA439) },
+ { qcom_board_id(SDA429) },
{ qcom_board_id(IPQ8070) },
{ qcom_board_id(IPQ8071) },
+ { qcom_board_id(QM215) },
{ qcom_board_id(IPQ8072A) },
{ qcom_board_id(IPQ8074A) },
{ qcom_board_id(IPQ8076A) },
@@ -330,18 +408,20 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(IPQ8071A) },
{ qcom_board_id(IPQ6018) },
{ qcom_board_id(IPQ6028) },
+ { qcom_board_id(SDM429W) },
{ qcom_board_id(SM4250) },
{ qcom_board_id(IPQ6000) },
{ qcom_board_id(IPQ6010) },
{ qcom_board_id(SC7180) },
{ qcom_board_id(SM6350) },
+ { qcom_board_id(QCM2150) },
+ { qcom_board_id(SDA429W) },
{ qcom_board_id(SM8350) },
{ qcom_board_id(SM6115) },
{ qcom_board_id(SC8280XP) },
{ qcom_board_id(IPQ6005) },
{ qcom_board_id(QRB5165) },
{ qcom_board_id(SM8450) },
- { qcom_board_id(SM8550) },
{ qcom_board_id(SM7225) },
{ qcom_board_id(SA8295P) },
{ qcom_board_id(SA8540P) },
@@ -352,6 +432,7 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(SC7280) },
{ qcom_board_id(SC7180P) },
{ qcom_board_id(SM6375) },
+ { qcom_board_id(SM8550) },
{ qcom_board_id(QRU1000) },
{ qcom_board_id(QDU1000) },
{ qcom_board_id(QDU1010) },
@@ -512,6 +593,15 @@ static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo,
&qcom_socinfo->info.fmt);
switch (qcom_socinfo->info.fmt) {
+ case SOCINFO_VERSION(0, 16):
+ qcom_socinfo->info.feature_code = __le32_to_cpu(info->feature_code);
+ qcom_socinfo->info.pcode = __le32_to_cpu(info->pcode);
+
+ debugfs_create_u32("feature_code", 0444, qcom_socinfo->dbg_root,
+ &qcom_socinfo->info.feature_code);
+ debugfs_create_u32("pcode", 0444, qcom_socinfo->dbg_root,
+ &qcom_socinfo->info.pcode);
+ fallthrough;
case SOCINFO_VERSION(0, 15):
qcom_socinfo->info.nmodem_supported = __le32_to_cpu(info->nmodem_supported);
diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h
index f7aef3f310d7..22d7ba17804b 100644
--- a/include/dt-bindings/arm/qcom,ids.h
+++ b/include/dt-bindings/arm/qcom,ids.h
@@ -11,36 +11,62 @@
* The MSM chipset and hardware revision used by Qualcomm bootloaders, DTS for
* older chipsets (qcom,msm-id) and in socinfo driver:
*/
+#define QCOM_ID_MSM8260 70
+#define QCOM_ID_MSM8660 71
+#define QCOM_ID_APQ8060 86
#define QCOM_ID_MSM8960 87
#define QCOM_ID_APQ8064 109
+#define QCOM_ID_MSM8930 116
+#define QCOM_ID_MSM8630 117
+#define QCOM_ID_MSM8230 118
+#define QCOM_ID_APQ8030 119
+#define QCOM_ID_MSM8627 120
+#define QCOM_ID_MSM8227 121
#define QCOM_ID_MSM8660A 122
#define QCOM_ID_MSM8260A 123
#define QCOM_ID_APQ8060A 124
#define QCOM_ID_MSM8974 126
+#define QCOM_ID_MSM8225 127
+#define QCOM_ID_MSM8625 129
#define QCOM_ID_MPQ8064 130
#define QCOM_ID_MSM8960AB 138
#define QCOM_ID_APQ8060AB 139
#define QCOM_ID_MSM8260AB 140
#define QCOM_ID_MSM8660AB 141
+#define QCOM_ID_MSM8930AA 142
+#define QCOM_ID_MSM8630AA 143
+#define QCOM_ID_MSM8230AA 144
#define QCOM_ID_MSM8626 145
#define QCOM_ID_MSM8610 147
#define QCOM_ID_APQ8064AB 153
+#define QCOM_ID_MSM8930AB 154
+#define QCOM_ID_MSM8630AB 155
+#define QCOM_ID_MSM8230AB 156
+#define QCOM_ID_APQ8030AB 157
#define QCOM_ID_MSM8226 158
#define QCOM_ID_MSM8526 159
+#define QCOM_ID_APQ8030AA 160
#define QCOM_ID_MSM8110 161
#define QCOM_ID_MSM8210 162
#define QCOM_ID_MSM8810 163
#define QCOM_ID_MSM8212 164
#define QCOM_ID_MSM8612 165
#define QCOM_ID_MSM8112 166
+#define QCOM_ID_MSM8125 167
#define QCOM_ID_MSM8225Q 168
#define QCOM_ID_MSM8625Q 169
#define QCOM_ID_MSM8125Q 170
#define QCOM_ID_APQ8064AA 172
#define QCOM_ID_APQ8084 178
+#define QCOM_ID_MSM8130 179
+#define QCOM_ID_MSM8130AA 180
+#define QCOM_ID_MSM8130AB 181
+#define QCOM_ID_MSM8627AA 182
+#define QCOM_ID_MSM8227AA 183
#define QCOM_ID_APQ8074 184
#define QCOM_ID_MSM8274 185
#define QCOM_ID_MSM8674 186
+#define QCOM_ID_MDM9635 187
#define QCOM_ID_MSM8974PRO_AC 194
#define QCOM_ID_MSM8126 198
#define QCOM_ID_APQ8026 199
@@ -68,34 +94,72 @@
#define QCOM_ID_MSM8510 225
#define QCOM_ID_MSM8512 226
#define QCOM_ID_MSM8936 233
+#define QCOM_ID_MDM9640 234
#define QCOM_ID_MSM8939 239
#define QCOM_ID_APQ8036 240
#define QCOM_ID_APQ8039 241
+#define QCOM_ID_MSM8236 242
+#define QCOM_ID_MSM8636 243
+#define QCOM_ID_MSM8909 245
#define QCOM_ID_MSM8996 246
#define QCOM_ID_APQ8016 247
#define QCOM_ID_MSM8216 248
#define QCOM_ID_MSM8116 249
#define QCOM_ID_MSM8616 250
#define QCOM_ID_MSM8992 251
+#define QCOM_ID_APQ8092 252
#define QCOM_ID_APQ8094 253
+#define QCOM_ID_MSM8209 258
+#define QCOM_ID_MSM8208 259
+#define QCOM_ID_MDM9209 260
+#define QCOM_ID_MDM9309 261
+#define QCOM_ID_MDM9609 262
+#define QCOM_ID_MSM8239 263
+#define QCOM_ID_MSM8952 264
+#define QCOM_ID_APQ8009 265
#define QCOM_ID_MSM8956 266
+#define QCOM_ID_MSM8929 268
+#define QCOM_ID_MSM8629 269
+#define QCOM_ID_MSM8229 270
+#define QCOM_ID_APQ8029 271
+#define QCOM_ID_APQ8056 274
+#define QCOM_ID_MSM8609 275
+#define QCOM_ID_APQ8076 277
#define QCOM_ID_MSM8976 278
+#define QCOM_ID_MDM9650 279
+#define QCOM_ID_MDM9655 283
+#define QCOM_ID_MDM9250 284
+#define QCOM_ID_MDM9255 285
+#define QCOM_ID_MDM9350 286
+#define QCOM_ID_APQ8052 289
#define QCOM_ID_MDM9607 290
#define QCOM_ID_APQ8096 291
#define QCOM_ID_MSM8998 292
#define QCOM_ID_MSM8953 293
+#define QCOM_ID_MSM8937 294
+#define QCOM_ID_APQ8037 295
#define QCOM_ID_MDM8207 296
#define QCOM_ID_MDM9207 297
#define QCOM_ID_MDM9307 298
#define QCOM_ID_MDM9628 299
+#define QCOM_ID_MSM8909W 300
+#define QCOM_ID_APQ8009W 301
+#define QCOM_ID_MSM8996L 302
+#define QCOM_ID_MSM8917 303
#define QCOM_ID_APQ8053 304
#define QCOM_ID_MSM8996SG 305
+#define QCOM_ID_APQ8017 307
+#define QCOM_ID_MSM8217 308
+#define QCOM_ID_MSM8617 309
#define QCOM_ID_MSM8996AU 310
#define QCOM_ID_APQ8096AU 311
#define QCOM_ID_APQ8096SG 312
+#define QCOM_ID_MSM8940 313
+#define QCOM_ID_SDX201 314
#define QCOM_ID_SDM660 317
#define QCOM_ID_SDM630 318
#define QCOM_ID_APQ8098 319
+#define QCOM_ID_MSM8920 320
#define QCOM_ID_SDM845 321
#define QCOM_ID_MDM9206 322
#define QCOM_ID_IPQ8074 323
@@ -103,6 +167,8 @@
#define QCOM_ID_SDM658 325
#define QCOM_ID_SDA658 326
#define QCOM_ID_SDA630 327
+#define QCOM_ID_MSM8905 331
+#define QCOM_ID_SDX202 333
#define QCOM_ID_SDM450 338
#define QCOM_ID_SM8150 339
#define QCOM_ID_SDA845 341
@@ -114,10 +180,15 @@
#define QCOM_ID_SDM632 349
#define QCOM_ID_SDA632 350
#define QCOM_ID_SDA450 351
+#define QCOM_ID_SDM439 353
+#define QCOM_ID_SDM429 354
#define QCOM_ID_SM8250 356
#define QCOM_ID_SA8155 362
+#define QCOM_ID_SDA439 363
+#define QCOM_ID_SDA429 364
#define QCOM_ID_IPQ8070 375
#define QCOM_ID_IPQ8071 376
+#define QCOM_ID_QM215 386
#define QCOM_ID_IPQ8072A 389
#define QCOM_ID_IPQ8074A 390
#define QCOM_ID_IPQ8076A 391
@@ -127,11 +198,14 @@
#define QCOM_ID_IPQ8071A 396
#define QCOM_ID_IPQ6018 402
#define QCOM_ID_IPQ6028 403
+#define QCOM_ID_SDM429W 416
#define QCOM_ID_SM4250 417
#define QCOM_ID_IPQ6000 421
#define QCOM_ID_IPQ6010 422
#define QCOM_ID_SC7180 425
#define QCOM_ID_SM6350 434
+#define QCOM_ID_QCM2150 436
+#define QCOM_ID_SDA429W 437
#define QCOM_ID_SM8350 439
#define QCOM_ID_SM6115 444
#define QCOM_ID_SC8280XP 449
@@ -165,6 +239,7 @@
#define QCOM_BOARD_ID_MTP 8
#define QCOM_BOARD_ID_DRAGONBOARD 10
+#define QCOM_BOARD_ID_QRD 11
#define QCOM_BOARD_ID_SBC 24
#endif /* _DT_BINDINGS_ARM_QCOM_IDS_H */
diff --git a/include/dt-bindings/firmware/qcom,scm.h b/include/dt-bindings/firmware/qcom,scm.h
new file mode 100644
index 000000000000..1a4e68fa0744
--- /dev/null
+++ b/include/dt-bindings/firmware/qcom,scm.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * Copyright (c) 2010-2015, 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015 Linaro Ltd.
+ */
+
+#ifndef _DT_BINDINGS_FIRMWARE_QCOM_SCM_H
+#define _DT_BINDINGS_FIRMWARE_QCOM_SCM_H
+
+#define QCOM_SCM_VMID_HLOS 0x3
+#define QCOM_SCM_VMID_MSS_MSA 0xF
+#define QCOM_SCM_VMID_WLAN 0x18
+#define QCOM_SCM_VMID_WLAN_CE 0x19
+#define QCOM_SCM_VMID_NAV 0x2B
+
+#endif
diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h
index 1e19e258a74d..6df4ee400ef8 100644
--- a/include/dt-bindings/power/qcom-rpmpd.h
+++ b/include/dt-bindings/power/qcom-rpmpd.h
@@ -4,6 +4,25 @@
#ifndef _DT_BINDINGS_POWER_QCOM_RPMPD_H
#define _DT_BINDINGS_POWER_QCOM_RPMPD_H
+/* SA8775P Power Domain Indexes */
+#define SA8775P_CX 0
+#define SA8775P_CX_AO 1
+#define SA8775P_DDR 2
+#define SA8775P_EBI 3
+#define SA8775P_GFX 4
+#define SA8775P_LCX 5
+#define SA8775P_LMX 6
+#define SA8775P_MMCX 7
+#define SA8775P_MMCX_AO 8
+#define SA8775P_MSS 9
+#define SA8775P_MX 10
+#define SA8775P_MX_AO 11
+#define SA8775P_MXC 12
+#define SA8775P_MXC_AO 13
+#define SA8775P_NSP0 14
+#define SA8775P_NSP1 15
+#define SA8775P_XO 16
+
/* SDM670 Power Domain Indexes */
#define SDM670_MX 0
#define SDM670_MX_AO 1
@@ -306,16 +325,6 @@
#define SDM660_SSCMX 8
#define SDM660_SSCMX_VFL 9
-/* SM4250 Power Domains */
-#define SM4250_VDDCX 0
-#define SM4250_VDDCX_AO 1
-#define SM4250_VDDCX_VFL 2
-#define SM4250_VDDMX 3
-#define SM4250_VDDMX_AO 4
-#define SM4250_VDDMX_VFL 5
-#define SM4250_VDD_LPI_CX 6
-#define SM4250_VDD_LPI_MX 7
-
/* SM6115 Power Domains */
#define SM6115_VDDCX 0
#define SM6115_VDDCX_AO 1
diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h
index f8335644a01a..1e449a5d7f5c 100644
--- a/include/linux/qcom_scm.h
+++ b/include/linux/qcom_scm.h
@@ -9,6 +9,8 @@
#include <linux/types.h>
#include <linux/cpumask.h>
+#include <dt-bindings/firmware/qcom,scm.h>
+
#define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
#define QCOM_SCM_CPU_PWR_DOWN_L2_ON 0x0
#define QCOM_SCM_CPU_PWR_DOWN_L2_OFF 0x1
@@ -51,10 +53,6 @@ enum qcom_scm_ice_cipher {
QCOM_SCM_ICE_CIPHER_AES_256_CBC = 4,
};
-#define QCOM_SCM_VMID_HLOS 0x3
-#define QCOM_SCM_VMID_MSS_MSA 0xF
-#define QCOM_SCM_VMID_WLAN 0x18
-#define QCOM_SCM_VMID_WLAN_CE 0x19
#define QCOM_SCM_PERM_READ 0x4
#define QCOM_SCM_PERM_WRITE 0x2
#define QCOM_SCM_PERM_EXEC 0x1
diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h
index 23c5b30f3511..be98aebcb3e1 100644
--- a/include/linux/soc/qcom/apr.h
+++ b/include/linux/soc/qcom/apr.h
@@ -153,7 +153,7 @@ typedef struct apr_device gpr_device_t;
struct apr_driver {
int (*probe)(struct apr_device *sl);
- int (*remove)(struct apr_device *sl);
+ void (*remove)(struct apr_device *sl);
int (*callback)(struct apr_device *a,
struct apr_resp_pkt *d);
int (*gpr_callback)(struct gpr_resp_pkt *d, void *data, int op);
diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c
index 5358fefd4210..49cfb32cd209 100644
--- a/sound/soc/qcom/qdsp6/q6core.c
+++ b/sound/soc/qcom/qdsp6/q6core.c
@@ -339,7 +339,7 @@ static int q6core_probe(struct apr_device *adev)
return 0;
}
-static int q6core_exit(struct apr_device *adev)
+static void q6core_exit(struct apr_device *adev)
{
struct q6core *core = dev_get_drvdata(&adev->dev);
@@ -350,8 +350,6 @@ static int q6core_exit(struct apr_device *adev)
g_core = NULL;
kfree(core);
-
- return 0;
}
#ifdef CONFIG_OF