diff options
41 files changed, 1022 insertions, 1352 deletions
diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index 4193492ba73e..a66e99812b1f 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -39,6 +39,7 @@ properties: - qcom,scm-msm8996 - qcom,scm-msm8998 - qcom,scm-qdu1000 + - qcom,scm-sa8775p - qcom,scm-sc7180 - qcom,scm-sc7280 - qcom,scm-sc8280xp @@ -54,6 +55,7 @@ properties: - qcom,scm-sm8250 - qcom,scm-sm8350 - qcom,scm-sm8450 + - qcom,scm-sm8550 - qcom,scm-qcs404 - const: qcom,scm @@ -165,6 +167,7 @@ allOf: contains: enum: - qcom,scm-sm8450 + - qcom,scm-sm8550 then: properties: interconnects: false @@ -177,6 +180,7 @@ allOf: contains: enum: - qcom,scm-sm8450 + - qcom,scm-sm8550 then: properties: interrupts: false diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml new file mode 100644 index 000000000000..cf863683c21a --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,pmic-glink.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm PMIC GLINK firmware interface for battery management, USB + Type-C and other things. + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + +description: + The PMIC GLINK service, running on a coprocessor on some modern Qualcomm + platforms and implement USB Type-C handling and battery management. This + binding describes the component in the OS used to communicate with the + firmware and connect it's resources to those described in the Devicetree, + particularly the USB Type-C controllers relationship with USB and DisplayPort + components. + +properties: + compatible: + items: + - enum: + - qcom,sc8180x-pmic-glink + - qcom,sc8280xp-pmic-glink + - qcom,sm8350-pmic-glink + - const: qcom,pmic-glink + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + '^connector@\d$': + $ref: /schemas/connector/usb-connector.yaml# + + properties: + reg: true + + required: + - reg + + unevaluatedProperties: false + +required: + - compatible + +additionalProperties: false + +examples: + - |+ + pmic-glink { + compatible = "qcom,sc8280xp-pmic-glink", "qcom,pmic-glink"; + + #address-cells = <1>; + #size-cells = <0>; + + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + endpoint { + remote-endpoint = <&usb_role>; + }; + }; + + port@1 { + reg = <1>; + endpoint { + remote-endpoint = <&ss_phy_out>; + }; + }; + + port@2 { + reg = <2>; + endpoint { + remote-endpoint = <&sbu_mux>; + }; + }; + }; + }; + }; +... + diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml index b246500d3d5d..a4046ba60846 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml @@ -112,8 +112,9 @@ properties: $ref: /schemas/power/qcom,rpmpd.yaml# patternProperties: - '-regulators$': + '^regulators(-[0-9])?$': $ref: /schemas/regulator/qcom,rpmh-regulator.yaml# + unevaluatedProperties: false required: - compatible diff --git a/MAINTAINERS b/MAINTAINERS index 5b49887c1dc7..b7abbcb9927e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5821,14 +5821,6 @@ W: http://lists.twibble.org/mailman/listinfo/dc395x/ F: Documentation/scsi/dc395x.rst F: drivers/scsi/dc395x.* -DCC QTI DRIVER -M: Souradeep Chowdhury <quic_schowdhu@quicinc.com> -L: linux-arm-msm@vger.kernel.org -S: Maintained -F: Documentation/ABI/testing/debugfs-driver-dcc -F: Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml -F: drivers/soc/qcom/dcc.c - DCCP PROTOCOL L: dccp@vger.kernel.org S: Orphan @@ -17263,7 +17255,8 @@ F: drivers/clk/qcom/ F: include/dt-bindings/clock/qcom,* QUALCOMM CORE POWER REDUCTION (CPR) AVS DRIVER -M: Niklas Cassel <nks@flawful.org> +M: Bjorn Andersson <andersson@kernel.org> +M: Konrad Dybcio <konrad.dybcio@linaro.org> L: linux-pm@vger.kernel.org L: linux-arm-msm@vger.kernel.org S: Maintained diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c index 5d2f386a46d8..eca2fe0f4314 100644 --- a/arch/arm/mach-qcom/platsmp.c +++ b/arch/arm/mach-qcom/platsmp.c @@ -14,7 +14,7 @@ #include <linux/of_address.h> #include <linux/smp.h> #include <linux/io.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <asm/smp_plat.h> diff --git a/drivers/cpuidle/cpuidle-qcom-spm.c b/drivers/cpuidle/cpuidle-qcom-spm.c index beedf22cbe78..4ac83918edf2 100644 --- a/drivers/cpuidle/cpuidle-qcom-spm.c +++ b/drivers/cpuidle/cpuidle-qcom-spm.c @@ -17,7 +17,7 @@ #include <linux/platform_device.h> #include <linux/cpuidle.h> #include <linux/cpu_pm.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <soc/qcom/spm.h> #include <asm/proc-fns.h> diff --git a/drivers/firmware/qcom_scm-legacy.c b/drivers/firmware/qcom_scm-legacy.c index 9f918b9e6f8f..029e6d117cb8 100644 --- a/drivers/firmware/qcom_scm-legacy.c +++ b/drivers/firmware/qcom_scm-legacy.c @@ -9,7 +9,7 @@ #include <linux/mutex.h> #include <linux/errno.h> #include <linux/err.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/arm-smccc.h> #include <linux/dma-mapping.h> diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c index bb3235a64b8f..16cf88acfa8e 100644 --- a/drivers/firmware/qcom_scm-smc.c +++ b/drivers/firmware/qcom_scm-smc.c @@ -8,7 +8,7 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <linux/types.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/arm-smccc.h> #include <linux/dma-mapping.h> diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 2000323722bf..468d4d5ab550 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -12,7 +12,7 @@ #include <linux/interconnect.h> #include <linux/module.h> #include <linux/types.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 660ba0db8900..d09221f97f71 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -5,7 +5,7 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/cpumask.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/pm_opp.h> #include <linux/nvmem-consumer.h> #include <linux/slab.h> diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 57586c794b84..89ff978b81bb 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -8,7 +8,7 @@ #include <linux/ascii85.h> #include <linux/interconnect.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/kernel.h> #include <linux/of_address.h> #include <linux/pm_opp.h> diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c index e7748461cffc..0752fe373351 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c @@ -3,7 +3,7 @@ */ #include "hdmi.h" -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #define HDCP_REG_ENABLE 0x01 #define HDCP_REG_DISABLE 0x00 diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c index 74e9ef2fd580..b5b14108e086 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c @@ -4,7 +4,7 @@ */ #include <linux/of_device.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/ratelimit.h> #include "arm-smmu.h" diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index 91d404deb115..ef42329e82ce 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -7,7 +7,7 @@ #include <linux/adreno-smmu-priv.h> #include <linux/delay.h> #include <linux/of_device.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include "arm-smmu.h" #include "arm-smmu-qcom.h" diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c index 270c3d9128ba..1e0b7b2e9fbd 100644 --- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c +++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c @@ -27,7 +27,7 @@ #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/pm_runtime.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/slab.h> #include <linux/spinlock.h> diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 142d4c74017c..e5759d7e9ede 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -12,7 +12,7 @@ #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/of_device.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/sizes.h> #include <linux/soc/qcom/mdt_loader.h> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index c9902a1dcf5d..04f80e754477 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -18,7 +18,7 @@ #include <linux/rpmsg.h> #include <linux/scatterlist.h> #include <linux/slab.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <uapi/misc/fastrpc.h> #include <linux/of_reserved_mem.h> diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 4ac8651d0b29..8ac81d57a3df 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -13,7 +13,7 @@ #include <linux/pm_opp.h> #include <linux/slab.h> #include <linux/iopoll.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/regulator/consumer.h> #include <linux/interconnect.h> #include <linux/pinctrl/consumer.h> diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index 4fb92f771974..90baf7a54d9a 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -16,7 +16,7 @@ #include <linux/of_device.h> #include <linux/of_address.h> #include <linux/pm_runtime.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/soc/qcom/mdt_loader.h> #include "ipa.h" diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 3f94fbf83702..90f457b8e1fe 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -13,7 +13,7 @@ #include <linux/module.h> #include <linux/net.h> #include <linux/platform_device.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/soc/qcom/smem.h> #include <linux/string.h> #include <net/sock.h> diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 47e9a8b0d474..e0128c69bfbf 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -14,7 +14,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/reboot.h> #include <linux/seq_file.h> #include <linux/slab.h> diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index fddb63cffee0..da2513bb6387 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -34,7 +34,7 @@ #include "qcom_pil_info.h" #include "qcom_q6v5.h" -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #define MPSS_CRASH_REASON_SMEM 421 diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index dc6f07ca8341..d5a049669616 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -18,7 +18,7 @@ #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> #include <linux/soc/qcom/mdt_loader.h> diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 68f37296b151..9881443cb8df 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -19,7 +19,7 @@ #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> #include <linux/soc/qcom/mdt_loader.h> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 21c4ce2315ba..a8f283086a21 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -70,14 +70,6 @@ config QCOM_LLCC SDM845. This provides interfaces to clients that use the LLCC. Say yes here to enable LLCC slice driver. -config QCOM_DCC - tristate "Qualcomm Technologies, Inc. Data Capture and Compare(DCC) engine driver" - depends on ARCH_QCOM || COMPILE_TEST - help - This option enables driver for Data Capture and Compare engine. DCC - driver provides interface to configure DCC block and read back - captured data from DCC's internal SRAM. - config QCOM_KRYO_L2_ACCESSORS bool depends on ARCH_QCOM && ARM64 || COMPILE_TEST @@ -99,6 +91,24 @@ config QCOM_OCMEM config QCOM_PDR_HELPERS tristate select QCOM_QMI_HELPERS + depends on NET + +config QCOM_PMIC_GLINK + tristate "Qualcomm PMIC GLINK driver" + depends on RPMSG + depends on TYPEC + depends on DRM + depends on NET + depends on OF + select AUXILIARY_BUS + select QCOM_PDR_HELPERS + help + The Qualcomm PMIC GLINK driver provides access, over GLINK, to the + USB and battery firmware running on one of the coprocessors in + several modern Qualcomm platforms. + + Say yes here to support USB-C and battery status on modern Qualcomm + platforms. config QCOM_QMI_HELPERS tristate diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 3b92c6c8e0ec..6e88da899f60 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -4,11 +4,12 @@ obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o obj-$(CONFIG_QCOM_CPR) += cpr.o -obj-$(CONFIG_QCOM_DCC) += dcc.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o +obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink.o +obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink_altmode.o obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o qmi_helpers-y += qmi_encdec.o qmi_interface.o obj-$(CONFIG_QCOM_RAMP_CTRL) += ramp_controller.o diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c deleted file mode 100644 index 5b50d638771d..000000000000 --- a/drivers/soc/qcom/dcc.c +++ /dev/null @@ -1,1300 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. - */ - -#include <linux/bitfield.h> -#include <linux/bitops.h> -#include <linux/debugfs.h> -#include <linux/delay.h> -#include <linux/fs.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/uaccess.h> - -#define STATUS_READY_TIMEOUT 5000 /* microseconds */ - -#define DCC_SRAM_NODE "dcc_sram" - -/* DCC registers */ -#define DCC_HW_INFO 0x04 -#define DCC_LL_NUM_INFO 0x10 -#define DCC_STATUS(vers) ((vers) == 1 ? 0x0c : 0x1c) -#define DCC_LL_LOCK 0x00 -#define DCC_LL_CFG 0x04 -#define DCC_LL_BASE 0x08 -#define DCC_FD_BASE 0x0c -#define DCC_LL_TIMEOUT 0x10 -#define DCC_LL_INT_ENABLE 0x18 -#define DCC_LL_INT_STATUS 0x1c -#define DCC_LL_SW_TRIGGER 0x2c -#define DCC_LL_BUS_ACCESS_STATUS 0x30 - -/* Default value used if a bit 6 in the HW_INFO register is set. */ -#define DCC_FIX_LOOP_OFFSET 16 - -/* Mask to find version info from HW_Info register */ -#define DCC_VER_INFO_MASK BIT(9) - -#define MAX_DCC_OFFSET GENMASK(9, 2) -#define MAX_DCC_LEN GENMASK(6, 0) -#define MAX_LOOP_CNT GENMASK(7, 0) -#define MAX_LOOP_ADDR 10 - -#define DCC_ADDR_DESCRIPTOR 0x00 -#define DCC_ADDR_LIMIT 27 -#define DCC_WORD_SIZE sizeof(u32) -#define DCC_ADDR_RANGE_MASK GENMASK(31, 4) -#define DCC_LOOP_DESCRIPTOR BIT(30) -#define DCC_RD_MOD_WR_DESCRIPTOR BIT(31) -#define DCC_LINK_DESCRIPTOR GENMASK(31, 30) -#define DCC_STATUS_MASK GENMASK(1, 0) -#define DCC_LOCK_MASK BIT(0) -#define DCC_LOOP_OFFSET_MASK BIT(6) -#define DCC_TRIGGER_MASK BIT(9) - -#define DCC_WRITE_MASK BIT(15) -#define DCC_WRITE_OFF_MASK GENMASK(7, 0) -#define DCC_WRITE_LEN_MASK GENMASK(14, 8) - -#define DCC_READ_IND 0x00 -#define DCC_WRITE_IND (BIT(28)) - -#define DCC_AHB_IND 0x00 -#define DCC_APB_IND BIT(29) - -#define DCC_MAX_LINK_LIST 8 - -#define DCC_VER_MASK2 GENMASK(5, 0) - -#define DCC_SRAM_WORD_LENGTH 4 - -#define DCC_RD_MOD_WR_ADDR 0xc105e - -enum dcc_descriptor_type { - DCC_READ_TYPE, - DCC_LOOP_TYPE, - DCC_READ_WRITE_TYPE, - DCC_WRITE_TYPE -}; - -struct dcc_config_entry { - u32 base; - u32 offset; - u32 len; - u32 loop_cnt; - u32 write_val; - u32 mask; - bool apb_bus; - enum dcc_descriptor_type desc_type; - struct list_head list; -}; - -/** - * struct dcc_drvdata - configuration information related to a dcc device - * @base: Base Address of the dcc device - * @dev: The device attached to the driver data - * @mutex: Lock to protect access and manipulation of dcc_drvdata - * @ram_base: Base address for the SRAM dedicated for the dcc device - * @ram_size: Total size of the SRAM dedicated for the dcc device - * @ram_offset: Offset to the SRAM dedicated for dcc device - * @mem_map_ver: Memory map version of DCC hardware - * @ram_cfg: Used for address limit calculation for dcc - * @ram_start: Starting address of DCC SRAM - * @sram_dev: Miscellaneous device equivalent of dcc SRAM - * @cfg_head: Points to the head of the linked list of addresses - * @dbg_dir: The dcc debugfs directory under which all the debugfs files are placed - * @nr_link_list: Total number of linkedlists supported by the DCC configuration - * @loop_shift: Loop offset bits range for the addresses - * @enable_bitmap: Bitmap to capture the enabled status of each linked list of addresses - */ -struct dcc_drvdata { - void __iomem *base; - void __iomem *ram_base; - struct device *dev; - struct mutex mutex; - size_t ram_size; - size_t ram_offset; - int mem_map_ver; - unsigned int ram_cfg; - unsigned int ram_start; - struct miscdevice sram_dev; - struct list_head *cfg_head; - struct dentry *dbg_dir; - size_t nr_link_list; - u8 loop_shift; - unsigned long *enable_bitmap; -}; - -struct dcc_cfg_attr { - u32 addr; - u32 prev_addr; - u32 prev_off; - u32 link; - u32 sram_offset; -}; - -struct dcc_cfg_loop_attr { - u32 loop_cnt; - u32 loop_len; - u32 loop_off; - bool loop_start; -}; - -static inline u32 dcc_list_offset(int version) -{ - return version == 1 ? 0x1c : version == 2 ? 0x2c : 0x34; -} - -static inline void dcc_list_writel(struct dcc_drvdata *drvdata, - u32 ll, u32 val, u32 off) -{ - u32 offset = dcc_list_offset(drvdata->mem_map_ver) + off; - - writel(val, drvdata->base + ll * 0x80 + offset); -} - -static inline u32 dcc_list_readl(struct dcc_drvdata *drvdata, u32 ll, u32 off) -{ - u32 offset = dcc_list_offset(drvdata->mem_map_ver) + off; - - return readl(drvdata->base + ll * 0x80 + offset); -} - -static void dcc_sram_write_auto(struct dcc_drvdata *drvdata, - u32 val, u32 *off) -{ - /* If the overflow condition is met increment the offset - * and return to indicate that overflow has occurred - */ - if (unlikely(*off > drvdata->ram_size - 4)) { - *off += 4; - return; - } - - writel(val, drvdata->ram_base + *off); - - *off += 4; -} - -static int dcc_sw_trigger(struct dcc_drvdata *drvdata) -{ - void __iomem *addr; - int ret = 0; - int i; - u32 status; - u32 ll_cfg; - u32 tmp_ll_cfg; - u32 val; - - mutex_lock(&drvdata->mutex); - - for (i = 0; i < drvdata->nr_link_list; i++) { - if (!test_bit(i, drvdata->enable_bitmap)) - continue; - ll_cfg = dcc_list_readl(drvdata, i, DCC_LL_CFG); - tmp_ll_cfg = ll_cfg & ~DCC_TRIGGER_MASK; - dcc_list_writel(drvdata, tmp_ll_cfg, i, DCC_LL_CFG); - dcc_list_writel(drvdata, 1, i, DCC_LL_SW_TRIGGER); - dcc_list_writel(drvdata, ll_cfg, i, DCC_LL_CFG); - } - - addr = drvdata->base + DCC_STATUS(drvdata->mem_map_ver); - if (readl_poll_timeout(addr, val, !FIELD_GET(DCC_STATUS_MASK, val), - 1, STATUS_READY_TIMEOUT)) { - dev_err(drvdata->dev, "DCC is busy after receiving sw trigger\n"); - ret = -EBUSY; - goto out_unlock; - } - - for (i = 0; i < drvdata->nr_link_list; i++) { - if (!test_bit(i, drvdata->enable_bitmap)) - continue; - - status = dcc_list_readl(drvdata, i, DCC_LL_BUS_ACCESS_STATUS); - if (!status) - continue; - - dev_err(drvdata->dev, "Read access error for list %d err: 0x%x\n", - i, status); - ll_cfg = dcc_list_readl(drvdata, i, DCC_LL_CFG); - tmp_ll_cfg = ll_cfg & ~DCC_TRIGGER_MASK; - dcc_list_writel(drvdata, tmp_ll_cfg, i, DCC_LL_CFG); - dcc_list_writel(drvdata, DCC_STATUS_MASK, i, DCC_LL_BUS_ACCESS_STATUS); - dcc_list_writel(drvdata, ll_cfg, i, DCC_LL_CFG); - ret = -ENODATA; - break; - } - -out_unlock: - mutex_unlock(&drvdata->mutex); - return ret; -} - -static void dcc_ll_cfg_reset_link(struct dcc_cfg_attr *cfg) -{ - cfg->addr = 0x00; - cfg->link = 0; - cfg->prev_off = 0; - cfg->prev_addr = cfg->addr; -} - -static void dcc_emit_read_write(struct dcc_drvdata *drvdata, - struct dcc_config_entry *entry, - struct dcc_cfg_attr *cfg) -{ - if (cfg->link) { - /* - * write new offset = 1 to continue - * processing the list - */ - - dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); - - /* Reset link and prev_off */ - dcc_ll_cfg_reset_link(cfg); - } - - cfg->addr = DCC_RD_MOD_WR_DESCRIPTOR; - dcc_sram_write_auto(drvdata, cfg->addr, &cfg->sram_offset); - - dcc_sram_write_auto(drvdata, entry->mask, &cfg->sram_offset); - - dcc_sram_write_auto(drvdata, entry->write_val, &cfg->sram_offset); - - cfg->addr = 0; -} - -static void dcc_emit_loop(struct dcc_drvdata *drvdata, struct dcc_config_entry *entry, - struct dcc_cfg_attr *cfg, - struct dcc_cfg_loop_attr *cfg_loop, - u32 *total_len) -{ - int loop; - - /* Check if we need to write link of prev entry */ - if (cfg->link) - dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); - - if (cfg_loop->loop_start) { - loop = (cfg->sram_offset - cfg_loop->loop_off) / 4; - loop |= (cfg_loop->loop_cnt << drvdata->loop_shift) & - GENMASK(DCC_ADDR_LIMIT, drvdata->loop_shift); - loop |= DCC_LOOP_DESCRIPTOR; - *total_len += (*total_len - cfg_loop->loop_len) * cfg_loop->loop_cnt; - - dcc_sram_write_auto(drvdata, loop, &cfg->sram_offset); - - cfg_loop->loop_start = false; - cfg_loop->loop_len = 0; - cfg_loop->loop_off = 0; - } else { - cfg_loop->loop_start = true; - cfg_loop->loop_cnt = entry->loop_cnt - 1; - cfg_loop->loop_len = *total_len; - cfg_loop->loop_off = cfg->sram_offset; - } - - /* Reset link and prev_off */ - dcc_ll_cfg_reset_link(cfg); -} - -static void dcc_emit_write(struct dcc_drvdata *drvdata, - struct dcc_config_entry *entry, - struct dcc_cfg_attr *cfg) -{ - u32 off; - - if (cfg->link) { - /* - * write new offset = 1 to continue - * processing the list - */ - dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); - - /* Reset link and prev_off */ - cfg->addr = 0x00; - cfg->prev_off = 0; - cfg->prev_addr = cfg->addr; - } - - off = entry->offset / 4; - /* write new offset-length pair to correct position */ - cfg->link |= ((off & DCC_WRITE_OFF_MASK) | DCC_WRITE_MASK | - FIELD_PREP(DCC_WRITE_LEN_MASK, entry->len)); - cfg->link |= DCC_LINK_DESCRIPTOR; - - /* Address type */ - cfg->addr = (entry->base >> 4) & GENMASK(DCC_ADDR_LIMIT, 0); - if (entry->apb_bus) - cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_WRITE_IND | DCC_APB_IND; - else - cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_WRITE_IND | DCC_AHB_IND; - dcc_sram_write_auto(drvdata, cfg->addr, &cfg->sram_offset); - - dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); - - dcc_sram_write_auto(drvdata, entry->write_val, &cfg->sram_offset); - - cfg->addr = 0x00; - cfg->link = 0; -} - -static int dcc_emit_read(struct dcc_drvdata *drvdata, - struct dcc_config_entry *entry, - struct dcc_cfg_attr *cfg, - u32 *pos, u32 *total_len) -{ - u32 off; - u32 temp_off; - - cfg->addr = (entry->base >> 4) & GENMASK(27, 0); - - if (entry->apb_bus) - cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_READ_IND | DCC_APB_IND; - else - cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_READ_IND | DCC_AHB_IND; - - off = entry->offset / 4; - - *total_len += entry->len * 4; - - if (!cfg->prev_addr || cfg->prev_addr != cfg->addr || cfg->prev_off > off) { - /* Check if we need to write prev link entry */ - if (cfg->link) - dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); - dev_dbg(drvdata->dev, "DCC: sram address 0x%x\n", cfg->sram_offset); - - /* Write address */ - dcc_sram_write_auto(drvdata, cfg->addr, &cfg->sram_offset); - - /* Reset link and prev_off */ - cfg->link = 0; - cfg->prev_off = 0; - } - - if ((off - cfg->prev_off) > 0xff || entry->len > MAX_DCC_LEN) { - dev_err(drvdata->dev, "DCC: Programming error Base: 0x%x, offset 0x%x\n", - entry->base, entry->offset); - return -EINVAL; - } - - if (cfg->link) { - /* - * link already has one offset-length so new - * offset-length needs to be placed at - * bits [29:15] - */ - *pos = 15; - - /* Clear bits [31:16] */ - cfg->link &= GENMASK(14, 0); - } else { - /* - * link is empty, so new offset-length needs - * to be placed at bits [15:0] - */ - *pos = 0; - cfg->link = 1 << 15; - } - - /* write new offset-length pair to correct position */ - temp_off = (off - cfg->prev_off) & GENMASK(7, 0); - cfg->link |= temp_off | ((entry->len << 8) & GENMASK(14, 8)) << *pos; - - cfg->link |= DCC_LINK_DESCRIPTOR; - - if (*pos) { - dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); - cfg->link = 0; - } - - cfg->prev_off = off + entry->len - 1; - cfg->prev_addr = cfg->addr; - return 0; -} - -static int dcc_emit_config(struct dcc_drvdata *drvdata, unsigned int curr_list) -{ - int ret; - u32 total_len, pos; - struct dcc_config_entry *entry; - struct dcc_cfg_attr cfg; - struct dcc_cfg_loop_attr cfg_loop; - - memset(&cfg, 0, sizeof(cfg)); - memset(&cfg_loop, 0, sizeof(cfg_loop)); - cfg.sram_offset = drvdata->ram_cfg * 4; - total_len = 0; - - list_for_each_entry(entry, &drvdata->cfg_head[curr_list], list) { - switch (entry->desc_type) { - case DCC_READ_WRITE_TYPE: - dcc_emit_read_write(drvdata, entry, &cfg); - break; - - case DCC_LOOP_TYPE: - dcc_emit_loop(drvdata, entry, &cfg, &cfg_loop, &total_len); - break; - - case DCC_WRITE_TYPE: - dcc_emit_write(drvdata, entry, &cfg); - break; - - case DCC_READ_TYPE: - ret = dcc_emit_read(drvdata, entry, &cfg, &pos, &total_len); - if (ret) - goto err; - break; - } - } - - if (cfg.link) - dcc_sram_write_auto(drvdata, cfg.link, &cfg.sram_offset); - - if (cfg_loop.loop_start) { - dev_err(drvdata->dev, "DCC: Programming error: Loop unterminated\n"); - ret = -EINVAL; - goto err; - } - - /* Handling special case of list ending with a rd_mod_wr */ - if (cfg.addr == DCC_RD_MOD_WR_DESCRIPTOR) { - cfg.addr = (DCC_RD_MOD_WR_ADDR) & GENMASK(27, 0); - cfg.addr |= DCC_ADDR_DESCRIPTOR; - dcc_sram_write_auto(drvdata, cfg.addr, &cfg.sram_offset); - } - - /* Setting zero to indicate end of the list */ - cfg.link = DCC_LINK_DESCRIPTOR; - dcc_sram_write_auto(drvdata, cfg.link, &cfg.sram_offset); - - /* Check if sram offset exceeds the ram size */ - if (cfg.sram_offset > drvdata->ram_size) - goto overstep; - - /* Update ram_cfg and check if the data will overstep */ - drvdata->ram_cfg = (cfg.sram_offset + total_len) / 4; - - if (cfg.sram_offset + total_len > drvdata->ram_size) { - cfg.sram_offset += total_len; - goto overstep; - } - - drvdata->ram_start = cfg.sram_offset / 4; - return 0; -overstep: - ret = -EINVAL; - memset_io(drvdata->ram_base, 0, drvdata->ram_size); - -err: - return ret; -} - -static bool dcc_valid_list(struct dcc_drvdata *drvdata, unsigned int curr_list) -{ - u32 lock_reg; - - if (list_empty(&drvdata->cfg_head[curr_list])) - return false; - - if (test_bit(curr_list, drvdata->enable_bitmap)) { - dev_err(drvdata->dev, "List %d is already enabled\n", curr_list); - return false; - } - - lock_reg = dcc_list_readl(drvdata, curr_list, DCC_LL_LOCK); - if (lock_reg & DCC_LOCK_MASK) { - dev_err(drvdata->dev, "List %d is already locked\n", curr_list); - return false; - } - - return true; -} - -static bool is_dcc_enabled(struct dcc_drvdata *drvdata) -{ - int list; - - for (list = 0; list < drvdata->nr_link_list; list++) - if (test_bit(list, drvdata->enable_bitmap)) - return true; - - return false; -} - -static int dcc_enable(struct dcc_drvdata *drvdata, unsigned int curr_list) -{ - int ret; - u32 ram_cfg_base; - - mutex_lock(&drvdata->mutex); - - if (!dcc_valid_list(drvdata, curr_list)) { - ret = -EINVAL; - goto out_unlock; - } - - /* Fill dcc sram with the poison value. - * This helps in understanding bus - * hang from registers returning a zero - */ - if (!is_dcc_enabled(drvdata)) - memset_io(drvdata->ram_base, 0xde, drvdata->ram_size); - - /* 1. Take ownership of the list */ - dcc_list_writel(drvdata, DCC_LOCK_MASK, curr_list, DCC_LL_LOCK); - - /* 2. Program linked-list in the SRAM */ - ram_cfg_base = drvdata->ram_cfg; - ret = dcc_emit_config(drvdata, curr_list); - if (ret) { - dcc_list_writel(drvdata, 0, curr_list, DCC_LL_LOCK); - goto out_unlock; - } - - /* 3. Program DCC_RAM_CFG reg */ - dcc_list_writel(drvdata, ram_cfg_base + - drvdata->ram_offset / 4, curr_list, DCC_LL_BASE); - dcc_list_writel(drvdata, drvdata->ram_start + - drvdata->ram_offset / 4, curr_list, DCC_FD_BASE); - dcc_list_writel(drvdata, 0xFFF, curr_list, DCC_LL_TIMEOUT); - - /* 4. Clears interrupt status register */ - dcc_list_writel(drvdata, 0, curr_list, DCC_LL_INT_ENABLE); - dcc_list_writel(drvdata, (BIT(0) | BIT(1) | BIT(2)), - curr_list, DCC_LL_INT_STATUS); - - set_bit(curr_list, drvdata->enable_bitmap); - - /* 5. Configure trigger */ - dcc_list_writel(drvdata, DCC_TRIGGER_MASK, - curr_list, DCC_LL_CFG); - -out_unlock: - mutex_unlock(&drvdata->mutex); - return ret; -} - -static void dcc_disable(struct dcc_drvdata *drvdata, int curr_list) -{ - mutex_lock(&drvdata->mutex); - - if (!test_bit(curr_list, drvdata->enable_bitmap)) - goto out_unlock; - dcc_list_writel(drvdata, 0, curr_list, DCC_LL_CFG); - dcc_list_writel(drvdata, 0, curr_list, DCC_LL_BASE); - dcc_list_writel(drvdata, 0, curr_list, DCC_FD_BASE); - dcc_list_writel(drvdata, 0, curr_list, DCC_LL_LOCK); - clear_bit(curr_list, drvdata->enable_bitmap); -out_unlock: - mutex_unlock(&drvdata->mutex); -} - -static u32 dcc_filp_curr_list(const struct file *filp) -{ - struct dentry *dentry = file_dentry(filp); - int curr_list, ret; - - ret = kstrtoint(dentry->d_parent->d_name.name, 0, &curr_list); - if (ret) - return ret; - - return curr_list; -} - -static ssize_t enable_read(struct file *filp, char __user *userbuf, - size_t count, loff_t *ppos) -{ - char *buf; - struct dcc_drvdata *drvdata = filp->private_data; - - mutex_lock(&drvdata->mutex); - - if (is_dcc_enabled(drvdata)) - buf = "Y\n"; - else - buf = "N\n"; - - mutex_unlock(&drvdata->mutex); - - return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); -} - -static ssize_t enable_write(struct file *filp, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - int ret = 0, curr_list; - bool val; - struct dcc_drvdata *drvdata = filp->private_data; - - curr_list = dcc_filp_curr_list(filp); - if (curr_list < 0) - return curr_list; - - ret = kstrtobool_from_user(userbuf, count, &val); - if (ret < 0) - return ret; - - if (val) { - ret = dcc_enable(drvdata, curr_list); - if (ret) - return ret; - } else { - dcc_disable(drvdata, curr_list); - } - - return count; -} - -static const struct file_operations enable_fops = { - .read = enable_read, - .write = enable_write, - .open = simple_open, - .llseek = generic_file_llseek, -}; - -static ssize_t trigger_write(struct file *filp, - const char __user *user_buf, size_t count, - loff_t *ppos) -{ - int ret; - unsigned int val; - struct dcc_drvdata *drvdata = filp->private_data; - - ret = kstrtouint_from_user(user_buf, count, 0, &val); - if (ret < 0) - return ret; - - if (val != 1) - return -EINVAL; - - ret = dcc_sw_trigger(drvdata); - if (ret < 0) - return ret; - - return count; -} - -static const struct file_operations trigger_fops = { - .write = trigger_write, - .open = simple_open, - .llseek = generic_file_llseek, -}; - -static int dcc_config_add(struct dcc_drvdata *drvdata, unsigned int addr, - unsigned int len, bool apb_bus, int curr_list) -{ - int ret = 0; - struct dcc_config_entry *entry, *pentry; - unsigned int base, offset; - - mutex_lock(&drvdata->mutex); - - if (!len || len > drvdata->ram_size / DCC_WORD_SIZE) { - dev_err(drvdata->dev, "DCC: Invalid length\n"); - ret = -EINVAL; - goto out_unlock; - } - - base = addr & DCC_ADDR_RANGE_MASK; - - if (!list_empty(&drvdata->cfg_head[curr_list])) { - pentry = list_last_entry(&drvdata->cfg_head[curr_list], - struct dcc_config_entry, list); - - if (pentry->desc_type == DCC_READ_TYPE && - addr >= (pentry->base + pentry->offset) && - addr <= (pentry->base + pentry->offset + MAX_DCC_OFFSET)) { - /* Re-use base address from last entry */ - base = pentry->base; - - if ((pentry->len * 4 + pentry->base + pentry->offset) - == addr) { - len += pentry->len; - - if (len > MAX_DCC_LEN) - pentry->len = MAX_DCC_LEN; - else - pentry->len = len; - - addr = pentry->base + pentry->offset + - pentry->len * 4; - len -= pentry->len; - } - } - } - - offset = addr - base; - - while (len) { - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - ret = -ENOMEM; - goto out_unlock; - } - - entry->base = base; - entry->offset = offset; - entry->len = min_t(u32, len, MAX_DCC_LEN); - entry->desc_type = DCC_READ_TYPE; - entry->apb_bus = apb_bus; - INIT_LIST_HEAD(&entry->list); - list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]); - - len -= entry->len; - offset += MAX_DCC_LEN * 4; - } - -out_unlock: - mutex_unlock(&drvdata->mutex); - return ret; -} - -static ssize_t dcc_config_add_read(struct dcc_drvdata *drvdata, char *buf, int curr_list) -{ - bool bus; - int len, nval; - unsigned int base; - char apb_bus[4]; - - nval = sscanf(buf, "%x %i %3s", &base, &len, apb_bus); - if (nval <= 0 || nval > 3) - return -EINVAL; - - if (nval == 1) { - len = 1; - bus = false; - } else if (nval == 2) { - bus = false; - } else if (!strcmp("apb", apb_bus)) { - bus = true; - } else if (!strcmp("ahb", apb_bus)) { - bus = false; - } else { - return -EINVAL; - } - - return dcc_config_add(drvdata, base, len, bus, curr_list); -} - -static void dcc_config_reset(struct dcc_drvdata *drvdata) -{ - struct dcc_config_entry *entry, *temp; - int curr_list; - - mutex_lock(&drvdata->mutex); - - for (curr_list = 0; curr_list < drvdata->nr_link_list; curr_list++) { - list_for_each_entry_safe(entry, temp, - &drvdata->cfg_head[curr_list], list) { - list_del(&entry->list); - kfree(entry); - } - } - drvdata->ram_start = 0; - drvdata->ram_cfg = 0; - mutex_unlock(&drvdata->mutex); -} - -static ssize_t config_reset_write(struct file *filp, - const char __user *user_buf, size_t count, - loff_t *ppos) -{ - unsigned int val; - int ret; - struct dcc_drvdata *drvdata = filp->private_data; - - ret = kstrtouint_from_user(user_buf, count, 0, &val); - if (ret < 0) - return ret; - - if (val) - dcc_config_reset(drvdata); - - return count; -} - -static const struct file_operations config_reset_fops = { - .write = config_reset_write, - .open = simple_open, - .llseek = generic_file_llseek, -}; - -static ssize_t ready_read(struct file *filp, char __user *userbuf, - size_t count, loff_t *ppos) -{ - int ret = 0; - char *buf; - struct dcc_drvdata *drvdata = filp->private_data; - - mutex_lock(&drvdata->mutex); - - if (!is_dcc_enabled(drvdata)) { - ret = -EINVAL; - goto out_unlock; - } - - if (!FIELD_GET(BIT(1), readl(drvdata->base + DCC_STATUS(drvdata->mem_map_ver)))) - buf = "Y\n"; - else - buf = "N\n"; -out_unlock: - mutex_unlock(&drvdata->mutex); - - if (ret < 0) - return -EINVAL; - else - return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf) + 1); -} - -static const struct file_operations ready_fops = { - .read = ready_read, - .open = simple_open, - .llseek = generic_file_llseek, -}; - -static int dcc_add_loop(struct dcc_drvdata *drvdata, unsigned long loop_cnt, int curr_list) -{ - struct dcc_config_entry *entry; - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - entry->loop_cnt = min_t(u32, loop_cnt, MAX_LOOP_CNT); - entry->desc_type = DCC_LOOP_TYPE; - INIT_LIST_HEAD(&entry->list); - list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]); - - return 0; -} - -static ssize_t dcc_config_add_loop(struct dcc_drvdata *drvdata, char *buf, int curr_list) -{ - int ret, i = 0; - char *token, *input; - char delim[2] = " "; - unsigned int val[MAX_LOOP_ADDR]; - - input = buf; - - while ((token = strsep(&input, delim)) && i < MAX_LOOP_ADDR) { - ret = kstrtoint(token, 0, &val[i++]); - if (ret) - return ret; - } - - if (token) { - dev_err(drvdata->dev, "Max limit %u of loop address exceeded", - MAX_LOOP_ADDR); - return -EINVAL; - } - - if (val[1] < 1 || val[1] > 8) - return -EINVAL; - - ret = dcc_add_loop(drvdata, val[0], curr_list); - if (ret) - return ret; - - for (i = 0; i < val[1]; i++) - dcc_config_add(drvdata, val[i + 2], 1, false, curr_list); - - return dcc_add_loop(drvdata, 1, curr_list); -} - -static int dcc_rd_mod_wr_add(struct dcc_drvdata *drvdata, unsigned int mask, - unsigned int val, int curr_list) -{ - int ret = 0; - struct dcc_config_entry *entry; - - mutex_lock(&drvdata->mutex); - - if (list_empty(&drvdata->cfg_head[curr_list])) { - dev_err(drvdata->dev, "DCC: No read address programmed\n"); - ret = -EPERM; - goto out_unlock; - } - - entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); - if (!entry) { - ret = -ENOMEM; - goto out_unlock; - } - - entry->desc_type = DCC_READ_WRITE_TYPE; - entry->mask = mask; - entry->write_val = val; - list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]); -out_unlock: - mutex_unlock(&drvdata->mutex); - return ret; -} - -static ssize_t dcc_config_add_read_write(struct dcc_drvdata *drvdata, char *buf, int curr_list) -{ - int ret; - int nval; - unsigned int addr, mask, val; - - nval = sscanf(buf, "%x %x %x", &addr, &mask, &val); - - if (nval <= 1 || nval > 3) - return -EINVAL; - - ret = dcc_config_add(drvdata, addr, 1, false, curr_list); - if (ret) - return ret; - - return dcc_rd_mod_wr_add(drvdata, mask, val, curr_list); -} - -static int dcc_add_write(struct dcc_drvdata *drvdata, unsigned int addr, - unsigned int write_val, int apb_bus, int curr_list) -{ - struct dcc_config_entry *entry; - - entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - entry->desc_type = DCC_WRITE_TYPE; - entry->base = addr & GENMASK(31, 4); - entry->offset = addr - entry->base; - entry->write_val = write_val; - entry->len = 1; - entry->apb_bus = apb_bus; - list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]); - - return 0; -} - -static ssize_t dcc_config_add_write(struct dcc_drvdata *drvdata, char *buf, int curr_list) -{ - bool bus; - int nval; - unsigned int addr, write_val; - char apb_bus[4]; - - nval = sscanf(buf, "%x %x %3s", &addr, &write_val, apb_bus); - - if (nval <= 1 || nval > 3) - return -EINVAL; - - if (nval == 2) - bus = true; - - if (nval == 3) { - if (!strcmp("apb", apb_bus)) - bus = true; - else if (!strcmp("ahb", apb_bus)) - bus = false; - else - return -EINVAL; - } - - return dcc_add_write(drvdata, addr, write_val, bus, curr_list); -} - -static int config_show(struct seq_file *m, void *data) -{ - struct dcc_drvdata *drvdata = m->private; - struct dcc_config_entry *entry; - int index = 0, curr_list; - - curr_list = dcc_filp_curr_list(m->file); - if (curr_list < 0) - return curr_list; - - mutex_lock(&drvdata->mutex); - - list_for_each_entry(entry, &drvdata->cfg_head[curr_list], list) { - index++; - switch (entry->desc_type) { - case DCC_READ_WRITE_TYPE: - seq_printf(m, "RW mask: 0x%x, val: 0x%x\n index: 0x%x\n", - entry->mask, entry->write_val, index); - break; - case DCC_LOOP_TYPE: - seq_printf(m, "L index: 0x%x Loop: %d\n", index, entry->loop_cnt); - break; - case DCC_WRITE_TYPE: - seq_printf(m, "W Base:0x%x, Offset: 0x%x, val: 0x%x, APB: %d\n, Index: 0x%x\n", - entry->base, entry->offset, entry->write_val, entry->apb_bus, - index); - break; - case DCC_READ_TYPE: - seq_printf(m, "R Base:0x%x, Offset: 0x%x, len: 0x%x, APB: %d\n, Index: 0x%x\n", - entry->base, entry->offset, entry->len, entry->apb_bus, index); - } - } - mutex_unlock(&drvdata->mutex); - return 0; -} - -static int config_open(struct inode *inode, struct file *file) -{ - struct dcc_drvdata *drvdata = inode->i_private; - - return single_open(file, config_show, drvdata); -} - -static ssize_t config_write(struct file *filp, - const char __user *user_buf, size_t count, - loff_t *ppos) -{ - int ret, curr_list; - char *token, buf[50]; - char *bufp = buf; - char *delim = " "; - struct dcc_drvdata *drvdata = filp->private_data; - - if (count > sizeof(buf) || count == 0) - return -EINVAL; - - ret = copy_from_user(buf, user_buf, count); - if (ret) - return -EFAULT; - - curr_list = dcc_filp_curr_list(filp); - if (curr_list < 0) - return curr_list; - - if (buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - return -EINVAL; - - token = strsep(&bufp, delim); - - if (!strcmp("R", token)) { - ret = dcc_config_add_read(drvdata, bufp, curr_list); - } else if (!strcmp("W", token)) { - ret = dcc_config_add_write(drvdata, bufp, curr_list); - } else if (!strcmp("RW", token)) { - ret = dcc_config_add_read_write(drvdata, bufp, curr_list); - } else if (!strcmp("L", token)) { - ret = dcc_config_add_loop(drvdata, bufp, curr_list); - } else { - dev_err(drvdata->dev, "%s is not a correct input\n", token); - return -EINVAL; - } - - if (ret) - return ret; - - return count; -} - -static const struct file_operations config_fops = { - .open = config_open, - .read = seq_read, - .write = config_write, - .llseek = seq_lseek, - .release = single_release, -}; - -static void dcc_delete_debug_dir(struct dcc_drvdata *drvdata) -{ - debugfs_remove_recursive(drvdata->dbg_dir); -}; - -static void dcc_create_debug_dir(struct dcc_drvdata *drvdata) -{ - int i; - char list_num[10]; - struct dentry *list; - struct device *dev = drvdata->dev; - - drvdata->dbg_dir = debugfs_create_dir(dev_name(dev), NULL); - if (IS_ERR(drvdata->dbg_dir)) { - pr_err("can't create debugfs dir\n"); - return; - } - - for (i = 0; i <= drvdata->nr_link_list; i++) { - sprintf(list_num, "%d", i); - list = debugfs_create_dir(list_num, drvdata->dbg_dir); - debugfs_create_file("enable", 0600, list, drvdata, &enable_fops); - debugfs_create_file("config", 0600, list, drvdata, &config_fops); - } - - debugfs_create_file("trigger", 0200, drvdata->dbg_dir, drvdata, &trigger_fops); - debugfs_create_file("ready", 0400, drvdata->dbg_dir, drvdata, &ready_fops); - debugfs_create_file("config_reset", 0200, drvdata->dbg_dir, - drvdata, &config_reset_fops); -} - -static ssize_t dcc_sram_read(struct file *file, char __user *data, - size_t len, loff_t *ppos) -{ - struct dcc_drvdata *drvdata = container_of(file->private_data, struct dcc_drvdata, sram_dev); - unsigned char *buf; - - /* EOF check */ - if (*ppos >= drvdata->ram_size) - return 0; - - if ((*ppos + len) > drvdata->ram_size) - len = (drvdata->ram_size - *ppos); - - buf = kzalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - memcpy_fromio(buf, drvdata->ram_base + *ppos, len); - - if (copy_to_user(data, buf, len)) { - kfree(buf); - return -EFAULT; - } - - *ppos += len; - - kfree(buf); - - return len; -} - -static const struct file_operations dcc_sram_fops = { - .owner = THIS_MODULE, - .read = dcc_sram_read, - .llseek = no_llseek, -}; - -static int dcc_sram_dev_init(struct dcc_drvdata *drvdata) -{ - drvdata->sram_dev.minor = MISC_DYNAMIC_MINOR; - drvdata->sram_dev.name = DCC_SRAM_NODE; - drvdata->sram_dev.fops = &dcc_sram_fops; - - return misc_register(&drvdata->sram_dev); -} - -static void dcc_sram_dev_exit(struct dcc_drvdata *drvdata) -{ - misc_deregister(&drvdata->sram_dev); -} - -static int dcc_probe(struct platform_device *pdev) -{ - u32 val; - int ret = 0, i; - struct device *dev = &pdev->dev; - struct dcc_drvdata *drvdata; - struct resource *res; - - drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); - if (!drvdata) - return -ENOMEM; - - drvdata->dev = &pdev->dev; - platform_set_drvdata(pdev, drvdata); - - drvdata->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(drvdata->base)) - return PTR_ERR(drvdata->base); - - drvdata->ram_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res); - if (IS_ERR(drvdata->ram_base)) - return PTR_ERR(drvdata->ram_base); - - drvdata->ram_size = resource_size(res); - - drvdata->ram_offset = (size_t)of_device_get_match_data(&pdev->dev); - - val = readl(drvdata->base + DCC_HW_INFO); - - if (FIELD_GET(DCC_VER_INFO_MASK, val)) { - drvdata->mem_map_ver = 3; - drvdata->nr_link_list = readl(drvdata->base + DCC_LL_NUM_INFO); - if (!drvdata->nr_link_list) - return -EINVAL; - } else if ((val & DCC_VER_MASK2) == DCC_VER_MASK2) { - drvdata->mem_map_ver = 2; - drvdata->nr_link_list = readl(drvdata->base + DCC_LL_NUM_INFO); - if (!drvdata->nr_link_list) - return -EINVAL; - } else { - drvdata->mem_map_ver = 1; - drvdata->nr_link_list = DCC_MAX_LINK_LIST; - } - - /* Either set the fixed loop offset or calculate - * it from the total number of words in dcc_sram. - * Max consecutive addresses dcc can loop is - * equivalent to the words in dcc_sram. - */ - if (val & DCC_LOOP_OFFSET_MASK) - drvdata->loop_shift = DCC_FIX_LOOP_OFFSET; - else - drvdata->loop_shift = get_bitmask_order((drvdata->ram_offset + - drvdata->ram_size) / DCC_SRAM_WORD_LENGTH - 1); - - mutex_init(&drvdata->mutex); - - drvdata->enable_bitmap = devm_kcalloc(dev, BITS_TO_LONGS(drvdata->nr_link_list), - sizeof(*drvdata->enable_bitmap), GFP_KERNEL); - if (!drvdata->enable_bitmap) - return -ENOMEM; - - drvdata->cfg_head = devm_kcalloc(dev, drvdata->nr_link_list, - sizeof(*drvdata->cfg_head), GFP_KERNEL); - if (!drvdata->cfg_head) - return -ENOMEM; - - for (i = 0; i < drvdata->nr_link_list; i++) - INIT_LIST_HEAD(&drvdata->cfg_head[i]); - - ret = dcc_sram_dev_init(drvdata); - if (ret) { - dev_err(drvdata->dev, "DCC: sram node not registered.\n"); - return ret; - } - - dcc_create_debug_dir(drvdata); - - return 0; -} - -static int dcc_remove(struct platform_device *pdev) -{ - struct dcc_drvdata *drvdata = platform_get_drvdata(pdev); - - dcc_delete_debug_dir(drvdata); - dcc_sram_dev_exit(drvdata); - dcc_config_reset(drvdata); - - return 0; -} - -static const struct of_device_id dcc_match_table[] = { - { .compatible = "qcom,sc7180-dcc", .data = (void *)0x6000 }, - { .compatible = "qcom,sc7280-dcc", .data = (void *)0x12000 }, - { .compatible = "qcom,sdm845-dcc", .data = (void *)0x6000 }, - { .compatible = "qcom,sm8150-dcc", .data = (void *)0x5000 }, - { } -}; -MODULE_DEVICE_TABLE(of, dcc_match_table); - -static struct platform_driver dcc_driver = { - .probe = dcc_probe, - .remove = dcc_remove, - .driver = { - .name = "qcom-dcc", - .of_match_table = dcc_match_table, - }, -}; -module_platform_driver(dcc_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Qualcomm Technologies Inc. DCC driver"); diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 3f11554df2f3..33dd8c315eb7 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -12,7 +12,7 @@ #include <linux/firmware.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/soc/qcom/mdt_loader.h> diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c index c92d26b73e6f..199fe9872035 100644 --- a/drivers/soc/qcom/ocmem.c +++ b/drivers/soc/qcom/ocmem.c @@ -16,7 +16,7 @@ #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/types.h> diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c new file mode 100644 index 000000000000..bb3fb57abcc6 --- /dev/null +++ b/drivers/soc/qcom/pmic_glink.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Linaro Ltd + */ +#include <linux/auxiliary_bus.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rpmsg.h> +#include <linux/slab.h> +#include <linux/soc/qcom/pdr.h> +#include <linux/soc/qcom/pmic_glink.h> + +struct pmic_glink { + struct device *dev; + struct pdr_handle *pdr; + + struct rpmsg_endpoint *ept; + + struct auxiliary_device altmode_aux; + struct auxiliary_device ps_aux; + struct auxiliary_device ucsi_aux; + + /* serializing client_state and pdr_state updates */ + struct mutex state_lock; + unsigned int client_state; + unsigned int pdr_state; + + /* serializing clients list updates */ + struct mutex client_lock; + struct list_head clients; +}; + +static struct pmic_glink *__pmic_glink; +static DEFINE_MUTEX(__pmic_glink_lock); + +struct pmic_glink_client { + struct list_head node; + + struct pmic_glink *pg; + unsigned int id; + + void (*cb)(const void *data, size_t len, void *priv); + void (*pdr_notify)(void *priv, int state); + void *priv; +}; + +static void _devm_pmic_glink_release_client(struct device *dev, void *res) +{ + struct pmic_glink_client *client = (struct pmic_glink_client *)res; + struct pmic_glink *pg = client->pg; + + mutex_lock(&pg->client_lock); + list_del(&client->node); + mutex_unlock(&pg->client_lock); +} + +struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev, + unsigned int id, + void (*cb)(const void *, size_t, void *), + void (*pdr)(void *, int), + void *priv) +{ + struct pmic_glink_client *client; + struct pmic_glink *pg = dev_get_drvdata(dev->parent); + + client = devres_alloc(_devm_pmic_glink_release_client, sizeof(*client), GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); + + client->pg = pg; + client->id = id; + client->cb = cb; + client->pdr_notify = pdr; + client->priv = priv; + + mutex_lock(&pg->client_lock); + list_add(&client->node, &pg->clients); + mutex_unlock(&pg->client_lock); + + devres_add(dev, client); + + return client; +} +EXPORT_SYMBOL_GPL(devm_pmic_glink_register_client); + +int pmic_glink_send(struct pmic_glink_client *client, void *data, size_t len) +{ + struct pmic_glink *pg = client->pg; + + return rpmsg_send(pg->ept, data, len); +} +EXPORT_SYMBOL_GPL(pmic_glink_send); + +static int pmic_glink_rpmsg_callback(struct rpmsg_device *rpdev, void *data, + int len, void *priv, u32 addr) +{ + struct pmic_glink_client *client; + struct pmic_glink_hdr *hdr; + struct pmic_glink *pg = dev_get_drvdata(&rpdev->dev); + + if (len < sizeof(*hdr)) { + dev_warn(pg->dev, "ignoring truncated message\n"); + return 0; + } + + hdr = data; + + list_for_each_entry(client, &pg->clients, node) { + if (client->id == le32_to_cpu(hdr->owner)) + client->cb(data, len, client->priv); + } + + return 0; +} + +static void pmic_glink_aux_release(struct device *dev) {} + +static int pmic_glink_add_aux_device(struct pmic_glink *pg, + struct auxiliary_device *aux, + const char *name) +{ + struct device *parent = pg->dev; + int ret; + + aux->name = name; + aux->dev.parent = parent; + aux->dev.release = pmic_glink_aux_release; + device_set_of_node_from_dev(&aux->dev, parent); + ret = auxiliary_device_init(aux); + if (ret) + return ret; + + ret = auxiliary_device_add(aux); + if (ret) + auxiliary_device_uninit(aux); + + return ret; +} + +static void pmic_glink_del_aux_device(struct pmic_glink *pg, + struct auxiliary_device *aux) +{ + auxiliary_device_delete(aux); + auxiliary_device_uninit(aux); +} + +static void pmic_glink_state_notify_clients(struct pmic_glink *pg) +{ + struct pmic_glink_client *client; + unsigned int new_state = pg->client_state; + + if (pg->client_state != SERVREG_SERVICE_STATE_UP) { + if (pg->pdr_state == SERVREG_SERVICE_STATE_UP && pg->ept) + new_state = SERVREG_SERVICE_STATE_UP; + } else { + if (pg->pdr_state == SERVREG_SERVICE_STATE_UP && pg->ept) + new_state = SERVREG_SERVICE_STATE_DOWN; + } + + if (new_state != pg->client_state) { + list_for_each_entry(client, &pg->clients, node) + client->pdr_notify(client->priv, new_state); + pg->client_state = new_state; + } +} + +static void pmic_glink_pdr_callback(int state, char *svc_path, void *priv) +{ + struct pmic_glink *pg = priv; + + mutex_lock(&pg->state_lock); + pg->pdr_state = state; + + pmic_glink_state_notify_clients(pg); + mutex_unlock(&pg->state_lock); +} + +static int pmic_glink_rpmsg_probe(struct rpmsg_device *rpdev) +{ + struct pmic_glink *pg = __pmic_glink; + int ret = 0; + + mutex_lock(&__pmic_glink_lock); + if (!pg) { + ret = dev_err_probe(&rpdev->dev, -ENODEV, "no pmic_glink device to attach to\n"); + goto out_unlock; + } + + dev_set_drvdata(&rpdev->dev, pg); + + mutex_lock(&pg->state_lock); + pg->ept = rpdev->ept; + pmic_glink_state_notify_clients(pg); + mutex_unlock(&pg->state_lock); + +out_unlock: + mutex_unlock(&__pmic_glink_lock); + return ret; +} + +static void pmic_glink_rpmsg_remove(struct rpmsg_device *rpdev) +{ + struct pmic_glink *pg; + + mutex_lock(&__pmic_glink_lock); + pg = __pmic_glink; + if (!pg) + goto out_unlock; + + mutex_lock(&pg->state_lock); + pg->ept = NULL; + pmic_glink_state_notify_clients(pg); + mutex_unlock(&pg->state_lock); +out_unlock: + mutex_unlock(&__pmic_glink_lock); +} + +static const struct rpmsg_device_id pmic_glink_rpmsg_id_match[] = { + { "PMIC_RTR_ADSP_APPS" }, + {} +}; + +static struct rpmsg_driver pmic_glink_rpmsg_driver = { + .probe = pmic_glink_rpmsg_probe, + .remove = pmic_glink_rpmsg_remove, + .callback = pmic_glink_rpmsg_callback, + .id_table = pmic_glink_rpmsg_id_match, + .drv = { + .name = "qcom_pmic_glink_rpmsg", + }, +}; + +static int pmic_glink_probe(struct platform_device *pdev) +{ + struct pdr_service *service; + struct pmic_glink *pg; + int ret; + + pg = devm_kzalloc(&pdev->dev, sizeof(*pg), GFP_KERNEL); + if (!pg) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, pg); + + pg->dev = &pdev->dev; + + INIT_LIST_HEAD(&pg->clients); + mutex_init(&pg->client_lock); + mutex_init(&pg->state_lock); + + ret = pmic_glink_add_aux_device(pg, &pg->altmode_aux, "altmode"); + if (ret) + return ret; + ret = pmic_glink_add_aux_device(pg, &pg->ps_aux, "power-supply"); + if (ret) + goto out_release_altmode_aux; + + pg->pdr = pdr_handle_alloc(pmic_glink_pdr_callback, pg); + if (IS_ERR(pg->pdr)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(pg->pdr), "failed to initialize pdr\n"); + goto out_release_aux_devices; + } + + service = pdr_add_lookup(pg->pdr, "tms/servreg", "msm/adsp/charger_pd"); + if (IS_ERR(service)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(service), + "failed adding pdr lookup for charger_pd\n"); + goto out_release_pdr_handle; + } + + mutex_lock(&__pmic_glink_lock); + __pmic_glink = pg; + mutex_unlock(&__pmic_glink_lock); + + return 0; + +out_release_pdr_handle: + pdr_handle_release(pg->pdr); +out_release_aux_devices: + pmic_glink_del_aux_device(pg, &pg->ps_aux); +out_release_altmode_aux: + pmic_glink_del_aux_device(pg, &pg->altmode_aux); + + return ret; +} + +static int pmic_glink_remove(struct platform_device *pdev) +{ + struct pmic_glink *pg = dev_get_drvdata(&pdev->dev); + + pdr_handle_release(pg->pdr); + + pmic_glink_del_aux_device(pg, &pg->ps_aux); + pmic_glink_del_aux_device(pg, &pg->altmode_aux); + + mutex_lock(&__pmic_glink_lock); + __pmic_glink = NULL; + mutex_unlock(&__pmic_glink_lock); + + return 0; +} + +static const struct of_device_id pmic_glink_of_match[] = { + { .compatible = "qcom,pmic-glink", }, + {} +}; +MODULE_DEVICE_TABLE(of, pmic_glink_of_match); + +static struct platform_driver pmic_glink_driver = { + .probe = pmic_glink_probe, + .remove = pmic_glink_remove, + .driver = { + .name = "qcom_pmic_glink", + .of_match_table = pmic_glink_of_match, + }, +}; + +static int pmic_glink_init(void) +{ + platform_driver_register(&pmic_glink_driver); + register_rpmsg_driver(&pmic_glink_rpmsg_driver); + + return 0; +}; +module_init(pmic_glink_init); + +static void pmic_glink_exit(void) +{ + unregister_rpmsg_driver(&pmic_glink_rpmsg_driver); + platform_driver_unregister(&pmic_glink_driver); +}; +module_exit(pmic_glink_exit); + +MODULE_DESCRIPTION("Qualcomm PMIC GLINK driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c new file mode 100644 index 000000000000..4d7895bdeaf2 --- /dev/null +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Linaro Ltd + */ +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/mutex.h> +#include <linux/property.h> +#include <linux/soc/qcom/pdr.h> +#include <drm/drm_bridge.h> + +#include <linux/usb/typec_altmode.h> +#include <linux/usb/typec_dp.h> +#include <linux/usb/typec_mux.h> + +#include <linux/soc/qcom/pmic_glink.h> + +#define PMIC_GLINK_MAX_PORTS 2 + +#define USBC_SC8180X_NOTIFY_IND 0x13 +#define USBC_CMD_WRITE_REQ 0x15 +#define USBC_NOTIFY_IND 0x16 + +#define ALTMODE_PAN_EN 0x10 +#define ALTMODE_PAN_ACK 0x11 + +struct usbc_write_req { + struct pmic_glink_hdr hdr; + __le32 cmd; + __le32 arg; + __le32 reserved; +}; + +#define NOTIFY_PAYLOAD_SIZE 16 +struct usbc_notify { + struct pmic_glink_hdr hdr; + char payload[NOTIFY_PAYLOAD_SIZE]; + u32 reserved; +}; + +struct usbc_sc8180x_notify { + struct pmic_glink_hdr hdr; + __le32 notification; + __le32 reserved[2]; +}; + +enum pmic_glink_altmode_pin_assignment { + DPAM_HPD_OUT, + DPAM_HPD_A, + DPAM_HPD_B, + DPAM_HPD_C, + DPAM_HPD_D, + DPAM_HPD_E, + DPAM_HPD_F, +}; + +struct pmic_glink_altmode; + +#define work_to_altmode_port(w) container_of((w), struct pmic_glink_altmode_port, work) + +struct pmic_glink_altmode_port { + struct pmic_glink_altmode *altmode; + unsigned int index; + + struct typec_switch *typec_switch; + struct typec_mux *typec_mux; + struct typec_mux_state state; + struct typec_altmode dp_alt; + + struct work_struct work; + + struct drm_bridge bridge; + + enum typec_orientation orientation; + u16 svid; + u8 dp_data; + u8 mode; + u8 hpd_state; + u8 hpd_irq; +}; + +#define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work) + +struct pmic_glink_altmode { + struct device *dev; + + unsigned int owner_id; + + /* To synchronize WRITE_REQ acks */ + struct mutex lock; + + struct completion pan_ack; + struct pmic_glink_client *client; + + struct work_struct enable_work; + + struct pmic_glink_altmode_port ports[PMIC_GLINK_MAX_PORTS]; +}; + +static int pmic_glink_altmode_request(struct pmic_glink_altmode *altmode, u32 cmd, u32 arg) +{ + struct usbc_write_req req = {}; + unsigned long left; + int ret; + + /* + * The USBC_CMD_WRITE_REQ ack doesn't identify the request, so wait for + * one ack at a time. + */ + mutex_lock(&altmode->lock); + + req.hdr.owner = cpu_to_le32(altmode->owner_id); + req.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP); + req.hdr.opcode = cpu_to_le32(USBC_CMD_WRITE_REQ); + req.cmd = cpu_to_le32(cmd); + req.arg = cpu_to_le32(arg); + + ret = pmic_glink_send(altmode->client, &req, sizeof(req)); + if (ret) { + dev_err(altmode->dev, "failed to send altmode request: %#x (%d)\n", cmd, ret); + goto out_unlock; + } + + left = wait_for_completion_timeout(&altmode->pan_ack, 5 * HZ); + if (!left) { + dev_err(altmode->dev, "timeout waiting for altmode request ack for: %#x\n", cmd); + ret = -ETIMEDOUT; + } + +out_unlock: + mutex_unlock(&altmode->lock); + return ret; +} + +static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, + struct pmic_glink_altmode_port *port, + u8 mode, bool hpd_state, + bool hpd_irq) +{ + struct typec_displayport_data dp_data = {}; + int ret; + + dp_data.status = DP_STATUS_ENABLED; + if (hpd_state) + dp_data.status |= DP_STATUS_HPD_STATE; + if (hpd_irq) + dp_data.status |= DP_STATUS_IRQ_HPD; + dp_data.conf = DP_CONF_SET_PIN_ASSIGN(mode); + + port->state.alt = &port->dp_alt; + port->state.data = &dp_data; + port->state.mode = TYPEC_MODAL_STATE(mode); + + ret = typec_mux_set(port->typec_mux, &port->state); + if (ret) + dev_err(altmode->dev, "failed to switch mux to DP\n"); +} + +static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, + struct pmic_glink_altmode_port *port) +{ + int ret; + + port->state.alt = NULL; + port->state.data = NULL; + port->state.mode = TYPEC_STATE_USB; + + ret = typec_mux_set(port->typec_mux, &port->state); + if (ret) + dev_err(altmode->dev, "failed to switch mux to USB\n"); +} + +static void pmic_glink_altmode_worker(struct work_struct *work) +{ + struct pmic_glink_altmode_port *alt_port = work_to_altmode_port(work); + struct pmic_glink_altmode *altmode = alt_port->altmode; + + typec_switch_set(alt_port->typec_switch, alt_port->orientation); + + if (alt_port->svid == USB_TYPEC_DP_SID) + pmic_glink_altmode_enable_dp(altmode, alt_port, alt_port->mode, + alt_port->hpd_state, alt_port->hpd_irq); + else + pmic_glink_altmode_enable_usb(altmode, alt_port); + + if (alt_port->hpd_state) + drm_bridge_hpd_notify(&alt_port->bridge, connector_status_connected); + else + drm_bridge_hpd_notify(&alt_port->bridge, connector_status_disconnected); + + pmic_glink_altmode_request(altmode, ALTMODE_PAN_ACK, alt_port->index); +}; + +static enum typec_orientation pmic_glink_altmode_orientation(unsigned int orientation) +{ + if (orientation == 0) + return TYPEC_ORIENTATION_NORMAL; + else if (orientation == 1) + return TYPEC_ORIENTATION_REVERSE; + else + return TYPEC_ORIENTATION_NONE; +} + +#define SC8180X_PORT_MASK 0x000000ff +#define SC8180X_ORIENTATION_MASK 0x0000ff00 +#define SC8180X_MUX_MASK 0x00ff0000 +#define SC8180X_MODE_MASK 0x3f000000 +#define SC8180X_HPD_STATE_MASK 0x40000000 +#define SC8180X_HPD_IRQ_MASK 0x80000000 + +static void pmic_glink_altmode_sc8180xp_notify(struct pmic_glink_altmode *altmode, + const void *data, size_t len) +{ + struct pmic_glink_altmode_port *alt_port; + const struct usbc_sc8180x_notify *msg; + u32 notification; + u8 orientation; + u8 hpd_state; + u8 hpd_irq; + u16 svid; + u8 port; + u8 mode; + u8 mux; + + if (len != sizeof(*msg)) { + dev_warn(altmode->dev, "invalid length of USBC_NOTIFY indication: %zd\n", len); + return; + } + + msg = data; + notification = le32_to_cpu(msg->notification); + port = FIELD_GET(SC8180X_PORT_MASK, notification); + orientation = FIELD_GET(SC8180X_ORIENTATION_MASK, notification); + mux = FIELD_GET(SC8180X_MUX_MASK, notification); + mode = FIELD_GET(SC8180X_MODE_MASK, notification); + hpd_state = FIELD_GET(SC8180X_HPD_STATE_MASK, notification); + hpd_irq = FIELD_GET(SC8180X_HPD_IRQ_MASK, notification); + + svid = mux == 2 ? USB_TYPEC_DP_SID : 0; + + if (!altmode->ports[port].altmode) { + dev_dbg(altmode->dev, "notification on undefined port %d\n", port); + return; + } + + alt_port = &altmode->ports[port]; + alt_port->orientation = pmic_glink_altmode_orientation(orientation); + alt_port->svid = svid; + alt_port->mode = mode; + alt_port->hpd_state = hpd_state; + alt_port->hpd_irq = hpd_irq; + schedule_work(&alt_port->work); +} + +#define SC8280XP_DPAM_MASK 0x3f +#define SC8280XP_HPD_STATE_MASK BIT(6) +#define SC8280XP_HPD_IRQ_MASK BIT(7) + +static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmode, + u16 svid, const void *data, size_t len) +{ + struct pmic_glink_altmode_port *alt_port; + const struct usbc_notify *notify; + u8 orientation; + u8 hpd_state; + u8 hpd_irq; + u8 mode; + u8 port; + + if (len != sizeof(*notify)) { + dev_warn(altmode->dev, "invalid length USBC_NOTIFY_IND: %zd\n", + len); + return; + } + + notify = data; + + port = notify->payload[0]; + orientation = notify->payload[1]; + mode = FIELD_GET(SC8280XP_DPAM_MASK, notify->payload[8]) - DPAM_HPD_A; + hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]); + hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]); + + if (!altmode->ports[port].altmode) { + dev_dbg(altmode->dev, "notification on undefined port %d\n", port); + return; + } + + alt_port = &altmode->ports[port]; + alt_port->orientation = pmic_glink_altmode_orientation(orientation); + alt_port->svid = svid; + alt_port->mode = mode; + alt_port->hpd_state = hpd_state; + alt_port->hpd_irq = hpd_irq; + schedule_work(&alt_port->work); +} + +static void pmic_glink_altmode_callback(const void *data, size_t len, void *priv) +{ + struct pmic_glink_altmode *altmode = priv; + const struct pmic_glink_hdr *hdr = data; + u16 opcode; + u16 svid; + + opcode = le32_to_cpu(hdr->opcode) & 0xff; + svid = le32_to_cpu(hdr->opcode) >> 16; + + switch (opcode) { + case USBC_CMD_WRITE_REQ: + complete(&altmode->pan_ack); + break; + case USBC_NOTIFY_IND: + pmic_glink_altmode_sc8280xp_notify(altmode, svid, data, len); + break; + case USBC_SC8180X_NOTIFY_IND: + pmic_glink_altmode_sc8180xp_notify(altmode, data, len); + break; + } +} + +static int pmic_glink_altmode_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL; +} + +static const struct drm_bridge_funcs pmic_glink_altmode_bridge_funcs = { + .attach = pmic_glink_altmode_attach, +}; + +static void pmic_glink_altmode_put_mux(void *data) +{ + typec_mux_put(data); +} + +static void pmic_glink_altmode_put_switch(void *data) +{ + typec_switch_put(data); +} + +static void pmic_glink_altmode_enable_worker(struct work_struct *work) +{ + struct pmic_glink_altmode *altmode = work_to_altmode(work); + int ret; + + ret = pmic_glink_altmode_request(altmode, ALTMODE_PAN_EN, 0); + if (ret) + dev_err(altmode->dev, "failed to request altmode notifications\n"); +} + +static void pmic_glink_altmode_pdr_notify(void *priv, int state) +{ + struct pmic_glink_altmode *altmode = priv; + + if (state == SERVREG_SERVICE_STATE_UP) + schedule_work(&altmode->enable_work); +} + +static const struct of_device_id pmic_glink_altmode_of_quirks[] = { + { .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)PMIC_GLINK_OWNER_USBC }, + {} +}; + +static int pmic_glink_altmode_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct pmic_glink_altmode_port *alt_port; + struct pmic_glink_altmode *altmode; + struct typec_altmode_desc mux_desc = {}; + const struct of_device_id *match; + struct fwnode_handle *fwnode; + struct device *dev = &adev->dev; + u32 port; + int ret; + + altmode = devm_kzalloc(dev, sizeof(*altmode), GFP_KERNEL); + if (!altmode) + return -ENOMEM; + + altmode->dev = dev; + + match = of_match_device(pmic_glink_altmode_of_quirks, dev->parent); + if (match) + altmode->owner_id = (unsigned long)match->data; + else + altmode->owner_id = PMIC_GLINK_OWNER_USBC_PAN; + + INIT_WORK(&altmode->enable_work, pmic_glink_altmode_enable_worker); + init_completion(&altmode->pan_ack); + mutex_init(&altmode->lock); + + device_for_each_child_node(dev, fwnode) { + ret = fwnode_property_read_u32(fwnode, "reg", &port); + if (ret < 0) { + dev_err(dev, "missing reg property of %pOFn\n", fwnode); + return ret; + } + + if (port >= ARRAY_SIZE(altmode->ports)) { + dev_warn(dev, "invalid connector number, ignoring\n"); + continue; + } + + if (altmode->ports[port].altmode) { + dev_err(dev, "multiple connector definition for port %u\n", port); + return -EINVAL; + } + + alt_port = &altmode->ports[port]; + alt_port->altmode = altmode; + alt_port->index = port; + INIT_WORK(&alt_port->work, pmic_glink_altmode_worker); + + alt_port->bridge.funcs = &pmic_glink_altmode_bridge_funcs; + alt_port->bridge.of_node = to_of_node(fwnode); + alt_port->bridge.ops = DRM_BRIDGE_OP_HPD; + alt_port->bridge.type = DRM_MODE_CONNECTOR_USB; + + ret = devm_drm_bridge_add(dev, &alt_port->bridge); + if (ret) + return ret; + + alt_port->dp_alt.svid = USB_TYPEC_DP_SID; + alt_port->dp_alt.mode = USB_TYPEC_DP_MODE; + alt_port->dp_alt.active = 1; + + mux_desc.svid = USB_TYPEC_DP_SID; + mux_desc.mode = USB_TYPEC_DP_MODE; + alt_port->typec_mux = fwnode_typec_mux_get(fwnode, &mux_desc); + if (IS_ERR(alt_port->typec_mux)) + return dev_err_probe(dev, PTR_ERR(alt_port->typec_mux), + "failed to acquire mode-switch for port: %d\n", + port); + + ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_mux, + alt_port->typec_mux); + if (ret) + return ret; + + alt_port->typec_switch = fwnode_typec_switch_get(fwnode); + if (IS_ERR(alt_port->typec_switch)) + return dev_err_probe(dev, PTR_ERR(alt_port->typec_switch), + "failed to acquire orientation-switch for port: %d\n", + port); + + ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_switch, + alt_port->typec_switch); + if (ret) + return ret; + } + + altmode->client = devm_pmic_glink_register_client(dev, + altmode->owner_id, + pmic_glink_altmode_callback, + pmic_glink_altmode_pdr_notify, + altmode); + return PTR_ERR_OR_ZERO(altmode->client); +} + +static const struct auxiliary_device_id pmic_glink_altmode_id_table[] = { + { .name = "pmic_glink.altmode", }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, pmic_glink_altmode_id_table); + +static struct auxiliary_driver pmic_glink_altmode_driver = { + .name = "pmic_glink_altmode", + .probe = pmic_glink_altmode_probe, + .id_table = pmic_glink_altmode_id_table, +}; + +module_auxiliary_driver(pmic_glink_altmode_driver); + +MODULE_DESCRIPTION("Qualcomm PMIC GLINK Altmode driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index 6228af057120..c207bb96c523 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -92,7 +92,7 @@ static int qcom_subsystem_sleep_stats_show(struct seq_file *s, void *unused) /* Items are allocated lazily, so lookup pointer each time */ stat = qcom_smem_get(subsystem->pid, subsystem->smem_item, NULL); if (IS_ERR(stat)) - return -EIO; + return 0; qcom_print_stats(s, stat); @@ -170,20 +170,14 @@ static void qcom_create_soc_sleep_stat_files(struct dentry *root, void __iomem * static void qcom_create_subsystem_stat_files(struct dentry *root, const struct stats_config *config) { - const struct sleep_stats *stat; int i; if (!config->subsystem_stats_in_smem) return; - for (i = 0; i < ARRAY_SIZE(subsystems); i++) { - stat = qcom_smem_get(subsystems[i].pid, subsystems[i].smem_item, NULL); - if (IS_ERR(stat)) - continue; - + for (i = 0; i < ARRAY_SIZE(subsystems); i++) debugfs_create_file(subsystems[i].name, 0400, root, (void *)&subsystems[i], &qcom_subsystem_sleep_stats_fops); - } } static int qcom_stats_probe(struct platform_device *pdev) diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c index 9d59ad509a5c..2d3ee22b9249 100644 --- a/drivers/soc/qcom/rmtfs_mem.c +++ b/drivers/soc/qcom/rmtfs_mem.c @@ -14,7 +14,7 @@ #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/io.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1) #define NUM_MAX_VMIDS 2 diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index 7e3b6a7ea34c..523627d5d398 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -233,6 +233,7 @@ static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev) static const struct of_device_id qcom_smd_rpm_of_match[] = { { .compatible = "qcom,rpm-apq8084" }, { .compatible = "qcom,rpm-ipq6018" }, + { .compatible = "qcom,rpm-ipq9574" }, { .compatible = "qcom,rpm-msm8226" }, { .compatible = "qcom,rpm-msm8909" }, { .compatible = "qcom,rpm-msm8916" }, diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 3b970a80f3aa..e9012ca1a87b 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -174,6 +174,8 @@ struct socinfo { __le32 pcode; __le32 npartnamemap_offset; __le32 nnum_partname_mapping; + /* Version 17 */ + __le32 oem_variant; }; #ifdef CONFIG_DEBUG_FS @@ -196,6 +198,7 @@ struct socinfo_params { u32 nmodem_supported; u32 feature_code; u32 pcode; + u32 oem_variant; }; struct smem_image_version { @@ -281,6 +284,10 @@ static const struct soc_id soc_id[] = { { qcom_board_id(MSM8126) }, { qcom_board_id(APQ8026) }, { qcom_board_id(MSM8926) }, + { qcom_board_id(IPQ8062) }, + { qcom_board_id(IPQ8064) }, + { qcom_board_id(IPQ8066) }, + { qcom_board_id(IPQ8068) }, { qcom_board_id(MSM8326) }, { qcom_board_id(MSM8916) }, { qcom_board_id(MSM8994) }, @@ -336,6 +343,8 @@ static const struct soc_id soc_id[] = { { qcom_board_id(MSM8609) }, { qcom_board_id(APQ8076) }, { qcom_board_id(MSM8976) }, + { qcom_board_id(IPQ8065) }, + { qcom_board_id(IPQ8069) }, { qcom_board_id(MDM9650) }, { qcom_board_id(MDM9655) }, { qcom_board_id(MDM9250) }, @@ -439,6 +448,8 @@ static const struct soc_id soc_id[] = { { qcom_board_id(QRU1032) }, { qcom_board_id(QRU1052) }, { qcom_board_id(QRU1062) }, + { qcom_board_id(IPQ5332) }, + { qcom_board_id(IPQ5322) }, }; static const char *socinfo_machine(struct device *dev, unsigned int id) @@ -593,6 +604,11 @@ static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, &qcom_socinfo->info.fmt); switch (qcom_socinfo->info.fmt) { + case SOCINFO_VERSION(0, 17): + qcom_socinfo->info.oem_variant = __le32_to_cpu(info->oem_variant); + debugfs_create_u32("oem_variant", 0444, qcom_socinfo->dbg_root, + &qcom_socinfo->info.oem_variant); + fallthrough; case SOCINFO_VERSION(0, 16): qcom_socinfo->info.feature_code = __le32_to_cpu(info->feature_code); qcom_socinfo->info.pcode = __le32_to_cpu(info->pcode); diff --git a/drivers/thermal/qcom/lmh.c b/drivers/thermal/qcom/lmh.c index 4122a51e9874..f6edb12ec004 100644 --- a/drivers/thermal/qcom/lmh.c +++ b/drivers/thermal/qcom/lmh.c @@ -10,7 +10,7 @@ #include <linux/platform_device.h> #include <linux/of_platform.h> #include <linux/slab.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #define LMH_NODE_DCVS 0x44435653 #define LMH_CLUSTER0_NODE_ID 0x6370302D diff --git a/drivers/ufs/host/ufs-qcom-ice.c b/drivers/ufs/host/ufs-qcom-ice.c index 62387ccd5b30..453978877ae9 100644 --- a/drivers/ufs/host/ufs-qcom-ice.c +++ b/drivers/ufs/host/ufs-qcom-ice.c @@ -8,7 +8,7 @@ #include <linux/delay.h> #include <linux/platform_device.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include "ufs-qcom.h" diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index 22d7ba17804b..aa95439708dc 100644 --- a/include/dt-bindings/arm/qcom,ids.h +++ b/include/dt-bindings/arm/qcom,ids.h @@ -71,6 +71,10 @@ #define QCOM_ID_MSM8126 198 #define QCOM_ID_APQ8026 199 #define QCOM_ID_MSM8926 200 +#define QCOM_ID_IPQ8062 201 +#define QCOM_ID_IPQ8064 202 +#define QCOM_ID_IPQ8066 203 +#define QCOM_ID_IPQ8068 204 #define QCOM_ID_MSM8326 205 #define QCOM_ID_MSM8916 206 #define QCOM_ID_MSM8994 207 @@ -127,6 +131,8 @@ #define QCOM_ID_APQ8076 277 #define QCOM_ID_MSM8976 278 #define QCOM_ID_MDM9650 279 +#define QCOM_ID_IPQ8065 280 +#define QCOM_ID_IPQ8069 281 #define QCOM_ID_MDM9655 283 #define QCOM_ID_MDM9250 284 #define QCOM_ID_MDM9255 285 @@ -229,6 +235,8 @@ #define QCOM_ID_QRU1032 588 #define QCOM_ID_QRU1052 589 #define QCOM_ID_QRU1062 590 +#define QCOM_ID_IPQ5332 592 +#define QCOM_ID_IPQ5322 593 /* * The board type and revision information, used by Qualcomm bootloaders and diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index 6df4ee400ef8..293e5119354b 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -210,6 +210,7 @@ #define RPMH_REGULATOR_LEVEL_RETENTION 16 #define RPMH_REGULATOR_LEVEL_MIN_SVS 48 #define RPMH_REGULATOR_LEVEL_LOW_SVS 64 +#define RPMH_REGULATOR_LEVEL_LOW_SVS_L1 80 #define RPMH_REGULATOR_LEVEL_SVS 128 #define RPMH_REGULATOR_LEVEL_SVS_L0 144 #define RPMH_REGULATOR_LEVEL_SVS_L1 192 diff --git a/include/linux/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index 1e449a5d7f5c..1e449a5d7f5c 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h diff --git a/include/linux/soc/qcom/pmic_glink.h b/include/linux/soc/qcom/pmic_glink.h new file mode 100644 index 000000000000..fd124aa18c81 --- /dev/null +++ b/include/linux/soc/qcom/pmic_glink.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022, Linaro Ltd + */ +#ifndef __SOC_QCOM_PMIC_GLINK_H__ +#define __SOC_QCOM_PMIC_GLINK_H__ + +struct pmic_glink; +struct pmic_glink_client; + +#define PMIC_GLINK_OWNER_BATTMGR 32778 +#define PMIC_GLINK_OWNER_USBC 32779 +#define PMIC_GLINK_OWNER_USBC_PAN 32780 + +#define PMIC_GLINK_REQ_RESP 1 +#define PMIC_GLINK_NOTIFY 2 + +struct pmic_glink_hdr { + __le32 owner; + __le32 type; + __le32 opcode; +}; + +int pmic_glink_send(struct pmic_glink_client *client, void *data, size_t len); + +struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev, + unsigned int id, + void (*cb)(const void *, size_t, void *), + void (*pdr)(void *, int), + void *priv); + +#endif |