Commit 01e9d2c6 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'qcom-drivers-for-6.3' of...

Merge tag 'qcom-drivers-for-6.3' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into soc/drivers

Qualcomm driver updates for v6.3

This introduces a new driver for the Data Capture and Compare block,
which provides a mechanism for capturing hardware state (access MMIO
registers) either upon request of triggered automatically e.g. upon a
watchdog bite, for post mortem analysis.

The remote filesystem memory share driver gains support for having its
memory bound to more than a single VMID.

The SCM driver gains the minimal support needed to support a new
mechanism where secure world can put calls on hold and later request
them to be retried.

Support for the new SA8775P platform is added to rpmhpd, QDU1000 is
added to the SCM driver and a long list of platforms are added to the
socinfo driver. Support for socinfo data revision 16 is also introduced.

Lastly a driver to program the ramp controller in MSM8976 is introduced.

* tag 'qcom-drivers-for-6.3' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: (33 commits)
  firmware: qcom: scm: Add wait-queue handling logic
  dt-bindings: firmware: qcom,scm: Add optional interrupt
  Revert "dt-bindings: power: rpmpd: Add SM4250 support"
  Revert "soc: qcom: rpmpd: Add SM4250 support"
  soc: qcom: socinfo: Add a bunch of older SoCs
  dt-bindings: arm: qcom,ids: Add a bunch of older SoCs
  dt-bindings: arm: qcom,ids: Add QRD board ID
  soc: qcom: socinfo: Fix soc_id order
  dt-bindings: soc: qcom: smd-rpm: Exclude MSM8936 from glink-channels
  dt-bindings: firmware: qcom: scm: Separate VMIDs from header to bindings
  soc: qcom: rmtfs: Optionally map RMTFS to more VMs
  dt-bindings: reserved-memory: rmtfs: Make qcom,vmid an array
  dt-bindings: firmware: scm: Add QDU1000/QRU1000 compatible
  dt-bindings: firmware: qcom,scm: narrow clocks and interconnects
  dt-bindings: firmware: qcom,scm: document MSM8226 clocks
  soc: qcom: ramp_controller: Make things static
  soc: qcom: rmphpd: add power domains for sa8775p
  dt-bindings: power: qcom,rpmpd: document sa8775p
  PM: AVS: qcom-cpr: Fix an error handling path in cpr_probe()
  soc: qcom: dcc: rewrite description of dcc sysfs files
  ...

Link: https://lore.kernel.org/r/20230126163008.3676950-1-andersson@kernel.orgSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 4a54ecf3 6bf32599
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.
......@@ -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
......
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>;
};
};
};
......@@ -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
......
......@@ -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
......
......@@ -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
# 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>;
};
# 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>;
};
# 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>;
};
};
};
......@@ -80,6 +80,7 @@ if:
enum:
- qcom,rpm-apq8084
- qcom,rpm-msm8916
- qcom,rpm-msm8936
- qcom,rpm-msm8974
- qcom,rpm-msm8976
- qcom,rpm-msm8953
......
......@@ -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
......
......@@ -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;
......
......@@ -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();
/*
......
......@@ -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)
{
......
......@@ -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
......
......@@ -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
......
......@@ -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;
}
......
......@@ -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)
......
// 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");
// 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");
......@@ -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;
......
......@@ -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 },
......
......@@ -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 },
......
......@@ -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);
......
......@@ -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 */
/* 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
......@@ -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
......
......@@ -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
......
......@@ -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);
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment