Qualcomm driver updates for v5.16

This drops the use of power-domains for exposing the load_state from the
 QMP driver to clients, to avoid issues related to system suspend.
 
 SMP2P becomes wakeup capable, to allow dying remoteprocs to wake up
 Linux from suspend to perform recovery.
 
 It adds RPM power-domain support for SM6350 and MSM8953 and base RPM
 support for MSM8953 and QCM2290.
 
 It adds support for MSM8996, SDM630 and SDM660 in the SPM driver, which
 will enable the introduction of proper voltage scaling of the CPU
 subsystem.
 
 Support for releasing secondary CPUs on MSM8226 is introduced.
 
 The Asynchronous Packet Router (APR) driver is extended to support the
 new Generic Packet Router (GPR) variant, which is used to communicate
 with the firmware in the new AudioReach audio driver.
 
 Lastly it transitions a number of drivers to safer string functions, as
 well as switching things to use devm_platform_ioremap_resource().
 -----BEGIN PGP SIGNATURE-----
 
 iQJPBAABCAA5FiEEBd4DzF816k8JZtUlCx85Pw2ZrcUFAmFlxugbHGJqb3JuLmFu
 ZGVyc3NvbkBsaW5hcm8ub3JnAAoJEAsfOT8Nma3F3A0P/ixLiu0XktPOyFZ+3Ag/
 5KKan6wlH8JO0AfxbOolk0MkC1/8uk1V+Kuw4b/22jTO4w/9Illi9jSmV0wR3IBh
 26sWpyxNK7N+7uAnBDqlZekQYTg5xKqJGn69kxe0oIm8yAogGkxZnlIWt7JGtWS0
 NcyLLe9gS/B/q6ppTkxlYhSMB3Qp6bU/lRjBWTcHlxaNxGyNbGw4zp5ncze8ui9R
 s0JdFhJ/T1ptuWuIhrEWXEVapQtf0pgPvYSWxRYoBRU4N1F4HcRcLVpUU6eSvFEg
 BKxN11dCW4o0njLBpdPtg3u7AL31A95OPFiVwpWEb6Wov1DAdQYfYSoc7r2uqKvL
 QgvE/Fq1uPNPsFH2AgJjmku7nb2S0UP2msIhsZjugwxCQXtHgv2uPdYSN/MOudEZ
 MdjbbAHKW9+TR4gTVzA7b7OVzB73+tjHo/4hUjy8J5RlRKujbzHHXLRjvpNAILqn
 y457eUFqYxR1C3CKy2GrgR/PvNcBdDVq0ikuv4elYZEIaPL/Wxg6V7RJSHzgAgU/
 7XArrGvCXdLXy/e07KJ4Su74OBOsXnqLLr+Xsw5+h42XCnDfmI3caQlQjTXxJxN4
 EifHOoPCa8JNtXzWKSBSO7Pb5teSYh5XlAQs8nGVbrKMhly/MDLeNzSL+uu8DGNw
 B9Cf/mM9bhs3Q69Z37WgOdK0
 =FE4j
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmFnQuMACgkQmmx57+YA
 GNk90g/9F5Sh8wfMQ3WYZMu10L431dXYKkafccK8SFgNhTxTuJ7xPbJzXxFQW6Qp
 Ky0DAKcJB/tW/jCMh7L/d7pET4tedRncZd6Pk8kkn8uRC9qiTEFyPKP+4trADnhw
 Q/gf8xbEJz13aDzFbfW2Ko9Rx0HJQ/BSWVzZ5fpkUmoSSBnxm+rq9T3e10BNX1QT
 /Bg1M77zyYWtlPKexjxBEz7EZuQIEmN9gkCCriYTMw34hfVYszsNkIZnIGfe/wAa
 F6iSjIwVqf4DbM6QzabNoCUzZf50Ij4Vdlhvk5FUt3quPIvcULzIxQxI3o/J5sJ2
 2mowPuiVjmqx4R8XFILod0+KU4NNUGcb2YFLZg+XjbopArfLZaa2OV40m2QDY/Ay
 /BHXBtxLDDcDqwOX6Y7NEfbNFUqLlFv00vuZEJ9KT/19ZfJgCw4qoxw44aldjQEo
 3pvEACOHLKoI+FOeb6LHL0erfc1jz/qul37oWI361rDIfWsWsZtbyc+gItMDLGYa
 C1qUHoP5vMmse0tILT6AJNR23aGJFapKkzTNWB6j5Pi/0sdv3zGbhxubf+TxMRtx
 5BhEP2N5pHdKVopxVSG2JVOtT12jE68tV0E00erEoPIG35C+DyADn5i9lg4g20Ix
 MwxwtgAC0cws8tpwsDvFOCj8SPn9HDtEGMJMejo5xuEIxqjAb2E=
 =LLU7
 -----END PGP SIGNATURE-----

Merge tag 'qcom-drivers-for-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into arm/drivers

Qualcomm driver updates for v5.16

This drops the use of power-domains for exposing the load_state from the
QMP driver to clients, to avoid issues related to system suspend.

SMP2P becomes wakeup capable, to allow dying remoteprocs to wake up
Linux from suspend to perform recovery.

It adds RPM power-domain support for SM6350 and MSM8953 and base RPM
support for MSM8953 and QCM2290.

It adds support for MSM8996, SDM630 and SDM660 in the SPM driver, which
will enable the introduction of proper voltage scaling of the CPU
subsystem.

Support for releasing secondary CPUs on MSM8226 is introduced.

The Asynchronous Packet Router (APR) driver is extended to support the
new Generic Packet Router (GPR) variant, which is used to communicate
with the firmware in the new AudioReach audio driver.

Lastly it transitions a number of drivers to safer string functions, as
well as switching things to use devm_platform_ioremap_resource().

* tag 'qcom-drivers-for-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: (40 commits)
  soc: qcom: apr: Add GPR support
  soc: dt-bindings: qcom: add gpr bindings
  soc: qcom: apr: make code more reuseable
  soc: dt-bindings: qcom: apr: deprecate qcom,apr-domain property
  soc: dt-bindings: qcom: apr: convert to yaml
  dt-bindings: soc: qcom: aoss: Delete unused power-domain definitions
  dt-bindings: msm/dp: Remove aoss-qmp header
  soc: qcom: aoss: Drop power domain support
  dt-bindings: soc: qcom: aoss: Drop the load state power-domain
  soc: qcom: smp2p: Add wakeup capability to SMP2P IRQ
  dt-bindings: power: rpmpd: Add SM6350 to rpmpd binding
  dt-bindings: soc: qcom: aoss: Add SM6350 compatible
  soc: qcom: llcc: Disable MMUHWT retention
  soc: qcom: smd-rpm: Add QCM2290 compatible
  dt-bindings: soc: qcom: smd-rpm: Add QCM2290 compatible
  firmware: qcom_scm: Add compatible for MSM8953 SoC
  dt-bindings: firmware: qcom-scm: Document msm8953 bindings
  soc: qcom: pdr: Prefer strscpy over strcpy
  soc: qcom: rpmh-rsc: Make use of the helper function devm_platform_ioremap_resource_byname()
  soc: qcom: gsbi: Make use of the helper function devm_platform_ioremap_resource()
  ...

Link: https://lore.kernel.org/r/20211012173442.1017010-1-bjorn.andersson@linaro.org
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2021-10-13 22:34:41 +02:00
Родитель 030f4e72aa f27591125a
Коммит 936fc53f3d
36 изменённых файлов: 1263 добавлений и 602 удалений

Просмотреть файл

@ -209,6 +209,7 @@ properties:
- qcom,gcc-msm8660
- qcom,kpss-acc-v1
- qcom,kpss-acc-v2
- qcom,msm8226-smp
- renesas,apmu
- renesas,r9a06g032-smp
- rockchip,rk3036-smp
@ -293,7 +294,7 @@ properties:
Specifies the ACC* node associated with this CPU.
Required for systems that have an "enable-method" property
value of "qcom,kpss-acc-v1" or "qcom,kpss-acc-v2"
value of "qcom,kpss-acc-v1", "qcom,kpss-acc-v2" or "qcom,msm8226-smp"
* arm/msm/qcom,kpss-acc.txt

Просмотреть файл

