From aa1a13c0e32a5fd079f4a502ed48ac3bff1497cf Mon Sep 17 00:00:00 2001 From: Teguh Sobirin Date: Thu, 28 Nov 2024 09:08:38 +0700 Subject: [PATCH 1/3] Add Retroid Pocket SM8250 Patch series and enable peripheral in current config --- config/kernel/linux-sm8250-current.config | 135 +- ...don't-choke-on-disabling-the-writeba.patch | 40 + ...om--sm8250--add-uart16-Signed-off-by.patch | 53 + ...-Driver-for-Qualcomm-SMB5-Signed-off.patch | 997 ++++++++++ ...ower--supply--Driver-for-Qualcomm-FG.patch | 1418 ++++++++++++++ ...om--pm8150b--Add-a-charger-Signed-of.patch | 63 + ...om--pm8150b--Add-a-FG-Signed-off-by-.patch | 32 + ...d-DDIC-CH13726A-panel-Signed-off-by-.patch | 397 ++++ ...er-for-HEROIC-HTR3212-Signed-off-by-.patch | 353 ++++ ..._Input--driver-for-AYN-Odin2-Gamepad.patch | 363 ++++ ...ut--add-Qualcomm-SPMI-haptics-driver.patch | 1092 +++++++++++ ...com--pm8150b--introduce-spmi-haptics.patch | 47 + ...om--add-SM8250-Retroid-Pocket-varian.patch | 1683 +++++++++++++++++ 13 files changed, 6659 insertions(+), 14 deletions(-) create mode 100644 patch/kernel/archive/sm8250-6.11/0030_drm-msm-dpu1--don't-choke-on-disabling-the-writeba.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0031_arm64--dts--qcom--sm8250--add-uart16-Signed-off-by.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0032_power--supply--Driver-for-Qualcomm-SMB5-Signed-off.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0033_power--supply--Driver-for-Qualcomm-FG.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0034_arm64--dts--qcom--pm8150b--Add-a-charger-Signed-of.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0035_arm64--dts--qcom--pm8150b--Add-a-FG-Signed-off-by-.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0036_drm--panel--Add-DDIC-CH13726A-panel-Signed-off-by-.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0037_leds--Add-driver-for-HEROIC-HTR3212-Signed-off-by-.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0038_Input--driver-for-AYN-Odin2-Gamepad.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0039_input--add-Qualcomm-SPMI-haptics-driver.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0040_arm64--dts--qcom--pm8150b--introduce-spmi-haptics.patch create mode 100644 patch/kernel/archive/sm8250-6.11/0041_arm64--dts--qcom--add-SM8250-Retroid-Pocket-varian.patch diff --git a/config/kernel/linux-sm8250-current.config b/config/kernel/linux-sm8250-current.config index 178339236da7..a4a69da3036c 100644 --- a/config/kernel/linux-sm8250-current.config +++ b/config/kernel/linux-sm8250-current.config @@ -2776,7 +2776,37 @@ CONFIG_MOUSE_PS2_SMBUS=y # CONFIG_MOUSE_GPIO is not set # CONFIG_MOUSE_SYNAPTICS_I2C is not set # CONFIG_MOUSE_SYNAPTICS_USB is not set -# CONFIG_INPUT_JOYSTICK is not set +CONFIG_INPUT_JOYSTICK=y +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADC is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_WARRIOR is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TWIDJOY is not set +# CONFIG_JOYSTICK_ZHENHUA is not set +# CONFIG_JOYSTICK_AS5011 is not set +# CONFIG_JOYSTICK_JOYDUMP is not set +# CONFIG_JOYSTICK_XPAD is not set +CONFIG_JOYSTICK_ODIN2=m +# CONFIG_JOYSTICK_PSXPAD_SPI is not set +# CONFIG_JOYSTICK_PXRC is not set +# CONFIG_JOYSTICK_QWIIC is not set +# CONFIG_JOYSTICK_FSIA6B is not set +# CONFIG_JOYSTICK_SENSEHAT is not set +# CONFIG_JOYSTICK_SEESAW is not set # CONFIG_INPUT_TABLET is not set CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_NT36523_SPI=m @@ -2830,7 +2860,7 @@ CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_ONCELL=m # CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set # CONFIG_TOUCHSCREEN_INEXIO is not set # CONFIG_TOUCHSCREEN_PENMOUNT is not set -# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set +CONFIG_TOUCHSCREEN_EDT_FT5X06=m # CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set # CONFIG_TOUCHSCREEN_TOUCHWIN is not set # CONFIG_TOUCHSCREEN_PIXCIR is not set @@ -2864,6 +2894,7 @@ CONFIG_INPUT_MISC=y # CONFIG_INPUT_E3X0_BUTTON is not set CONFIG_INPUT_PM8941_PWRKEY=y CONFIG_INPUT_PM8XXX_VIBRATOR=m +CONFIG_INPUT_QCOM_SPMI_HAPTICS=m # CONFIG_INPUT_MMA8450 is not set # CONFIG_INPUT_GPIO_BEEPER is not set # CONFIG_INPUT_GPIO_DECODER is not set @@ -2944,9 +2975,10 @@ CONFIG_LDISC_AUTOLOAD=y # CONFIG_SERIAL_UARTLITE is not set CONFIG_SERIAL_CORE=y # CONFIG_SERIAL_JSM is not set -# CONFIG_SERIAL_MSM is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y CONFIG_SERIAL_QCOM_GENI=y -# CONFIG_SERIAL_QCOM_GENI_CONSOLE is not set +CONFIG_SERIAL_QCOM_GENI_CONSOLE=y # CONFIG_SERIAL_SIFIVE is not set # CONFIG_SERIAL_SCCNXP is not set # CONFIG_SERIAL_SC16IS7XX is not set @@ -3455,6 +3487,8 @@ CONFIG_BATTERY_BQ27XXX_I2C=y # CONFIG_CHARGER_BD99954 is not set # CONFIG_BATTERY_UG3105 is not set # CONFIG_CHARGER_QCOM_SMB2 is not set +CONFIG_CHARGER_QCOM_SMB5=m +CONFIG_BATTERY_QCOM_FG=m # CONFIG_FUEL_GAUGE_MM8013 is not set CONFIG_HWMON=y # CONFIG_HWMON_DEBUG_CHIP is not set @@ -4337,6 +4371,7 @@ CONFIG_DRM_PANEL=y # CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set # CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A is not set # CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set +CONFIG_DRM_PANEL_DDIC_CH13726A=m # CONFIG_DRM_PANEL_EBBG_FT8719 is not set # CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set # CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set @@ -4603,7 +4638,7 @@ CONFIG_DUMMY_CONSOLE_ROWS=25 CONFIG_FRAMEBUFFER_CONSOLE=y # CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y -# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y # CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set # end of Console display driver support @@ -4953,7 +4988,7 @@ CONFIG_SND_SOC_WCD938X_SDW=m # CONFIG_SND_SOC_WM8974 is not set # CONFIG_SND_SOC_WM8978 is not set # CONFIG_SND_SOC_WM8985 is not set -# CONFIG_SND_SOC_WSA881X is not set +CONFIG_SND_SOC_WSA881X=m # CONFIG_SND_SOC_WSA883X is not set # CONFIG_SND_SOC_WSA884X is not set # CONFIG_SND_SOC_ZL38060 is not set @@ -5463,10 +5498,15 @@ CONFIG_TYPEC=y CONFIG_TYPEC_TCPM=m CONFIG_TYPEC_TCPCI=m # CONFIG_TYPEC_RT1711H is not set +# CONFIG_TYPEC_MT6360 is not set # CONFIG_TYPEC_TCPCI_MAXIM is not set -# CONFIG_TYPEC_FUSB302 is not set +CONFIG_TYPEC_FUSB302=m CONFIG_TYPEC_QCOM_PMIC=m -# CONFIG_TYPEC_UCSI is not set +CONFIG_TYPEC_UCSI=m +CONFIG_UCSI_CCG=m +# CONFIG_UCSI_ACPI is not set +# CONFIG_UCSI_STM32G0 is not set +CONFIG_UCSI_PMIC_GLINK=m CONFIG_TYPEC_TPS6598X=m # CONFIG_TYPEC_ANX7411 is not set # CONFIG_TYPEC_RT1719 is not set @@ -5477,11 +5517,11 @@ CONFIG_TYPEC_HD3SS3220=m # # USB Type-C Multiplexer/DeMultiplexer Switch support # -# CONFIG_TYPEC_MUX_FSA4480 is not set -# CONFIG_TYPEC_MUX_GPIO_SBU is not set +CONFIG_TYPEC_MUX_FSA4480=m +CONFIG_TYPEC_MUX_GPIO_SBU=m # CONFIG_TYPEC_MUX_PI3USB30532 is not set # CONFIG_TYPEC_MUX_IT5205 is not set -# CONFIG_TYPEC_MUX_NB7VPQ904M is not set +CONFIG_TYPEC_MUX_NB7VPQ904M=m # CONFIG_TYPEC_MUX_PTN36502 is not set # CONFIG_TYPEC_MUX_WCD939X_USBSS is not set # end of USB Type-C Multiplexer/DeMultiplexer Switch support @@ -5489,11 +5529,72 @@ CONFIG_TYPEC_HD3SS3220=m # # USB Type-C Alternate Mode drivers # -# CONFIG_TYPEC_DP_ALTMODE is not set +CONFIG_TYPEC_DP_ALTMODE=m +# CONFIG_TYPEC_NVIDIA_ALTMODE is not set # end of USB Type-C Alternate Mode drivers CONFIG_USB_ROLE_SWITCH=y -# CONFIG_MMC is not set +CONFIG_MMC=y +CONFIG_PWRSEQ_EMMC=y +# CONFIG_PWRSEQ_SD8787 is not set +CONFIG_PWRSEQ_SIMPLE=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_QCOM_DML=y +# CONFIG_MMC_STM32_SDMMC is not set +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +# CONFIG_MMC_SDHCI_PCI is not set +# CONFIG_MMC_SDHCI_ACPI is not set +CONFIG_MMC_SDHCI_PLTFM=y +# CONFIG_MMC_SDHCI_OF_ARASAN is not set +# CONFIG_MMC_SDHCI_OF_AT91 is not set +# CONFIG_MMC_SDHCI_OF_ESDHC is not set +# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set +# CONFIG_MMC_SDHCI_OF_SPARX5 is not set +# CONFIG_MMC_SDHCI_CADENCE is not set +# CONFIG_MMC_SDHCI_ESDHC_IMX is not set +# CONFIG_MMC_SDHCI_TEGRA is not set +# CONFIG_MMC_SDHCI_S3C is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_F_SDH30 is not set +# CONFIG_MMC_SDHCI_MILBEAUT is not set +# CONFIG_MMC_SDHCI_IPROC is not set +# CONFIG_MMC_SDHCI_NPCM is not set +# CONFIG_MMC_MESON_GX is not set +# CONFIG_MMC_MESON_MX_SDIO is not set +CONFIG_MMC_SDHCI_MSM=y +# CONFIG_MMC_MXC is not set +# CONFIG_MMC_TIFM_SD is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_SDHCI_SPRD is not set +# CONFIG_MMC_SDHI is not set +# CONFIG_MMC_UNIPHIER is not set +# CONFIG_MMC_CB710 is not set +# CONFIG_MMC_VIA_SDMMC is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_SH_MMCIF is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MMC_USDHI6ROL0 is not set +# CONFIG_MMC_SUNXI is not set +CONFIG_MMC_CQHCI=y +# CONFIG_MMC_HSQ is not set +# CONFIG_MMC_TOSHIBA_PCI is not set +# CONFIG_MMC_BCM2835 is not set +# CONFIG_MMC_MTK is not set +# CONFIG_MMC_SDHCI_BRCMSTB is not set +# CONFIG_MMC_SDHCI_XENON is not set +# CONFIG_MMC_SDHCI_AM654 is not set +# CONFIG_MMC_OWL is not set CONFIG_SCSI_UFSHCD=y CONFIG_SCSI_UFS_BSG=y # CONFIG_SCSI_UFS_HWMON is not set @@ -5506,7 +5607,7 @@ CONFIG_SCSI_UFS_QCOM=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y # CONFIG_LEDS_CLASS_FLASH is not set -# CONFIG_LEDS_CLASS_MULTICOLOR is not set +CONFIG_LEDS_CLASS_MULTICOLOR=y # CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set # @@ -5519,6 +5620,7 @@ CONFIG_LEDS_CLASS=y # CONFIG_LEDS_BCM6358 is not set # CONFIG_LEDS_CR0014114 is not set # CONFIG_LEDS_EL15203000 is not set +CONFIG_LEDS_HTR3212=m # CONFIG_LEDS_LM3530 is not set # CONFIG_LEDS_LM3532 is not set # CONFIG_LEDS_LM3642 is not set @@ -5562,6 +5664,11 @@ CONFIG_LEDS_SYSCON=y # # RGB LED drivers # +CONFIG_LEDS_GROUP_MULTICOLOR=y +# CONFIG_LEDS_KTD202X is not set +# CONFIG_LEDS_NCP5623 is not set +CONFIG_LEDS_PWM_MULTICOLOR=y +CONFIG_LEDS_QCOM_LPG=y # # LED Triggers diff --git a/patch/kernel/archive/sm8250-6.11/0030_drm-msm-dpu1--don't-choke-on-disabling-the-writeba.patch b/patch/kernel/archive/sm8250-6.11/0030_drm-msm-dpu1--don't-choke-on-disabling-the-writeba.patch new file mode 100644 index 000000000000..4ab8620123d9 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.11/0030_drm-msm-dpu1--don't-choke-on-disabling-the-writeba.patch @@ -0,0 +1,40 @@ +From 5344b8b729fc379a54d6aa4fa1ae648395efa62c Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov +Date: Fri, 2 Aug 2024 22:47:33 +0300 +Subject: [PATCH] drm/msm/dpu1: don't choke on disabling the writeback + connector + +During suspend/resume process all connectors are explicitly disabled and +then reenabled. However resume fails because of the connector_status check: + +[ 1185.831970] [dpu error]connector not connected 3 + +It doesn't make sense to check for the Writeback connected status (and +other drivers don't perform such check), so drop the check. + +Fixes: 71174f362d67 ("drm/msm/dpu: move writeback's atomic_check to dpu_writeback.c") +Cc: stable@vger.kernel.org +Reported-by: Leonard Lausen +Closes: https://gitlab.freedesktop.org/drm/msm/-/issues/57 +Signed-off-by: Dmitry Baryshkov +--- + drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c +index 16f144cbc0c98..8ff496082902b 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c +@@ -42,9 +42,6 @@ static int dpu_wb_conn_atomic_check(struct drm_connector *connector, + if (!conn_state || !conn_state->connector) { + DPU_ERROR("invalid connector state\n"); + return -EINVAL; +- } else if (conn_state->connector->status != connector_status_connected) { +- DPU_ERROR("connector not connected %d\n", conn_state->connector->status); +- return -EINVAL; + } + + crtc = conn_state->crtc; +-- +2.34.1 + diff --git a/patch/kernel/archive/sm8250-6.11/0031_arm64--dts--qcom--sm8250--add-uart16-Signed-off-by.patch b/patch/kernel/archive/sm8250-6.11/0031_arm64--dts--qcom--sm8250--add-uart16-Signed-off-by.patch new file mode 100644 index 000000000000..601d6407f28d --- /dev/null +++ b/patch/kernel/archive/sm8250-6.11/0031_arm64--dts--qcom--sm8250--add-uart16-Signed-off-by.patch @@ -0,0 +1,53 @@ +From 275fcb4f35b6cffcaabde3f876b4453996a77ead Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 20 Nov 2024 22:58:09 +0700 +Subject: [PATCH] arm64: dts: qcom: sm8250: add uart16 Signed-off-by: Teguh + Sobirin + +--- + arch/arm64/boot/dts/qcom/sm8250.dtsi | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi +index 9d6c97d1fd9d6..eaf6cb8bdcb8f 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi +@@ -1155,6 +1155,23 @@ spi16: spi@888000 { + status = "disabled"; + }; + ++ uart16: serial@888000 { ++ compatible = "qcom,geni-uart"; ++ reg = <0 0x00888000 0 0x4000>; ++ clock-names = "se"; ++ clocks = <&gcc GCC_QUPV3_WRAP2_S2_CLK>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&qup_uart16_default>; ++ interrupts = ; ++ power-domains = <&rpmhpd RPMHPD_CX>; ++ operating-points-v2 = <&qup_opp_table>; ++ interconnects = <&qup_virt MASTER_QUP_CORE_2 0 &qup_virt SLAVE_QUP_CORE_2 0>, ++ <&gem_noc MASTER_AMPSS_M0 0 &config_noc SLAVE_QUP_2 0>; ++ interconnect-names = "qup-core", ++ "qup-config"; ++ status = "disabled"; ++ }; ++ + i2c17: i2c@88c000 { + compatible = "qcom,geni-i2c"; + reg = <0 0x0088c000 0 0x4000>; +@@ -5748,6 +5765,11 @@ qup_uart12_default: qup-uart12-default-state { + function = "qup12"; + }; + ++ qup_uart16_default: qup-uart16-default-state { ++ pins = "gpio50", "gpio51"; ++ function = "qup16"; ++ }; ++ + qup_uart17_default: qup-uart17-default-state { + pins = "gpio52", "gpio53", "gpio54", "gpio55"; + function = "qup17"; +-- +2.34.1 + diff --git a/patch/kernel/archive/sm8250-6.11/0032_power--supply--Driver-for-Qualcomm-SMB5-Signed-off.patch b/patch/kernel/archive/sm8250-6.11/0032_power--supply--Driver-for-Qualcomm-SMB5-Signed-off.patch new file mode 100644 index 000000000000..9fe70d09bec5 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.11/0032_power--supply--Driver-for-Qualcomm-SMB5-Signed-off.patch @@ -0,0 +1,997 @@ +From 8894f9ffb67033dcbfa54770c987e576e2ff82fb Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Sun, 24 Nov 2024 23:04:43 +0700 +Subject: [PATCH] power: supply: Driver for Qualcomm SMB5 Signed-off-by: Teguh + Sobirin + +--- + drivers/power/supply/Kconfig | 9 + + drivers/power/supply/Makefile | 1 + + drivers/power/supply/qcom_pm8150b_charger.c | 945 ++++++++++++++++++++ + 3 files changed, 955 insertions(+) + create mode 100644 drivers/power/supply/qcom_pm8150b_charger.c + +diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig +index bcfa63fb9f1e2..ec3f3d5b20c8f 100644 +--- a/drivers/power/supply/Kconfig ++++ b/drivers/power/supply/Kconfig +@@ -1008,6 +1008,15 @@ config CHARGER_QCOM_SMB2 + adds support for the SMB2 switch mode battery charger found + in PMI8998 and related PMICs. + ++config CHARGER_QCOM_SMB5 ++ tristate "Qualcomm PMI8998 PMIC charger driver" ++ depends on MFD_SPMI_PMIC ++ depends on IIO ++ help ++ Say Y here to enable the Qualcomm PMIC Charger driver. This ++ adds support for the SMB5 switch mode battery charger found ++ in PM8150B and related PMICs. ++ + config FUEL_GAUGE_MM8013 + tristate "Mitsumi MM8013 fuel gauge driver" + depends on I2C +diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile +index 8dcb415453171..9237f62701582 100644 +--- a/drivers/power/supply/Makefile ++++ b/drivers/power/supply/Makefile +@@ -116,4 +116,5 @@ obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o + obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o + obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o + obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_pmi8998_charger.o ++obj-$(CONFIG_CHARGER_QCOM_SMB5) += qcom_pm8150b_charger.o + obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o +diff --git a/drivers/power/supply/qcom_pm8150b_charger.c b/drivers/power/supply/qcom_pm8150b_charger.c +new file mode 100644 +index 0000000000000..48791e145345d +--- /dev/null ++++ b/drivers/power/supply/qcom_pm8150b_charger.c +@@ -0,0 +1,945 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024, Teguh Sobirin . ++ * ++ * This driver is for the switch-mode battery charger power delivery ++ * and boost hardware found in pm8150b and related PMICs. ++ * This work based on pmi8998 charger driver by ++ * Caleb Connolly ++ * Should be merged with the existing charger driver in the future. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* clang-format off */ ++#define BATTERY_CHARGER_STATUS_1 0x06 ++#define ICL_INCR_REQ_FOR_PRECHG_BIT BIT(7) ++#define ZERO_CHARGE_CURRENT_BIT BIT(6) ++#define STEP_CHARGING_STATUS_SHIFT 3 ++#define STEP_CHARGING_STATUS_MASK GENMASK(5, 3) ++#define BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0) ++ ++#define BATTERY_CHARGER_STATUS_2 0x07 ++#define DROP_IN_BATTERY_VOLTAGE_REFERENCE_BIT BIT(7) ++#define VBATT_LTET_RECHARGE_BIT BIT(6) ++#define VBATT_GTET_INHIBIT_BIT BIT(5) ++#define VBATT_GTET_FLOAT_VOLTAGE_BIT BIT(4) ++#define BATT_GT_FULL_ON_BIT BIT(3) ++#define CHARGER_ERROR_STATUS_SFT_EXPIRE_BIT BIT(2) ++#define CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(1) ++#define CHARGER_ERROR_STATUS_BAT_TERM_MISSING_BIT BIT(0) ++ ++#define BATTERY_CHARGER_STATUS_4 0x0A ++#define CHARGE_CURRENT_REFERENCE_MASK GENMASK(7, 0) ++ ++#define BATTERY_CHARGER_STATUS_7_REG 0x0D ++#define BAT_TEMP_STATUS_SOFT_LIMIT_MASK GENMASK(5, 4) ++#define BAT_TEMP_STATUS_HOT_SOFT_BIT BIT(5) ++#define BAT_TEMP_STATUS_COLD_SOFT_BIT BIT(4) ++#define BAT_TEMP_STATUS_HARD_LIMIT_MASK GENMASK(3, 2) ++#define BAT_TEMP_STATUS_TOO_HOT_BIT BIT(3) ++#define BAT_TEMP_STATUS_TOO_COLD_BIT BIT(2) ++#define BAT_TEMP_STATUS_TOO_HOT_AFP_BIT BIT(1) ++#define BAT_TEMP_STATUS_TOO_COLD_AFP_BIT BIT(0) ++ ++#define CHARGING_ENABLE_CMD 0x42 ++#define CHARGING_ENABLE_CMD_BIT BIT(0) ++#define CHARGING_ENABLE_POF_BIT BIT(1) ++ ++#define CHGR_CFG2 0x51 ++#define EN_FAVOR_IN_BIT BIT(5) ++#define BAT_OV_ECC_BIT BIT(4) ++#define I_TERM_BIT BIT(3) ++#define AUTO_RECHG_BIT BIT(2) ++#define SOC_BASED_RECHG_BIT BIT(1) ++#define CHARGER_INHIBIT_BIT BIT(0) ++ ++#define PRE_CHARGE_CURRENT_CFG 0x60 ++#define PRE_CHARGE_CURRENT_SETTING_MASK GENMASK(2, 0) ++ ++#define FAST_CHARGE_CURRENT_CFG 0x61 ++#define FAST_CHARGE_CURRENT_SETTING_MASK GENMASK(7, 0) ++ ++#define FLOAT_VOLTAGE_CFG 0x70 ++#define FLOAT_VOLTAGE_SETTING_MASK GENMASK(7, 0) ++ ++#define CHARGE_RCHG_SOC_THRESHOLD_CFG_REG 0x7D ++#define CHARGE_RCHG_SOC_THRESHOLD_CFG_MASK GENMASK(7, 0) ++ ++#define ICL_STATUS 0x107 ++#define ICL_INPUT_CURRENT_LIMIT_MASK GENMASK(7, 0) ++ ++#define AICL_STATUS 0x108 ++#define AICL_INPUT_CURRENT_LIMIT_MASK GENMASK(7, 0) ++ ++#define POWER_PATH_STATUS 0x10B ++#define P_PATH_INPUT_SS_DONE_BIT BIT(7) ++#define P_PATH_USBIN_SUSPEND_STS_BIT BIT(6) ++#define P_PATH_DCIN_SUSPEND_STS_BIT BIT(5) ++#define P_PATH_USE_USBIN_BIT BIT(4) ++#define P_PATH_USE_DCIN_BIT BIT(3) ++#define P_PATH_POWER_PATH_MASK GENMASK(2, 1) ++#define P_PATH_VALID_INPUT_POWER_SOURCE_STS_BIT BIT(0) ++ ++#define OTG_CFG 0x153 ++#define OTG_RESERVED_BIT BIT(7) ++#define FAST_ROLE_SWAP_START_OPTION_BIT BIT(6) ++#define DIS_OTG_ON_TSD_BIT BIT(5) ++#define OTG_CFG_4_BIT BIT(4) ++#define EN_SOC_BASED_OTG_UVLO_BIT BIT(3) ++#define ENABLE_OTG_IN_DEBUG_MODE_BIT BIT(2) ++#define OTG_EN_SRC_CFG_BIT BIT(1) ++#define OTG_HICCUP_CNTR_RST_TIMER_SEL_BIT BIT(0) ++ ++#define APSD_STATUS 0x307 ++#define APSD_STATUS_7_BIT BIT(7) ++#define HVDCP_CHECK_TIMEOUT_BIT BIT(6) ++#define SLOW_PLUGIN_TIMEOUT_BIT BIT(5) ++#define ENUMERATION_DONE_BIT BIT(4) ++#define VADP_CHANGE_DONE_AFTER_AUTH_BIT BIT(3) ++#define QC_AUTH_DONE_STATUS_BIT BIT(2) ++#define QC_CHARGER_BIT BIT(1) ++#define APSD_DTC_STATUS_DONE_BIT BIT(0) ++ ++#define APSD_RESULT_STATUS 0x308 ++#define APSD_RESULT_STATUS_7_BIT BIT(7) ++#define APSD_RESULT_STATUS_MASK GENMASK(6, 0) ++#define QC_3P0_BIT BIT(6) ++#define QC_2P0_BIT BIT(5) ++#define FLOAT_CHARGER_BIT BIT(4) ++#define DCP_CHARGER_BIT BIT(3) ++#define CDP_CHARGER_BIT BIT(2) ++#define OCP_CHARGER_BIT BIT(1) ++#define SDP_CHARGER_BIT BIT(0) ++ ++#define USBIN_INT_RT_STS_OFFSET 0x310 ++#define USBIN_PLUGIN_RT_STS_BIT BIT(4) ++ ++#define USBIN_CMD_IL 0x340 ++#define USBIN_SUSPEND_BIT BIT(0) ++ ++#define CMD_APSD 0x341 ++#define APSD_RERUN_BIT BIT(0) ++ ++#define CMD_ICL_OVERRIDE 0x342 ++#define ICL_OVERRIDE_BIT BIT(0) ++ ++#define TYPE_C_CFG 0x358 ++#define BC1P2_START_ON_CC_BIT BIT(7) ++ ++#define HVDCP_PULSE_COUNT_MAX 0x35b ++#define HVDCP_PULSE_COUNT_MAX_QC2_MASK GENMASK(7, 6) ++ ++#define USBIN_ADAPTER_ALLOW_CFG 0x360 ++#define USBIN_ADAPTER_ALLOW_MASK GENMASK(3, 0) ++ ++#define USBIN_OPTIONS_1_CFG 0x362 ++#define HVDCP_AUTH_ALG_EN_CFG_BIT BIT(6) ++#define HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT BIT(5) ++#define BC1P2_SRC_DETECT_BIT BIT(3) ++#define HVDCP_EN_BIT BIT(2) ++#define HVDCP_NO_AUTH_QC3_CFG_BIT BIT(1) ++ ++#define USBIN_LOAD_CFG 0x365 ++#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) ++#define USBIN_AICL_STEP_TIMING_SEL_MASK GENMASK(3, 2) ++#define USBIN_IN_COLLAPSE_GF_SEL_MASK GENMASK(1, 0) ++ ++#define USBIN_ICL_OPTIONS 0x366 ++#define CFG_USB3P0_SEL_BIT BIT(2) ++#define USB51_MODE_BIT BIT(1) ++#define USBIN_MODE_CHG_BIT BIT(0) ++ ++#define USBIN_CURRENT_LIMIT_CFG 0x370 ++#define USBIN_CURRENT_LIMIT_MASK GENMASK(7, 0) ++ ++#define USBIN_AICL_OPTIONS_CFG 0x380 ++#define SUSPEND_ON_COLLAPSE_USBIN_BIT BIT(7) ++#define USBIN_AICL_PERIODIC_RERUN_EN_BIT BIT(4) ++#define USBIN_AICL_ADC_EN_BIT BIT(3) ++#define USBIN_AICL_EN_BIT BIT(2) ++ ++#define USBIN_5V_AICL_THRESHOLD_CFG 0x381 ++#define USBIN_5V_AICL_THRESHOLD_CFG_MASK GENMASK(2, 0) ++ ++#define USBIN_CONT_AICL_THRESHOLD_CFG 0x384 ++#define USBIN_CONT_AICL_THRESHOLD_CFG_MASK GENMASK(5, 0) ++ ++#define DCIN_CMD_IL 0x440 ++#define DCIN_SUSPEND_BIT BIT(0) ++ ++#define TYPE_C_SNK_STATUS 0x506 ++#define DETECTED_SRC_TYPE_MASK GENMASK(6, 0) ++#define SNK_RP_STD_DAM_BIT BIT(6) ++#define SNK_RP_1P5_DAM_BIT BIT(5) ++#define SNK_RP_3P0_DAM_BIT BIT(4) ++#define SNK_DAM_MASK GENMASK(6, 4) ++#define SNK_DAM_500MA_BIT BIT(6) ++#define SNK_DAM_1500MA_BIT BIT(5) ++#define SNK_DAM_3000MA_BIT BIT(4) ++#define SNK_RP_STD_BIT BIT(3) ++#define SNK_RP_1P5_BIT BIT(2) ++#define SNK_RP_3P0_BIT BIT(1) ++#define SNK_RP_SHORT_BIT BIT(0) ++ ++#define TYPE_C_MODE_CFG 0x544 ++#define TYPEC_TRY_MODE_MASK GENMASK(4, 3) ++#define EN_TRY_SNK_BIT BIT(4) ++#define EN_TRY_SRC_BIT BIT(3) ++#define TYPEC_POWER_ROLE_CMD_MASK GENMASK(2, 0) ++#define EN_SRC_ONLY_BIT BIT(2) ++#define EN_SNK_ONLY_BIT BIT(1) ++#define TYPEC_DISABLE_CMD_BIT BIT(0) ++ ++#define TYPEC_TYPE_C_VCONN_CONTROL 0x546 ++#define VCONN_EN_ORIENTATION_BIT BIT(2) ++#define VCONN_EN_VALUE_BIT BIT(1) ++#define VCONN_EN_SRC_BIT BIT(0) ++ ++#define TYPE_C_DEBUG_ACCESS_SINK 0x54a ++#define TYPEC_DEBUG_ACCESS_SINK_MASK GENMASK(4, 0) ++ ++#define DEBUG_ACCESS_SRC_CFG 0x54C ++#define EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT BIT(0) ++ ++#define TYPE_C_EXIT_STATE_CFG 0x550 ++#define BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT BIT(3) ++#define SEL_SRC_UPPER_REF_BIT BIT(2) ++#define EXIT_SNK_BASED_ON_CC_BIT BIT(0) ++ ++#define TYPE_C_INTERRUPT_EN_CFG_1 0x55e ++#define TYPEC_LEGACY_CABLE_INT_EN_BIT BIT(7) ++#define TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN_BIT BIT(6) ++#define TYPEC_TRYSOURCE_DETECT_INT_EN_BIT BIT(5) ++#define TYPEC_TRYSINK_DETECT_INT_EN_BIT BIT(4) ++#define TYPEC_CCOUT_DETACH_INT_EN_BIT BIT(3) ++#define TYPEC_CCOUT_ATTACH_INT_EN_BIT BIT(2) ++#define TYPEC_VBUS_DEASSERT_INT_EN_BIT BIT(1) ++#define TYPEC_VBUS_ASSERT_INT_EN_BIT BIT(0) ++ ++#define BARK_BITE_WDOG_PET 0x643 ++#define BARK_BITE_WDOG_PET_BIT BIT(0) ++ ++#define WD_CFG 0x651 ++#define WATCHDOG_TRIGGER_AFP_EN_BIT BIT(7) ++#define BARK_WDOG_INT_EN_BIT BIT(6) ++#define BITE_WDOG_INT_EN_BIT BIT(5) ++#define SFT_AFTER_WDOG_IRQ_MASK GENMASK(4, 3) ++#define WDOG_IRQ_SFT_BIT BIT(2) ++#define WDOG_TIMER_EN_ON_PLUGIN_BIT BIT(1) ++#define WDOG_TIMER_EN_BIT BIT(0) ++ ++#define SNARL_BARK_BITE_WD_CFG 0x653 ++#define BITE_WDOG_DISABLE_CHARGING_CFG_BIT BIT(7) ++#define SNARL_WDOG_TIMEOUT_MASK GENMASK(6, 4) ++#define BARK_WDOG_TIMEOUT_MASK GENMASK(3, 2) ++#define BITE_WDOG_TIMEOUT_MASK GENMASK(1, 0) ++ ++#define AICL_RERUN_TIME_CFG 0x661 ++#define AICL_RERUN_TIME_MASK GENMASK(1, 0) ++ ++#define SDP_CURRENT_UA 500000 ++#define CDP_CURRENT_UA 3000000 ++#define DCP_CURRENT_UA 3300000 ++#define CURRENT_MAX_UA DCP_CURRENT_UA ++ ++/* pmi8150b registers represent current in increments of 1/40th of an amp */ ++#define CURRENT_SCALE_FACTOR 50000 ++/* clang-format on */ ++ ++enum charger_status { ++ TRICKLE_CHARGE = 0, ++ PRE_CHARGE, ++ FAST_CHARGE, ++ FULLON_CHARGE, ++ TAPER_CHARGE, ++ TERMINATE_CHARGE, ++ INHIBIT_CHARGE, ++ DISABLE_CHARGE, ++}; ++ ++struct smb5_register { ++ u16 addr; ++ u8 mask; ++ u8 val; ++}; ++ ++/** ++ * struct smb5_chip - smb5 chip structure ++ * @dev: Device reference for power_supply ++ * @name: The platform device name ++ * @base: Base address for smb5 registers ++ * @regmap: Register map ++ * @batt_info: Battery data from DT ++ * @status_change_work: Worker to handle plug/unplug events ++ * @cable_irq: USB plugin IRQ ++ * @wakeup_enabled: If the cable IRQ will cause a wakeup ++ * @usb_in_i_chan: USB_IN current measurement channel ++ * @usb_in_v_chan: USB_IN voltage measurement channel ++ * @chg_psy: Charger power supply instance ++ */ ++struct smb5_chip { ++ struct device *dev; ++ const char *name; ++ unsigned int base; ++ struct regmap *regmap; ++ struct power_supply_battery_info *batt_info; ++ ++ struct delayed_work status_change_work; ++ int cable_irq; ++ bool wakeup_enabled; ++ ++ struct iio_channel *usb_in_i_chan; ++ struct iio_channel *usb_in_v_chan; ++ ++ struct power_supply *chg_psy; ++}; ++ ++static enum power_supply_property smb5_properties[] = { ++ POWER_SUPPLY_PROP_MANUFACTURER, ++ POWER_SUPPLY_PROP_MODEL_NAME, ++ POWER_SUPPLY_PROP_CURRENT_MAX, ++ POWER_SUPPLY_PROP_CURRENT_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_ONLINE, ++ POWER_SUPPLY_PROP_USB_TYPE, ++}; ++ ++static enum power_supply_usb_type smb5_usb_types[] = { ++ POWER_SUPPLY_USB_TYPE_UNKNOWN, ++ POWER_SUPPLY_USB_TYPE_SDP, ++ POWER_SUPPLY_USB_TYPE_DCP, ++ POWER_SUPPLY_USB_TYPE_CDP, ++}; ++ ++static int smb5_get_prop_usb_online(struct smb5_chip *chip, int *val) ++{ ++ unsigned int stat; ++ int rc; ++ ++ rc = regmap_read(chip->regmap, chip->base + USBIN_INT_RT_STS_OFFSET, &stat); ++ if (rc < 0) { ++ dev_err(chip->dev, "Couldn't read USBIN_RT_STS rc=%d\n", rc); ++ return rc; ++ } ++ ++ *val = (stat & USBIN_PLUGIN_RT_STS_BIT); ++ return 0; ++} ++ ++/* ++ * Qualcomm "automatic power source detection" aka APSD ++ * tells us what type of charger we're connected to. ++ */ ++static int smb5_apsd_get_charger_type(struct smb5_chip *chip, int *val) ++{ ++ unsigned int apsd_stat, stat; ++ int usb_online = 0; ++ int rc; ++ ++ rc = smb5_get_prop_usb_online(chip, &usb_online); ++ if (!usb_online) { ++ *val = POWER_SUPPLY_USB_TYPE_UNKNOWN; ++ return rc; ++ } ++ ++ rc = regmap_read(chip->regmap, chip->base + APSD_STATUS, &apsd_stat); ++ if (rc < 0) { ++ dev_err(chip->dev, "Failed to read apsd status, rc = %d", rc); ++ return rc; ++ } ++ if (!(apsd_stat & APSD_DTC_STATUS_DONE_BIT)) { ++ dev_err(chip->dev, "Apsd not ready"); ++ return -EAGAIN; ++ } ++ ++ rc = regmap_read(chip->regmap, chip->base + APSD_RESULT_STATUS, &stat); ++ if (rc < 0) { ++ dev_err(chip->dev, "Failed to read apsd result, rc = %d", rc); ++ return rc; ++ } ++ ++ stat &= APSD_RESULT_STATUS_MASK; ++ ++ if (stat & CDP_CHARGER_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_CDP; ++ } else if (stat & DCP_CHARGER_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_DCP; ++ } else if (stat & OCP_CHARGER_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_DCP; ++ } else if (stat & FLOAT_CHARGER_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_DCP; ++ } else if (stat & QC_2P0_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_DCP; ++ } else if (stat & QC_3P0_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_DCP; ++ } else { ++ *val = POWER_SUPPLY_USB_TYPE_SDP; ++ } ++ ++ return 0; ++} ++ ++static int smb5_get_prop_status(struct smb5_chip *chip, int *val) ++{ ++ unsigned char stat[2]; ++ int usb_online = 0; ++ int rc; ++ ++ rc = smb5_get_prop_usb_online(chip, &usb_online); ++ if (!usb_online) { ++ *val = POWER_SUPPLY_STATUS_DISCHARGING; ++ return rc; ++ } ++ ++ rc = regmap_bulk_read(chip->regmap, ++ chip->base + BATTERY_CHARGER_STATUS_1, &stat, 2); ++ if (rc < 0) { ++ dev_err(chip->dev, "Failed to read charging status ret=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (stat[1] & VBATT_GTET_INHIBIT_BIT) { ++ *val = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ return 0; ++ } ++ ++ stat[0] = stat[0] & BATTERY_CHARGER_STATUS_MASK; ++ ++ switch (stat[0]) { ++ case TRICKLE_CHARGE: ++ case PRE_CHARGE: ++ case FAST_CHARGE: ++ case FULLON_CHARGE: ++ case TAPER_CHARGE: ++ *val = POWER_SUPPLY_STATUS_CHARGING; ++ return rc; ++ case DISABLE_CHARGE: ++ *val = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ return rc; ++ case TERMINATE_CHARGE: ++ case INHIBIT_CHARGE: ++ *val = POWER_SUPPLY_STATUS_FULL; ++ return rc; ++ default: ++ *val = POWER_SUPPLY_STATUS_UNKNOWN; ++ return rc; ++ } ++} ++ ++static inline int smb5_get_current_limit(struct smb5_chip *chip, ++ unsigned int *val) ++{ ++ int rc = regmap_read(chip->regmap, chip->base + AICL_STATUS, val); ++ ++ if (rc >= 0) ++ *val *= CURRENT_SCALE_FACTOR; ++ return rc; ++} ++ ++static int smb5_set_current_limit(struct smb5_chip *chip, unsigned int val) ++{ ++ unsigned char val_raw; ++ ++ if (val > 4950000) { ++ dev_err(chip->dev, ++ "Can't set current limit higher than 4950000uA"); ++ return -EINVAL; ++ } ++ val_raw = val / CURRENT_SCALE_FACTOR; ++ ++ return regmap_write(chip->regmap, chip->base + USBIN_CURRENT_LIMIT_CFG, ++ val_raw); ++} ++ ++static void smb5_status_change_work(struct work_struct *work) ++{ ++ unsigned int charger_type, current_ua; ++ int usb_online = 0; ++ int count, rc; ++ struct smb5_chip *chip; ++ ++ chip = container_of(work, struct smb5_chip, status_change_work.work); ++ ++ smb5_get_prop_usb_online(chip, &usb_online); ++ if (!usb_online) ++ return; ++ ++ for (count = 0; count < 3; count++) { ++ dev_dbg(chip->dev, "get charger type retry %d\n", count); ++ rc = smb5_apsd_get_charger_type(chip, &charger_type); ++ if (rc != -EAGAIN) ++ break; ++ msleep(100); ++ } ++ ++ if (rc < 0 && rc != -EAGAIN) { ++ dev_err(chip->dev, "get charger type failed: %d\n", rc); ++ return; ++ } ++ ++ if (rc < 0) { ++ rc = regmap_update_bits(chip->regmap, chip->base + CMD_APSD, ++ APSD_RERUN_BIT, APSD_RERUN_BIT); ++ schedule_delayed_work(&chip->status_change_work, ++ msecs_to_jiffies(1000)); ++ dev_dbg(chip->dev, "get charger type failed, rerun apsd\n"); ++ return; ++ } ++ ++ switch (charger_type) { ++ case POWER_SUPPLY_USB_TYPE_CDP: ++ current_ua = CDP_CURRENT_UA; ++ break; ++ case POWER_SUPPLY_USB_TYPE_DCP: ++ current_ua = DCP_CURRENT_UA; ++ break; ++ case POWER_SUPPLY_USB_TYPE_SDP: ++ default: ++ current_ua = SDP_CURRENT_UA; ++ break; ++ } ++ ++ smb5_set_current_limit(chip, current_ua); ++ power_supply_changed(chip->chg_psy); ++} ++ ++static int smb5_get_iio_chan(struct smb5_chip *chip, struct iio_channel *chan, ++ int *val) ++{ ++ int rc; ++ union power_supply_propval status; ++ ++ rc = power_supply_get_property(chip->chg_psy, POWER_SUPPLY_PROP_STATUS, ++ &status); ++ if (rc < 0 || status.intval != POWER_SUPPLY_STATUS_CHARGING) { ++ *val = 0; ++ return 0; ++ } ++ ++ if (IS_ERR(chan)) { ++ dev_err(chip->dev, "Failed to chan, err = %li", PTR_ERR(chan)); ++ return PTR_ERR(chan); ++ } ++ ++ return iio_read_channel_processed(chan, val); ++} ++ ++static int smb5_get_prop_health(struct smb5_chip *chip, int *val) ++{ ++ int rc; ++ unsigned int stat; ++ ++ rc = regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2, ++ &stat); ++ if (rc < 0) { ++ dev_err(chip->dev, "Couldn't read charger status rc=%d\n", rc); ++ return rc; ++ } ++ ++ if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT) { ++ dev_err(chip->dev, "battery over-voltage"); ++ } ++ ++ rc = regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_7_REG, ++ &stat); ++ if (rc < 0) { ++ dev_err(chip->dev, "Couldn't read charger status 7 rc=%d\n", rc); ++ return rc; ++ } ++ ++ if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT) ++ *val = POWER_SUPPLY_HEALTH_COLD; ++ else if (stat & BAT_TEMP_STATUS_TOO_HOT_BIT) ++ *val = POWER_SUPPLY_HEALTH_OVERHEAT; ++ else if (stat & BAT_TEMP_STATUS_COLD_SOFT_BIT) ++ *val = POWER_SUPPLY_HEALTH_COOL; ++ else if (stat & BAT_TEMP_STATUS_HOT_SOFT_BIT) ++ *val = POWER_SUPPLY_HEALTH_WARM; ++ else ++ *val = POWER_SUPPLY_HEALTH_GOOD; ++ ++ return 0; ++} ++ ++static int smb5_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct smb5_chip *chip = power_supply_get_drvdata(psy); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_MANUFACTURER: ++ val->strval = "Qualcomm"; ++ return 0; ++ case POWER_SUPPLY_PROP_MODEL_NAME: ++ val->strval = chip->name; ++ return 0; ++ case POWER_SUPPLY_PROP_CURRENT_MAX: ++ return smb5_get_current_limit(chip, &val->intval); ++ case POWER_SUPPLY_PROP_CURRENT_NOW: ++ return smb5_get_iio_chan(chip, chip->usb_in_i_chan, ++ &val->intval); ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ return smb5_get_iio_chan(chip, chip->usb_in_v_chan, ++ &val->intval); ++ case POWER_SUPPLY_PROP_ONLINE: ++ return smb5_get_prop_usb_online(chip, &val->intval); ++ case POWER_SUPPLY_PROP_STATUS: ++ return smb5_get_prop_status(chip, &val->intval); ++ case POWER_SUPPLY_PROP_HEALTH: ++ return smb5_get_prop_health(chip, &val->intval); ++ case POWER_SUPPLY_PROP_USB_TYPE: ++ return smb5_apsd_get_charger_type(chip, &val->intval); ++ default: ++ dev_err(chip->dev, "invalid property: %d\n", psp); ++ return -EINVAL; ++ } ++} ++ ++static int smb5_set_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ const union power_supply_propval *val) ++{ ++ struct smb5_chip *chip = power_supply_get_drvdata(psy); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_CURRENT_MAX: ++ return smb5_set_current_limit(chip, val->intval); ++ default: ++ dev_err(chip->dev, "No setter for property: %d\n", psp); ++ return -EINVAL; ++ } ++} ++ ++static int smb5_property_is_writable(struct power_supply *psy, ++ enum power_supply_property psp) ++{ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_CURRENT_MAX: ++ return 1; ++ default: ++ return 0; ++ } ++} ++ ++static irqreturn_t smb5_handle_batt_overvoltage(int irq, void *data) ++{ ++ struct smb5_chip *chip = data; ++ unsigned int status; ++ ++ regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2, ++ &status); ++ ++ if (status & CHARGER_ERROR_STATUS_BAT_OV_BIT) { ++ /* The hardware stops charging automatically */ ++ dev_err(chip->dev, "battery overvoltage detected\n"); ++ power_supply_changed(chip->chg_psy); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t smb5_handle_usb_plugin(int irq, void *data) ++{ ++ struct smb5_chip *chip = data; ++ ++ power_supply_changed(chip->chg_psy); ++ ++ schedule_delayed_work(&chip->status_change_work, ++ msecs_to_jiffies(1500)); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t smb5_handle_usb_icl_change(int irq, void *data) ++{ ++ struct smb5_chip *chip = data; ++ ++ power_supply_changed(chip->chg_psy); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t smb5_handle_wdog_bark(int irq, void *data) ++{ ++ struct smb5_chip *chip = data; ++ int rc; ++ ++ power_supply_changed(chip->chg_psy); ++ ++ rc = regmap_write(chip->regmap, BARK_BITE_WDOG_PET, ++ BARK_BITE_WDOG_PET_BIT); ++ if (rc < 0) ++ dev_err(chip->dev, "Couldn't pet the dog rc=%d\n", rc); ++ ++ return IRQ_HANDLED; ++} ++ ++static const struct power_supply_desc smb5_psy_desc = { ++ .name = "pmi8998_charger", ++ .type = POWER_SUPPLY_TYPE_USB, ++ .usb_types = smb5_usb_types, ++ .num_usb_types = ARRAY_SIZE(smb5_usb_types), ++ .properties = smb5_properties, ++ .num_properties = ARRAY_SIZE(smb5_properties), ++ .get_property = smb5_get_property, ++ .set_property = smb5_set_property, ++ .property_is_writeable = smb5_property_is_writable, ++}; ++ ++/* Init sequence derived from vendor downstream driver */ ++static const struct smb5_register smb5_init_seq[] = { ++ { .addr = USBIN_CMD_IL, .mask = USBIN_SUSPEND_BIT, .val = 0 }, ++ { .addr = AICL_RERUN_TIME_CFG, .mask = AICL_RERUN_TIME_MASK, .val = 0 }, ++ /* ++ * By default configure us as an upstream facing port ++ * FIXME: This will be handled by the type-c driver ++ */ ++ { .addr = TYPE_C_MODE_CFG, ++ .mask = EN_TRY_SNK_BIT | EN_SNK_ONLY_BIT, ++ .val = EN_TRY_SNK_BIT }, ++ { .addr = TYPEC_TYPE_C_VCONN_CONTROL, ++ .mask = VCONN_EN_ORIENTATION_BIT | VCONN_EN_SRC_BIT | ++ VCONN_EN_VALUE_BIT, ++ .val = VCONN_EN_SRC_BIT }, ++ { .addr = DEBUG_ACCESS_SRC_CFG, ++ .mask = EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT, ++ .val = EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT }, ++ { .addr = TYPE_C_EXIT_STATE_CFG, ++ .mask = SEL_SRC_UPPER_REF_BIT, ++ .val = SEL_SRC_UPPER_REF_BIT }, ++ /* ++ * Disable Type-C factory mode and stay in Attached.SRC state when VCONN ++ * over-current happens ++ */ ++ { .addr = TYPE_C_CFG, ++ .mask = BC1P2_START_ON_CC_BIT, ++ .val = 0 }, ++ { .addr = TYPE_C_DEBUG_ACCESS_SINK, ++ .mask = TYPEC_DEBUG_ACCESS_SINK_MASK, ++ .val = 0x17 }, ++ /* Configure VBUS for software control */ ++ { .addr = OTG_CFG, .mask = OTG_EN_SRC_CFG_BIT, .val = 0 }, ++ /* ++ * Use VBAT to determine the recharge threshold when battery is full ++ * rather than the state of charge. ++ */ ++ { .addr = CHARGE_RCHG_SOC_THRESHOLD_CFG_REG, ++ .mask = CHARGE_RCHG_SOC_THRESHOLD_CFG_MASK, ++ .val = 98 }, ++ /* Enable charging */ ++ { .addr = CHARGING_ENABLE_CMD, ++ .mask = CHARGING_ENABLE_CMD_BIT, ++ .val = CHARGING_ENABLE_CMD_BIT }, ++ /* Enable BC1P2 Src detect */ ++ { .addr = USBIN_OPTIONS_1_CFG, ++ .mask = BC1P2_SRC_DETECT_BIT, ++ .val = BC1P2_SRC_DETECT_BIT }, ++ /* Set the default SDP charger type to a 500ma USB 2.0 port */ ++ { .addr = USBIN_ICL_OPTIONS, ++ .mask = USBIN_MODE_CHG_BIT, ++ .val = USBIN_MODE_CHG_BIT }, ++ { .addr = CMD_ICL_OVERRIDE, ++ .mask = ICL_OVERRIDE_BIT, ++ .val = 0 }, ++ { .addr = USBIN_LOAD_CFG, ++ .mask = ICL_OVERRIDE_AFTER_APSD_BIT, ++ .val = 0 }, ++ /* Disable watchdog */ ++ { .addr = SNARL_BARK_BITE_WD_CFG, .mask = 0xff, .val = 0 }, ++ { .addr = WD_CFG, ++ .mask = WATCHDOG_TRIGGER_AFP_EN_BIT | WDOG_TIMER_EN_ON_PLUGIN_BIT | ++ BARK_WDOG_INT_EN_BIT, ++ .val = 0 }, ++ /* ++ * Enable Automatic Input Current Limit, this will slowly ramp up the current ++ * When connected to a wall charger, and automatically stop when it detects ++ * the charger current limit (voltage drop?) or it reaches the programmed limit. ++ */ ++ { .addr = USBIN_AICL_OPTIONS_CFG, ++ .mask = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_ADC_EN_BIT ++ | USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT, ++ .val = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_ADC_EN_BIT ++ | USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT }, ++}; ++ ++static int smb5_init_hw(struct smb5_chip *chip) ++{ ++ int rc, i; ++ for (i = 0; i < ARRAY_SIZE(smb5_init_seq); i++) { ++ dev_dbg(chip->dev, "%d: Writing 0x%02x to 0x%02x\n", i, ++ smb5_init_seq[i].val, smb5_init_seq[i].addr); ++ rc = regmap_update_bits(chip->regmap, ++ chip->base + smb5_init_seq[i].addr, ++ smb5_init_seq[i].mask, ++ smb5_init_seq[i].val); ++ if (rc < 0) ++ return dev_err_probe(chip->dev, rc, ++ "%s: init command %d failed\n", ++ __func__, i); ++ } ++ return 0; ++} ++ ++static int smb5_init_irq(struct smb5_chip *chip, int *irq, const char *name, ++ irqreturn_t (*handler)(int irq, void *data)) ++{ ++ int irqnum; ++ int rc; ++ ++ irqnum = platform_get_irq_byname(to_platform_device(chip->dev), name); ++ if (irqnum < 0) ++ return irqnum; ++ ++ rc = devm_request_threaded_irq(chip->dev, irqnum, NULL, handler, ++ IRQF_ONESHOT, name, chip); ++ if (rc < 0) ++ return dev_err_probe(chip->dev, rc, "Couldn't request irq %s\n", ++ name); ++ ++ if (irq) ++ *irq = irqnum; ++ ++ return 0; ++} ++ ++static int smb5_probe(struct platform_device *pdev) ++{ ++ struct power_supply_config supply_config = {}; ++ struct power_supply_desc *desc; ++ struct smb5_chip *chip; ++ int rc, irq; ++ ++ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ chip->dev = &pdev->dev; ++ chip->name = pdev->name; ++ ++ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); ++ if (!chip->regmap) ++ return dev_err_probe(chip->dev, -ENODEV, ++ "failed to locate the regmap\n"); ++ ++ rc = device_property_read_u32(chip->dev, "reg", &chip->base); ++ if (rc < 0) ++ return dev_err_probe(chip->dev, rc, ++ "Couldn't read base address\n"); ++ ++ chip->usb_in_v_chan = devm_iio_channel_get(chip->dev, "usb_in_v_div_16"); ++ if (IS_ERR(chip->usb_in_v_chan)) ++ return dev_err_probe(chip->dev, PTR_ERR(chip->usb_in_v_chan), ++ "Couldn't get usb_in_v_div_16 IIO channel\n"); ++ ++ chip->usb_in_i_chan = devm_iio_channel_get(chip->dev, "usb_in_i_uv"); ++ if (IS_ERR(chip->usb_in_i_chan)) { ++ return dev_err_probe(chip->dev, PTR_ERR(chip->usb_in_i_chan), ++ "Couldn't get usb_in_i_uv IIO channel\n"); ++ } ++ ++ rc = smb5_init_hw(chip); ++ if (rc < 0) ++ return rc; ++ ++ supply_config.drv_data = chip; ++ supply_config.of_node = pdev->dev.of_node; ++ ++ desc = devm_kzalloc(chip->dev, sizeof(smb5_psy_desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ memcpy(desc, &smb5_psy_desc, sizeof(smb5_psy_desc)); ++ desc->name = ++ devm_kasprintf(chip->dev, GFP_KERNEL, "%s-charger", ++ (const char *)device_get_match_data(chip->dev)); ++ if (!desc->name) ++ return -ENOMEM; ++ ++ chip->chg_psy = ++ devm_power_supply_register(chip->dev, desc, &supply_config); ++ if (IS_ERR(chip->chg_psy)) ++ return dev_err_probe(chip->dev, PTR_ERR(chip->chg_psy), ++ "failed to register power supply\n"); ++ ++ rc = power_supply_get_battery_info(chip->chg_psy, &chip->batt_info); ++ if (rc) ++ return dev_err_probe(chip->dev, rc, ++ "Failed to get battery info\n"); ++ ++ rc = devm_delayed_work_autocancel(chip->dev, &chip->status_change_work, ++ smb5_status_change_work); ++ if (rc) ++ return dev_err_probe(chip->dev, rc, ++ "Failed to init status change work\n"); ++ ++ rc = (chip->batt_info->voltage_max_design_uv - 3487500) / 7500 + 1; ++ rc = regmap_update_bits(chip->regmap, chip->base + FLOAT_VOLTAGE_CFG, ++ FLOAT_VOLTAGE_SETTING_MASK, rc); ++ if (rc < 0) ++ return dev_err_probe(chip->dev, rc, "Couldn't set vbat max\n"); ++ ++ rc = smb5_init_irq(chip, &irq, "bat-ov", smb5_handle_batt_overvoltage); ++ if (rc < 0) ++ return rc; ++ ++ rc = smb5_init_irq(chip, &chip->cable_irq, "usbin-plugin", ++ smb5_handle_usb_plugin); ++ if (rc < 0) ++ return rc; ++ ++ rc = smb5_init_irq(chip, &irq, "usbin-icl-change", ++ smb5_handle_usb_icl_change); ++ if (rc < 0) ++ return rc; ++ rc = smb5_init_irq(chip, &irq, "wdog-bark", smb5_handle_wdog_bark); ++ if (rc < 0) ++ return rc; ++ ++ rc = dev_pm_set_wake_irq(chip->dev, chip->cable_irq); ++ if (rc < 0) ++ return dev_err_probe(chip->dev, rc, "Couldn't set wake irq\n"); ++ ++ platform_set_drvdata(pdev, chip); ++ ++ /* Initialise charger state */ ++ schedule_delayed_work(&chip->status_change_work, 0); ++ ++ return 0; ++} ++ ++static const struct of_device_id smb5_match_id_table[] = { ++ { .compatible = "qcom,pm8150b-charger", .data = "pm8150b" }, ++ { /* sentinal */ } ++}; ++MODULE_DEVICE_TABLE(of, smb5_match_id_table); ++ ++static struct platform_driver qcom_spmi_smb5 = { ++ .probe = smb5_probe, ++ .driver = { ++ .name = "qcom-pm8150b-charger", ++ .of_match_table = smb5_match_id_table, ++ }, ++}; ++ ++module_platform_driver(qcom_spmi_smb5); ++ ++MODULE_AUTHOR("Caleb Connolly "); ++MODULE_DESCRIPTION("Qualcomm SMB5 Charger Driver"); ++MODULE_LICENSE("GPL"); +-- +2.34.1 + diff --git a/patch/kernel/archive/sm8250-6.11/0033_power--supply--Driver-for-Qualcomm-FG.patch b/patch/kernel/archive/sm8250-6.11/0033_power--supply--Driver-for-Qualcomm-FG.patch new file mode 100644 index 000000000000..190a8e0cca53 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.11/0033_power--supply--Driver-for-Qualcomm-FG.patch @@ -0,0 +1,1418 @@ +From 98c6370f7380874d5b78871b3d24d92b74b4bdcb Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 20 Nov 2024 23:00:52 +0700 +Subject: [PATCH] power: supply: Driver for Qualcomm FG + +support for pm8150 taken from: +https://github.com/map220v/sm8150-mainline +Signed-off-by: Teguh Sobirin +--- + drivers/power/supply/Kconfig | 8 + + drivers/power/supply/Makefile | 1 + + drivers/power/supply/qcom_fg.c | 1364 ++++++++++++++++++++++++++++++++ + 3 files changed, 1373 insertions(+) + create mode 100644 drivers/power/supply/qcom_fg.c + +diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig +index ec3f3d5b20c8f..b1e56c53f6248 100644 +--- a/drivers/power/supply/Kconfig ++++ b/drivers/power/supply/Kconfig +@@ -1017,6 +1017,14 @@ config CHARGER_QCOM_SMB5 + adds support for the SMB5 switch mode battery charger found + in PM8150B and related PMICs. + ++config BATTERY_QCOM_FG ++ tristate "Qualcomm PMIC fuel gauge driver" ++ depends on MFD_SPMI_PMIC ++ help ++ Say Y here to enable the Qualcomm PMIC Fuel Gauge driver. This ++ adds support for battery fuel gauging and state of charge of ++ battery connected to the fuel gauge. ++ + config FUEL_GAUGE_MM8013 + tristate "Mitsumi MM8013 fuel gauge driver" + depends on I2C +diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile +index 9237f62701582..52a75dae57816 100644 +--- a/drivers/power/supply/Makefile ++++ b/drivers/power/supply/Makefile +@@ -117,4 +117,5 @@ obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o + obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o + obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_pmi8998_charger.o + obj-$(CONFIG_CHARGER_QCOM_SMB5) += qcom_pm8150b_charger.o ++obj-$(CONFIG_BATTERY_QCOM_FG) += qcom_fg.o + obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o +diff --git a/drivers/power/supply/qcom_fg.c b/drivers/power/supply/qcom_fg.c +new file mode 100644 +index 0000000000000..a3b164994fe4e +--- /dev/null ++++ b/drivers/power/supply/qcom_fg.c +@@ -0,0 +1,1364 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* SOC */ ++#define BATT_MONOTONIC_SOC 0x009 ++ ++/* BATT */ ++#define PARAM_ADDR_BATT_TEMP 0x150 ++#define BATT_INFO_JEITA_COLD 0x162 ++#define BATT_INFO_JEITA_COOL 0x163 ++#define BATT_INFO_JEITA_WARM 0x164 ++#define BATT_INFO_JEITA_HOT 0x165 ++#define PARAM_ADDR_BATT_VOLTAGE 0x1a0 ++#define PARAM_ADDR_BATT_CURRENT 0x1a2 ++ ++/* RRADC */ ++#define ADC_RR_BATT_TEMP_LSB 0x288 ++ ++/* MEMIF */ ++#define MEM_INTF_STS(chip) (chip->ops->memif_base + 0x10) ++#define MEM_INTF_CFG(chip) (chip->ops->memif_base + 0x50) ++#define MEM_INTF_CTL(chip) (chip->ops->memif_base + 0x51) ++#define MEM_INTF_IMA_CFG(chip) (chip->ops->memif_base + 0x52) ++#define MEM_INTF_IMA_EXP_STS(chip) (chip->ops->memif_base + 0x55) ++#define MEM_INTF_IMA_HW_STS(chip) (chip->ops->memif_base + 0x56) ++#define MEM_INTF_IMA_ERR_STS(chip) (chip->ops->memif_base + 0x5f) ++#define MEM_INTF_IMA_BYTE_EN(chip) (chip->ops->memif_base + 0x60) ++#define MEM_INTF_ADDR_LSB(chip) (chip->ops->memif_base + 0x61) ++#define MEM_INTF_RD_DATA0(chip) (chip->ops->memif_base + 0x67) ++#define MEM_INTF_WR_DATA0(chip) (chip->ops->memif_base + 0x63) ++#define MEM_IF_DMA_STS(chip) (chip->ops->memif_base + 0x70) ++#define MEM_IF_DMA_CTL(chip) (chip->ops->memif_base + 0x71) ++ ++/* SRAM addresses */ ++#define TEMP_THRESHOLD 0x454 ++#define BATT_TEMP 0x550 ++#define BATT_VOLTAGE_CURRENT 0x5cc ++ ++#define BATT_TEMP_LSB_MASK GENMASK(7, 0) ++#define BATT_TEMP_MSB_MASK GENMASK(2, 0) ++ ++#define BATT_TEMP_JEITA_COLD 100 ++#define BATT_TEMP_JEITA_COOL 50 ++#define BATT_TEMP_JEITA_WARM 400 ++#define BATT_TEMP_JEITA_HOT 450 ++ ++#define MEM_INTF_AVAIL BIT(0) ++#define MEM_INTF_CTL_BURST BIT(7) ++#define MEM_INTF_CTL_WR_EN BIT(6) ++#define RIF_MEM_ACCESS_REQ BIT(7) ++ ++#define MEM_IF_TIMEOUT_MS 5000 ++#define SRAM_ACCESS_RELEASE_DELAY_MS 500 ++ ++struct qcom_fg_chip; ++ ++struct qcom_fg_ops { ++ int (*get_capacity)(struct qcom_fg_chip *chip, int *); ++ int (*get_temperature)(struct qcom_fg_chip *chip, int *); ++ int (*get_current)(struct qcom_fg_chip *chip, int *); ++ int (*get_voltage)(struct qcom_fg_chip *chip, int *); ++ int (*get_temp_threshold)(struct qcom_fg_chip *chip, ++ enum power_supply_property psp, int *); ++ int (*set_temp_threshold)(struct qcom_fg_chip *chip, ++ enum power_supply_property psp, int); ++ ++ short memif_base; ++}; ++ ++struct qcom_fg_chip { ++ struct device *dev; ++ unsigned int base; ++ struct regmap *regmap; ++ const struct qcom_fg_ops *ops; ++ struct notifier_block nb; ++ ++ struct power_supply *batt_psy; ++ struct power_supply_battery_info *batt_info; ++ struct power_supply *chg_psy; ++ int status; ++ struct delayed_work status_changed_work; ++ ++ struct completion sram_access_granted; ++ struct completion sram_access_revoked; ++ struct workqueue_struct *sram_wq; ++ struct delayed_work sram_release_access_work; ++ spinlock_t sram_request_lock; ++ spinlock_t sram_rw_lock; ++ int sram_requests; ++}; ++ ++/************************ ++ * IO FUNCTIONS ++ * **********************/ ++ ++/** ++ * @brief qcom_fg_read() - Read multiple registers with regmap_bulk_read ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to read values into ++ * @param addr Address to read from ++ * @param len Number of registers (bytes) to read ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_read(struct qcom_fg_chip *chip, u8 *val, u16 addr, int len) ++{ ++ if (((chip->base + addr) & 0xff00) == 0) ++ return -EINVAL; ++ ++ dev_vdbg(chip->dev, "%s: Reading 0x%x bytes from 0x%x", __func__, len, addr); ++ ++ return regmap_bulk_read(chip->regmap, chip->base + addr, val, len); ++} ++ ++/** ++ * @brief qcom_fg_write() - Write multiple registers with regmap_bulk_write ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to write values from ++ * @param addr Address to write to ++ * @param len Number of registers (bytes) to write ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_write(struct qcom_fg_chip *chip, u8 *val, u16 addr, int len) ++{ ++ bool sec_access = (addr & 0xff) > 0xd0; ++ u8 sec_addr_val = 0xa5; ++ int ret; ++ ++ if (((chip->base + addr) & 0xff00) == 0) ++ return -EINVAL; ++ ++ dev_vdbg(chip->dev, "%s: Writing 0x%x to 0x%x", __func__, *val, addr); ++ ++ if (sec_access) { ++ ret = regmap_bulk_write(chip->regmap, ++ ((chip->base + addr) & 0xff00) | 0xd0, ++ &sec_addr_val, 1); ++ if (ret) ++ return ret; ++ } ++ ++ return regmap_bulk_write(chip->regmap, chip->base + addr, val, len); ++} ++ ++/** ++ * @brief qcom_fg_masked_write() - like qcom_fg_write but applies ++ * a mask first. ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to write values from ++ * @param addr Address to write to ++ * @param len Number of registers (bytes) to write ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_masked_write(struct qcom_fg_chip *chip, u16 addr, u8 mask, u8 val) ++{ ++ u8 reg; ++ int ret; ++ ++ ret = qcom_fg_read(chip, ®, addr, 1); ++ if (ret) ++ return ret; ++ ++ reg &= ~mask; ++ reg |= val & mask; ++ ++ return qcom_fg_write(chip, ®, addr, 1); ++} ++ ++/************************ ++ * SRAM FUNCTIONS ++ * **********************/ ++ ++/** ++ * @brief qcom_fg_sram_check_access() - Check if SRAM is accessible ++ * ++ * @param chip Pointer to chip ++ * @return bool true if accessible, false otherwise ++ */ ++static bool qcom_fg_sram_check_access(struct qcom_fg_chip *chip) ++{ ++ u8 mem_if_status; ++ int ret; ++ ++ ret = qcom_fg_read(chip, &mem_if_status, ++ MEM_INTF_STS(chip), 1); ++ ++ if (ret || !(mem_if_status & MEM_INTF_AVAIL)) ++ return false; ++ ++ ret = qcom_fg_read(chip, &mem_if_status, ++ MEM_INTF_CFG(chip), 1); ++ ++ if (ret) ++ return false; ++ ++ return !!(mem_if_status & RIF_MEM_ACCESS_REQ); ++} ++ ++/** ++ * @brief qcom_fg_sram_request_access() - Request access to SRAM and wait for it ++ * ++ * @param chip Pointer to chip ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_sram_request_access(struct qcom_fg_chip *chip) ++{ ++ bool sram_accessible; ++ int ret; ++ ++ spin_lock(&chip->sram_request_lock); ++ ++ sram_accessible = qcom_fg_sram_check_access(chip); ++ ++ dev_vdbg(chip->dev, "Requesting SRAM access, current state: %d, requests: %d\n", ++ sram_accessible, chip->sram_requests); ++ ++ if (!sram_accessible && chip->sram_requests == 0) { ++ ret = qcom_fg_masked_write(chip, MEM_INTF_CFG(chip), ++ RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to set SRAM access request bit: %d\n", ret); ++ ++ spin_unlock(&chip->sram_request_lock); ++ return ret; ++ } ++ } ++ ++ chip->sram_requests++; ++ ++ spin_unlock(&chip->sram_request_lock); ++ ++ /* Wait to get access to SRAM, and try again if interrupted */ ++ do { ++ ret = wait_for_completion_interruptible_timeout( ++ &chip->sram_access_granted, ++ msecs_to_jiffies(MEM_IF_TIMEOUT_MS)); ++ } while(ret == -ERESTARTSYS); ++ ++ if (ret <= 0) { ++ ret = -ETIMEDOUT; ++ ++ spin_lock(&chip->sram_request_lock); ++ chip->sram_requests--; ++ spin_unlock(&chip->sram_request_lock); ++ } else { ++ ret = 0; ++ ++ reinit_completion(&chip->sram_access_revoked); ++ } ++ ++ return ret; ++} ++ ++/** ++ * @brief qcom_fg_sram_release_access() - Release access to SRAM ++ * ++ * @param chip Pointer to chip ++ * @return int 0 on success, negative errno on error ++ */ ++static void qcom_fg_sram_release_access(struct qcom_fg_chip *chip) ++{ ++ spin_lock(&chip->sram_request_lock); ++ ++ chip->sram_requests--; ++ ++ if(WARN(chip->sram_requests < 0, ++ "sram_requests=%d, cannot be negative! resetting to 0.\n", ++ chip->sram_requests)) ++ chip->sram_requests = 0; ++ ++ if(chip->sram_requests == 0) ++ /* Schedule access release */ ++ queue_delayed_work(chip->sram_wq, &chip->sram_release_access_work, ++ msecs_to_jiffies(SRAM_ACCESS_RELEASE_DELAY_MS)); ++ ++ spin_unlock(&chip->sram_request_lock); ++} ++ ++static void qcom_fg_sram_release_access_worker(struct work_struct *work) ++{ ++ struct qcom_fg_chip *chip; ++ bool wait = false; ++ int ret; ++ ++ chip = container_of(work, struct qcom_fg_chip, sram_release_access_work.work); ++ ++ spin_lock(&chip->sram_request_lock); ++ ++ /* Request access release if there are still no access requests */ ++ if(chip->sram_requests == 0) { ++ qcom_fg_masked_write(chip, MEM_INTF_CFG(chip), RIF_MEM_ACCESS_REQ, 0); ++ wait = true; ++ } ++ ++ spin_unlock(&chip->sram_request_lock); ++ ++ if(!wait) ++ return; ++ ++ /* Wait for SRAM access to be released, and try again if interrupted */ ++ do { ++ ret = wait_for_completion_interruptible_timeout( ++ &chip->sram_access_revoked, ++ msecs_to_jiffies(MEM_IF_TIMEOUT_MS)); ++ } while(ret == -ERESTARTSYS); ++ ++ reinit_completion(&chip->sram_access_granted); ++} ++ ++/** ++ * @brief qcom_fg_sram_config_access() - Configure access to SRAM ++ * ++ * @param chip Pointer to chip ++ * @param write 0 for read access, 1 for write access ++ * @param burst 1 to access mutliple addresses successively ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_sram_config_access(struct qcom_fg_chip *chip, ++ bool write, bool burst) ++{ ++ u8 intf_ctl; ++ int ret; ++ ++ intf_ctl = (write ? MEM_INTF_CTL_WR_EN : 0) ++ | (burst ? MEM_INTF_CTL_BURST : 0); ++ ++ ret = qcom_fg_write(chip, &intf_ctl, ++ MEM_INTF_CTL(chip), 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to configure SRAM access: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_sram_read() - Read data from SRAM ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to read values into ++ * @param addr Address to read from ++ * @param len Number of bytes to read ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_sram_read(struct qcom_fg_chip *chip, ++ u8 *val, u16 addr, int len, int offset) ++{ ++ u8 *rd_data = val; ++ int ret = 0; ++ ++ ret = qcom_fg_sram_request_access(chip); ++ if (ret) { ++ dev_err(chip->dev, "Failed to request SRAM access: %d", ret); ++ return ret; ++ } ++ ++ spin_lock(&chip->sram_rw_lock); ++ ++ dev_vdbg(chip->dev, ++ "Reading address 0x%x with offset %d of length %d from SRAM", ++ addr, len, offset); ++ ++ ret = qcom_fg_sram_config_access(chip, 0, (len > 4)); ++ if (ret) { ++ dev_err(chip->dev, "Failed to configure SRAM access: %d", ret); ++ goto out; ++ } ++ ++ while(len > 0) { ++ /* Set SRAM address register */ ++ ret = qcom_fg_write(chip, (u8 *) &addr, ++ MEM_INTF_ADDR_LSB(chip), 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to set SRAM address: %d", ret); ++ goto out; ++ } ++ ++ ret = qcom_fg_read(chip, rd_data, ++ MEM_INTF_RD_DATA0(chip) + offset, len); ++ ++ addr += 4; ++ ++ if (ret) ++ goto out; ++ ++ rd_data += 4 - offset; ++ len -= 4 - offset; ++ offset = 0; ++ } ++out: ++ spin_unlock(&chip->sram_rw_lock); ++ qcom_fg_sram_release_access(chip); ++ ++ return ret; ++} ++ ++/** ++ * @brief qcom_fg_sram_write() - Write data to SRAM ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to write values from ++ * @param addr Address to write to ++ * @param len Number of bytes to write ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_sram_write(struct qcom_fg_chip *chip, ++ u8 *val, u16 addr, int len, int offset) ++{ ++ u8 *wr_data = val; ++ int ret; ++ ++ ret = qcom_fg_sram_request_access(chip); ++ if (ret) { ++ dev_err(chip->dev, "Failed to request SRAM access: %d", ret); ++ return ret; ++ } ++ ++ spin_lock(&chip->sram_rw_lock); ++ ++ dev_vdbg(chip->dev, ++ "Wrtiting address 0x%x with offset %d of length %d to SRAM", ++ addr, len, offset); ++ ++ ret = qcom_fg_sram_config_access(chip, 1, (len > 4)); ++ if (ret) { ++ dev_err(chip->dev, "Failed to configure SRAM access: %d", ret); ++ goto out; ++ } ++ ++ while(len > 0) { ++ /* Set SRAM address register */ ++ ret = qcom_fg_write(chip, (u8 *) &addr, ++ MEM_INTF_ADDR_LSB(chip), 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to set SRAM address: %d", ret); ++ goto out; ++ } ++ ++ ret = qcom_fg_write(chip, wr_data, ++ MEM_INTF_WR_DATA0(chip) + offset, len); ++ ++ addr += 4; ++ ++ if (ret) ++ goto out; ++ ++ wr_data += 4 - offset; ++ len -= 4 - offset; ++ offset = 0; ++ } ++out: ++ spin_unlock(&chip->sram_rw_lock); ++ qcom_fg_sram_release_access(chip); ++ ++ return ret; ++} ++ ++/************************* ++ * BATTERY STATUS ++ * ***********************/ ++ ++/** ++ * @brief qcom_fg_get_capacity() - Get remaining capacity of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_get_capacity(struct qcom_fg_chip *chip, int *val) ++{ ++ u8 cap[2]; ++ int ret; ++ ++ ret = qcom_fg_read(chip, cap, BATT_MONOTONIC_SOC, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read capacity: %d", ret); ++ return ret; ++ } ++ ++ if (cap[0] != cap[1]) { ++ cap[0] = cap[0] < cap[1] ? cap[0] : cap[1]; ++ } ++ ++ *val = DIV_ROUND_CLOSEST((cap[0] - 1) * 98, 0xff - 2) + 1; ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_get_temperature() - Get temperature of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_get_temperature(struct qcom_fg_chip *chip, int *val) ++{ ++ int temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_sram_read(chip, readval, BATT_TEMP, 2, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read temperature: %d", ret); ++ return ret; ++ } ++ ++ temp = readval[1] << 8 | readval[0]; ++ *val = temp * 625 / 1000 - 2730; ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_get_current() - Get current being drawn from battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_get_current(struct qcom_fg_chip *chip, int *val) ++{ ++ s16 temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_sram_read(chip, readval, BATT_VOLTAGE_CURRENT, 2, 3); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read current: %d", ret); ++ return ret; ++ } ++ ++ temp = (s16)(readval[1] << 8 | readval[0]); ++ *val = div_s64((s64)temp * 152587, 1000); ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_get_voltage() - Get voltage of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_get_voltage(struct qcom_fg_chip *chip, int *val) ++{ ++ int temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_sram_read(chip, readval, BATT_VOLTAGE_CURRENT, 2, 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read voltage: %d", ret); ++ return ret; ++ } ++ ++ temp = readval[1] << 8 | readval[0]; ++ *val = div_u64((u64)temp * 152587, 1000); ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_get_temp_threshold() - Get configured temperature thresholds ++ * ++ * @param chip Pointer to chip ++ * @param psp Power supply property of temperature limit ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_get_temp_threshold(struct qcom_fg_chip *chip, ++ enum power_supply_property psp, int *val) ++{ ++ u8 temp; ++ int offset; ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_TEMP_MIN: ++ offset = 0; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_MAX: ++ offset = 1; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: ++ offset = 2; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ++ offset = 3; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = qcom_fg_sram_read(chip, &temp, TEMP_THRESHOLD, 1, offset); ++ if (ret < 0) { ++ dev_err(chip->dev, "Failed to read JEITA property %d level: %d\n", psp, ret); ++ return ret; ++ } ++ ++ *val = (temp - 30) * 10; ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_set_temp_threshold() - Configure temperature thresholds ++ * ++ * @param chip Pointer to chip ++ * @param psp Power supply property of temperature limit ++ * @param val Pointer to get value from ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_set_temp_threshold(struct qcom_fg_chip *chip, ++ enum power_supply_property psp, int val) ++{ ++ u8 temp; ++ int offset; ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_TEMP_MIN: ++ offset = 0; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_MAX: ++ offset = 1; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: ++ offset = 2; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ++ offset = 3; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ temp = val / 10 + 30; ++ ++ ret = qcom_fg_sram_write(chip, &temp, TEMP_THRESHOLD, 1, offset); ++ if (ret < 0) { ++ dev_err(chip->dev, "Failed to write JEITA property %d level: %d\n", psp, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/************************* ++ * BATTERY STATUS, GEN3 ++ * ***********************/ ++ ++/** ++ * @brief qcom_fg_gen3_get_temperature() - Get temperature of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_gen3_get_temperature(struct qcom_fg_chip *chip, int *val) ++{ ++ int temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_read(chip, readval, PARAM_ADDR_BATT_TEMP, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read temperature: %d\n", ret); ++ return ret; ++ } ++ ++ temp = ((readval[1] & BATT_TEMP_MSB_MASK) << 8) | ++ (readval[0] & BATT_TEMP_LSB_MASK); ++ temp = DIV_ROUND_CLOSEST(temp * 10, 4); ++ ++ *val = temp -2730; ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_gen3_get_current() - Get current being drawn from battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_gen3_get_current(struct qcom_fg_chip *chip, int *val) ++{ ++ s16 temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_read(chip, readval, PARAM_ADDR_BATT_CURRENT, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read current: %d\n", ret); ++ return ret; ++ } ++ ++ //handle rev 1 too ++ temp = (s16)(readval[1] << 8 | readval[0]); ++ *val = div_s64((s64)temp * 488281, 1000); ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_gen3_get_voltage() - Get voltage of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_gen3_get_voltage(struct qcom_fg_chip *chip, int *val) ++{ ++ int temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_read(chip, readval, PARAM_ADDR_BATT_VOLTAGE, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read voltage: %d\n", ret); ++ return ret; ++ } ++ ++ //handle rev 1 too ++ temp = readval[1] << 8 | readval[0]; ++ *val = div_u64((u64)temp * 122070, 1000); ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_gen3_get_temp_threshold() - Get configured temperature thresholds ++ * ++ * @param chip Pointer to chip ++ * @param psp Power supply property of temperature limit ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_gen3_get_temp_threshold(struct qcom_fg_chip *chip, ++ enum power_supply_property psp, int *val) ++{ ++ u8 temp; ++ u16 reg; ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_TEMP_MIN: ++ reg = BATT_INFO_JEITA_COLD; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_MAX: ++ reg = BATT_INFO_JEITA_HOT; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: ++ reg = BATT_INFO_JEITA_COOL; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ++ reg = BATT_INFO_JEITA_WARM; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = qcom_fg_read(chip, &temp, reg, 1); ++ if (ret < 0) { ++ dev_err(chip->dev, "Failed to read JEITA property %d level: %d\n", psp, ret); ++ return ret; ++ } ++ ++ /* Resolution is 0.5C. Base is -30C. */ ++ *val = (((5 * temp) / 10) - 30) * 10; ++ return 0; ++} ++ ++/************************* ++ * BATTERY STATUS, GEN4 ++ * ***********************/ ++ ++/** ++ * @brief qcom_fg_gen4_get_temperature() - Get temperature of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_gen4_get_temperature(struct qcom_fg_chip *chip, int *val) ++{ ++ int temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_read(chip, readval, ADC_RR_BATT_TEMP_LSB, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read temperature: %d", ret); ++ return ret; ++ } ++ ++ temp = readval[1] << 8 | readval[0]; ++ *val = temp * 10; ++ return 0; ++} ++ ++/************************ ++ * BATTERY POWER SUPPLY ++ * **********************/ ++ ++/* Pre-Gen3 fuel gauge. PMI8996 and older */ ++static const struct qcom_fg_ops ops_fg = { ++ .get_capacity = qcom_fg_get_capacity, ++ .get_temperature = qcom_fg_get_temperature, ++ .get_current = qcom_fg_get_current, ++ .get_voltage = qcom_fg_get_voltage, ++ .get_temp_threshold = qcom_fg_get_temp_threshold, ++ .set_temp_threshold = qcom_fg_set_temp_threshold, ++ .memif_base = 0x400, ++}; ++ ++/* Gen3 fuel gauge. PMI8998 and newer */ ++static const struct qcom_fg_ops ops_fg_gen3 = { ++ .get_capacity = qcom_fg_get_capacity, ++ .get_temperature = qcom_fg_gen3_get_temperature, ++ .get_current = qcom_fg_gen3_get_current, ++ .get_voltage = qcom_fg_gen3_get_voltage, ++ .get_temp_threshold = qcom_fg_gen3_get_temp_threshold, ++ .memif_base = 0x400, ++}; ++ ++/* Gen4 fuel gauge. PM8150B and newer */ ++static const struct qcom_fg_ops ops_fg_gen4 = { ++ .get_capacity = qcom_fg_get_capacity, ++ .get_temperature = qcom_fg_gen4_get_temperature, ++ .get_current = qcom_fg_gen3_get_current, ++ .get_voltage = qcom_fg_gen3_get_voltage, ++ .get_temp_threshold = qcom_fg_gen3_get_temp_threshold, ++ .memif_base = 0x300, ++}; ++ ++static enum power_supply_property qcom_fg_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_CAPACITY, ++ POWER_SUPPLY_PROP_CURRENT_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, ++ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_TEMP_MIN, ++ POWER_SUPPLY_PROP_TEMP_MAX, ++ POWER_SUPPLY_PROP_TEMP_ALERT_MIN, ++ POWER_SUPPLY_PROP_TEMP_ALERT_MAX, ++}; ++ ++static int qcom_fg_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct qcom_fg_chip *chip = power_supply_get_drvdata(psy); ++ int temp, ret = 0; ++ ++ dev_dbg(chip->dev, "Getting property: %d", psp); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ /* Get status from charger if available */ ++ if (chip->chg_psy && ++ chip->status != POWER_SUPPLY_STATUS_UNKNOWN) { ++ val->intval = chip->status; ++ break; ++ } else { ++ /* ++ * Fall back to capacity and current-based ++ * status checking ++ */ ++ ret = chip->ops->get_capacity(chip, &temp); ++ if (ret) { ++ val->intval = POWER_SUPPLY_STATUS_UNKNOWN; ++ break; ++ } ++ if (temp == 100) { ++ val->intval = POWER_SUPPLY_STATUS_FULL; ++ break; ++ } ++ ++ ret = chip->ops->get_current(chip, &temp); ++ if (ret) { ++ val->intval = POWER_SUPPLY_STATUS_UNKNOWN; ++ break; ++ } ++ if (temp < 0) ++ val->intval = POWER_SUPPLY_STATUS_CHARGING; ++ else if (temp > 0) ++ val->intval = POWER_SUPPLY_STATUS_DISCHARGING; ++ else ++ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ } ++ ++ break; ++ case POWER_SUPPLY_PROP_TECHNOLOGY: ++ val->intval = POWER_SUPPLY_TECHNOLOGY_LION; ++ break; ++ case POWER_SUPPLY_PROP_CAPACITY: ++ ret = chip->ops->get_capacity(chip, &val->intval); ++ break; ++ case POWER_SUPPLY_PROP_CURRENT_NOW: ++ ret = chip->ops->get_current(chip, &val->intval); ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ ret = chip->ops->get_voltage(chip, &val->intval); ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: ++ val->intval = chip->batt_info->voltage_min_design_uv; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: ++ val->intval = chip->batt_info->voltage_max_design_uv; ++ break; ++ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: ++ val->intval = chip->batt_info->charge_full_design_uah; ++ break; ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = 1; ++ break; ++ case POWER_SUPPLY_PROP_TEMP: ++ ret = chip->ops->get_temperature(chip, &val->intval); ++ break; ++ case POWER_SUPPLY_PROP_TEMP_MIN: ++ case POWER_SUPPLY_PROP_TEMP_MAX: ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ++ ret = chip->ops->get_temp_threshold(chip, psp, &val->intval); ++ break; ++ default: ++ dev_err(chip->dev, "invalid property: %d\n", psp); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static const struct power_supply_desc batt_psy_desc = { ++ .name = "qcom-battery", ++ .type = POWER_SUPPLY_TYPE_BATTERY, ++ .properties = qcom_fg_props, ++ .num_properties = ARRAY_SIZE(qcom_fg_props), ++ .get_property = qcom_fg_get_property, ++}; ++ ++/******************** ++ * INIT FUNCTIONS ++ * ******************/ ++ ++static int qcom_fg_iacs_clear_sequence(struct qcom_fg_chip *chip) ++{ ++ u8 temp; ++ int ret; ++ ++ /* clear the error */ ++ ret = qcom_fg_masked_write(chip, MEM_INTF_IMA_CFG(chip), BIT(2), BIT(2)); ++ if (ret) { ++ dev_err(chip->dev, "Failed to write IMA_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ temp = 0x4; ++ ret = qcom_fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to write MEM_INTF_ADDR_MSB: %d\n", ret); ++ return ret; ++ } ++ ++ temp = 0x0; ++ ret = qcom_fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to write WR_DATA3: %d\n", ret); ++ return ret; ++ } ++ ++ ret = qcom_fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to write RD_DATA3: %d\n", ret); ++ return ret; ++ } ++ ++ ret = qcom_fg_masked_write(chip, MEM_INTF_IMA_CFG(chip), BIT(2), 0); ++ if (ret) { ++ dev_err(chip->dev, "Failed to write IMA_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qcom_fg_clear_ima(struct qcom_fg_chip *chip, ++ bool check_hw_sts) ++{ ++ u8 err_sts, exp_sts, hw_sts; ++ bool run_err_clr_seq = false; ++ int ret; ++ ++ ret = qcom_fg_read(chip, &err_sts, ++ MEM_INTF_IMA_ERR_STS(chip), 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read IMA_ERR_STS: %d\n", ret); ++ return ret; ++ } ++ ++ ret = qcom_fg_read(chip, &exp_sts, ++ MEM_INTF_IMA_EXP_STS(chip), 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read IMA_EXP_STS: %d\n", ret); ++ return ret; ++ } ++ ++ if (check_hw_sts) { ++ ret = qcom_fg_read(chip, &hw_sts, ++ MEM_INTF_IMA_HW_STS(chip), 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read IMA_HW_STS: %d\n", ret); ++ return ret; ++ } ++ /* ++ * Lower nibble should be equal to upper nibble before SRAM ++ * transactions begins from SW side. ++ */ ++ if ((hw_sts & 0x0f) != hw_sts >> 4) { ++ dev_dbg(chip->dev, "IMA HW not in correct state, hw_sts=%x\n", ++ hw_sts); ++ run_err_clr_seq = true; ++ } ++ } ++ ++ if (exp_sts & (BIT(0) | BIT(1) | BIT(3) | ++ BIT(4) | BIT(5) | BIT(6) | ++ BIT(7))) { ++ dev_dbg(chip->dev, "IMA exception bit set, exp_sts=%x\n", exp_sts); ++ run_err_clr_seq = true; ++ } ++ ++ if (run_err_clr_seq) { ++ ret = qcom_fg_iacs_clear_sequence(chip); ++ if (!ret) ++ return -EAGAIN; ++ } ++ ++ return 0; ++} ++ ++static irqreturn_t qcom_fg_handle_soc_delta(int irq, void *data) ++{ ++ struct qcom_fg_chip *chip = data; ++ ++ /* Signal change in state of charge */ ++ power_supply_changed(chip->batt_psy); ++ dev_dbg(chip->dev, "SOC changed"); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t qcom_fg_handle_mem_avail(int irq, void *data) ++{ ++ struct qcom_fg_chip *chip = data; ++ ++ if (qcom_fg_sram_check_access(chip)) { ++ complete_all(&chip->sram_access_granted); ++ dev_dbg(chip->dev, "SRAM access granted"); ++ } else { ++ complete_all(&chip->sram_access_revoked); ++ dev_dbg(chip->dev, "SRAM access revoked"); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void qcom_fg_status_changed_worker(struct work_struct *work) ++{ ++ struct qcom_fg_chip *chip = container_of(work, struct qcom_fg_chip, ++ status_changed_work.work); ++ ++ power_supply_changed(chip->batt_psy); ++} ++ ++static int qcom_fg_notifier_call(struct notifier_block *nb, ++ unsigned long val, void *v) ++{ ++ struct qcom_fg_chip *chip = container_of(nb, struct qcom_fg_chip, nb); ++ struct power_supply *psy = v; ++ union power_supply_propval propval; ++ int ret; ++ ++ if (psy == chip->chg_psy) { ++ ret = power_supply_get_property(psy, ++ POWER_SUPPLY_PROP_STATUS, &propval); ++ if (ret) ++ chip->status = POWER_SUPPLY_STATUS_UNKNOWN; ++ ++ chip->status = propval.intval; ++ ++ power_supply_changed(chip->batt_psy); ++ ++ if (chip->status == POWER_SUPPLY_STATUS_UNKNOWN) { ++ /* ++ * REVISIT: Find better solution or remove current-based ++ * status checking once checking is properly implemented ++ * in charger drivers ++ ++ * Sometimes it take a while for current to stabilize, ++ * so signal property change again later to make sure ++ * current-based status is properly detected. ++ */ ++ cancel_delayed_work_sync(&chip->status_changed_work); ++ schedule_delayed_work(&chip->status_changed_work, ++ msecs_to_jiffies(1000)); ++ } ++ } ++ ++ return NOTIFY_OK; ++} ++ ++static int qcom_fg_probe(struct platform_device *pdev) ++{ ++ struct power_supply_config supply_config = {}; ++ struct qcom_fg_chip *chip; ++ const __be32 *prop_addr; ++ int irq; ++ u8 dma_status; ++ bool error_present; ++ int ret; ++ ++ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ chip->dev = &pdev->dev; ++ chip->ops = of_device_get_match_data(&pdev->dev); ++ ++ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); ++ if (!chip->regmap) { ++ dev_err(chip->dev, "Failed to locate the regmap\n"); ++ return -ENODEV; ++ } ++ ++ /* Get base address */ ++ prop_addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL); ++ if (!prop_addr) { ++ dev_err(chip->dev, "Failed to read SOC base address from dt\n"); ++ return -EINVAL; ++ } ++ chip->base = be32_to_cpu(*prop_addr); ++ ++ /* ++ * Change the FG_MEM_INT interrupt to track IACS_READY ++ * condition instead of end-of-transaction. This makes sure ++ * that the next transaction starts only after the hw is ready. ++ * IACS_INTR_SRC_SLCT is BIT(3) ++ */ ++ ret = qcom_fg_masked_write(chip, ++ MEM_INTF_IMA_CFG(chip), BIT(3), BIT(3)); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to configure interrupt sourete: %d\n", ret); ++ return ret; ++ } ++ ++ ret = qcom_fg_clear_ima(chip, true); ++ if (ret && ret != -EAGAIN) { ++ dev_err(chip->dev, "Failed to clear IMA exception: %d\n", ret); ++ return ret; ++ } ++ ++ /* Check and clear DMA errors */ ++ ret = qcom_fg_read(chip, &dma_status, MEM_IF_DMA_STS(chip), 1); ++ if (ret < 0) { ++ dev_err(chip->dev, "Failed to read dma_status: %d\n", ret); ++ return ret; ++ } ++ ++ error_present = dma_status & (BIT(1) | BIT(2)); ++ ret = qcom_fg_masked_write(chip, MEM_IF_DMA_CTL(chip), BIT(0), ++ error_present ? BIT(0) : 0); ++ if (ret < 0) { ++ dev_err(chip->dev, "Failed to write dma_ctl: %d\n", ret); ++ return ret; ++ } ++ ++ supply_config.drv_data = chip; ++ supply_config.of_node = pdev->dev.of_node; ++ ++ chip->batt_psy = devm_power_supply_register(chip->dev, ++ &batt_psy_desc, &supply_config); ++ if (IS_ERR(chip->batt_psy)) { ++ if (PTR_ERR(chip->batt_psy) != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "Failed to register battery\n"); ++ return PTR_ERR(chip->batt_psy); ++ } ++ ++ platform_set_drvdata(pdev, chip); ++ ++ ret = power_supply_get_battery_info(chip->batt_psy, &chip->batt_info); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to get battery info: %d\n", ret); ++ return ret; ++ } ++ ++ /* Initialize SRAM */ ++ if (of_device_is_compatible(pdev->dev.of_node, "qcom,pmi8994-fg")) { ++ irq = of_irq_get_byname(pdev->dev.of_node, "mem-avail"); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "Failed to get irq mem-avail byname: %d\n", ++ irq); ++ return irq; ++ } ++ ++ init_completion(&chip->sram_access_granted); ++ init_completion(&chip->sram_access_revoked); ++ ++ chip->sram_wq = create_singlethread_workqueue("qcom_fg"); ++ INIT_DELAYED_WORK(&chip->sram_release_access_work, ++ qcom_fg_sram_release_access_worker); ++ ++ ret = devm_request_threaded_irq(chip->dev, irq, NULL, ++ qcom_fg_handle_mem_avail, ++ IRQF_ONESHOT, "mem-avail", chip); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to request mem-avail IRQ: %d\n", ret); ++ return ret; ++ } ++ ++ spin_lock_init(&chip->sram_request_lock); ++ spin_lock_init(&chip->sram_rw_lock); ++ chip->sram_requests = 0; ++ } ++ ++ /* Set default temperature thresholds */ ++ if (chip->ops->set_temp_threshold) { ++ ret = chip->ops->set_temp_threshold(chip, ++ POWER_SUPPLY_PROP_TEMP_MIN, ++ BATT_TEMP_JEITA_COLD); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to set cold threshold: %d\n", ret); ++ return ret; ++ } ++ ++ ret = chip->ops->set_temp_threshold(chip, ++ POWER_SUPPLY_PROP_TEMP_MAX, ++ BATT_TEMP_JEITA_WARM); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to set warm threshold: %d\n", ret); ++ return ret; ++ } ++ ++ ret = chip->ops->set_temp_threshold(chip, ++ POWER_SUPPLY_PROP_TEMP_ALERT_MIN, ++ BATT_TEMP_JEITA_COOL); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to set cool threshold: %d\n", ret); ++ return ret; ++ } ++ ++ ret = chip->ops->set_temp_threshold(chip, ++ POWER_SUPPLY_PROP_TEMP_ALERT_MAX, ++ BATT_TEMP_JEITA_HOT); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to set hot threshold: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ /* Get soc-delta IRQ */ ++ irq = of_irq_get_byname(pdev->dev.of_node, "soc-delta"); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "Failed to get irq soc-delta byname: %d\n", ++ irq); ++ return irq; ++ } ++ ++ ret = devm_request_threaded_irq(chip->dev, irq, NULL, ++ qcom_fg_handle_soc_delta, ++ IRQF_ONESHOT, "soc-delta", chip); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to request soc-delta IRQ: %d\n", ret); ++ return ret; ++ } ++ ++ /* Optional: Get charger power supply for status checking */ ++ chip->chg_psy = power_supply_get_by_phandle(chip->dev->of_node, ++ "power-supplies"); ++ if (IS_ERR(chip->chg_psy)) { ++ ret = PTR_ERR(chip->chg_psy); ++ dev_warn(chip->dev, "Failed to get charger supply: %d\n", ret); ++ chip->chg_psy = NULL; ++ } ++ ++ if (chip->chg_psy) { ++ INIT_DELAYED_WORK(&chip->status_changed_work, ++ qcom_fg_status_changed_worker); ++ ++ chip->nb.notifier_call = qcom_fg_notifier_call; ++ ret = power_supply_reg_notifier(&chip->nb); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to register notifier: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static void qcom_fg_remove(struct platform_device *pdev) ++{ ++ struct qcom_fg_chip *chip = platform_get_drvdata(pdev); ++ ++ power_supply_put_battery_info(chip->batt_psy, chip->batt_info); ++ ++ if(chip->sram_wq) ++ destroy_workqueue(chip->sram_wq); ++} ++ ++static const struct of_device_id fg_match_id_table[] = { ++ { .compatible = "qcom,pmi8994-fg", .data = &ops_fg }, ++ { .compatible = "qcom,pmi8998-fg", .data = &ops_fg_gen3 }, ++ { .compatible = "qcom,pm8150b-fg", .data = &ops_fg_gen4 }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, fg_match_id_table); ++ ++static struct platform_driver qcom_fg_driver = { ++ .probe = qcom_fg_probe, ++ .remove_new = qcom_fg_remove, ++ .driver = { ++ .name = "qcom-fg", ++ .of_match_table = fg_match_id_table, ++ }, ++}; ++ ++module_platform_driver(qcom_fg_driver); ++ ++MODULE_AUTHOR("Caleb Connolly "); ++MODULE_AUTHOR("Joel Selvaraj "); ++MODULE_AUTHOR("Yassine Oudjana "); ++MODULE_DESCRIPTION("Qualcomm PMIC Fuel Gauge Driver"); ++MODULE_LICENSE("GPL v2"); +\ No newline at end of file +-- +2.34.1 + diff --git a/patch/kernel/archive/sm8250-6.11/0034_arm64--dts--qcom--pm8150b--Add-a-charger-Signed-of.patch b/patch/kernel/archive/sm8250-6.11/0034_arm64--dts--qcom--pm8150b--Add-a-charger-Signed-of.patch new file mode 100644 index 000000000000..259a3f421c69 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.11/0034_arm64--dts--qcom--pm8150b--Add-a-charger-Signed-of.patch @@ -0,0 +1,63 @@ +From 000b9f019f77eca40d579aa86bba3b1b63c374d0 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 20 Nov 2024 23:01:45 +0700 +Subject: [PATCH] arm64: dts: qcom: pm8150b: Add a charger Signed-off-by: Teguh + Sobirin + +--- + arch/arm64/boot/dts/qcom/pm8150b.dtsi | 32 +++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/pm8150b.dtsi b/arch/arm64/boot/dts/qcom/pm8150b.dtsi +index 3f7b0b6a1d109..d0cfe63619547 100644 +--- a/arch/arm64/boot/dts/qcom/pm8150b.dtsi ++++ b/arch/arm64/boot/dts/qcom/pm8150b.dtsi +@@ -107,6 +107,26 @@ pm8150b_temp: temp-alarm@2400 { + #thermal-sensor-cells = <0>; + }; + ++ pm8150b_charger: charger@1000 { ++ compatible = "qcom,pm8150b-charger"; ++ reg = <0x1000>; ++ ++ interrupts = <0x2 0x13 0x4 IRQ_TYPE_EDGE_BOTH>, ++ <0x2 0x12 0x2 IRQ_TYPE_EDGE_BOTH>, ++ <0x2 0x16 0x1 IRQ_TYPE_EDGE_RISING>, ++ <0x2 0x13 0x7 IRQ_TYPE_EDGE_RISING>; ++ interrupt-names = "usbin-plugin", ++ "bat-ov", ++ "wdog-bark", ++ "usbin-icl-change"; ++ ++ io-channels = <&pm8150b_adc 7>, ++ <&pm8150b_adc 8>; ++ io-channel-names = "usb_in_i_uv", "usb_in_v_div_16"; ++ ++ status = "disabled"; ++ }; ++ + pm8150b_adc: adc@3100 { + compatible = "qcom,spmi-adc5"; + reg = <0x3100>; +@@ -133,6 +153,18 @@ channel@6 { + label = "die_temp"; + }; + ++ channel@7 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ label = "usb_in_i_uv"; ++ }; ++ ++ channel@8 { ++ reg = ; ++ qcom,pre-scaling = <1 16>; ++ label = "usb_in_v_div_16"; ++ }; ++ + channel@9 { + reg = ; + qcom,pre-scaling = <1 1>; +-- +2.34.1 + diff --git a/patch/kernel/archive/sm8250-6.11/0035_arm64--dts--qcom--pm8150b--Add-a-FG-Signed-off-by-.patch b/patch/kernel/archive/sm8250-6.11/0035_arm64--dts--qcom--pm8150b--Add-a-FG-Signed-off-by-.patch new file mode 100644 index 000000000000..02f13ad12eda --- /dev/null +++ b/patch/kernel/archive/sm8250-6.11/0035_arm64--dts--qcom--pm8150b--Add-a-FG-Signed-off-by-.patch @@ -0,0 +1,32 @@ +From 9cf8452cf472d813961ff4a970a759d197c5b99f Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 20 Nov 2024 23:02:30 +0700 +Subject: [PATCH] arm64: dts: qcom: pm8150b: Add a FG Signed-off-by: Teguh + Sobirin + +--- + arch/arm64/boot/dts/qcom/pm8150b.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/pm8150b.dtsi b/arch/arm64/boot/dts/qcom/pm8150b.dtsi +index d0cfe63619547..42ef21238f18b 100644 +--- a/arch/arm64/boot/dts/qcom/pm8150b.dtsi ++++ b/arch/arm64/boot/dts/qcom/pm8150b.dtsi +@@ -127,6 +127,14 @@ pm8150b_charger: charger@1000 { + status = "disabled"; + }; + ++ pm8150b_fg: fuel-gauge@4000 { ++ compatible = "qcom,pm8150b-fg"; ++ reg = <0x4000>; ++ interrupts = <0x2 0x40 0x3 IRQ_TYPE_EDGE_RISING>; ++ interrupt-names = "soc-delta"; ++ status = "disabled"; ++ }; ++ + pm8150b_adc: adc@3100 { + compatible = "qcom,spmi-adc5"; + reg = <0x3100>; +-- +2.34.1 + diff --git a/patch/kernel/archive/sm8250-6.11/0036_drm--panel--Add-DDIC-CH13726A-panel-Signed-off-by-.patch b/patch/kernel/archive/sm8250-6.11/0036_drm--panel--Add-DDIC-CH13726A-panel-Signed-off-by-.patch new file mode 100644 index 000000000000..f770162c3524 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.11/0036_drm--panel--Add-DDIC-CH13726A-panel-Signed-off-by-.patch @@ -0,0 +1,397 @@ +From a61edacbee6718f398f37358dc705dd434f3c219 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 20 Nov 2024 23:04:35 +0700 +Subject: [PATCH] drm: panel: Add DDIC CH13726A panel Signed-off-by: Teguh + Sobirin + +--- + drivers/gpu/drm/panel/Kconfig | 11 + + drivers/gpu/drm/panel/Makefile | 1 + + drivers/gpu/drm/panel/panel-ddic-ch13726a.c | 341 ++++++++++++++++++++ + 3 files changed, 353 insertions(+) + create mode 100644 drivers/gpu/drm/panel/panel-ddic-ch13726a.c + +diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig +index 9f49b0189d3b8..c6fcccdcffa07 100644 +--- a/drivers/gpu/drm/panel/Kconfig ++++ b/drivers/gpu/drm/panel/Kconfig +@@ -87,6 +87,17 @@ config DRM_PANEL_BOE_TV101WUM_NL6 + Say Y here if you want to support for BOE TV101WUM and AUO KD101N80 + 45NA WUXGA PANEL DSI Video Mode panel + ++config DRM_PANEL_DDIC_CH13726A ++ tristate "DDIC CH13726A-based DSI panel" ++ depends on OF ++ depends on DRM_MIPI_DSI ++ depends on BACKLIGHT_CLASS_DEVICE ++ select DRM_DISPLAY_DP_HELPER ++ select DRM_DISPLAY_HELPER ++ help ++ Say Y here if you want to enable support for DDIC CH13726A-based ++ display panels. ++ + config DRM_PANEL_EBBG_FT8719 + tristate "EBBG FT8719 panel driver" + depends on OF +diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile +index 5581387707c62..239a1049fb89e 100644 +--- a/drivers/gpu/drm/panel/Makefile ++++ b/drivers/gpu/drm/panel/Makefile +@@ -7,6 +7,7 @@ obj-$(CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0) += panel-boe-bf060y8m-aj0.o + obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o + obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o + obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o ++obj-$(CONFIG_DRM_PANEL_DDIC_CH13726A) += panel-ddic-ch13726a.o + obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o + obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o + obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o +diff --git a/drivers/gpu/drm/panel/panel-ddic-ch13726a.c b/drivers/gpu/drm/panel/panel-ddic-ch13726a.c +new file mode 100644 +index 0000000000000..772bb057c44d8 +--- /dev/null ++++ b/drivers/gpu/drm/panel/panel-ddic-ch13726a.c +@@ -0,0 +1,341 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * DDIC CH13726A MIPI-DSI panel driver ++ * Copyright (c) 2024, Teguh Sobirin . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include