Qualcomm driver updates for v6.9
This introduces the Qualcomm Programmable Boot Sequencer (PBS) driver. The Qualcomm SMEM no longer acquires the hwspinlock during the "get" operation, to improve the system behavior during the recovery of a remoteproc that crashed with the hwspinlock held. The Qualcomm Always On Subsystem (AOSS) message protocol driver gains tracepoints, printf annotation, and a debugfs interface is introduced for tweaking system properties during development and debugging. The Qualcomm socinfo driver gains data for SM8475, QCM8550 and QCS8550 platforms, and the PM2250 is renamed to PM4125. Support for controlling the voltage regulator in SPM/SAW2 is introduced. The gfx.lvl power-domain is dropped for SA8540P, as this resource was incorrectly inherited from SC8280XP. Additionally some code cleanup improvements is introduced across APR, LLCC, SMP2P and SPM. -----BEGIN PGP SIGNATURE----- iQJJBAABCAAzFiEEBd4DzF816k8JZtUlCx85Pw2ZrcUFAmXarn0VHGFuZGVyc3Nv bkBrZXJuZWwub3JnAAoJEAsfOT8Nma3FzZoP/2hYUMS+sR2kD7gZDGi8oV71C+dQ Vmo1MQIhGjYEb6HSHdrxp/11GPlLVU45xuk9/FFVqAPrxNQm2y9NC60Ye1TQNWZR XS+w5+YkIFdfZIbZ8GGmlPspeXDSnq2DGWQjht+cBNyASzhjO5ICP79WQ0jfHgjH l5pNw3YMamc7pwdX2oslxXV8HzDr4JxzpveV0+QbsVDU4J04mrxoBRmv2cdqv2Eb 5jNR4HjmCQkdI/bp/2frCmWmGP9bqP24C6XWCe4lXuCAsmNKAeEeuyABnsPNRYrO 5ELd2sPpXBLBUhR9w/0rY9ouIsJ500BD7U1yLrHocwGICL1Ro7dGjnyp6j/7Tm9X 1nx2mHK7ywH7E6BxVTcmDS3CtymcMknpUfUjANJ/yQrAmcqvKRA3F0ELiXywrndQ 0R97qjc9ixVaM5LU4kMgQG50NZCmsNPJ7a3smOihpmfJK+6Mmkc5XxFRYuJgL9Ag b1PwnVgzeBPQAs+Tx7i5bBfFOa49zVxHYIFfu+XY50DL80viiKCRAeXQNyWmJ0qM 3tKgMeIY2nXZJ0DvySFfro51AnZcXEMa3Bc5qNZlM4jaqXGPkDocAV9CYKntlXFU POnOnGo4liZalWqel3tTuDoBu3nVAq0q4+ooWl755TRGBtIvW1tWAOgldjg6v3Ef 6BgAWh8GrEHM0etB =T09a -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmXl7kUACgkQYKtH/8kJ UidEfQ//eugpCwNrv6q1TdlSBJkwlVV/3VJLewh4SCdpckQNJ9ysxDt0hnbTVVz8 ykR0ilYLrpuinbWSaBIZp4oxypIHOEhXzQf1diyrAjM9T5mVnv+Ytdy3qDrQ1SOM yg8n64aaBCrJiCJk3vHitS2DxStbmikX8i0dzQNeG7YskplrkbKXqtXjQ8zmanAc 2Y4Ot1DMoTKjp4t6mC7yF5YXTEfCkg0eHaIdzMREarWRUU+g0qqR43T416AhayM5 4mNH42/0rxJtvN3+EeQkVBXMWu2hmHt9esdR216GZ22fS+J9HAVOGPM+oga1Td7c 947T2BB55AalfjiNxWbfabxNmHyEuVyYWaDAJO/qU0ZIE+539QAVaW9xrTxYeOFp PbctLehm3DUPmR453R4oUTGC6UD+OnZOj+ppy/vB08UbdF5bV0KRT/hviOR+bb6U VCYRbDIsfzY1fFFLJShZONogZADtciDjel7WATwhttw503NLH5DaebD+OjPbHVgq U4y7maUGPkbAPJT4YbYOcetqVOnYiFp8OG3J/JDTOr3+3RNtInPYao5TSb1QNjGh A7+4jKf0X8err26xpmGXfa2nG8hUNOPn/PYVNwxgWmq0P74KR2GwenB4ze+SME2J sc6ZQy4LAnU8KSAZpLVPrv78R9cbyCvCtWbQpKzY8l8PfzhaK24= =bPSM -----END PGP SIGNATURE----- Merge tag 'qcom-drivers-for-6.9' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into soc/drivers Qualcomm driver updates for v6.9 This introduces the Qualcomm Programmable Boot Sequencer (PBS) driver. The Qualcomm SMEM no longer acquires the hwspinlock during the "get" operation, to improve the system behavior during the recovery of a remoteproc that crashed with the hwspinlock held. The Qualcomm Always On Subsystem (AOSS) message protocol driver gains tracepoints, printf annotation, and a debugfs interface is introduced for tweaking system properties during development and debugging. The Qualcomm socinfo driver gains data for SM8475, QCM8550 and QCS8550 platforms, and the PM2250 is renamed to PM4125. Support for controlling the voltage regulator in SPM/SAW2 is introduced. The gfx.lvl power-domain is dropped for SA8540P, as this resource was incorrectly inherited from SC8280XP. Additionally some code cleanup improvements is introduced across APR, LLCC, SMP2P and SPM. * tag 'qcom-drivers-for-6.9' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: (23 commits) dt-bindings: soc: qcom: qcom,saw2: add msm8226 l2 compatible soc: qcom: spm: add support for voltage regulator soc: qcom: spm: remove driver-internal structures from the driver API dt-bindings: soc: qcom: qcom,saw2: define optional regulator node dt-bindings: soc: qcom: qcom,saw2: add missing compatible strings dt-bindings: soc: qcom: merge qcom,saw2.txt into qcom,spm.yaml soc: qcom: llcc: Check return value on Broadcast_OR reg read soc: qcom: socinfo: Add Soc IDs for SM8475 family dt-bindings: arm: qcom,ids: Add IDs for SM8475 family soc: qcom: apr: make aprbus const dt-bindings: soc: qcom: qcom,pmic-glink: document X1E80100 compatible soc: qcom: add QCOM PBS driver dt-bindings: soc: qcom: Add qcom,pbs bindings pmdomain: qcom: rpmhpd: Drop SA8540P gfx.lvl soc: qcom: socinfo: rename PM2250 to PM4125 soc: qcom: aoss: Add tracepoints in qmp_send() soc: qcom: socinfo: add SoC Info support for QCM8550 and QCS8550 platform dt-bindings: arm: qcom,ids: add SoC ID for QCM8550 and QCS8550 soc: qcom: aoss: Add debugfs interface for sending messages soc: qcom: smem: remove hwspinlock from item get routine ... Link: https://lore.kernel.org/r/20240225030612.480241-1-andersson@kernel.org Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
daa0987e1f
@ -1,58 +0,0 @@
|
||||
SPM AVS Wrapper 2 (SAW2)
|
||||
|
||||
The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the
|
||||
Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable
|
||||
power-controller that transitions a piece of hardware (like a processor or
|
||||
subsystem) into and out of low power modes via a direct connection to
|
||||
the PMIC. It can also be wired up to interact with other processors in the
|
||||
system, notifying them when a low power state is entered or exited.
|
||||
|
||||
Multiple revisions of the SAW hardware are supported using these Device Nodes.
|
||||
SAW2 revisions differ in the register offset and configuration data. Also, the
|
||||
same revision of the SAW in different SoCs may have different configuration
|
||||
data due the differences in hardware capabilities. Hence the SoC name, the
|
||||
version of the SAW hardware in that SoC and the distinction between cpu (big
|
||||
or Little) or cache, may be needed to uniquely identify the SAW register
|
||||
configuration and initialization data. The compatible string is used to
|
||||
indicate this parameter.
|
||||
|
||||
PROPERTIES
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: Must have
|
||||
"qcom,saw2"
|
||||
A more specific value could be one of:
|
||||
"qcom,apq8064-saw2-v1.1-cpu"
|
||||
"qcom,msm8226-saw2-v2.1-cpu"
|
||||
"qcom,msm8974-saw2-v2.1-cpu"
|
||||
"qcom,apq8084-saw2-v2.1-cpu"
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: the first element specifies the base address and size of
|
||||
the register region. An optional second element specifies
|
||||
the base address and size of the alias register region.
|
||||
|
||||
- regulator:
|
||||
Usage: optional
|
||||
Value type: boolean
|
||||
Definition: Indicates that this SPM device acts as a regulator device
|
||||
device for the core (CPU or Cache) the SPM is attached
|
||||
to.
|
||||
|
||||
Example 1:
|
||||
|
||||
power-controller@2099000 {
|
||||
compatible = "qcom,saw2";
|
||||
reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
|
||||
regulator;
|
||||
};
|
||||
|
||||
Example 2:
|
||||
saw0: power-controller@f9089000 {
|
||||
compatible = "qcom,apq8084-saw2-v2.1-cpu", "qcom,saw2";
|
||||
reg = <0xf9089000 0x1000>, <0xf9009000 0x1000>;
|
||||
};
|
46
Documentation/devicetree/bindings/soc/qcom/qcom,pbs.yaml
Normal file
46
Documentation/devicetree/bindings/soc/qcom/qcom,pbs.yaml
Normal file
@ -0,0 +1,46 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/qcom/qcom,pbs.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Technologies, Inc. Programmable Boot Sequencer
|
||||
|
||||
maintainers:
|
||||
- Anjelique Melendez <quic_amelende@quicinc.com>
|
||||
|
||||
description: |
|
||||
The Qualcomm Technologies, Inc. Programmable Boot Sequencer (PBS)
|
||||
supports triggering power up and power down sequences for clients
|
||||
upon request.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,pmi632-pbs
|
||||
- const: qcom,pbs
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/spmi/spmi.h>
|
||||
|
||||
pmic@0 {
|
||||
reg = <0x0 SPMI_USID>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pbs@7400 {
|
||||
compatible = "qcom,pmi632-pbs", "qcom,pbs";
|
||||
reg = <0x7400>;
|
||||
};
|
||||
};
|
@ -32,6 +32,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sm8650-pmic-glink
|
||||
- qcom,x1e80100-pmic-glink
|
||||
- const: qcom,sm8550-pmic-glink
|
||||
- const: qcom,pmic-glink
|
||||
|
||||
@ -65,6 +66,7 @@ allOf:
|
||||
enum:
|
||||
- qcom,sm8450-pmic-glink
|
||||
- qcom,sm8550-pmic-glink
|
||||
- qcom,x1e80100-pmic-glink
|
||||
then:
|
||||
properties:
|
||||
orientation-gpios: false
|
||||
|
@ -35,6 +35,8 @@ properties:
|
||||
description: Phandle to an RPM MSG RAM slice containing the master stats
|
||||
minItems: 1
|
||||
maxItems: 5
|
||||
items:
|
||||
maxItems: 1
|
||||
|
||||
qcom,master-names:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
|
@ -1,23 +1,33 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/qcom/qcom,spm.yaml#
|
||||
$id: http://devicetree.org/schemas/soc/qcom/qcom,saw2.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Subsystem Power Manager
|
||||
title: Qualcomm Subsystem Power Manager / SPM AVS Wrapper 2 (SAW2)
|
||||
|
||||
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.
|
||||
The Qualcomm Subsystem Power Manager is used to control the peripheral logic
|
||||
surrounding the application cores in Qualcomm platforms.
|
||||
|
||||
The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the
|
||||
Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable
|
||||
power-controller that transitions a piece of hardware (like a processor or
|
||||
subsystem) into and out of low power modes via a direct connection to
|
||||
the PMIC. It can also be wired up to interact with other processors in the
|
||||
system, notifying them when a low power state is entered or exited.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,ipq4019-saw2-cpu
|
||||
- qcom,ipq4019-saw2-l2
|
||||
- qcom,ipq8064-saw2-cpu
|
||||
- qcom,sdm660-gold-saw2-v4.1-l2
|
||||
- qcom,sdm660-silver-saw2-v4.1-l2
|
||||
- qcom,msm8998-gold-saw2-v4.1-l2
|
||||
@ -26,16 +36,27 @@ properties:
|
||||
- qcom,msm8916-saw2-v3.0-cpu
|
||||
- qcom,msm8939-saw2-v3.0-cpu
|
||||
- qcom,msm8226-saw2-v2.1-cpu
|
||||
- qcom,msm8226-saw2-v2.1-l2
|
||||
- qcom,msm8960-saw2-cpu
|
||||
- qcom,msm8974-saw2-v2.1-cpu
|
||||
- qcom,msm8974-saw2-v2.1-l2
|
||||
- qcom,msm8976-gold-saw2-v2.3-l2
|
||||
- qcom,msm8976-silver-saw2-v2.3-l2
|
||||
- qcom,apq8084-saw2-v2.1-cpu
|
||||
- qcom,apq8084-saw2-v2.1-l2
|
||||
- qcom,apq8064-saw2-v1.1-cpu
|
||||
- const: qcom,saw2
|
||||
|
||||
reg:
|
||||
description: Base address and size of the SPM register region
|
||||
maxItems: 1
|
||||
items:
|
||||
- description: Base address and size of the SPM register region
|
||||
- description: Base address and size of the alias register region
|
||||
minItems: 1
|
||||
|
||||
regulator:
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
description: Indicates that this SPM device acts as a regulator device
|
||||
device for the core (CPU or Cache) the SPM is attached to.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
@ -82,4 +103,17 @@ examples:
|
||||
reg = <0x17912000 0x1000>;
|
||||
};
|
||||
|
||||
- |
|
||||
/*
|
||||
* Example 3: SAW2 with the bundled regulator definition.
|
||||
*/
|
||||
power-manager@2089000 {
|
||||
compatible = "qcom,apq8064-saw2-v1.1-cpu", "qcom,saw2";
|
||||
reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
|
||||
|
||||
regulator {
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
};
|
||||
};
|
||||
...
|
@ -217,7 +217,6 @@ static struct rpmhpd *sa8540p_rpmhpds[] = {
|
||||
[SC8280XP_CX] = &cx,
|
||||
[SC8280XP_CX_AO] = &cx_ao,
|
||||
[SC8280XP_EBI] = &ebi,
|
||||
[SC8280XP_GFX] = &gfx,
|
||||
[SC8280XP_LCX] = &lcx,
|
||||
[SC8280XP_LMX] = &lmx,
|
||||
[SC8280XP_MMCX] = &mmcx,
|
||||
|
@ -268,4 +268,13 @@ config QCOM_INLINE_CRYPTO_ENGINE
|
||||
tristate
|
||||
select QCOM_SCM
|
||||
|
||||
config QCOM_PBS
|
||||
tristate "PBS trigger support for Qualcomm Technologies, Inc. PMICS"
|
||||
depends on SPMI
|
||||
help
|
||||
This driver supports configuring software programmable boot sequencer (PBS)
|
||||
trigger event through PBS RAM on Qualcomm Technologies, Inc. PMICs.
|
||||
This module provides the APIs to the client drivers that wants to send the
|
||||
PBS trigger event to the PBS RAM.
|
||||
|
||||
endmenu
|
||||
|
@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
CFLAGS_rpmh-rsc.o := -I$(src)
|
||||
CFLAGS_qcom_aoss.o := -I$(src)
|
||||
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
|
||||
@ -34,3 +35,4 @@ obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o
|
||||
obj-$(CONFIG_QCOM_ICC_BWMON) += icc-bwmon.o
|
||||
qcom_ice-objs += ice.o
|
||||
obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE) += qcom_ice.o
|
||||
obj-$(CONFIG_QCOM_PBS) += qcom-pbs.o
|
||||
|
@ -399,7 +399,7 @@ static int apr_uevent(const struct device *dev, struct kobj_uevent_env *env)
|
||||
return add_uevent_var(env, "MODALIAS=apr:%s", adev->name);
|
||||
}
|
||||
|
||||
struct bus_type aprbus = {
|
||||
const struct bus_type aprbus = {
|
||||
.name = "aprbus",
|
||||
.match = apr_device_match,
|
||||
.probe = apr_device_probe,
|
||||
|
@ -859,6 +859,8 @@ static int llcc_update_act_ctrl(u32 sid,
|
||||
ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg,
|
||||
slice_status, !(slice_status & status),
|
||||
0, LLCC_STATUS_READ_DELAY);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (drv_data->version >= LLCC_VERSION_4_1_0_0)
|
||||
ret = regmap_write(drv_data->bcast_regmap, act_clear_reg,
|
||||
|
236
drivers/soc/qcom/qcom-pbs.c
Normal file
236
drivers/soc/qcom/qcom-pbs.c
Normal file
@ -0,0 +1,236 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spmi.h>
|
||||
#include <linux/soc/qcom/qcom-pbs.h>
|
||||
|
||||
#define PBS_CLIENT_TRIG_CTL 0x42
|
||||
#define PBS_CLIENT_SW_TRIG_BIT BIT(7)
|
||||
#define PBS_CLIENT_SCRATCH1 0x50
|
||||
#define PBS_CLIENT_SCRATCH2 0x51
|
||||
#define PBS_CLIENT_SCRATCH2_ERROR 0xFF
|
||||
|
||||
#define RETRIES 2000
|
||||
#define DELAY 1100
|
||||
|
||||
struct pbs_dev {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct mutex lock;
|
||||
struct device_link *link;
|
||||
|
||||
u32 base;
|
||||
};
|
||||
|
||||
static int qcom_pbs_wait_for_ack(struct pbs_dev *pbs, u8 bit_pos)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2,
|
||||
val, val & BIT(bit_pos), DELAY, DELAY * RETRIES);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(pbs->dev, "Timeout for PBS ACK/NACK for bit %u\n", bit_pos);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (val == PBS_CLIENT_SCRATCH2_ERROR) {
|
||||
ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0);
|
||||
dev_err(pbs->dev, "NACK from PBS for bit %u\n", bit_pos);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(pbs->dev, "PBS sequence for bit %u executed!\n", bit_pos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_pbs_trigger_event() - Trigger the PBS RAM sequence
|
||||
* @pbs: Pointer to PBS device
|
||||
* @bitmap: bitmap
|
||||
*
|
||||
* This function is used to trigger the PBS RAM sequence to be
|
||||
* executed by the client driver.
|
||||
*
|
||||
* The PBS trigger sequence involves
|
||||
* 1. setting the PBS sequence bit in PBS_CLIENT_SCRATCH1
|
||||
* 2. Initiating the SW PBS trigger
|
||||
* 3. Checking the equivalent bit in PBS_CLIENT_SCRATCH2 for the
|
||||
* completion of the sequence.
|
||||
* 4. If PBS_CLIENT_SCRATCH2 == 0xFF, the PBS sequence failed to execute
|
||||
*
|
||||
* Return: 0 on success, < 0 on failure
|
||||
*/
|
||||
int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap)
|
||||
{
|
||||
unsigned int val;
|
||||
u16 bit_pos;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!bitmap))
|
||||
return -EINVAL;
|
||||
|
||||
if (IS_ERR_OR_NULL(pbs))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pbs->lock);
|
||||
ret = regmap_read(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, &val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (val == PBS_CLIENT_SCRATCH2_ERROR) {
|
||||
/* PBS error - clear SCRATCH2 register */
|
||||
ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (bit_pos = 0; bit_pos < 8; bit_pos++) {
|
||||
if (!(bitmap & BIT(bit_pos)))
|
||||
continue;
|
||||
|
||||
/* Clear the PBS sequence bit position */
|
||||
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2,
|
||||
BIT(bit_pos), 0);
|
||||
if (ret < 0)
|
||||
goto out_clear_scratch1;
|
||||
|
||||
/* Set the PBS sequence bit position */
|
||||
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1,
|
||||
BIT(bit_pos), BIT(bit_pos));
|
||||
if (ret < 0)
|
||||
goto out_clear_scratch1;
|
||||
|
||||
/* Initiate the SW trigger */
|
||||
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_TRIG_CTL,
|
||||
PBS_CLIENT_SW_TRIG_BIT, PBS_CLIENT_SW_TRIG_BIT);
|
||||
if (ret < 0)
|
||||
goto out_clear_scratch1;
|
||||
|
||||
ret = qcom_pbs_wait_for_ack(pbs, bit_pos);
|
||||
if (ret < 0)
|
||||
goto out_clear_scratch1;
|
||||
|
||||
/* Clear the PBS sequence bit position */
|
||||
regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, BIT(bit_pos), 0);
|
||||
regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0);
|
||||
}
|
||||
|
||||
out_clear_scratch1:
|
||||
/* Clear all the requested bitmap */
|
||||
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, bitmap, 0);
|
||||
|
||||
out:
|
||||
mutex_unlock(&pbs->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_pbs_trigger_event);
|
||||
|
||||
/**
|
||||
* get_pbs_client_device() - Get the PBS device used by client
|
||||
* @dev: Client device
|
||||
*
|
||||
* This function is used to get the PBS device that is being
|
||||
* used by the client.
|
||||
*
|
||||
* Return: pbs_dev on success, ERR_PTR on failure
|
||||
*/
|
||||
struct pbs_dev *get_pbs_client_device(struct device *dev)
|
||||
{
|
||||
struct device_node *pbs_dev_node;
|
||||
struct platform_device *pdev;
|
||||
struct pbs_dev *pbs;
|
||||
|
||||
pbs_dev_node = of_parse_phandle(dev->of_node, "qcom,pbs", 0);
|
||||
if (!pbs_dev_node) {
|
||||
dev_err(dev, "Missing qcom,pbs property\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
pdev = of_find_device_by_node(pbs_dev_node);
|
||||
if (!pdev) {
|
||||
dev_err(dev, "Unable to find PBS dev_node\n");
|
||||
pbs = ERR_PTR(-EPROBE_DEFER);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pbs = platform_get_drvdata(pdev);
|
||||
if (!pbs) {
|
||||
dev_err(dev, "Cannot get pbs instance from %s\n", dev_name(&pdev->dev));
|
||||
platform_device_put(pdev);
|
||||
pbs = ERR_PTR(-EPROBE_DEFER);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pbs->link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER);
|
||||
if (!pbs->link) {
|
||||
dev_err(&pdev->dev, "Failed to create device link to consumer %s\n", dev_name(dev));
|
||||
platform_device_put(pdev);
|
||||
pbs = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
of_node_put(pbs_dev_node);
|
||||
return pbs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_pbs_client_device);
|
||||
|
||||
static int qcom_pbs_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pbs_dev *pbs;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
pbs = devm_kzalloc(&pdev->dev, sizeof(*pbs), GFP_KERNEL);
|
||||
if (!pbs)
|
||||
return -ENOMEM;
|
||||
|
||||
pbs->dev = &pdev->dev;
|
||||
pbs->regmap = dev_get_regmap(pbs->dev->parent, NULL);
|
||||
if (!pbs->regmap) {
|
||||
dev_err(pbs->dev, "Couldn't get parent's regmap\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = device_property_read_u32(pbs->dev, "reg", &val);
|
||||
if (ret < 0) {
|
||||
dev_err(pbs->dev, "Couldn't find reg, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
pbs->base = val;
|
||||
mutex_init(&pbs->lock);
|
||||
|
||||
platform_set_drvdata(pdev, pbs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_pbs_match_table[] = {
|
||||
{ .compatible = "qcom,pbs" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_pbs_match_table);
|
||||
|
||||
static struct platform_driver qcom_pbs_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-pbs",
|
||||
.of_match_table = qcom_pbs_match_table,
|
||||
},
|
||||
.probe = qcom_pbs_probe,
|
||||
};
|
||||
module_platform_driver(qcom_pbs_driver)
|
||||
|
||||
MODULE_DESCRIPTION("QCOM PBS DRIVER");
|
||||
MODULE_LICENSE("GPL");
|
@ -3,6 +3,7 @@
|
||||
* Copyright (c) 2019, Linaro Ltd
|
||||
*/
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
@ -13,6 +14,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/soc/qcom/qcom_aoss.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace-aoss.h"
|
||||
|
||||
#define QMP_DESC_MAGIC 0x0
|
||||
#define QMP_DESC_VERSION 0x4
|
||||
#define QMP_DESC_FEATURES 0x8
|
||||
@ -44,6 +48,8 @@
|
||||
|
||||
#define QMP_NUM_COOLING_RESOURCES 2
|
||||
|
||||
#define QMP_DEBUGFS_FILES 4
|
||||
|
||||
static bool qmp_cdev_max_state = 1;
|
||||
|
||||
struct qmp_cooling_device {
|
||||
@ -82,6 +88,8 @@ struct qmp {
|
||||
|
||||
struct clk_hw qdss_clk;
|
||||
struct qmp_cooling_device *cooling_devs;
|
||||
struct dentry *debugfs_root;
|
||||
struct dentry *debugfs_files[QMP_DEBUGFS_FILES];
|
||||
};
|
||||
|
||||
static void qmp_kick(struct qmp *qmp)
|
||||
@ -214,7 +222,7 @@ static bool qmp_message_empty(struct qmp *qmp)
|
||||
*
|
||||
* Return: 0 on success, negative errno on failure
|
||||
*/
|
||||
int qmp_send(struct qmp *qmp, const char *fmt, ...)
|
||||
int __printf(2, 3) qmp_send(struct qmp *qmp, const char *fmt, ...)
|
||||
{
|
||||
char buf[QMP_MSG_LEN];
|
||||
long time_left;
|
||||
@ -235,6 +243,8 @@ int qmp_send(struct qmp *qmp, const char *fmt, ...)
|
||||
|
||||
mutex_lock(&qmp->tx_lock);
|
||||
|
||||
trace_aoss_send(buf);
|
||||
|
||||
/* The message RAM only implements 32-bit accesses */
|
||||
__iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32),
|
||||
buf, sizeof(buf) / sizeof(u32));
|
||||
@ -256,6 +266,8 @@ int qmp_send(struct qmp *qmp, const char *fmt, ...)
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
trace_aoss_send_done(buf, ret);
|
||||
|
||||
mutex_unlock(&qmp->tx_lock);
|
||||
|
||||
return ret;
|
||||
@ -475,6 +487,91 @@ void qmp_put(struct qmp *qmp)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qmp_put);
|
||||
|
||||
struct qmp_debugfs_entry {
|
||||
const char *name;
|
||||
const char *fmt;
|
||||
bool is_bool;
|
||||
const char *true_val;
|
||||
const char *false_val;
|
||||
};
|
||||
|
||||
static const struct qmp_debugfs_entry qmp_debugfs_entries[QMP_DEBUGFS_FILES] = {
|
||||
{ "ddr_frequency_mhz", "{class: ddr, res: fixed, val: %u}", false },
|
||||
{ "prevent_aoss_sleep", "{class: aoss_slp, res: sleep: %s}", true, "enable", "disable" },
|
||||
{ "prevent_cx_collapse", "{class: cx_mol, res: cx, val: %s}", true, "mol", "off" },
|
||||
{ "prevent_ddr_collapse", "{class: ddr_mol, res: ddr, val: %s}", true, "mol", "off" },
|
||||
};
|
||||
|
||||
static ssize_t qmp_debugfs_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
const struct qmp_debugfs_entry *entry = NULL;
|
||||
struct qmp *qmp = file->private_data;
|
||||
char buf[QMP_MSG_LEN];
|
||||
unsigned int uint_val;
|
||||
const char *str_val;
|
||||
bool bool_val;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) {
|
||||
if (qmp->debugfs_files[i] == file->f_path.dentry) {
|
||||
entry = &qmp_debugfs_entries[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (WARN_ON(!entry))
|
||||
return -EFAULT;
|
||||
|
||||
if (entry->is_bool) {
|
||||
ret = kstrtobool_from_user(user_buf, count, &bool_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
str_val = bool_val ? entry->true_val : entry->false_val;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf), entry->fmt, str_val);
|
||||
if (ret >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
ret = kstrtou32_from_user(user_buf, count, 0, &uint_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf), entry->fmt, uint_val);
|
||||
if (ret >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = qmp_send(qmp, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations qmp_debugfs_fops = {
|
||||
.open = simple_open,
|
||||
.write = qmp_debugfs_write,
|
||||
};
|
||||
|
||||
static void qmp_debugfs_create(struct qmp *qmp)
|
||||
{
|
||||
const struct qmp_debugfs_entry *entry;
|
||||
int i;
|
||||
|
||||
qmp->debugfs_root = debugfs_create_dir("qcom_aoss", NULL);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) {
|
||||
entry = &qmp_debugfs_entries[i];
|
||||
|
||||
qmp->debugfs_files[i] = debugfs_create_file(entry->name, 0200,
|
||||
qmp->debugfs_root,
|
||||
qmp,
|
||||
&qmp_debugfs_fops);
|
||||
}
|
||||
}
|
||||
|
||||
static int qmp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qmp *qmp;
|
||||
@ -523,6 +620,8 @@ static int qmp_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, qmp);
|
||||
|
||||
qmp_debugfs_create(qmp);
|
||||
|
||||
return 0;
|
||||
|
||||
err_close_qmp:
|
||||
@ -537,6 +636,8 @@ static void qmp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qmp *qmp = platform_get_drvdata(pdev);
|
||||
|
||||
debugfs_remove_recursive(qmp->debugfs_root);
|
||||
|
||||
qmp_qdss_clk_remove(qmp);
|
||||
qmp_cooling_devices_remove(qmp);
|
||||
|
||||
|
@ -655,8 +655,6 @@ invalid_canary:
|
||||
void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
|
||||
{
|
||||
struct smem_partition *part;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
void *ptr = ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
if (!__smem)
|
||||
@ -665,12 +663,6 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
|
||||
if (WARN_ON(item >= __smem->item_count))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
|
||||
HWSPINLOCK_TIMEOUT,
|
||||
&flags);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
|
||||
part = &__smem->partitions[host];
|
||||
ptr = qcom_smem_get_private(__smem, part, item, size);
|
||||
@ -681,10 +673,7 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
|
||||
ptr = qcom_smem_get_global(__smem, item, size);
|
||||
}
|
||||
|
||||
hwspin_unlock_irqrestore(__smem->hwlock, &flags);
|
||||
|
||||
return ptr;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_smem_get);
|
||||
|
||||
|
@ -58,8 +58,8 @@
|
||||
* @valid_entries: number of allocated entries
|
||||
* @flags:
|
||||
* @entries: individual communication entries
|
||||
* @name: name of the entry
|
||||
* @value: content of the entry
|
||||
* @entries.name: name of the entry
|
||||
* @entries.value: content of the entry
|
||||
*/
|
||||
struct smp2p_smem_item {
|
||||
u32 magic;
|
||||
@ -275,6 +275,8 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p)
|
||||
*
|
||||
* Handle notifications from the remote side to handle newly allocated entries
|
||||
* or any changes to the state bits of existing entries.
|
||||
*
|
||||
* Return: %IRQ_HANDLED
|
||||
*/
|
||||
static irqreturn_t qcom_smp2p_intr(int irq, void *data)
|
||||
{
|
||||
|
@ -124,7 +124,7 @@ static const char *const pmic_models[] = {
|
||||
[50] = "PM8350B",
|
||||
[51] = "PMR735A",
|
||||
[52] = "PMR735B",
|
||||
[55] = "PM2250",
|
||||
[55] = "PM4125",
|
||||
[58] = "PM8450",
|
||||
[65] = "PM8010",
|
||||
[69] = "PM8550VS",
|
||||
@ -424,8 +424,11 @@ static const struct soc_id soc_id[] = {
|
||||
{ qcom_board_id(IPQ9510) },
|
||||
{ qcom_board_id(QRB4210) },
|
||||
{ qcom_board_id(QRB2210) },
|
||||
{ qcom_board_id(SM8475) },
|
||||
{ qcom_board_id(SM8475P) },
|
||||
{ qcom_board_id(SA8775P) },
|
||||
{ qcom_board_id(QRU1000) },
|
||||
{ qcom_board_id(SM8475_2) },
|
||||
{ qcom_board_id(QDU1000) },
|
||||
{ qcom_board_id(SM8650) },
|
||||
{ qcom_board_id(SM4450) },
|
||||
@ -437,6 +440,8 @@ static const struct soc_id soc_id[] = {
|
||||
{ qcom_board_id(IPQ5322) },
|
||||
{ qcom_board_id(IPQ5312) },
|
||||
{ qcom_board_id(IPQ5302) },
|
||||
{ qcom_board_id(QCS8550) },
|
||||
{ qcom_board_id(QCM8550) },
|
||||
{ qcom_board_id(IPQ5300) },
|
||||
};
|
||||
|
||||
|
@ -6,20 +6,40 @@
|
||||
* SAW power controller driver
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/linear_range.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include <linux/regulator/driver.h>
|
||||
|
||||
#include <soc/qcom/spm.h>
|
||||
|
||||
#define FIELD_SET(current, mask, val) \
|
||||
(((current) & ~(mask)) | FIELD_PREP((mask), (val)))
|
||||
|
||||
#define SPM_CTL_INDEX 0x7f
|
||||
#define SPM_CTL_INDEX_SHIFT 4
|
||||
#define SPM_CTL_EN BIT(0)
|
||||
|
||||
/* These registers might be specific to SPM 1.1 */
|
||||
#define SPM_VCTL_VLVL GENMASK(7, 0)
|
||||
#define SPM_PMIC_DATA_0_VLVL GENMASK(7, 0)
|
||||
#define SPM_PMIC_DATA_1_MIN_VSEL GENMASK(5, 0)
|
||||
#define SPM_PMIC_DATA_1_MAX_VSEL GENMASK(21, 16)
|
||||
|
||||
#define SPM_1_1_AVS_CTL_AVS_ENABLED BIT(27)
|
||||
#define SPM_AVS_CTL_MAX_VLVL GENMASK(22, 17)
|
||||
#define SPM_AVS_CTL_MIN_VLVL GENMASK(15, 10)
|
||||
|
||||
enum spm_reg {
|
||||
SPM_REG_CFG,
|
||||
SPM_REG_SPM_CTL,
|
||||
@ -29,13 +49,44 @@ enum spm_reg {
|
||||
SPM_REG_PMIC_DATA_1,
|
||||
SPM_REG_VCTL,
|
||||
SPM_REG_SEQ_ENTRY,
|
||||
SPM_REG_SPM_STS,
|
||||
SPM_REG_STS0,
|
||||
SPM_REG_STS1,
|
||||
SPM_REG_PMIC_STS,
|
||||
SPM_REG_AVS_CTL,
|
||||
SPM_REG_AVS_LIMIT,
|
||||
SPM_REG_RST,
|
||||
SPM_REG_NR,
|
||||
};
|
||||
|
||||
#define MAX_PMIC_DATA 2
|
||||
#define MAX_SEQ_DATA 64
|
||||
|
||||
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];
|
||||
|
||||
smp_call_func_t set_vdd;
|
||||
/* for now we support only a single range */
|
||||
struct linear_range *range;
|
||||
unsigned int ramp_delay;
|
||||
unsigned int init_uV;
|
||||
};
|
||||
|
||||
struct spm_driver_data {
|
||||
void __iomem *reg_base;
|
||||
const struct spm_reg_data *reg_data;
|
||||
struct device *dev;
|
||||
unsigned int volt_sel;
|
||||
int reg_cpu;
|
||||
};
|
||||
|
||||
static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = {
|
||||
[SPM_REG_AVS_CTL] = 0x904,
|
||||
[SPM_REG_AVS_LIMIT] = 0x908,
|
||||
@ -169,6 +220,10 @@ static const struct spm_reg_data spm_reg_8226_cpu = {
|
||||
|
||||
static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = {
|
||||
[SPM_REG_CFG] = 0x08,
|
||||
[SPM_REG_STS0] = 0x0c,
|
||||
[SPM_REG_STS1] = 0x10,
|
||||
[SPM_REG_VCTL] = 0x14,
|
||||
[SPM_REG_AVS_CTL] = 0x18,
|
||||
[SPM_REG_SPM_CTL] = 0x20,
|
||||
[SPM_REG_PMIC_DLY] = 0x24,
|
||||
[SPM_REG_PMIC_DATA_0] = 0x28,
|
||||
@ -176,7 +231,12 @@ static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = {
|
||||
[SPM_REG_SEQ_ENTRY] = 0x80,
|
||||
};
|
||||
|
||||
static void smp_set_vdd_v1_1(void *data);
|
||||
|
||||
/* SPM register data for 8064 */
|
||||
static struct linear_range spm_v1_1_regulator_range =
|
||||
REGULATOR_LINEAR_RANGE(700000, 0, 56, 12500);
|
||||
|
||||
static const struct spm_reg_data spm_reg_8064_cpu = {
|
||||
.reg_offset = spm_reg_offset_v1_1,
|
||||
.spm_cfg = 0x1F,
|
||||
@ -187,6 +247,10 @@ static const struct spm_reg_data spm_reg_8064_cpu = {
|
||||
0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
|
||||
.start_index[PM_SLEEP_MODE_STBY] = 0,
|
||||
.start_index[PM_SLEEP_MODE_SPC] = 2,
|
||||
.set_vdd = smp_set_vdd_v1_1,
|
||||
.range = &spm_v1_1_regulator_range,
|
||||
.init_uV = 1300000,
|
||||
.ramp_delay = 1250,
|
||||
};
|
||||
|
||||
static inline void spm_register_write(struct spm_driver_data *drv,
|
||||
@ -238,6 +302,185 @@ void spm_set_low_power_mode(struct spm_driver_data *drv,
|
||||
spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
|
||||
}
|
||||
|
||||
static int spm_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector)
|
||||
{
|
||||
struct spm_driver_data *drv = rdev_get_drvdata(rdev);
|
||||
|
||||
drv->volt_sel = selector;
|
||||
|
||||
/* Always do the SAW register writes on the corresponding CPU */
|
||||
return smp_call_function_single(drv->reg_cpu, drv->reg_data->set_vdd, drv, true);
|
||||
}
|
||||
|
||||
static int spm_get_voltage_sel(struct regulator_dev *rdev)
|
||||
{
|
||||
struct spm_driver_data *drv = rdev_get_drvdata(rdev);
|
||||
|
||||
return drv->volt_sel;
|
||||
}
|
||||
|
||||
static const struct regulator_ops spm_reg_ops = {
|
||||
.set_voltage_sel = spm_set_voltage_sel,
|
||||
.get_voltage_sel = spm_get_voltage_sel,
|
||||
.list_voltage = regulator_list_voltage_linear_range,
|
||||
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
||||
};
|
||||
|
||||
static void smp_set_vdd_v1_1(void *data)
|
||||
{
|
||||
struct spm_driver_data *drv = data;
|
||||
unsigned int vctl, data0, data1, avs_ctl, sts;
|
||||
unsigned int vlevel, volt_sel;
|
||||
bool avs_enabled;
|
||||
|
||||
volt_sel = drv->volt_sel;
|
||||
vlevel = volt_sel | 0x80; /* band */
|
||||
|
||||
avs_ctl = spm_register_read(drv, SPM_REG_AVS_CTL);
|
||||
vctl = spm_register_read(drv, SPM_REG_VCTL);
|
||||
data0 = spm_register_read(drv, SPM_REG_PMIC_DATA_0);
|
||||
data1 = spm_register_read(drv, SPM_REG_PMIC_DATA_1);
|
||||
|
||||
avs_enabled = avs_ctl & SPM_1_1_AVS_CTL_AVS_ENABLED;
|
||||
|
||||
/* If AVS is enabled, switch it off during the voltage change */
|
||||
if (avs_enabled) {
|
||||
avs_ctl &= ~SPM_1_1_AVS_CTL_AVS_ENABLED;
|
||||
spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl);
|
||||
}
|
||||
|
||||
/* Kick the state machine back to idle */
|
||||
spm_register_write(drv, SPM_REG_RST, 1);
|
||||
|
||||
vctl = FIELD_SET(vctl, SPM_VCTL_VLVL, vlevel);
|
||||
data0 = FIELD_SET(data0, SPM_PMIC_DATA_0_VLVL, vlevel);
|
||||
data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MIN_VSEL, volt_sel);
|
||||
data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MAX_VSEL, volt_sel);
|
||||
|
||||
spm_register_write(drv, SPM_REG_VCTL, vctl);
|
||||
spm_register_write(drv, SPM_REG_PMIC_DATA_0, data0);
|
||||
spm_register_write(drv, SPM_REG_PMIC_DATA_1, data1);
|
||||
|
||||
if (read_poll_timeout_atomic(spm_register_read,
|
||||
sts, sts == vlevel,
|
||||
1, 200, false,
|
||||
drv, SPM_REG_STS1)) {
|
||||
dev_err_ratelimited(drv->dev, "timeout setting the voltage (%x %x)!\n", sts, vlevel);
|
||||
goto enable_avs;
|
||||
}
|
||||
|
||||
if (avs_enabled) {
|
||||
unsigned int max_avs = volt_sel;
|
||||
unsigned int min_avs = max(max_avs, 4U) - 4;
|
||||
|
||||
avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MIN_VLVL, min_avs);
|
||||
avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MAX_VLVL, max_avs);
|
||||
spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl);
|
||||
}
|
||||
|
||||
enable_avs:
|
||||
if (avs_enabled) {
|
||||
avs_ctl |= SPM_1_1_AVS_CTL_AVS_ENABLED;
|
||||
spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl);
|
||||
}
|
||||
}
|
||||
|
||||
static int spm_get_cpu(struct device *dev)
|
||||
{
|
||||
int cpu;
|
||||
bool found;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct device_node *cpu_node, *saw_node;
|
||||
|
||||
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 == dev->of_node);
|
||||
of_node_put(saw_node);
|
||||
of_node_put(cpu_node);
|
||||
|
||||
if (found)
|
||||
return cpu;
|
||||
}
|
||||
|
||||
/* L2 SPM is not bound to any CPU, voltage setting is not supported */
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
static int spm_register_regulator(struct device *dev, struct spm_driver_data *drv)
|
||||
{
|
||||
struct regulator_config config = {
|
||||
.dev = dev,
|
||||
.driver_data = drv,
|
||||
};
|
||||
struct regulator_desc *rdesc;
|
||||
struct regulator_dev *rdev;
|
||||
int ret;
|
||||
bool found;
|
||||
|
||||
if (!drv->reg_data->set_vdd)
|
||||
return 0;
|
||||
|
||||
rdesc = devm_kzalloc(dev, sizeof(*rdesc), GFP_KERNEL);
|
||||
if (!rdesc)
|
||||
return -ENOMEM;
|
||||
|
||||
rdesc->name = "spm";
|
||||
rdesc->of_match = of_match_ptr("regulator");
|
||||
rdesc->type = REGULATOR_VOLTAGE;
|
||||
rdesc->owner = THIS_MODULE;
|
||||
rdesc->ops = &spm_reg_ops;
|
||||
|
||||
rdesc->linear_ranges = drv->reg_data->range;
|
||||
rdesc->n_linear_ranges = 1;
|
||||
rdesc->n_voltages = rdesc->linear_ranges[rdesc->n_linear_ranges - 1].max_sel + 1;
|
||||
rdesc->ramp_delay = drv->reg_data->ramp_delay;
|
||||
|
||||
ret = spm_get_cpu(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drv->reg_cpu = ret;
|
||||
dev_dbg(dev, "SAW2 bound to CPU %d\n", drv->reg_cpu);
|
||||
|
||||
/*
|
||||
* Program initial voltage, otherwise registration will also try
|
||||
* setting the voltage, which might result in undervolting the CPU.
|
||||
*/
|
||||
drv->volt_sel = DIV_ROUND_UP(drv->reg_data->init_uV - rdesc->min_uV,
|
||||
rdesc->uV_step);
|
||||
ret = linear_range_get_selector_high(drv->reg_data->range,
|
||||
drv->reg_data->init_uV,
|
||||
&drv->volt_sel,
|
||||
&found);
|
||||
if (ret) {
|
||||
dev_err(dev, "Initial uV value out of bounds\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Always do the SAW register writes on the corresponding CPU */
|
||||
smp_call_function_single(drv->reg_cpu, drv->reg_data->set_vdd, drv, true);
|
||||
|
||||
rdev = devm_regulator_register(dev, rdesc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(dev, "failed to register regulator\n");
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int spm_register_regulator(struct device *dev, struct spm_driver_data *drv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct of_device_id spm_match_table[] = {
|
||||
{ .compatible = "qcom,sdm660-gold-saw2-v4.1-l2",
|
||||
.data = &spm_reg_660_gold_l2 },
|
||||
@ -288,6 +531,7 @@ static int spm_dev_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
|
||||
drv->reg_data = match_id->data;
|
||||
drv->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, drv);
|
||||
|
||||
/* Write the SPM sequences first.. */
|
||||
@ -315,7 +559,7 @@ static int spm_dev_probe(struct platform_device *pdev)
|
||||
if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL])
|
||||
spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
|
||||
|
||||
return 0;
|
||||
return spm_register_regulator(&pdev->dev, drv);
|
||||
}
|
||||
|
||||
static struct platform_driver spm_driver = {
|
||||
|
48
drivers/soc/qcom/trace-aoss.h
Normal file
48
drivers/soc/qcom/trace-aoss.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM qcom_aoss
|
||||
|
||||
#if !defined(_TRACE_QCOM_AOSS_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_QCOM_AOSS_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(aoss_send,
|
||||
TP_PROTO(const char *msg),
|
||||
TP_ARGS(msg),
|
||||
TP_STRUCT__entry(
|
||||
__string(msg, msg)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(msg, msg);
|
||||
),
|
||||
TP_printk("%s", __get_str(msg))
|
||||
);
|
||||
|
||||
TRACE_EVENT(aoss_send_done,
|
||||
TP_PROTO(const char *msg, int ret),
|
||||
TP_ARGS(msg, ret),
|
||||
TP_STRUCT__entry(
|
||||
__string(msg, msg)
|
||||
__field(int, ret)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(msg, msg);
|
||||
__entry->ret = ret;
|
||||
),
|
||||
TP_printk("%s: %d", __get_str(msg), __entry->ret)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_QCOM_AOSS_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace-aoss
|
||||
|
||||
#include <trace/define_trace.h>
|
@ -252,8 +252,11 @@
|
||||
#define QCOM_ID_IPQ9510 521
|
||||
#define QCOM_ID_QRB4210 523
|
||||
#define QCOM_ID_QRB2210 524
|
||||
#define QCOM_ID_SM8475 530
|
||||
#define QCOM_ID_SM8475P 531
|
||||
#define QCOM_ID_SA8775P 534
|
||||
#define QCOM_ID_QRU1000 539
|
||||
#define QCOM_ID_SM8475_2 540
|
||||
#define QCOM_ID_QDU1000 545
|
||||
#define QCOM_ID_SM8650 557
|
||||
#define QCOM_ID_SM4450 568
|
||||
@ -265,6 +268,8 @@
|
||||
#define QCOM_ID_IPQ5322 593
|
||||
#define QCOM_ID_IPQ5312 594
|
||||
#define QCOM_ID_IPQ5302 595
|
||||
#define QCOM_ID_QCS8550 603
|
||||
#define QCOM_ID_QCM8550 604
|
||||
#define QCOM_ID_IPQ5300 624
|
||||
|
||||
/*
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <dt-bindings/soc/qcom,apr.h>
|
||||
#include <dt-bindings/soc/qcom,gpr.h>
|
||||
|
||||
extern struct bus_type aprbus;
|
||||
extern const struct bus_type aprbus;
|
||||
|
||||
#define APR_HDR_LEN(hdr_len) ((hdr_len)/4)
|
||||
|
||||
|
30
include/linux/soc/qcom/qcom-pbs.h
Normal file
30
include/linux/soc/qcom/qcom-pbs.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _QCOM_PBS_H
|
||||
#define _QCOM_PBS_H
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct device_node;
|
||||
struct pbs_dev;
|
||||
|
||||
#if IS_ENABLED(CONFIG_QCOM_PBS)
|
||||
int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap);
|
||||
struct pbs_dev *get_pbs_client_device(struct device *client_dev);
|
||||
#else
|
||||
static inline int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline struct pbs_dev *get_pbs_client_device(struct device *client_dev)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -49,7 +49,7 @@
|
||||
#define PMK8350_SUBTYPE 0x2f
|
||||
#define PMR735B_SUBTYPE 0x34
|
||||
#define PM6350_SUBTYPE 0x36
|
||||
#define PM2250_SUBTYPE 0x37
|
||||
#define PM4125_SUBTYPE 0x37
|
||||
|
||||
#define PMI8998_FAB_ID_SMIC 0x11
|
||||
#define PMI8998_FAB_ID_GF 0x30
|
||||
|
@ -7,11 +7,6 @@
|
||||
#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,
|
||||
@ -20,23 +15,7 @@ enum pm_sleep_mode {
|
||||
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;
|
||||
};
|
||||
|
||||
struct spm_driver_data;
|
||||
void spm_set_low_power_mode(struct spm_driver_data *drv,
|
||||
enum pm_sleep_mode mode);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user