@ -95,7 +95,6 @@ examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/qcom,dispcc-sc7180.h>
#include <dt-bindings/power/qcom-aoss-qmp.h>
#include <dt-bindings/power/qcom-rpmpd.h>
displayport-controller@ae90000 {

Просмотреть файл

@ -13,8 +13,10 @@ Required properties:
* "qcom,scm-ipq806x"
* "qcom,scm-ipq8074"
* "qcom,scm-mdm9607"
* "qcom,scm-msm8226"
* "qcom,scm-msm8660"
* "qcom,scm-msm8916"
* "qcom,scm-msm8953"
* "qcom,scm-msm8960"
* "qcom,scm-msm8974"
* "qcom,scm-msm8994"
@ -33,7 +35,7 @@ Required properties:
* core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660" and
"qcom,scm-msm8960"
* core, iface and bus clocks required for "qcom,scm-apq8084",
"qcom,scm-msm8916" and "qcom,scm-msm8974"
"qcom,scm-msm8916", "qcom,scm-msm8953" and "qcom,scm-msm8974"
- clock-names: Must contain "core" for the core clock, "iface" for the interface
clock and "bus" for the bus clock per the requirements of the compatible.
- qcom,dload-mode: phandle to the TCSR hardware block and offset of the

Просмотреть файл

@ -19,6 +19,7 @@ properties:
- qcom,mdm9607-rpmpd
- qcom,msm8916-rpmpd
- qcom,msm8939-rpmpd
- qcom,msm8953-rpmpd
- qcom,msm8976-rpmpd
- qcom,msm8994-rpmpd
- qcom,msm8996-rpmpd
@ -31,6 +32,7 @@ properties:
- qcom,sdm845-rpmhpd
- qcom,sdx55-rpmhpd
- qcom,sm6115-rpmpd
- qcom,sm6350-rpmhpd
- qcom,sm8150-rpmhpd
- qcom,sm8250-rpmhpd
- qcom,sm8350-rpmhpd

Просмотреть файл

@ -19,8 +19,7 @@ description:
The AOSS side channel exposes control over a set of resources, used to control
a set of debug related clocks and to affect the low power state of resources
related to the secondary subsystems. These resources are exposed as a set of
power-domains.
related to the secondary subsystems.
properties:
compatible:
@ -30,6 +29,7 @@ properties:
- qcom,sc7280-aoss-qmp
- qcom,sc8180x-aoss-qmp
- qcom,sdm845-aoss-qmp
- qcom,sm6350-aoss-qmp
- qcom,sm8150-aoss-qmp
- qcom,sm8250-aoss-qmp
- qcom,sm8350-aoss-qmp
@ -57,13 +57,6 @@ properties:
description:
The single clock represents the QDSS clock.
"#power-domain-cells":
const: 1
description: |
The provided power-domains are:
CDSP state (0), LPASS state (1), modem state (2), SLPI
state (3), SPSS state (4) and Venus state (5).
required:
- compatible
- reg
@ -101,7 +94,6 @@ examples:
mboxes = <&apss_shared 0>;
#clock-cells = <0>;
#power-domain-cells = <1>;
cx_cdev: cx {
#cooling-cells = <2>;

Просмотреть файл

@ -1,134 +0,0 @@
Qualcomm APR (Asynchronous Packet Router) binding
This binding describes the Qualcomm APR. APR is a IPC protocol for
communication between Application processor and QDSP. APR is mainly
used for audio/voice services on the QDSP.
- compatible:
Usage: required
Value type: <stringlist>
Definition: must be "qcom,apr-v<VERSION-NUMBER>", example "qcom,apr-v2"
- qcom,apr-domain
Usage: required
Value type: <u32>
Definition: Destination processor ID.
Possible values are :
1 - APR simulator
2 - PC
3 - MODEM
4 - ADSP
5 - APPS
6 - MODEM2
7 - APPS2
= APR SERVICES
Each subnode of the APR node represents service tied to this apr. The name
of the nodes are not important. The properties of these nodes are defined
by the individual bindings for the specific service
- All APR services MUST contain the following property:
- reg
Usage: required
Value type: <u32>
Definition: APR Service ID
Possible values are :
3 - DSP Core Service
4 - Audio Front End Service.
5 - Voice Stream Manager Service.
6 - Voice processing manager.
7 - Audio Stream Manager Service.
8 - Audio Device Manager Service.
9 - Multimode voice manager.
10 - Core voice stream.
11 - Core voice processor.
12 - Ultrasound stream manager.
13 - Listen stream manager.
- qcom,protection-domain
Usage: optional
Value type: <stringlist>
Definition: Must list the protection domain service name and path
that the particular apr service has a dependency on.
Possible values are :
"avs/audio", "msm/adsp/audio_pd".
"kernel/elf_loader", "msm/modem/wlan_pd".
"tms/servreg", "msm/adsp/audio_pd".
"tms/servreg", "msm/modem/wlan_pd".
"tms/servreg", "msm/slpi/sensor_pd".
= EXAMPLE
The following example represents a QDSP based sound card on a MSM8996 device
which uses apr as communication between Apps and QDSP.
apr {
compatible = "qcom,apr-v2";
qcom,apr-domain = <APR_DOMAIN_ADSP>;
apr-service@3 {
compatible = "qcom,q6core";
reg = <APR_SVC_ADSP_CORE>;
};
apr-service@4 {
compatible = "qcom,q6afe";
reg = <APR_SVC_AFE>;
dais {
#sound-dai-cells = <1>;
dai@1 {
reg = <HDMI_RX>;
};
};
};
apr-service@7 {
compatible = "qcom,q6asm";
reg = <APR_SVC_ASM>;
...
};
apr-service@8 {
compatible = "qcom,q6adm";
reg = <APR_SVC_ADM>;
...
};
};
= EXAMPLE 2
The following example represents a QDSP based sound card with protection domain
dependencies specified. Here some of the apr services are dependent on services
running on protection domain hosted on ADSP/SLPI remote processors while others
have no such dependency.
apr {
compatible = "qcom,apr-v2";
qcom,glink-channels = "apr_audio_svc";
qcom,apr-domain = <APR_DOMAIN_ADSP>;
apr-service@3 {
compatible = "qcom,q6core";
reg = <APR_SVC_ADSP_CORE>;
};
q6afe: apr-service@4 {
compatible = "qcom,q6afe";
reg = <APR_SVC_AFE>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
...
};
q6asm: apr-service@7 {
compatible = "qcom,q6asm";
reg = <APR_SVC_ASM>;
qcom,protection-domain = "tms/servreg", "msm/slpi/sensor_pd";
...
};
q6adm: apr-service@8 {
compatible = "qcom,q6adm";
reg = <APR_SVC_ADM>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
...
};
};

Просмотреть файл

@ -0,0 +1,177 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/soc/qcom/qcom,apr.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Qualcomm APR/GPR (Asynchronous/Generic Packet Router) binding
maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description: |
This binding describes the Qualcomm APR/GPR, APR/GPR is a IPC protocol for
communication between Application processor and QDSP. APR/GPR is mainly
used for audio/voice services on the QDSP.
properties:
compatible:
enum:
- qcom,apr-v2
- qcom,gpr
qcom,apr-domain:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2, 3, 4, 5, 6, 7]
description:
Selects the processor domain for apr
1 = APR simulator
2 = PC Domain
3 = Modem Domain
4 = ADSP Domain
5 = Application processor Domain
6 = Modem2 Domain
7 = Application Processor2 Domain
deprecated: true
qcom,domain:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
maximum: 7
description:
Selects the processor domain for apr
1 = APR simulator
2 = PC Domain
3 = Modem Domain
4 = ADSP Domain
5 = Application processor Domain
6 = Modem2 Domain
7 = Application Processor2 Domain
Selects the processor domain for gpr
1 = Modem Domain
2 = Audio DSP Domain
3 = Application Processor Domain
'#address-cells':
const: 1
'#size-cells':
const: 0
#APR/GPR Services
patternProperties:
"^service@[1-9a-d]$":
type: object
description:
APR/GPR node's client devices use subnodes for desired static port services.
properties:
compatible:
enum:
- qcom,q6core
- qcom,q6asm
- qcom,q6afe
- qcom,q6adm
- qcom,q6apm
- qcom,q6prm
reg:
minimum: 1
maximum: 13
description:
APR Service ID
3 = DSP Core Service
4 = Audio Front End Service.
5 = Voice Stream Manager Service.
6 = Voice processing manager.
7 = Audio Stream Manager Service.
8 = Audio Device Manager Service.
9 = Multimode voice manager.
10 = Core voice stream.
11 = Core voice processor.
12 = Ultrasound stream manager.
13 = Listen stream manager.
GPR Service ID
1 = Audio Process Manager Service
2 = Proxy Resource Manager Service.
3 = AMDB Service.
4 = Voice processing manager.
qcom,protection-domain:
$ref: /schemas/types.yaml#/definitions/string-array
description: protection domain service name and path for apr service
possible values are
"avs/audio", "msm/adsp/audio_pd".
"kernel/elf_loader", "msm/modem/wlan_pd".
"tms/servreg", "msm/adsp/audio_pd".
"tms/servreg", "msm/modem/wlan_pd".
"tms/servreg", "msm/slpi/sensor_pd".
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^.*@[0-9a-f]+$":
type: object
description:
Service based devices like clock controllers or digital audio interfaces.
additionalProperties: false
required:
- compatible
- qcom,domain
additionalProperties: false
examples:
- |
#include <dt-bindings/soc/qcom,apr.h>
apr {
compatible = "qcom,apr-v2";
qcom,domain = <APR_DOMAIN_ADSP>;
#address-cells = <1>;
#size-cells = <0>;
q6core: service@3 {
compatible = "qcom,q6core";
reg = <APR_SVC_ADSP_CORE>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
};
q6afe: service@4 {
compatible = "qcom,q6afe";
reg = <APR_SVC_AFE>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
};
q6asm: service@7 {
compatible = "qcom,q6asm";
reg = <APR_SVC_ASM>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
};
q6adm: service@8 {
compatible = "qcom,q6adm";
reg = <APR_SVC_ADM>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
};
};
- |
#include <dt-bindings/soc/qcom,gpr.h>
gpr {
compatible = "qcom,gpr";
qcom,domain = <GPR_DOMAIN_ID_ADSP>;
#address-cells = <1>;
#size-cells = <0>;
service@1 {
compatible = "qcom,q6apm";
reg = <GPR_APM_MODULE_IID>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
};
};

Просмотреть файл

@ -34,6 +34,7 @@ properties:
- qcom,rpm-ipq6018
- qcom,rpm-msm8226
- qcom,rpm-msm8916
- qcom,rpm-msm8953
- qcom,rpm-msm8974
- qcom,rpm-msm8976
- qcom,rpm-msm8996
@ -41,6 +42,7 @@ properties:
- qcom,rpm-sdm660
- qcom,rpm-sm6115
- qcom,rpm-sm6125
- qcom,rpm-qcm2290
- qcom,rpm-qcs404
qcom,smd-channels:
@ -57,6 +59,7 @@ if:
- qcom,rpm-apq8084
- qcom,rpm-msm8916
- qcom,rpm-msm8974
- qcom,rpm-msm8953
then:
required:
- qcom,smd-channels

Просмотреть файл

@ -0,0 +1,80 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/soc/qcom/qcom,spm.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Qualcomm Subsystem Power Manager binding
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
description: |
This binding describes the Qualcomm Subsystem Power Manager, used to control
the peripheral logic surrounding the application cores in Qualcomm platforms.
properties:
compatible:
items:
- enum:
- qcom,sdm660-gold-saw2-v4.1-l2
- qcom,sdm660-silver-saw2-v4.1-l2
- qcom,msm8998-gold-saw2-v4.1-l2
- qcom,msm8998-silver-saw2-v4.1-l2
- qcom,msm8226-saw2-v2.1-cpu
- qcom,msm8974-saw2-v2.1-cpu
- qcom,apq8084-saw2-v2.1-cpu
- qcom,apq8064-saw2-v1.1-cpu
- const: qcom,saw2
reg:
description: Base address and size of the SPM register region
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
/* Example 1: SoC using SAW2 and kpss-acc-v2 CPUIdle */
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "qcom,kryo";
device_type = "cpu";
enable-method = "qcom,kpss-acc-v2";
qcom,saw = <&saw0>;
reg = <0x0>;
operating-points-v2 = <&cpu_opp_table>;
};
};
saw0: power-manager@f9089000 {
compatible = "qcom,msm8974-saw2-v2.1-cpu", "qcom,saw2";
reg = <0xf9089000 0x1000>;
};
- |
/*
* Example 2: New-gen multi cluster SoC using SAW only for L2;
* This does not require any cpuidle driver, nor any cpu phandle.
*/
power-manager@17812000 {
compatible = "qcom,msm8998-gold-saw2-v4.1-l2", "qcom,saw2";
reg = <0x17812000 0x1000>;
};
power-manager@17912000 {
compatible = "qcom,msm8998-silver-saw2-v4.1-l2", "qcom,saw2";
reg = <0x17912000 0x1000>;
};
...

Просмотреть файл

@ -29,6 +29,7 @@
#define COREPOR_RST BIT(5)
#define CORE_RST BIT(4)
#define L2DT_SLP BIT(3)
#define CORE_MEM_CLAMP BIT(1)
#define CLAMP BIT(0)
#define APC_PWR_GATE_CTL 0x14
@ -75,6 +76,62 @@ static int scss_release_secondary(unsigned int cpu)
return 0;
}
static int cortex_a7_release_secondary(unsigned int cpu)
{
int ret = 0;
void __iomem *reg;
struct device_node *cpu_node, *acc_node;
u32 reg_val;
cpu_node = of_get_cpu_node(cpu, NULL);
if (!cpu_node)
return -ENODEV;
acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
if (!acc_node) {
ret = -ENODEV;
goto out_acc;
}
reg = of_iomap(acc_node, 0);
if (!reg) {
ret = -ENOMEM;
goto out_acc_map;
}
/* Put the CPU into reset. */
reg_val = CORE_RST | COREPOR_RST | CLAMP | CORE_MEM_CLAMP;
writel(reg_val, reg + APCS_CPU_PWR_CTL);
/* Turn on the BHS and set the BHS_CNT to 16 XO clock cycles */
writel(BHS_EN | (0x10 << BHS_CNT_SHIFT), reg + APC_PWR_GATE_CTL);
/* Wait for the BHS to settle */
udelay(2);
reg_val &= ~CORE_MEM_CLAMP;
writel(reg_val, reg + APCS_CPU_PWR_CTL);
reg_val |= L2DT_SLP;
writel(reg_val, reg + APCS_CPU_PWR_CTL);
udelay(2);
reg_val = (reg_val | BIT(17)) & ~CLAMP;
writel(reg_val, reg + APCS_CPU_PWR_CTL);
udelay(2);
/* Release CPU out of reset and bring it to life. */
reg_val &= ~(CORE_RST | COREPOR_RST);
writel(reg_val, reg + APCS_CPU_PWR_CTL);
reg_val |= CORE_PWRD_UP;
writel(reg_val, reg + APCS_CPU_PWR_CTL);
iounmap(reg);
out_acc_map:
of_node_put(acc_node);
out_acc:
of_node_put(cpu_node);
return ret;
}
static int kpssv1_release_secondary(unsigned int cpu)
{
int ret = 0;
@ -281,6 +338,11 @@ static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle)
return qcom_boot_secondary(cpu, scss_release_secondary);
}
static int cortex_a7_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
return qcom_boot_secondary(cpu, cortex_a7_release_secondary);
}
static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
return qcom_boot_secondary(cpu, kpssv1_release_secondary);
@ -315,6 +377,15 @@ static const struct smp_operations smp_msm8660_ops __initconst = {
};
CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops);
static const struct smp_operations qcom_smp_cortex_a7_ops __initconst = {
.smp_prepare_cpus = qcom_smp_prepare_cpus,
.smp_boot_secondary = cortex_a7_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = qcom_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(qcom_smp_msm8226, "qcom,msm8226-smp", &qcom_smp_cortex_a7_ops);
static const struct smp_operations qcom_smp_kpssv1_ops __initconst = {
.smp_prepare_cpus = qcom_smp_prepare_cpus,
.smp_boot_secondary = kpssv1_boot_secondary,

Просмотреть файл

@ -112,6 +112,7 @@ config ARM_QCOM_SPM_CPUIDLE
select CPU_IDLE_MULTIPLE_DRIVERS
select DT_IDLE_STATES
select QCOM_SCM
select QCOM_SPM
help
Select this to enable cpuidle for Qualcomm processors.
The Subsystem Power Manager (SPM) controls low power modes for the

Просмотреть файл

@ -18,158 +18,18 @@
#include <linux/cpuidle.h>
#include <linux/cpu_pm.h>
#include <linux/qcom_scm.h>
#include <soc/qcom/spm.h>
#include <asm/proc-fns.h>
#include <asm/suspend.h>
#include "dt_idle_states.h"
#define MAX_PMIC_DATA 2
#define MAX_SEQ_DATA 64
#define SPM_CTL_INDEX 0x7f
#define SPM_CTL_INDEX_SHIFT 4
#define SPM_CTL_EN BIT(0)
enum pm_sleep_mode {
PM_SLEEP_MODE_STBY,
PM_SLEEP_MODE_RET,
PM_SLEEP_MODE_SPC,
PM_SLEEP_MODE_PC,
PM_SLEEP_MODE_NR,
};
enum spm_reg {
SPM_REG_CFG,
SPM_REG_SPM_CTL,
SPM_REG_DLY,
SPM_REG_PMIC_DLY,
SPM_REG_PMIC_DATA_0,
SPM_REG_PMIC_DATA_1,
SPM_REG_VCTL,
SPM_REG_SEQ_ENTRY,
SPM_REG_SPM_STS,
SPM_REG_PMIC_STS,
SPM_REG_NR,
};
struct spm_reg_data {
const u8 *reg_offset;
u32 spm_cfg;
u32 spm_dly;
u32 pmic_dly;
u32 pmic_data[MAX_PMIC_DATA];
u8 seq[MAX_SEQ_DATA];
u8 start_index[PM_SLEEP_MODE_NR];
};
struct spm_driver_data {
struct cpuidle_qcom_spm_data {
struct cpuidle_driver cpuidle_driver;
void __iomem *reg_base;
const struct spm_reg_data *reg_data;
struct spm_driver_data *spm;
};
static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = {
[SPM_REG_CFG] = 0x08,
[SPM_REG_SPM_CTL] = 0x30,
[SPM_REG_DLY] = 0x34,
[SPM_REG_SEQ_ENTRY] = 0x80,
};
/* SPM register data for 8974, 8084 */
static const struct spm_reg_data spm_reg_8974_8084_cpu = {
.reg_offset = spm_reg_offset_v2_1,
.spm_cfg = 0x1,
.spm_dly = 0x3C102800,
.seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03,
0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30,
0x0F },
.start_index[PM_SLEEP_MODE_STBY] = 0,
.start_index[PM_SLEEP_MODE_SPC] = 3,
};
/* SPM register data for 8226 */
static const struct spm_reg_data spm_reg_8226_cpu = {
.reg_offset = spm_reg_offset_v2_1,
.spm_cfg = 0x0,
.spm_dly = 0x3C102800,
.seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90,
0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B,
0x80, 0x10, 0x26, 0x30, 0x0F },
.start_index[PM_SLEEP_MODE_STBY] = 0,
.start_index[PM_SLEEP_MODE_SPC] = 5,
};
static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = {
[SPM_REG_CFG] = 0x08,
[SPM_REG_SPM_CTL] = 0x20,
[SPM_REG_PMIC_DLY] = 0x24,
[SPM_REG_PMIC_DATA_0] = 0x28,
[SPM_REG_PMIC_DATA_1] = 0x2C,
[SPM_REG_SEQ_ENTRY] = 0x80,
};
/* SPM register data for 8064 */
static const struct spm_reg_data spm_reg_8064_cpu = {
.reg_offset = spm_reg_offset_v1_1,
.spm_cfg = 0x1F,
.pmic_dly = 0x02020004,
.pmic_data[0] = 0x0084009C,
.pmic_data[1] = 0x00A4001C,
.seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01,
0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
.start_index[PM_SLEEP_MODE_STBY] = 0,
.start_index[PM_SLEEP_MODE_SPC] = 2,
};
static inline void spm_register_write(struct spm_driver_data *drv,
enum spm_reg reg, u32 val)
{
if (drv->reg_data->reg_offset[reg])
writel_relaxed(val, drv->reg_base +
drv->reg_data->reg_offset[reg]);
}
/* Ensure a guaranteed write, before return */
static inline void spm_register_write_sync(struct spm_driver_data *drv,
enum spm_reg reg, u32 val)
{
u32 ret;
if (!drv->reg_data->reg_offset[reg])
return;
do {
writel_relaxed(val, drv->reg_base +
drv->reg_data->reg_offset[reg]);
ret = readl_relaxed(drv->reg_base +
drv->reg_data->reg_offset[reg]);
if (ret == val)
break;
cpu_relax();
} while (1);
}
static inline u32 spm_register_read(struct spm_driver_data *drv,
enum spm_reg reg)
{
return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]);
}
static void spm_set_low_power_mode(struct spm_driver_data *drv,
enum pm_sleep_mode mode)
{
u32 start_index;
u32 ctl_val;
start_index = drv->reg_data->start_index[mode];
ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL);
ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT);
ctl_val |= start_index << SPM_CTL_INDEX_SHIFT;
ctl_val |= SPM_CTL_EN;
spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
}
static int qcom_pm_collapse(unsigned long int unused)
{
qcom_scm_cpu_power_down(QCOM_SCM_CPU_PWR_DOWN_L2_ON);
@ -201,10 +61,10 @@ static int qcom_cpu_spc(struct spm_driver_data *drv)
static int spm_enter_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
struct spm_driver_data *data = container_of(drv, struct spm_driver_data,
cpuidle_driver);
struct cpuidle_qcom_spm_data *data = container_of(drv, struct cpuidle_qcom_spm_data,
cpuidle_driver);
return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data);
return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data->spm);
}
static struct cpuidle_driver qcom_spm_idle_driver = {
@ -225,134 +85,92 @@ static const struct of_device_id qcom_idle_state_match[] = {
{ },
};
static int spm_cpuidle_init(struct cpuidle_driver *drv, int cpu)
static int spm_cpuidle_register(struct device *cpuidle_dev, int cpu)
{
struct platform_device *pdev = NULL;
struct device_node *cpu_node, *saw_node;
struct cpuidle_qcom_spm_data *data = NULL;
int ret;
memcpy(drv, &qcom_spm_idle_driver, sizeof(*drv));
drv->cpumask = (struct cpumask *)cpumask_of(cpu);
cpu_node = of_cpu_device_node_get(cpu);
if (!cpu_node)
return -ENODEV;
/* Parse idle states from device tree */
ret = dt_init_idle_driver(drv, qcom_idle_state_match, 1);
saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
if (!saw_node)
return -ENODEV;
pdev = of_find_device_by_node(saw_node);
of_node_put(saw_node);
of_node_put(cpu_node);
if (!pdev)
return -ENODEV;
data = devm_kzalloc(cpuidle_dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->spm = dev_get_drvdata(&pdev->dev);
if (!data->spm)
return -EINVAL;
data->cpuidle_driver = qcom_spm_idle_driver;
data->cpuidle_driver.cpumask = (struct cpumask *)cpumask_of(cpu);
ret = dt_init_idle_driver(&data->cpuidle_driver,
qcom_idle_state_match, 1);
if (ret <= 0)
return ret ? : -ENODEV;
/* We have atleast one power down mode */
return qcom_scm_set_warm_boot_addr(cpu_resume_arm, drv->cpumask);
ret = qcom_scm_set_warm_boot_addr(cpu_resume_arm, cpumask_of(cpu));
if (ret)
return ret;
return cpuidle_register(&data->cpuidle_driver, NULL);
}
static struct spm_driver_data *spm_get_drv(struct platform_device *pdev,
int *spm_cpu)
static int spm_cpuidle_drv_probe(struct platform_device *pdev)
{
struct spm_driver_data *drv = NULL;
struct device_node *cpu_node, *saw_node;
int cpu;
bool found = 0;
for_each_possible_cpu(cpu) {
cpu_node = of_cpu_device_node_get(cpu);
if (!cpu_node)
continue;
saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
found = (saw_node == pdev->dev.of_node);
of_node_put(saw_node);
of_node_put(cpu_node);
if (found)
break;
}
if (found) {
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (drv)
*spm_cpu = cpu;
}
return drv;
}
static const struct of_device_id spm_match_table[] = {
{ .compatible = "qcom,msm8226-saw2-v2.1-cpu",
.data = &spm_reg_8226_cpu },
{ .compatible = "qcom,msm8974-saw2-v2.1-cpu",
.data = &spm_reg_8974_8084_cpu },
{ .compatible = "qcom,apq8084-saw2-v2.1-cpu",
.data = &spm_reg_8974_8084_cpu },
{ .compatible = "qcom,apq8064-saw2-v1.1-cpu",
.data = &spm_reg_8064_cpu },
{ },
};
static int spm_dev_probe(struct platform_device *pdev)
{
struct spm_driver_data *drv;
struct resource *res;
const struct of_device_id *match_id;
void __iomem *addr;
int cpu, ret;
if (!qcom_scm_is_available())
return -EPROBE_DEFER;
drv = spm_get_drv(pdev, &cpu);
if (!drv)
return -EINVAL;
platform_set_drvdata(pdev, drv);
for_each_possible_cpu(cpu) {
ret = spm_cpuidle_register(&pdev->dev, cpu);
if (ret && ret != -ENODEV) {
dev_err(&pdev->dev,
"Cannot register for CPU%d: %d\n", cpu, ret);
}
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(drv->reg_base))
return PTR_ERR(drv->reg_base);
match_id = of_match_node(spm_match_table, pdev->dev.of_node);
if (!match_id)
return -ENODEV;
drv->reg_data = match_id->data;
ret = spm_cpuidle_init(&drv->cpuidle_driver, cpu);
if (ret)
return ret;
/* Write the SPM sequences first.. */
addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
__iowrite32_copy(addr, drv->reg_data->seq,
ARRAY_SIZE(drv->reg_data->seq) / 4);
/*
* ..and then the control registers.
* On some SoC if the control registers are written first and if the
* CPU was held in reset, the reset signal could trigger the SPM state
* machine, before the sequences are completely written.
*/
spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
spm_register_write(drv, SPM_REG_PMIC_DATA_0,
drv->reg_data->pmic_data[0]);
spm_register_write(drv, SPM_REG_PMIC_DATA_1,
drv->reg_data->pmic_data[1]);
/* Set up Standby as the default low power mode */
spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
return cpuidle_register(&drv->cpuidle_driver, NULL);
}
static int spm_dev_remove(struct platform_device *pdev)
{
struct spm_driver_data *drv = platform_get_drvdata(pdev);
cpuidle_unregister(&drv->cpuidle_driver);
return 0;
}
static struct platform_driver spm_driver = {
.probe = spm_dev_probe,
.remove = spm_dev_remove,
static struct platform_driver spm_cpuidle_driver = {
.probe = spm_cpuidle_drv_probe,
.driver = {
.name = "saw",
.of_match_table = spm_match_table,
.name = "qcom-spm-cpuidle",
.suppress_bind_attrs = true,
},
};
builtin_platform_driver(spm_driver);
static int __init qcom_spm_cpuidle_init(void)
{
struct platform_device *pdev;
int ret;
ret = platform_driver_register(&spm_cpuidle_driver);
if (ret)
return ret;
pdev = platform_device_register_simple("qcom-spm-cpuidle",
-1, NULL, 0);
if (IS_ERR(pdev)) {
platform_driver_unregister(&spm_cpuidle_driver);
return PTR_ERR(pdev);
}
return 0;
}
device_initcall(qcom_spm_cpuidle_init);

Просмотреть файл

@ -1348,6 +1348,10 @@ static const struct of_device_id qcom_scm_dt_match[] = {
SCM_HAS_IFACE_CLK |
SCM_HAS_BUS_CLK)
},
{ .compatible = "qcom,scm-msm8953", .data = (void *)(SCM_HAS_CORE_CLK |
SCM_HAS_IFACE_CLK |
SCM_HAS_BUS_CLK)
},
{ .compatible = "qcom,scm-msm8974", .data = (void *)(SCM_HAS_CORE_CLK |
SCM_HAS_IFACE_CLK |
SCM_HAS_BUS_CLK)

Просмотреть файл

@ -190,6 +190,15 @@ config QCOM_SOCINFO
Say yes here to support the Qualcomm socinfo driver, providing
information about the SoC to user space.
config QCOM_SPM
tristate "Qualcomm Subsystem Power Manager (SPM)"
depends on ARCH_QCOM
select QCOM_SCM
help
Enable the support for the Qualcomm Subsystem Power Manager, used
to manage cores, L2 low power modes and to configure the internal
Adaptive Voltage Scaler parameters, where supported.
config QCOM_WCNSS_CTRL
tristate "Qualcomm WCNSS control driver"
depends on ARCH_QCOM || COMPILE_TEST
@ -199,7 +208,7 @@ config QCOM_WCNSS_CTRL
firmware to a newly booted WCNSS chip.
config QCOM_APR
tristate "Qualcomm APR Bus (Asynchronous Packet Router)"
tristate "Qualcomm APR/GPR Bus (Asynchronous/Generic Packet Router)"
depends on ARCH_QCOM || COMPILE_TEST
depends on RPMSG
depends on NET

Просмотреть файл

@ -20,6 +20,7 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o
obj-$(CONFIG_QCOM_SPM) += spm.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
obj-$(CONFIG_QCOM_APR) += apr.o
obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o

Просмотреть файл

@ -15,13 +15,23 @@
#include <linux/rpmsg.h>
#include <linux/of.h>
struct apr {
enum {
PR_TYPE_APR = 0,
PR_TYPE_GPR,
};
/* Some random values tbh which does not collide with static modules */
#define GPR_DYNAMIC_PORT_START 0x10000000
#define GPR_DYNAMIC_PORT_END 0x20000000
struct packet_router {
struct rpmsg_endpoint *ch;
struct device *dev;
spinlock_t svcs_lock;
spinlock_t rx_lock;
struct idr svcs_idr;
int dest_domain_id;
int type;
struct pdr_handle *pdr;
struct workqueue_struct *rxwq;
struct work_struct rx_work;
@ -44,26 +54,103 @@ struct apr_rx_buf {
*/
int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt)
{
struct apr *apr = dev_get_drvdata(adev->dev.parent);
struct packet_router *apr = dev_get_drvdata(adev->dev.parent);
struct apr_hdr *hdr;
unsigned long flags;
int ret;
spin_lock_irqsave(&adev->lock, flags);
spin_lock_irqsave(&adev->svc.lock, flags);
hdr = &pkt->hdr;
hdr->src_domain = APR_DOMAIN_APPS;
hdr->src_svc = adev->svc_id;
hdr->src_svc = adev->svc.id;
hdr->dest_domain = adev->domain_id;
hdr->dest_svc = adev->svc_id;
hdr->dest_svc = adev->svc.id;
ret = rpmsg_trysend(apr->ch, pkt, hdr->pkt_size);
spin_unlock_irqrestore(&adev->lock, flags);
spin_unlock_irqrestore(&adev->svc.lock, flags);
return ret ? ret : hdr->pkt_size;
}
EXPORT_SYMBOL_GPL(apr_send_pkt);
void gpr_free_port(gpr_port_t *port)
{
struct packet_router *gpr = port->pr;
unsigned long flags;
spin_lock_irqsave(&gpr->svcs_lock, flags);
idr_remove(&gpr->svcs_idr, port->id);
spin_unlock_irqrestore(&gpr->svcs_lock, flags);
kfree(port);
}
EXPORT_SYMBOL_GPL(gpr_free_port);
gpr_port_t *gpr_alloc_port(struct apr_device *gdev, struct device *dev,
gpr_port_cb cb, void *priv)
{
struct packet_router *pr = dev_get_drvdata(gdev->dev.parent);
gpr_port_t *port;
struct pkt_router_svc *svc;
int id;
port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!port)
return ERR_PTR(-ENOMEM);
svc = port;
svc->callback = cb;
svc->pr = pr;
svc->priv = priv;
svc->dev = dev;
spin_lock_init(&svc->lock);
spin_lock(&pr->svcs_lock);
id = idr_alloc_cyclic(&pr->svcs_idr, svc, GPR_DYNAMIC_PORT_START,
GPR_DYNAMIC_PORT_END, GFP_ATOMIC);
if (id < 0) {
dev_err(dev, "Unable to allocate dynamic GPR src port\n");
kfree(port);
spin_unlock(&pr->svcs_lock);
return ERR_PTR(id);
}
svc->id = id;
spin_unlock(&pr->svcs_lock);
return port;
}
EXPORT_SYMBOL_GPL(gpr_alloc_port);
static int pkt_router_send_svc_pkt(struct pkt_router_svc *svc, struct gpr_pkt *pkt)
{
struct packet_router *pr = svc->pr;
struct gpr_hdr *hdr;
unsigned long flags;
int ret;
hdr = &pkt->hdr;
spin_lock_irqsave(&svc->lock, flags);
ret = rpmsg_trysend(pr->ch, pkt, hdr->pkt_size);
spin_unlock_irqrestore(&svc->lock, flags);
return ret ? ret : hdr->pkt_size;
}
int gpr_send_pkt(struct apr_device *gdev, struct gpr_pkt *pkt)
{
return pkt_router_send_svc_pkt(&gdev->svc, pkt);
}
EXPORT_SYMBOL_GPL(gpr_send_pkt);
int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt)
{
return pkt_router_send_svc_pkt(port, pkt);
}
EXPORT_SYMBOL_GPL(gpr_send_port_pkt);
static void apr_dev_release(struct device *dev)
{
struct apr_device *adev = to_apr_device(dev);
@ -74,7 +161,7 @@ static void apr_dev_release(struct device *dev)
static int apr_callback(struct rpmsg_device *rpdev, void *buf,
int len, void *priv, u32 addr)
{
struct apr *apr = dev_get_drvdata(&rpdev->dev);
struct packet_router *apr = dev_get_drvdata(&rpdev->dev);
struct apr_rx_buf *abuf;
unsigned long flags;
@ -100,11 +187,11 @@ static int apr_callback(struct rpmsg_device *rpdev, void *buf,
return 0;
}
static int apr_do_rx_callback(struct apr *apr, struct apr_rx_buf *abuf)
static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf)
{
uint16_t hdr_size, msg_type, ver, svc_id;
struct apr_device *svc = NULL;
struct pkt_router_svc *svc;
struct apr_device *adev;
struct apr_driver *adrv = NULL;
struct apr_resp_pkt resp;
struct apr_hdr *hdr;
@ -145,12 +232,15 @@ static int apr_do_rx_callback(struct apr *apr, struct apr_rx_buf *abuf)
svc_id = hdr->dest_svc;
spin_lock_irqsave(&apr->svcs_lock, flags);
svc = idr_find(&apr->svcs_idr, svc_id);
if (svc && svc->dev.driver)
adrv = to_apr_driver(svc->dev.driver);
if (svc && svc->dev->driver) {
adev = svc_to_apr_device(svc);
adrv = to_apr_driver(adev->dev.driver);
}
spin_unlock_irqrestore(&apr->svcs_lock, flags);
if (!adrv) {
dev_err(apr->dev, "APR: service is not registered\n");
if (!adrv || !adev) {
dev_err(apr->dev, "APR: service is not registered (%d)\n",
svc_id);
return -EINVAL;
}
@ -164,20 +254,82 @@ static int apr_do_rx_callback(struct apr *apr, struct apr_rx_buf *abuf)
if (resp.payload_size > 0)
resp.payload = buf + hdr_size;
adrv->callback(svc, &resp);
adrv->callback(adev, &resp);
return 0;
}
static int gpr_do_rx_callback(struct packet_router *gpr, struct apr_rx_buf *abuf)
{
uint16_t hdr_size, ver;
struct pkt_router_svc *svc = NULL;
struct gpr_resp_pkt resp;
struct gpr_hdr *hdr;
unsigned long flags;
void *buf = abuf->buf;
int len = abuf->len;
hdr = buf;
ver = hdr->version;
if (ver > GPR_PKT_VER + 1)
return -EINVAL;
hdr_size = hdr->hdr_size;
if (hdr_size < GPR_PKT_HEADER_WORD_SIZE) {
dev_err(gpr->dev, "GPR: Wrong hdr size:%d\n", hdr_size);
return -EINVAL;
}
if (hdr->pkt_size < GPR_PKT_HEADER_BYTE_SIZE || hdr->pkt_size != len) {
dev_err(gpr->dev, "GPR: Wrong packet size\n");
return -EINVAL;
}
resp.hdr = *hdr;
resp.payload_size = hdr->pkt_size - (hdr_size * 4);
/*
* NOTE: hdr_size is not same as GPR_HDR_SIZE as remote can include
* optional headers in to gpr_hdr which should be ignored
*/
if (resp.payload_size > 0)
resp.payload = buf + (hdr_size * 4);
spin_lock_irqsave(&gpr->svcs_lock, flags);
svc = idr_find(&gpr->svcs_idr, hdr->dest_port);
spin_unlock_irqrestore(&gpr->svcs_lock, flags);
if (!svc) {
dev_err(gpr->dev, "GPR: Port(%x) is not registered\n",
hdr->dest_port);
return -EINVAL;
}
if (svc->callback)
svc->callback(&resp, svc->priv, 0);
return 0;
}
static void apr_rxwq(struct work_struct *work)
{
struct apr *apr = container_of(work, struct apr, rx_work);
struct packet_router *apr = container_of(work, struct packet_router, rx_work);
struct apr_rx_buf *abuf, *b;
unsigned long flags;
if (!list_empty(&apr->rx_list)) {
list_for_each_entry_safe(abuf, b, &apr->rx_list, node) {
apr_do_rx_callback(apr, abuf);
switch (apr->type) {
case PR_TYPE_APR:
apr_do_rx_callback(apr, abuf);
break;
case PR_TYPE_GPR:
gpr_do_rx_callback(apr, abuf);
break;
default:
break;
}
spin_lock_irqsave(&apr->rx_lock, flags);
list_del(&abuf->node);
spin_unlock_irqrestore(&apr->rx_lock, flags);
@ -201,7 +353,7 @@ static int apr_device_match(struct device *dev, struct device_driver *drv)
while (id->domain_id != 0 || id->svc_id != 0) {
if (id->domain_id == adev->domain_id &&
id->svc_id == adev->svc_id)
id->svc_id == adev->svc.id)
return 1;
id++;
}
@ -213,22 +365,27 @@ static int apr_device_probe(struct device *dev)
{
struct apr_device *adev = to_apr_device(dev);
struct apr_driver *adrv = to_apr_driver(dev->driver);
int ret;
return adrv->probe(adev);
ret = adrv->probe(adev);
if (!ret)
adev->svc.callback = adrv->gpr_callback;
return ret;
}
static void apr_device_remove(struct device *dev)
{
struct apr_device *adev = to_apr_device(dev);
struct apr_driver *adrv;
struct apr *apr = dev_get_drvdata(adev->dev.parent);
struct packet_router *apr = dev_get_drvdata(adev->dev.parent);
if (dev->driver) {
adrv = to_apr_driver(dev->driver);
if (adrv->remove)
adrv->remove(adev);
spin_lock(&apr->svcs_lock);
idr_remove(&apr->svcs_idr, adev->svc_id);
idr_remove(&apr->svcs_idr, adev->svc.id);
spin_unlock(&apr->svcs_lock);
}
}
@ -255,28 +412,43 @@ struct bus_type aprbus = {
EXPORT_SYMBOL_GPL(aprbus);
static int apr_add_device(struct device *dev, struct device_node *np,
const struct apr_device_id *id)
u32 svc_id, u32 domain_id)
{
struct apr *apr = dev_get_drvdata(dev);
struct packet_router *apr = dev_get_drvdata(dev);
struct apr_device *adev = NULL;
struct pkt_router_svc *svc;
int ret;
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
if (!adev)
return -ENOMEM;
spin_lock_init(&adev->lock);
adev->svc_id = svc_id;
svc = &adev->svc;
svc->id = svc_id;
svc->pr = apr;
svc->priv = adev;
svc->dev = dev;
spin_lock_init(&svc->lock);
adev->domain_id = domain_id;
adev->svc_id = id->svc_id;
adev->domain_id = id->domain_id;
adev->version = id->svc_version;
if (np)
snprintf(adev->name, APR_NAME_SIZE, "%pOFn", np);
else
strscpy(adev->name, id->name, APR_NAME_SIZE);
dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
id->domain_id, id->svc_id);
switch (apr->type) {
case PR_TYPE_APR:
dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
domain_id, svc_id);
break;
case PR_TYPE_GPR:
dev_set_name(&adev->dev, "gprsvc:%s:%x:%x", adev->name,
domain_id, svc_id);
break;
default:
break;
}
adev->dev.bus = &aprbus;
adev->dev.parent = dev;
@ -285,14 +457,13 @@ static int apr_add_device(struct device *dev, struct device_node *np,
adev->dev.driver = NULL;
spin_lock(&apr->svcs_lock);
idr_alloc(&apr->svcs_idr, adev, id->svc_id,
id->svc_id + 1, GFP_ATOMIC);
idr_alloc(&apr->svcs_idr, svc, svc_id, svc_id + 1, GFP_ATOMIC);
spin_unlock(&apr->svcs_lock);
of_property_read_string_index(np, "qcom,protection-domain",
1, &adev->service_path);
dev_info(dev, "Adding APR dev: %s\n", dev_name(&adev->dev));
dev_info(dev, "Adding APR/GPR dev: %s\n", dev_name(&adev->dev));
ret = device_register(&adev->dev);
if (ret) {
@ -306,7 +477,7 @@ static int apr_add_device(struct device *dev, struct device_node *np,
static int of_apr_add_pd_lookups(struct device *dev)
{
const char *service_name, *service_path;
struct apr *apr = dev_get_drvdata(dev);
struct packet_router *apr = dev_get_drvdata(dev);
struct device_node *node;
struct pdr_service *pds;
int ret;
@ -336,13 +507,14 @@ static int of_apr_add_pd_lookups(struct device *dev)
static void of_register_apr_devices(struct device *dev, const char *svc_path)
{
struct apr *apr = dev_get_drvdata(dev);
struct packet_router *apr = dev_get_drvdata(dev);
struct device_node *node;
const char *service_path;
int ret;
for_each_child_of_node(dev->of_node, node) {
struct apr_device_id id = { {0} };
u32 svc_id;
u32 domain_id;
/*
* This function is called with svc_path NULL during
@ -372,13 +544,13 @@ static void of_register_apr_devices(struct device *dev, const char *svc_path)
continue;
}
if (of_property_read_u32(node, "reg", &id.svc_id))
if (of_property_read_u32(node, "reg", &svc_id))
continue;
id.domain_id = apr->dest_domain_id;
domain_id = apr->dest_domain_id;
if (apr_add_device(dev, node, &id))
dev_err(dev, "Failed to add apr %d svc\n", id.svc_id);
if (apr_add_device(dev, node, svc_id, domain_id))
dev_err(dev, "Failed to add apr %d svc\n", svc_id);
}
}
@ -398,7 +570,7 @@ static int apr_remove_device(struct device *dev, void *svc_path)
static void apr_pd_status(int state, char *svc_path, void *priv)
{
struct apr *apr = (struct apr *)priv;
struct packet_router *apr = (struct packet_router *)priv;
switch (state) {
case SERVREG_SERVICE_STATE_UP:
@ -413,16 +585,26 @@ static void apr_pd_status(int state, char *svc_path, void *priv)
static int apr_probe(struct rpmsg_device *rpdev)
{
struct device *dev = &rpdev->dev;
struct apr *apr;
struct packet_router *apr;
int ret;
apr = devm_kzalloc(dev, sizeof(*apr), GFP_KERNEL);
if (!apr)
return -ENOMEM;
ret = of_property_read_u32(dev->of_node, "qcom,apr-domain", &apr->dest_domain_id);
ret = of_property_read_u32(dev->of_node, "qcom,domain", &apr->dest_domain_id);
if (of_device_is_compatible(dev->of_node, "qcom,gpr")) {
apr->type = PR_TYPE_GPR;
} else {
if (ret) /* try deprecated apr-domain property */
ret = of_property_read_u32(dev->of_node, "qcom,apr-domain",
&apr->dest_domain_id);
apr->type = PR_TYPE_APR;
}
if (ret) {
dev_err(dev, "APR Domain ID not specified in DT\n");
dev_err(dev, "Domain ID not specified in DT\n");
return ret;
}
@ -465,7 +647,7 @@ destroy_wq:
static void apr_remove(struct rpmsg_device *rpdev)
{
struct apr *apr = dev_get_drvdata(&rpdev->dev);
struct packet_router *apr = dev_get_drvdata(&rpdev->dev);
pdr_handle_release(apr->pdr);
device_for_each_child(&rpdev->dev, NULL, apr_remove_device);
@ -502,20 +684,21 @@ void apr_driver_unregister(struct apr_driver *drv)
}
EXPORT_SYMBOL_GPL(apr_driver_unregister);
static const struct of_device_id apr_of_match[] = {
static const struct of_device_id pkt_router_of_match[] = {
{ .compatible = "qcom,apr"},
{ .compatible = "qcom,apr-v2"},
{ .compatible = "qcom,gpr"},
{}
};
MODULE_DEVICE_TABLE(of, apr_of_match);
MODULE_DEVICE_TABLE(of, pkt_router_of_match);
static struct rpmsg_driver apr_driver = {
static struct rpmsg_driver packet_router_driver = {
.probe = apr_probe,
.remove = apr_remove,
.callback = apr_callback,
.drv = {
.name = "qcom,apr",
.of_match_table = apr_of_match,
.of_match_table = pkt_router_of_match,
},
};
@ -525,7 +708,7 @@ static int __init apr_init(void)
ret = bus_register(&aprbus);
if (!ret)
ret = register_rpmsg_driver(&apr_driver);
ret = register_rpmsg_driver(&packet_router_driver);
else
bus_unregister(&aprbus);
@ -535,7 +718,7 @@ static int __init apr_init(void)
static void __exit apr_exit(void)
{
bus_unregister(&aprbus);
unregister_rpmsg_driver(&apr_driver);
unregister_rpmsg_driver(&packet_router_driver);
}
subsys_initcall(apr_init);

Просмотреть файл

@ -1614,7 +1614,6 @@ static void cpr_debugfs_init(struct cpr_drv *drv)
static int cpr_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev = &pdev->dev;
struct cpr_drv *drv;
int irq, ret;
@ -1648,8 +1647,7 @@ static int cpr_probe(struct platform_device *pdev)
if (IS_ERR(drv->tcsr))
return PTR_ERR(drv->tcsr);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
drv->base = devm_ioremap_resource(dev, res);
drv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(drv->base))
return PTR_ERR(drv->base);

Просмотреть файл

@ -115,7 +115,7 @@ static const struct llcc_slice_config sc7280_data[] = {
{ LLCC_CMPT, 10, 768, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_GPUHTW, 11, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_GPU, 12, 512, 1, 0, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_MMUHWT, 13, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 1, 0},
{ LLCC_MMUHWT, 13, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 0, 1, 0},
{ LLCC_MDMPNG, 21, 768, 0, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_WLHW, 24, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_MODPE, 29, 64, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
@ -142,6 +142,16 @@ static const struct llcc_slice_config sdm845_data[] = {
{ LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 },
};
static const struct llcc_slice_config sm6350_data[] = {
{ LLCC_CPUSS, 1, 768, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 1 },
{ LLCC_MDM, 8, 512, 2, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_GPUHTW, 11, 256, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_GPU, 12, 512, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_MDMPNG, 21, 768, 0, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_NPU, 23, 768, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_MODPE, 29, 64, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
};
static const struct llcc_slice_config sm8150_data[] = {
{ LLCC_CPUSS, 1, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 1 },
{ LLCC_VIDSC0, 2, 512, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
@ -203,6 +213,11 @@ static const struct qcom_llcc_config sdm845_cfg = {
.need_llcc_cfg = false,
};
static const struct qcom_llcc_config sm6350_cfg = {
.sct_data = sm6350_data,
.size = ARRAY_SIZE(sm6350_data),
};
static const struct qcom_llcc_config sm8150_cfg = {
.sct_data = sm8150_data,
.size = ARRAY_SIZE(sm8150_data),
@ -626,6 +641,7 @@ static const struct of_device_id qcom_llcc_of_match[] = {
{ .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg },
{ .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfg },
{ .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg },
{ .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfg },
{ .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfg },
{ .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfg },
{ }

Просмотреть файл

@ -300,7 +300,6 @@ static int ocmem_dev_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
unsigned long reg, region_size;
int i, j, ret, num_banks;
struct resource *res;
struct ocmem *ocmem;
if (!qcom_scm_is_available())
@ -321,8 +320,7 @@ static int ocmem_dev_probe(struct platform_device *pdev)
return ret;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
ocmem->mmio = devm_ioremap_resource(&pdev->dev, res);
ocmem->mmio = devm_platform_ioremap_resource_byname(pdev, "ctrl");
if (IS_ERR(ocmem->mmio)) {
dev_err(&pdev->dev, "Failed to ioremap ocmem_ctrl resource\n");
return PTR_ERR(ocmem->mmio);

Просмотреть файл

@ -131,7 +131,7 @@ static int pdr_register_listener(struct pdr_handle *pdr,
return ret;
req.enable = enable;
strcpy(req.service_path, pds->service_path);
strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
&txn, SERVREG_REGISTER_LISTENER_REQ,
@ -257,7 +257,7 @@ static int pdr_send_indack_msg(struct pdr_handle *pdr, struct pdr_service *pds,
return ret;
req.transaction_id = tid;
strcpy(req.service_path, pds->service_path);
strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
&txn, SERVREG_SET_ACK_REQ,
@ -406,7 +406,7 @@ static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds)
return -ENOMEM;
/* Prepare req message */
strcpy(req.service_name, pds->service_name);
strscpy(req.service_name, pds->service_name, sizeof(req.service_name));
req.domain_offset_valid = true;
req.domain_offset = 0;
@ -531,8 +531,8 @@ struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr,
return ERR_PTR(-ENOMEM);
pds->service = SERVREG_NOTIFIER_SERVICE;
strcpy(pds->service_name, service_name);
strcpy(pds->service_path, service_path);
strscpy(pds->service_name, service_name, sizeof(pds->service_name));
strscpy(pds->service_path, service_path, sizeof(pds->service_path));
pds->need_locator_lookup = true;
mutex_lock(&pdr->list_lock);
@ -587,7 +587,7 @@ int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds)
break;
/* Prepare req message */
strcpy(req.service_path, pds->service_path);
strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
addr = pds->addr;
break;
}

Просмотреть файл

@ -871,7 +871,6 @@ EXPORT_SYMBOL(geni_icc_disable);
static int geni_se_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct geni_wrapper *wrapper;
int ret;
@ -880,8 +879,7 @@ static int geni_se_probe(struct platform_device *pdev)
return -ENOMEM;
wrapper->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wrapper->base = devm_ioremap_resource(dev, res);
wrapper->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wrapper->base))
return PTR_ERR(wrapper->base);

Просмотреть файл

@ -2,16 +2,16 @@
/*
* Copyright (c) 2019, Linaro Ltd
*/
#include <dt-bindings/power/qcom-aoss-qmp.h>
#include <linux/clk-provider.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/thermal.h>
#include <linux/slab.h>
#include <linux/soc/qcom/qcom_aoss.h>
#define QMP_DESC_MAGIC 0x0
#define QMP_DESC_VERSION 0x4
@ -64,7 +64,6 @@ struct qmp_cooling_device {
* @event: wait_queue for synchronization with the IRQ
* @tx_lock: provides synchronization between multiple callers of qmp_send()
* @qdss_clk: QDSS clock hw struct
* @pd_data: genpd data
* @cooling_devs: thermal cooling devices
*/
struct qmp {
@ -82,17 +81,9 @@ struct qmp {
struct mutex tx_lock;
struct clk_hw qdss_clk;
struct genpd_onecell_data pd_data;
struct qmp_cooling_device *cooling_devs;
};
struct qmp_pd {
struct qmp *qmp;
struct generic_pm_domain pd;
};
#define to_qmp_pd_resource(res) container_of(res, struct qmp_pd, pd)
static void qmp_kick(struct qmp *qmp)
{
mbox_send_message(qmp->mbox_chan, NULL);
@ -223,11 +214,14 @@ static bool qmp_message_empty(struct qmp *qmp)
*
* Return: 0 on success, negative errno on failure
*/
static int qmp_send(struct qmp *qmp, const void *data, size_t len)
int qmp_send(struct qmp *qmp, const void *data, size_t len)
{
long time_left;
int ret;
if (WARN_ON(IS_ERR_OR_NULL(qmp) || !data))
return -EINVAL;
if (WARN_ON(len + sizeof(u32) > qmp->size))
return -EINVAL;
@ -261,6 +255,7 @@ static int qmp_send(struct qmp *qmp, const void *data, size_t len)
return ret;
}
EXPORT_SYMBOL(qmp_send);
static int qmp_qdss_clk_prepare(struct clk_hw *hw)
{
@ -314,95 +309,6 @@ static void qmp_qdss_clk_remove(struct qmp *qmp)
clk_hw_unregister(&qmp->qdss_clk);
}
static int qmp_pd_power_toggle(struct qmp_pd *res, bool enable)
{
char buf[QMP_MSG_LEN] = {};
snprintf(buf, sizeof(buf),
"{class: image, res: load_state, name: %s, val: %s}",
res->pd.name, enable ? "on" : "off");
return qmp_send(res->qmp, buf, sizeof(buf));
}
static int qmp_pd_power_on(struct generic_pm_domain *domain)
{
return qmp_pd_power_toggle(to_qmp_pd_resource(domain), true);
}
static int qmp_pd_power_off(struct generic_pm_domain *domain)
{
return qmp_pd_power_toggle(to_qmp_pd_resource(domain), false);
}
static const char * const sdm845_resources[] = {
[AOSS_QMP_LS_CDSP] = "cdsp",
[AOSS_QMP_LS_LPASS] = "adsp",
[AOSS_QMP_LS_MODEM] = "modem",
[AOSS_QMP_LS_SLPI] = "slpi",
[AOSS_QMP_LS_SPSS] = "spss",
[AOSS_QMP_LS_VENUS] = "venus",
};
static int qmp_pd_add(struct qmp *qmp)
{
struct genpd_onecell_data *data = &qmp->pd_data;
struct device *dev = qmp->dev;
struct qmp_pd *res;
size_t num = ARRAY_SIZE(sdm845_resources);
int ret;
int i;
res = devm_kcalloc(dev, num, sizeof(*res), GFP_KERNEL);
if (!res)
return -ENOMEM;
data->domains = devm_kcalloc(dev, num, sizeof(*data->domains),
GFP_KERNEL);
if (!data->domains)
return -ENOMEM;
for (i = 0; i < num; i++) {
res[i].qmp = qmp;
res[i].pd.name = sdm845_resources[i];
res[i].pd.power_on = qmp_pd_power_on;
res[i].pd.power_off = qmp_pd_power_off;
ret = pm_genpd_init(&res[i].pd, NULL, true);
if (ret < 0) {
dev_err(dev, "failed to init genpd\n");
goto unroll_genpds;
}
data->domains[i] = &res[i].pd;
}
data->num_domains = i;
ret = of_genpd_add_provider_onecell(dev->of_node, data);
if (ret < 0)
goto unroll_genpds;
return 0;
unroll_genpds:
for (i--; i >= 0; i--)
pm_genpd_remove(data->domains[i]);
return ret;
}
static void qmp_pd_remove(struct qmp *qmp)
{
struct genpd_onecell_data *data = &qmp->pd_data;
struct device *dev = qmp->dev;
int i;
of_genpd_del_provider(dev->of_node);
for (i = 0; i < data->num_domains; i++)
pm_genpd_remove(data->domains[i]);
}
static int qmp_cdev_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
@ -519,9 +425,53 @@ static void qmp_cooling_devices_remove(struct qmp *qmp)
thermal_cooling_device_unregister(qmp->cooling_devs[i].cdev);
}
/**
* qmp_get() - get a qmp handle from a device
* @dev: client device pointer
*
* Return: handle to qmp device on success, ERR_PTR() on failure
*/
struct qmp *qmp_get(struct device *dev)
{
struct platform_device *pdev;
struct device_node *np;
struct qmp *qmp;
if (!dev || !dev->of_node)
return ERR_PTR(-EINVAL);
np = of_parse_phandle(dev->of_node, "qcom,qmp", 0);
if (!np)
return ERR_PTR(-ENODEV);
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev)
return ERR_PTR(-EINVAL);
qmp = platform_get_drvdata(pdev);
return qmp ? qmp : ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL(qmp_get);
/**
* qmp_put() - release a qmp handle
* @qmp: qmp handle obtained from qmp_get()
*/
void qmp_put(struct qmp *qmp)
{
/*
* Match get_device() inside of_find_device_by_node() in
* qmp_get()
*/
if (!IS_ERR_OR_NULL(qmp))
put_device(qmp->dev);
}
EXPORT_SYMBOL(qmp_put);
static int qmp_probe(struct platform_device *pdev)
{
struct resource *res;
struct qmp *qmp;
int irq;
int ret;
@ -534,8 +484,7 @@ static int qmp_probe(struct platform_device *pdev)
init_waitqueue_head(&qmp->event);
mutex_init(&qmp->tx_lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
qmp->msgram = devm_ioremap_resource(&pdev->dev, res);
qmp->msgram = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(qmp->msgram))
return PTR_ERR(qmp->msgram);
@ -563,10 +512,6 @@ static int qmp_probe(struct platform_device *pdev)
if (ret)
goto err_close_qmp;
ret = qmp_pd_add(qmp);
if (ret)
goto err_remove_qdss_clk;
ret = qmp_cooling_devices_register(qmp);
if (ret)
dev_err(&pdev->dev, "failed to register aoss cooling devices\n");
@ -575,8 +520,6 @@ static int qmp_probe(struct platform_device *pdev)
return 0;
err_remove_qdss_clk:
qmp_qdss_clk_remove(qmp);
err_close_qmp:
qmp_close(qmp);
err_free_mbox:
@ -590,7 +533,6 @@ static int qmp_remove(struct platform_device *pdev)
struct qmp *qmp = platform_get_drvdata(pdev);
qmp_qdss_clk_remove(qmp);
qmp_pd_remove(qmp);
qmp_cooling_devices_remove(qmp);
qmp_close(qmp);
@ -615,6 +557,7 @@ static struct platform_driver qmp_driver = {
.driver = {
.name = "qcom_aoss_qmp",
.of_match_table = qmp_dt_match,
.suppress_bind_attrs = true,
},
.probe = qmp_probe,
.remove = qmp_remove,

Просмотреть файл

@ -127,7 +127,6 @@ static int gsbi_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
struct device_node *tcsr_node;
const struct of_device_id *match;
struct resource *res;
void __iomem *base;
struct gsbi_info *gsbi;
int i, ret;
@ -139,8 +138,7 @@ static int gsbi_probe(struct platform_device *pdev)
if (!gsbi)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);

Просмотреть файл

@ -910,7 +910,6 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
{
struct device_node *dn = pdev->dev.of_node;
struct rsc_drv *drv;
struct resource *res;
char drv_id[10] = {0};
int ret, irq;
u32 solver_config;
@ -941,8 +940,7 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
drv->name = dev_name(&pdev->dev);
snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, drv_id);
base = devm_ioremap_resource(&pdev->dev, res);
base = devm_platform_ioremap_resource_byname(pdev, drv_id);
if (IS_ERR(base))
return PTR_ERR(base);

Просмотреть файл

@ -147,6 +147,21 @@ static const struct rpmhpd_desc sdx55_desc = {
.num_pds = ARRAY_SIZE(sdx55_rpmhpds),
};
/* SM6350 RPMH powerdomains */
static struct rpmhpd *sm6350_rpmhpds[] = {
[SM6350_CX] = &sdm845_cx,
[SM6350_GFX] = &sdm845_gfx,
[SM6350_LCX] = &sdm845_lcx,
[SM6350_LMX] = &sdm845_lmx,
[SM6350_MSS] = &sdm845_mss,
[SM6350_MX] = &sdm845_mx,
};
static const struct rpmhpd_desc sm6350_desc = {
.rpmhpds = sm6350_rpmhpds,
.num_pds = ARRAY_SIZE(sm6350_rpmhpds),
};
/* SM8150 RPMH powerdomains */
static struct rpmhpd sm8150_mmcx_ao;
@ -297,6 +312,7 @@ static const struct of_device_id rpmhpd_match_table[] = {
{ .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc },
{ .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc },
{ .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc},
{ .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc },
{ .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc },
{ .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc },
{ .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc },

Просмотреть файл

@ -185,6 +185,29 @@ static const struct rpmpd_desc msm8916_desc = {
.max_state = MAX_CORNER_RPMPD_STATE,
};
/* msm8953 RPM Power Domains */
DEFINE_RPMPD_PAIR(msm8953, vddmd, vddmd_ao, SMPA, LEVEL, 1);
DEFINE_RPMPD_PAIR(msm8953, vddcx, vddcx_ao, SMPA, LEVEL, 2);
DEFINE_RPMPD_PAIR(msm8953, vddmx, vddmx_ao, SMPA, LEVEL, 7);
DEFINE_RPMPD_VFL(msm8953, vddcx_vfl, SMPA, 2);
static struct rpmpd *msm8953_rpmpds[] = {
[MSM8953_VDDMD] = &msm8953_vddmd,
[MSM8953_VDDMD_AO] = &msm8953_vddmd_ao,
[MSM8953_VDDCX] = &msm8953_vddcx,
[MSM8953_VDDCX_AO] = &msm8953_vddcx_ao,
[MSM8953_VDDCX_VFL] = &msm8953_vddcx_vfl,
[MSM8953_VDDMX] = &msm8953_vddmx,
[MSM8953_VDDMX_AO] = &msm8953_vddmx_ao,
};
static const struct rpmpd_desc msm8953_desc = {
.rpmpds = msm8953_rpmpds,
.num_pds = ARRAY_SIZE(msm8953_rpmpds),
.max_state = RPM_SMD_LEVEL_TURBO,
};
/* msm8976 RPM Power Domains */
DEFINE_RPMPD_PAIR(msm8976, vddcx, vddcx_ao, SMPA, LEVEL, 2);
DEFINE_RPMPD_PAIR(msm8976, vddmx, vddmx_ao, SMPA, LEVEL, 6);
@ -377,6 +400,7 @@ static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc },
{ .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc },
{ .compatible = "qcom,msm8939-rpmpd", .data = &msm8939_desc },
{ .compatible = "qcom,msm8953-rpmpd", .data = &msm8953_desc },
{ .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc },
{ .compatible = "qcom,msm8994-rpmpd", .data = &msm8994_desc },
{ .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc },

Просмотреть файл

@ -236,6 +236,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = {
{ .compatible = "qcom,rpm-msm8226" },
{ .compatible = "qcom,rpm-msm8916" },
{ .compatible = "qcom,rpm-msm8936" },
{ .compatible = "qcom,rpm-msm8953" },
{ .compatible = "qcom,rpm-msm8974" },
{ .compatible = "qcom,rpm-msm8976" },
{ .compatible = "qcom,rpm-msm8994" },
@ -244,6 +245,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = {
{ .compatible = "qcom,rpm-sdm660" },
{ .compatible = "qcom,rpm-sm6115" },
{ .compatible = "qcom,rpm-sm6125" },
{ .compatible = "qcom,rpm-qcm2290" },
{ .compatible = "qcom,rpm-qcs404" },
{}
};

Просмотреть файл

@ -14,6 +14,7 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/smem_state.h>
@ -538,9 +539,26 @@ static int qcom_smp2p_probe(struct platform_device *pdev)
goto unwind_interfaces;
}
/*
* Treat smp2p interrupt as wakeup source, but keep it disabled
* by default. User space can decide enabling it depending on its
* use cases. For example if remoteproc crashes and device wants
* to handle it immediatedly (e.g. to not miss phone calls) it can
* enable wakeup source from user space, while other devices which
* do not have proper autosleep feature may want to handle it with
* other wakeup events (e.g. Power button) instead waking up immediately.
*/
device_set_wakeup_capable(&pdev->dev, true);
ret = dev_pm_set_wake_irq(&pdev->dev, irq);
if (ret)
goto set_wake_irq_fail;
return 0;
set_wake_irq_fail:
dev_pm_clear_wake_irq(&pdev->dev);
unwind_interfaces:
list_for_each_entry(entry, &smp2p->inbound, node)
irq_domain_remove(entry->domain);
@ -565,6 +583,8 @@ static int qcom_smp2p_remove(struct platform_device *pdev)
struct qcom_smp2p *smp2p = platform_get_drvdata(pdev);
struct smp2p_entry *entry;
dev_pm_clear_wake_irq(&pdev->dev);
list_for_each_entry(entry, &smp2p->inbound, node)
irq_domain_remove(entry->domain);

Просмотреть файл

@ -281,19 +281,31 @@ static const struct soc_id soc_id[] = {
{ 319, "APQ8098" },
{ 321, "SDM845" },
{ 322, "MDM9206" },
{ 323, "IPQ8074" },
{ 324, "SDA660" },
{ 325, "SDM658" },
{ 326, "SDA658" },
{ 327, "SDA630" },
{ 338, "SDM450" },
{ 341, "SDA845" },
{ 342, "IPQ8072" },
{ 343, "IPQ8076" },
{ 344, "IPQ8078" },
{ 345, "SDM636" },
{ 346, "SDA636" },
{ 349, "SDM632" },
{ 350, "SDA632" },
{ 351, "SDA450" },
{ 356, "SM8250" },
{ 375, "IPQ8070" },
{ 376, "IPQ8071" },
{ 389, "IPQ8072A" },
{ 390, "IPQ8074A" },
{ 391, "IPQ8076A" },
{ 392, "IPQ8078A" },
{ 394, "SM6125" },
{ 395, "IPQ8070A" },
{ 396, "IPQ8071A" },
{ 402, "IPQ6018" },
{ 403, "IPQ6028" },
{ 421, "IPQ6000" },

258
drivers/soc/qcom/spm.c Normal file
Просмотреть файл

@ -0,0 +1,258 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
* Copyright (c) 2014,2015, Linaro Ltd.
*
* SAW power controller driver
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <soc/qcom/spm.h>
#define SPM_CTL_INDEX 0x7f
#define SPM_CTL_INDEX_SHIFT 4
#define SPM_CTL_EN BIT(0)
enum spm_reg {
SPM_REG_CFG,
SPM_REG_SPM_CTL,
SPM_REG_DLY,
SPM_REG_PMIC_DLY,
SPM_REG_PMIC_DATA_0,
SPM_REG_PMIC_DATA_1,
SPM_REG_VCTL,
SPM_REG_SEQ_ENTRY,
SPM_REG_SPM_STS,
SPM_REG_PMIC_STS,
SPM_REG_AVS_CTL,
SPM_REG_AVS_LIMIT,
SPM_REG_NR,
};
static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = {
[SPM_REG_AVS_CTL] = 0x904,
[SPM_REG_AVS_LIMIT] = 0x908,
};
static const struct spm_reg_data spm_reg_660_gold_l2 = {
.reg_offset = spm_reg_offset_v4_1,
.avs_ctl = 0x1010031,
.avs_limit = 0x4580458,
};
static const struct spm_reg_data spm_reg_660_silver_l2 = {
.reg_offset = spm_reg_offset_v4_1,
.avs_ctl = 0x101c031,
.avs_limit = 0x4580458,
};
static const struct spm_reg_data spm_reg_8998_gold_l2 = {
.reg_offset = spm_reg_offset_v4_1,
.avs_ctl = 0x1010031,
.avs_limit = 0x4700470,
};
static const struct spm_reg_data spm_reg_8998_silver_l2 = {
.reg_offset = spm_reg_offset_v4_1,
.avs_ctl = 0x1010031,
.avs_limit = 0x4200420,
};
static const u16 spm_reg_offset_v2_1[SPM_REG_NR] = {
[SPM_REG_CFG] = 0x08,
[SPM_REG_SPM_CTL] = 0x30,
[SPM_REG_DLY] = 0x34,
[SPM_REG_SEQ_ENTRY] = 0x80,
};
/* SPM register data for 8974, 8084 */
static const struct spm_reg_data spm_reg_8974_8084_cpu = {
.reg_offset = spm_reg_offset_v2_1,
.spm_cfg = 0x1,
.spm_dly = 0x3C102800,
.seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03,
0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30,
0x0F },
.start_index[PM_SLEEP_MODE_STBY] = 0,
.start_index[PM_SLEEP_MODE_SPC] = 3,
};
/* SPM register data for 8226 */
static const struct spm_reg_data spm_reg_8226_cpu = {
.reg_offset = spm_reg_offset_v2_1,
.spm_cfg = 0x0,
.spm_dly = 0x3C102800,
.seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90,
0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B,
0x80, 0x10, 0x26, 0x30, 0x0F },
.start_index[PM_SLEEP_MODE_STBY] = 0,
.start_index[PM_SLEEP_MODE_SPC] = 5,
};
static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = {
[SPM_REG_CFG] = 0x08,
[SPM_REG_SPM_CTL] = 0x20,
[SPM_REG_PMIC_DLY] = 0x24,
[SPM_REG_PMIC_DATA_0] = 0x28,
[SPM_REG_PMIC_DATA_1] = 0x2C,
[SPM_REG_SEQ_ENTRY] = 0x80,
};
/* SPM register data for 8064 */
static const struct spm_reg_data spm_reg_8064_cpu = {
.reg_offset = spm_reg_offset_v1_1,
.spm_cfg = 0x1F,
.pmic_dly = 0x02020004,
.pmic_data[0] = 0x0084009C,
.pmic_data[1] = 0x00A4001C,
.seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01,
0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
.start_index[PM_SLEEP_MODE_STBY] = 0,
.start_index[PM_SLEEP_MODE_SPC] = 2,
};
static inline void spm_register_write(struct spm_driver_data *drv,
enum spm_reg reg, u32 val)
{
if (drv->reg_data->reg_offset[reg])
writel_relaxed(val, drv->reg_base +
drv->reg_data->reg_offset[reg]);
}
/* Ensure a guaranteed write, before return */
static inline void spm_register_write_sync(struct spm_driver_data *drv,
enum spm_reg reg, u32 val)
{
u32 ret;
if (!drv->reg_data->reg_offset[reg])
return;
do {
writel_relaxed(val, drv->reg_base +
drv->reg_data->reg_offset[reg]);
ret = readl_relaxed(drv->reg_base +
drv->reg_data->reg_offset[reg]);
if (ret == val)
break;
cpu_relax();
} while (1);
}
static inline u32 spm_register_read(struct spm_driver_data *drv,
enum spm_reg reg)
{
return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]);
}
void spm_set_low_power_mode(struct spm_driver_data *drv,
enum pm_sleep_mode mode)
{
u32 start_index;
u32 ctl_val;
start_index = drv->reg_data->start_index[mode];
ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL);
ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT);
ctl_val |= start_index << SPM_CTL_INDEX_SHIFT;
ctl_val |= SPM_CTL_EN;
spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
}
static const struct of_device_id spm_match_table[] = {
{ .compatible = "qcom,sdm660-gold-saw2-v4.1-l2",
.data = &spm_reg_660_gold_l2 },
{ .compatible = "qcom,sdm660-silver-saw2-v4.1-l2",
.data = &spm_reg_660_silver_l2 },
{ .compatible = "qcom,msm8226-saw2-v2.1-cpu",
.data = &spm_reg_8226_cpu },
{ .compatible = "qcom,msm8974-saw2-v2.1-cpu",
.data = &spm_reg_8974_8084_cpu },
{ .compatible = "qcom,msm8998-gold-saw2-v4.1-l2",
.data = &spm_reg_8998_gold_l2 },
{ .compatible = "qcom,msm8998-silver-saw2-v4.1-l2",
.data = &spm_reg_8998_silver_l2 },
{ .compatible = "qcom,apq8084-saw2-v2.1-cpu",
.data = &spm_reg_8974_8084_cpu },
{ .compatible = "qcom,apq8064-saw2-v1.1-cpu",
.data = &spm_reg_8064_cpu },
{ },
};
MODULE_DEVICE_TABLE(of, spm_match_table);
static int spm_dev_probe(struct platform_device *pdev)
{
const struct of_device_id *match_id;
struct spm_driver_data *drv;
struct resource *res;
void __iomem *addr;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(drv->reg_base))
return PTR_ERR(drv->reg_base);
match_id = of_match_node(spm_match_table, pdev->dev.of_node);
if (!match_id)
return -ENODEV;
drv->reg_data = match_id->data;
platform_set_drvdata(pdev, drv);
/* Write the SPM sequences first.. */
addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
__iowrite32_copy(addr, drv->reg_data->seq,
ARRAY_SIZE(drv->reg_data->seq) / 4);
/*
* ..and then the control registers.
* On some SoC if the control registers are written first and if the
* CPU was held in reset, the reset signal could trigger the SPM state
* machine, before the sequences are completely written.
*/
spm_register_write(drv, SPM_REG_AVS_CTL, drv->reg_data->avs_ctl);
spm_register_write(drv, SPM_REG_AVS_LIMIT, drv->reg_data->avs_limit);
spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
spm_register_write(drv, SPM_REG_PMIC_DATA_0,
drv->reg_data->pmic_data[0]);
spm_register_write(drv, SPM_REG_PMIC_DATA_1,
drv->reg_data->pmic_data[1]);
/* Set up Standby as the default low power mode */
if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL])
spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
return 0;
}
static struct platform_driver spm_driver = {
.probe = spm_dev_probe,
.driver = {
.name = "qcom_spm",
.of_match_table = spm_match_table,
},
};
static int __init qcom_spm_init(void)
{
return platform_driver_register(&spm_driver);
}
arch_initcall(qcom_spm_init);
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -1,14 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018, Linaro Ltd. */
#ifndef __DT_BINDINGS_POWER_QCOM_AOSS_QMP_H
#define __DT_BINDINGS_POWER_QCOM_AOSS_QMP_H
#define AOSS_QMP_LS_CDSP 0
#define AOSS_QMP_LS_LPASS 1
#define AOSS_QMP_LS_MODEM 2
#define AOSS_QMP_LS_SLPI 3
#define AOSS_QMP_LS_SPSS 4
#define AOSS_QMP_LS_VENUS 5
#endif

Просмотреть файл

@ -20,6 +20,14 @@
#define SDX55_MX 1
#define SDX55_CX 2
/* SM6350 Power Domain Indexes */
#define SM6350_CX 0
#define SM6350_GFX 1
#define SM6350_LCX 2
#define SM6350_LMX 3
#define SM6350_MSS 4
#define SM6350_MX 5
/* SM8150 Power Domain Indexes */
#define SM8150_MSS 0
#define SM8150_EBI 1
@ -133,6 +141,15 @@
#define MSM8916_VDDMX 3
#define MSM8916_VDDMX_AO 4
/* MSM8953 Power Domain Indexes */
#define MSM8953_VDDMD 0
#define MSM8953_VDDMD_AO 1
#define MSM8953_VDDCX 2
#define MSM8953_VDDCX_AO 3
#define MSM8953_VDDCX_VFL 4
#define MSM8953_VDDMX 5
#define MSM8953_VDDMX_AO 6
/* MSM8976 Power Domain Indexes */
#define MSM8976_VDDCX 0
#define MSM8976_VDDCX_AO 1

Просмотреть файл

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
#ifndef __DT_BINDINGS_QCOM_GPR_H
#define __DT_BINDINGS_QCOM_GPR_H
/* DOMAINS */
#define GPR_DOMAIN_ID_MODEM 1
#define GPR_DOMAIN_ID_ADSP 2
#define GPR_DOMAIN_ID_APPS 3
/* Static Services */
#define GPR_APM_MODULE_IID 1
#define GPR_PRM_MODULE_IID 2
#define GPR_AMDB_MODULE_IID 3
#define GPR_VCPM_MODULE_IID 4
#endif /* __DT_BINDINGS_QCOM_GPR_H */

Просмотреть файл

@ -7,6 +7,7 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <dt-bindings/soc/qcom,apr.h>
#include <dt-bindings/soc/qcom,gpr.h>
extern struct bus_type aprbus;
@ -75,10 +76,65 @@ struct apr_resp_pkt {
int payload_size;
};
struct gpr_hdr {
uint32_t version:4;
uint32_t hdr_size:4;
uint32_t pkt_size:24;
uint32_t dest_domain:8;
uint32_t src_domain:8;
uint32_t reserved:16;
uint32_t src_port;
uint32_t dest_port;
uint32_t token;
uint32_t opcode;
} __packed;
struct gpr_pkt {
struct gpr_hdr hdr;
uint32_t payload[];
};
struct gpr_resp_pkt {
struct gpr_hdr hdr;
void *payload;
int payload_size;
};
#define GPR_HDR_SIZE sizeof(struct gpr_hdr)
#define GPR_PKT_VER 0x0
#define GPR_PKT_HEADER_WORD_SIZE ((sizeof(struct gpr_pkt) + 3) >> 2)
#define GPR_PKT_HEADER_BYTE_SIZE (GPR_PKT_HEADER_WORD_SIZE << 2)
#define GPR_BASIC_RSP_RESULT 0x02001005
struct gpr_ibasic_rsp_result_t {
uint32_t opcode;
uint32_t status;
};
#define GPR_BASIC_EVT_ACCEPTED 0x02001006
struct gpr_ibasic_rsp_accepted_t {
uint32_t opcode;
};
/* Bits 0 to 15 -- Minor version, Bits 16 to 31 -- Major version */
#define APR_SVC_MAJOR_VERSION(v) ((v >> 16) & 0xFF)
#define APR_SVC_MINOR_VERSION(v) (v & 0xFF)
typedef int (*gpr_port_cb) (struct gpr_resp_pkt *d, void *priv, int op);
struct packet_router;
struct pkt_router_svc {
struct device *dev;
gpr_port_cb callback;
struct packet_router *pr;
spinlock_t lock;
int id;
void *priv;
};
typedef struct pkt_router_svc gpr_port_t;
struct apr_device {
struct device dev;
uint16_t svc_id;
@ -86,21 +142,26 @@ struct apr_device {
uint32_t version;
char name[APR_NAME_SIZE];
const char *service_path;
spinlock_t lock;
struct pkt_router_svc svc;
struct list_head node;
};
typedef struct apr_device gpr_device_t;
#define to_apr_device(d) container_of(d, struct apr_device, dev)
#define svc_to_apr_device(d) container_of(d, struct apr_device, svc)
struct apr_driver {
int (*probe)(struct apr_device *sl);
int (*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);
struct device_driver driver;
const struct apr_device_id *id_table;
};
typedef struct apr_driver gpr_driver_t;
#define to_apr_driver(d) container_of(d, struct apr_driver, driver)
/*
@ -123,7 +184,14 @@ void apr_driver_unregister(struct apr_driver *drv);
#define module_apr_driver(__apr_driver) \
module_driver(__apr_driver, apr_driver_register, \
apr_driver_unregister)
#define module_gpr_driver(__gpr_driver) module_apr_driver(__gpr_driver)
int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt);
gpr_port_t *gpr_alloc_port(gpr_device_t *gdev, struct device *dev,
gpr_port_cb cb, void *priv);
void gpr_free_port(gpr_port_t *port);
int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt);
int gpr_send_pkt(gpr_device_t *gdev, struct gpr_pkt *pkt);
#endif /* __QCOM_APR_H_ */

Просмотреть файл

@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef __QCOM_AOSS_H__
#define __QCOM_AOSS_H__
#include <linux/err.h>
#include <linux/device.h>
struct qmp;
#if IS_ENABLED(CONFIG_QCOM_AOSS_QMP)
int qmp_send(struct qmp *qmp, const void *data, size_t len);
struct qmp *qmp_get(struct device *dev);
void qmp_put(struct qmp *qmp);
#else
static inline int qmp_send(struct qmp *qmp, const void *data, size_t len)
{
return -ENODEV;
}
static inline struct qmp *qmp_get(struct device *dev)
{
return ERR_PTR(-ENODEV);
}
static inline void qmp_put(struct qmp *qmp)
{
}
#endif
#endif

43
include/soc/qcom/spm.h Normal file
Просмотреть файл

@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
* Copyright (c) 2014,2015, Linaro Ltd.
*/
#ifndef __SPM_H__
#define __SPM_H__
#include <linux/cpuidle.h>
#define MAX_PMIC_DATA 2
#define MAX_SEQ_DATA 64
enum pm_sleep_mode {
PM_SLEEP_MODE_STBY,
PM_SLEEP_MODE_RET,
PM_SLEEP_MODE_SPC,
PM_SLEEP_MODE_PC,
PM_SLEEP_MODE_NR,
};
struct spm_reg_data {
const u16 *reg_offset;
u32 spm_cfg;
u32 spm_dly;
u32 pmic_dly;
u32 pmic_data[MAX_PMIC_DATA];
u32 avs_ctl;
u32 avs_limit;
u8 seq[MAX_SEQ_DATA];
u8 start_index[PM_SLEEP_MODE_NR];
};
struct spm_driver_data {
void __iomem *reg_base;
const struct spm_reg_data *reg_data;
};
void spm_set_low_power_mode(struct spm_driver_data *drv,
enum pm_sleep_mode mode);
#endif /* __SPM_H__ */