summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-10-07 21:24:20 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2022-10-07 21:24:20 +0300
commitae9559594cb851aff774d5bea243b84c6acf761d (patch)
tree820db40e9049af7ec3a9c24d9b50c04802aed61c /drivers
parent79d11de9637a37035b4a72ccb528b3dfebc1bf67 (diff)
parent72a95859728a7866522e6633818bebc1c2519b17 (diff)
downloadlinux-ae9559594cb851aff774d5bea243b84c6acf761d.tar.xz
Merge tag 'mfd-next-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones: "Core Frameworks: - Fix 'mfd_of_node_list' OF node entry resource leak New Drivers: - Add support for Ocelot VSC7512 Networking Chip - Add support for MediaTek MT6370 subPMIC - Add support for Richtek RT5120 (I2C) PMIC New Device Support: - Add support for Rockchip RV1126 and RK3588 to Syscon - Add support for Rockchip RK817 Battery Charger to RK808 - Add support for Silergy SY7636a Voltage Regulator to Simple MFD - Add support for Qualcomm PMP8074 PMIC to QCOM SPMI - Add support for Secure Update to Intel M10 BMC New Functionality: - Provide SSP type to Intel's LPSS (PCI) SPI driver Fix-ups: - Remove legacy / unused code; stmpe, intel_soc_pmic_crc, syscon - Unify / simplify; intel_soc_pmic_crc - Trivial reordering / spelling, etc; Makefile, twl-core - Convert to managed resources; intel_soc_pmic_crc - Use appropriate APIs; intel_soc_pmic_crc - strscpy() conversion; htc-i2cpld, lpc_ich, mfd-core - GPIOD conversion; htc-i2cpld, stmpe - Add missing header file includes; twl4030-irq - DT goodies; stmpe, mediatek,mt6370, x-powers,axp152, aspeed,ast2x00-scu, mediatek,mt8195-scpsys, qcom,spmi-pmic, syscon, qcom,tcsr, rockchip,rk817, sprd,ums512-glbreg, dlg,da9063 Bug Fixes: - Properly check return values; sm501, htc-i2cpld - Repair Two-Wire Bus Mode; da9062-core - Fix error handling; intel_soc_pmic_core, fsl-imx25-tsadc, lp8788, lp8788-irq" * tag 'mfd-next-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (60 commits) mfd: syscon: Remove repetition of the regmap_get_val_endian() mfd: ocelot-spi: Add missing MODULE_DEVICE_TABLE power: supply: Add charger driver for Rockchip RK817 dt-bindings: mfd: mt6370: Fix the indentation in the example mfd: da9061: Fix Failed to set Two-Wire Bus Mode. mfd: htc-i2cpld: Fix an IS_ERR() vs NULL bug in htcpld_core_probe() dt-bindings: mfd: qcom,tcsr: Drop simple-mfd from IPQ6018 mfd: sm501: Add check for platform_driver_register() dt-bindings: mfd: mediatek: Add scpsys compatible for mt8186 mfd: twl4030: Add missed linux/device.h header dt-bindings: mfd: dlg,da9063: Add missing regulator patterns dt-bindings: mfd: sprd: Add bindings for ums512 global registers mfd: intel_soc_pmic_chtdc_ti: Switch from __maybe_unused to pm_sleep_ptr() etc dt-bindings: mfd: syscon: Add rk3588 QoS register compatible mfd: stmpe: Switch to using gpiod API mfd: qcom-spmi-pmic: Add pm7250b compatible dt-bindings: mfd: Add missing (unevaluated|additional)Properties on child nodes mfd/omap1: htc-i2cpld: Convert to a pure GPIO driver mfd: intel-m10-bmc: Add d5005 bmc secure update driver dt-bindings: mfd: syscon: Drop ref from reg-io-width ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/Kconfig1
-rw-r--r--drivers/mfd/Kconfig44
-rw-r--r--drivers/mfd/Makefile12
-rw-r--r--drivers/mfd/da9062-core.c1
-rw-r--r--drivers/mfd/fsl-imx25-tsadc.c34
-rw-r--r--drivers/mfd/htc-i2cpld.c60
-rw-r--r--drivers/mfd/intel-lpss-pci.c141
-rw-r--r--drivers/mfd/intel-m10-bmc.c1
-rw-r--r--drivers/mfd/intel_soc_pmic_chtdc_ti.c8
-rw-r--r--drivers/mfd/intel_soc_pmic_core.c158
-rw-r--r--drivers/mfd/intel_soc_pmic_core.h25
-rw-r--r--drivers/mfd/intel_soc_pmic_crc.c139
-rw-r--r--drivers/mfd/lp8788-irq.c3
-rw-r--r--drivers/mfd/lp8788.c12
-rw-r--r--drivers/mfd/lpc_ich.c2
-rw-r--r--drivers/mfd/mfd-core.c9
-rw-r--r--drivers/mfd/mt6370.c312
-rw-r--r--drivers/mfd/mt6370.h99
-rw-r--r--drivers/mfd/ocelot-spi.c1
-rw-r--r--drivers/mfd/qcom-spmi-pmic.c1
-rw-r--r--drivers/mfd/rk808.c16
-rw-r--r--drivers/mfd/rt5120.c124
-rw-r--r--drivers/mfd/sm501.c7
-rw-r--r--drivers/mfd/stmpe.c49
-rw-r--r--drivers/mfd/syscon.c8
-rw-r--r--drivers/mfd/twl-core.c2
-rw-r--r--drivers/mfd/twl4030-irq.c1
-rw-r--r--drivers/power/supply/Kconfig6
-rw-r--r--drivers/power/supply/Makefile1
-rw-r--r--drivers/power/supply/rk817_charger.c1211
-rw-r--r--drivers/regulator/Kconfig1
31 files changed, 2155 insertions, 334 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5695b266abcf..7ac3daaf59ce 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1758,6 +1758,7 @@ config SENSORS_SIS5595
config SENSORS_SY7636A
tristate "Silergy SY7636A"
+ depends on MFD_SY7636A
help
If you say yes here you get support for the thermistor readout of
the Silergy SY7636A PMIC.
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c3dd1fe8d8c9..8b93856de432 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -589,8 +589,8 @@ config LPC_SCH
config INTEL_SOC_PMIC
bool "Support for Crystal Cove PMIC"
- depends on ACPI && HAS_IOMEM && I2C=y && GPIOLIB && COMMON_CLK
- depends on X86 || COMPILE_TEST
+ depends on HAS_IOMEM && I2C=y && GPIOLIB && COMMON_CLK
+ depends on (X86 && ACPI) || COMPILE_TEST
depends on I2C_DESIGNWARE_PLATFORM=y
select MFD_CORE
select REGMAP_I2C
@@ -938,6 +938,22 @@ config MFD_MT6360
PMIC part includes 2-channel BUCKs and 2-channel LDOs
LDO part includes 4-channel LDOs
+config MFD_MT6370
+ tristate "MediaTek MT6370 SubPMIC"
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ depends on I2C
+ help
+ Say Y here to enable MT6370 SubPMIC functional support.
+ It consists of a single cell battery charger with ADC monitoring, RGB
+ LEDs, dual channel flashlight, WLED backlight driver, display bias
+ voltage supply, one general purpose LDO, and the USB Type-C & PD
+ controller complies with the latest USB Type-C and PD standards.
+
+ This driver can also be built as a module. If so, the module
+ will be called "mt6370".
+
config MFD_MT6397
tristate "MediaTek MT6397 PMIC Support"
select MFD_CORE
@@ -1117,6 +1133,16 @@ config MFD_SPMI_PMIC
Say M here if you want to include support for the SPMI PMIC
series as a module. The module will be called "qcom-spmi-pmic".
+config MFD_SY7636A
+ tristate "Silergy SY7636A voltage regulator"
+ depends on I2C
+ select MFD_SIMPLE_MFD_I2C
+ help
+ Enable support for Silergy SY7636A voltage regulator.
+
+ To enable support for building sub-devices as modules,
+ choose M here.
+
config MFD_RDC321X
tristate "RDC R-321x southbridge"
select MFD_CORE
@@ -1149,6 +1175,18 @@ config MFD_RT5033
sub-devices like charger, fuel gauge, flash LED, current source,
LDO and Buck.
+config MFD_RT5120
+ tristate "Richtek RT5120 Power Management IC"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ The enables support for Richtek RT5120 PMIC. It includes four high
+ efficiency buck converters and one LDO voltage regulator. The device
+ is targeted at providing the CPU voltage, memory, I/O and peripheral
+ power rails in home entertainment devices.
+
config MFD_RC5T583
bool "Ricoh RC5T583 Power Management system device"
depends on I2C=y
@@ -1224,7 +1262,7 @@ config MFD_SI476X_CORE
module will be called si476x-core.
config MFD_SIMPLE_MFD_I2C
- tristate "Simple Multi-Functional Device support (I2C)"
+ tristate
depends on I2C
select MFD_CORE
select REGMAP_I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 0004b7e86220..7ed3ef4a698c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -175,6 +175,11 @@ obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
obj-$(CONFIG_MFD_MP2629) += mp2629.o
+obj-$(CONFIG_MFD_MT6360) += mt6360-core.o
+obj-$(CONFIG_MFD_MT6370) += mt6370.o
+mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o
+obj-$(CONFIG_MFD_MT6397) += mt6397.o
+
pcf50633-objs := pcf50633-core.o pcf50633-irq.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
@@ -237,16 +242,13 @@ obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o
obj-$(CONFIG_MFD_DLN2) += dln2.o
obj-$(CONFIG_MFD_RT4831) += rt4831.o
obj-$(CONFIG_MFD_RT5033) += rt5033.o
+obj-$(CONFIG_MFD_RT5120) += rt5120.o
obj-$(CONFIG_MFD_SKY81452) += sky81452.o
-intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
-obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
+obj-$(CONFIG_INTEL_SOC_PMIC) += intel_soc_pmic_crc.o
obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC) += intel_soc_pmic_bxtwc.o
obj-$(CONFIG_INTEL_SOC_PMIC_CHTWC) += intel_soc_pmic_chtwc.o
obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o
-obj-$(CONFIG_MFD_MT6360) += mt6360-core.o
-mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o
-obj-$(CONFIG_MFD_MT6397) += mt6397.o
obj-$(CONFIG_INTEL_SOC_PMIC_MRFLD) += intel_soc_pmic_mrfld.o
obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c
index 0a80d82c6858..a26e473507c7 100644
--- a/drivers/mfd/da9062-core.c
+++ b/drivers/mfd/da9062-core.c
@@ -453,6 +453,7 @@ static const struct regmap_range da9061_aa_writeable_ranges[] = {
regmap_reg_range(DA9062AA_VBUCK1_B, DA9062AA_VBUCK4_B),
regmap_reg_range(DA9062AA_VBUCK3_B, DA9062AA_VBUCK3_B),
regmap_reg_range(DA9062AA_VLDO1_B, DA9062AA_VLDO4_B),
+ regmap_reg_range(DA9062AA_CONFIG_J, DA9062AA_CONFIG_J),
regmap_reg_range(DA9062AA_GP_ID_0, DA9062AA_GP_ID_19),
};
diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
index 37e5e02a1d05..823595bcc9b7 100644
--- a/drivers/mfd/fsl-imx25-tsadc.c
+++ b/drivers/mfd/fsl-imx25-tsadc.c
@@ -69,7 +69,7 @@ static int mx25_tsadc_setup_irq(struct platform_device *pdev,
int irq;
irq = platform_get_irq(pdev, 0);
- if (irq <= 0)
+ if (irq < 0)
return irq;
tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
@@ -84,6 +84,19 @@ static int mx25_tsadc_setup_irq(struct platform_device *pdev,
return 0;
}
+static int mx25_tsadc_unset_irq(struct platform_device *pdev)
+{
+ struct mx25_tsadc *tsadc = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (irq >= 0) {
+ irq_set_chained_handler_and_data(irq, NULL, NULL);
+ irq_domain_remove(tsadc->domain);
+ }
+
+ return 0;
+}
+
static void mx25_tsadc_setup_clk(struct platform_device *pdev,
struct mx25_tsadc *tsadc)
{
@@ -171,18 +184,21 @@ static int mx25_tsadc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, tsadc);
- return devm_of_platform_populate(dev);
+ ret = devm_of_platform_populate(dev);
+ if (ret)
+ goto err_irq;
+
+ return 0;
+
+err_irq:
+ mx25_tsadc_unset_irq(pdev);
+
+ return ret;
}
static int mx25_tsadc_remove(struct platform_device *pdev)
{
- struct mx25_tsadc *tsadc = platform_get_drvdata(pdev);
- int irq = platform_get_irq(pdev, 0);
-
- if (irq) {
- irq_set_chained_handler_and_data(irq, NULL, NULL);
- irq_domain_remove(tsadc->domain);
- }
+ mx25_tsadc_unset_irq(pdev);
return 0;
}
diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c
index 417b0355d904..b45b1346ab54 100644
--- a/drivers/mfd/htc-i2cpld.c
+++ b/drivers/mfd/htc-i2cpld.c
@@ -20,7 +20,9 @@
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/htcpld.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/machine.h>
+#include <linux/gpio/consumer.h>
#include <linux/slab.h>
struct htcpld_chip {
@@ -58,8 +60,8 @@ struct htcpld_data {
uint irq_start;
int nirqs;
uint chained_irq;
- unsigned int int_reset_gpio_hi;
- unsigned int int_reset_gpio_lo;
+ struct gpio_desc *int_reset_gpio_hi;
+ struct gpio_desc *int_reset_gpio_lo;
/* htcpld info */
struct htcpld_chip *chip;
@@ -196,9 +198,9 @@ static irqreturn_t htcpld_handler(int irq, void *dev)
* be asserted.
*/
if (htcpld->int_reset_gpio_hi)
- gpio_set_value(htcpld->int_reset_gpio_hi, 1);
+ gpiod_set_value(htcpld->int_reset_gpio_hi, 1);
if (htcpld->int_reset_gpio_lo)
- gpio_set_value(htcpld->int_reset_gpio_lo, 0);
+ gpiod_set_value(htcpld->int_reset_gpio_lo, 0);
return IRQ_HANDLED;
}
@@ -352,7 +354,7 @@ static int htcpld_register_chip_i2c(
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = plat_chip_data->addr;
- strlcpy(info.type, "htcpld-chip", I2C_NAME_SIZE);
+ strscpy(info.type, "htcpld-chip", I2C_NAME_SIZE);
info.platform_data = chip;
/* Add the I2C device. This calls the probe() function. */
@@ -562,34 +564,28 @@ static int htcpld_core_probe(struct platform_device *pdev)
return ret;
/* Request the GPIO(s) for the int reset and set them up */
- if (pdata->int_reset_gpio_hi) {
- ret = gpio_request(pdata->int_reset_gpio_hi, "htcpld-core");
- if (ret) {
- /*
- * If it failed, that sucks, but we can probably
- * continue on without it.
- */
- dev_warn(dev, "Unable to request int_reset_gpio_hi -- interrupts may not work\n");
- htcpld->int_reset_gpio_hi = 0;
- } else {
- htcpld->int_reset_gpio_hi = pdata->int_reset_gpio_hi;
- gpio_set_value(htcpld->int_reset_gpio_hi, 1);
- }
+ htcpld->int_reset_gpio_hi = gpiochip_request_own_desc(&htcpld->chip[2].chip_out,
+ 7, "htcpld-core", GPIO_ACTIVE_HIGH,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(htcpld->int_reset_gpio_hi)) {
+ /*
+ * If it failed, that sucks, but we can probably
+ * continue on without it.
+ */
+ htcpld->int_reset_gpio_hi = NULL;
+ dev_warn(dev, "Unable to request int_reset_gpio_hi -- interrupts may not work\n");
}
- if (pdata->int_reset_gpio_lo) {
- ret = gpio_request(pdata->int_reset_gpio_lo, "htcpld-core");
- if (ret) {
- /*
- * If it failed, that sucks, but we can probably
- * continue on without it.
- */
- dev_warn(dev, "Unable to request int_reset_gpio_lo -- interrupts may not work\n");
- htcpld->int_reset_gpio_lo = 0;
- } else {
- htcpld->int_reset_gpio_lo = pdata->int_reset_gpio_lo;
- gpio_set_value(htcpld->int_reset_gpio_lo, 0);
- }
+ htcpld->int_reset_gpio_lo = gpiochip_request_own_desc(&htcpld->chip[2].chip_out,
+ 0, "htcpld-core", GPIO_ACTIVE_HIGH,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(htcpld->int_reset_gpio_lo)) {
+ /*
+ * If it failed, that sucks, but we can probably
+ * continue on without it.
+ */
+ htcpld->int_reset_gpio_lo = NULL;
+ dev_warn(dev, "Unable to request int_reset_gpio_lo -- interrupts may not work\n");
}
dev_info(dev, "Initialized successfully\n");
diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c
index bb08b7a73fe1..dde31c50a632 100644
--- a/drivers/mfd/intel-lpss-pci.c
+++ b/drivers/mfd/intel-lpss-pci.c
@@ -14,6 +14,7 @@
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
+#include <linux/pxa2xx_ssp.h>
#include "intel-lpss.h"
@@ -73,8 +74,18 @@ static void intel_lpss_pci_remove(struct pci_dev *pdev)
static INTEL_LPSS_PM_OPS(intel_lpss_pci_pm_ops);
+static const struct property_entry spt_spi_properties[] = {
+ PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_SPT_SSP),
+ { }
+};
+
+static const struct software_node spt_spi_node = {
+ .properties = spt_spi_properties,
+};
+
static const struct intel_lpss_platform_info spt_info = {
.clk_rate = 120000000,
+ .swnode = &spt_spi_node,
};
static const struct property_entry spt_i2c_properties[] = {
@@ -108,8 +119,18 @@ static const struct intel_lpss_platform_info spt_uart_info = {
.swnode = &uart_node,
};
+static const struct property_entry bxt_spi_properties[] = {
+ PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BXT_SSP),
+ { }
+};
+
+static const struct software_node bxt_spi_node = {
+ .properties = bxt_spi_properties,
+};
+
static const struct intel_lpss_platform_info bxt_info = {
.clk_rate = 100000000,
+ .swnode = &bxt_spi_node,
};
static const struct intel_lpss_platform_info bxt_uart_info = {
@@ -166,6 +187,20 @@ static const struct intel_lpss_platform_info glk_i2c_info = {
.swnode = &glk_i2c_node,
};
+static const struct property_entry cnl_spi_properties[] = {
+ PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_CNL_SSP),
+ { }
+};
+
+static const struct software_node cnl_spi_node = {
+ .properties = cnl_spi_properties,
+};
+
+static const struct intel_lpss_platform_info cnl_info = {
+ .clk_rate = 120000000,
+ .swnode = &cnl_spi_node,
+};
+
static const struct intel_lpss_platform_info cnl_i2c_info = {
.clk_rate = 216000000,
.swnode = &spt_i2c_node,
@@ -176,12 +211,26 @@ static const struct intel_lpss_platform_info ehl_i2c_info = {
.swnode = &bxt_i2c_node,
};
+static const struct property_entry tgl_spi_properties[] = {
+ PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_CNL_SSP),
+ { }
+};
+
+static const struct software_node tgl_spi_node = {
+ .properties = tgl_spi_properties,
+};
+
+static const struct intel_lpss_platform_info tgl_info = {
+ .clk_rate = 100000000,
+ .swnode = &tgl_spi_node,
+};
+
static const struct pci_device_id intel_lpss_pci_ids[] = {
/* CML-LP */
{ PCI_VDEVICE(INTEL, 0x02a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x02a9), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x02aa), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x02ab), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x02aa), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x02ab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x02c5), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02c6), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02c7), (kernel_ulong_t)&spt_uart_info },
@@ -189,18 +238,18 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x02e9), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02ea), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02eb), (kernel_ulong_t)&cnl_i2c_info },
- { PCI_VDEVICE(INTEL, 0x02fb), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x02fb), (kernel_ulong_t)&cnl_info },
/* CML-H */
{ PCI_VDEVICE(INTEL, 0x06a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x06a9), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x06aa), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x06ab), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x06aa), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x06ab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x06c7), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x06e8), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x06e9), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x06ea), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x06eb), (kernel_ulong_t)&cnl_i2c_info },
- { PCI_VDEVICE(INTEL, 0x06fb), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x06fb), (kernel_ulong_t)&cnl_info },
/* BXT A-Step */
{ PCI_VDEVICE(INTEL, 0x0aac), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x0aae), (kernel_ulong_t)&bxt_i2c_info },
@@ -255,8 +304,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* ICL-LP */
{ PCI_VDEVICE(INTEL, 0x34a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x34a9), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x34aa), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x34ab), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x34aa), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x34ab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x34c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34c7), (kernel_ulong_t)&spt_uart_info },
@@ -264,15 +313,15 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x34e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34eb), (kernel_ulong_t)&bxt_i2c_info },
- { PCI_VDEVICE(INTEL, 0x34fb), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x34fb), (kernel_ulong_t)&cnl_info },
/* ICL-N */
{ PCI_VDEVICE(INTEL, 0x38a8), (kernel_ulong_t)&spt_uart_info },
/* TGL-H */
{ PCI_VDEVICE(INTEL, 0x43a7), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x43a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x43a9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x43aa), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x43ab), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x43aa), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x43ab), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x43ad), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43ae), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43d8), (kernel_ulong_t)&bxt_i2c_info },
@@ -281,8 +330,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x43e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43eb), (kernel_ulong_t)&bxt_i2c_info },
- { PCI_VDEVICE(INTEL, 0x43fb), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x43fd), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x43fb), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x43fd), (kernel_ulong_t)&tgl_info },
/* EHL */
{ PCI_VDEVICE(INTEL, 0x4b28), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x4b29), (kernel_ulong_t)&bxt_uart_info },
@@ -301,8 +350,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* JSL */
{ PCI_VDEVICE(INTEL, 0x4da8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x4da9), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x4daa), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x4dab), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x4daa), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x4dab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x4dc5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4dc6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4dc7), (kernel_ulong_t)&spt_uart_info },
@@ -310,12 +359,12 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x4de9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4dea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4deb), (kernel_ulong_t)&bxt_i2c_info },
- { PCI_VDEVICE(INTEL, 0x4dfb), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x4dfb), (kernel_ulong_t)&cnl_info },
/* ADL-P */
{ PCI_VDEVICE(INTEL, 0x51a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x51a9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x51aa), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x51ab), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x51aa), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x51ab), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x51c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51c7), (kernel_ulong_t)&bxt_uart_info },
@@ -325,12 +374,12 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x51e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51eb), (kernel_ulong_t)&bxt_i2c_info },
- { PCI_VDEVICE(INTEL, 0x51fb), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x51fb), (kernel_ulong_t)&tgl_info },
/* ADL-M */
{ PCI_VDEVICE(INTEL, 0x54a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x54a9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x54aa), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x54ab), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x54aa), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x54ab), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x54c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54c7), (kernel_ulong_t)&bxt_uart_info },
@@ -338,7 +387,7 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x54e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54eb), (kernel_ulong_t)&bxt_i2c_info },
- { PCI_VDEVICE(INTEL, 0x54fb), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x54fb), (kernel_ulong_t)&tgl_info },
/* APL */
{ PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info },
@@ -358,39 +407,39 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* RPL-S */
{ PCI_VDEVICE(INTEL, 0x7a28), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7a29), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7a2a), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x7a2b), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x7a2a), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7a2b), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7a4c), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a4d), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a4e), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a4f), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a5c), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7a79), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x7a7b), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x7a79), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7a7b), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7a7c), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a7d), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a7e), (kernel_ulong_t)&bxt_uart_info },
/* ADL-S */
{ PCI_VDEVICE(INTEL, 0x7aa8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7aa9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7aaa), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x7aab), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x7aaa), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7aab), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7acc), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7acd), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7ace), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7acf), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7adc), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7af9), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x7afb), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x7af9), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7afb), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7afc), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7afd), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7afe), (kernel_ulong_t)&bxt_uart_info },
/* MTL-P */
{ PCI_VDEVICE(INTEL, 0x7e25), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7e26), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7e27), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x7e30), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x7e46), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x7e27), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7e30), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7e46), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7e50), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7e51), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7e52), (kernel_ulong_t)&bxt_uart_info },
@@ -424,8 +473,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* CNL-LP */
{ PCI_VDEVICE(INTEL, 0x9da8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9da9), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x9daa), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x9dab), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x9daa), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x9dab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x9dc5), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9dc6), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9dc7), (kernel_ulong_t)&spt_uart_info },
@@ -433,12 +482,12 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x9de9), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9dea), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9deb), (kernel_ulong_t)&cnl_i2c_info },
- { PCI_VDEVICE(INTEL, 0x9dfb), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x9dfb), (kernel_ulong_t)&cnl_info },
/* TGL-LP */
{ PCI_VDEVICE(INTEL, 0xa0a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0a9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0xa0aa), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0xa0ab), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0xa0aa), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa0ab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa0c5), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0c6), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0c7), (kernel_ulong_t)&bxt_uart_info },
@@ -448,15 +497,15 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0xa0db), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0dc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0dd), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0xa0de), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0xa0df), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0xa0de), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa0df), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa0e8), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0e9), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0ea), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0eb), (kernel_ulong_t)&spt_i2c_info },
- { PCI_VDEVICE(INTEL, 0xa0fb), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0xa0fd), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0xa0fe), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0xa0fb), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa0fd), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa0fe), (kernel_ulong_t)&cnl_info },
/* SPT-H */
{ PCI_VDEVICE(INTEL, 0xa127), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa128), (kernel_ulong_t)&spt_uart_info },
@@ -479,14 +528,14 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* CNL-H */
{ PCI_VDEVICE(INTEL, 0xa328), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa329), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0xa32a), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0xa32b), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0xa32a), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa32b), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa347), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa368), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa369), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa36a), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa36b), (kernel_ulong_t)&cnl_i2c_info },
- { PCI_VDEVICE(INTEL, 0xa37b), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0xa37b), (kernel_ulong_t)&cnl_info },
/* CML-V */
{ PCI_VDEVICE(INTEL, 0xa3a7), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa3a8), (kernel_ulong_t)&spt_uart_info },
diff --git a/drivers/mfd/intel-m10-bmc.c b/drivers/mfd/intel-m10-bmc.c
index f4d0d72573c8..7e3319e5b22f 100644
--- a/drivers/mfd/intel-m10-bmc.c
+++ b/drivers/mfd/intel-m10-bmc.c
@@ -21,6 +21,7 @@ enum m10bmc_type {
static struct mfd_cell m10bmc_d5005_subdevs[] = {
{ .name = "d5005bmc-hwmon" },
+ { .name = "d5005bmc-sec-update" }
};
static struct mfd_cell m10bmc_pacn3000_subdevs[] = {
diff --git a/drivers/mfd/intel_soc_pmic_chtdc_ti.c b/drivers/mfd/intel_soc_pmic_chtdc_ti.c
index 1c7577b881ff..282b8fd08009 100644
--- a/drivers/mfd/intel_soc_pmic_chtdc_ti.c
+++ b/drivers/mfd/intel_soc_pmic_chtdc_ti.c
@@ -140,7 +140,7 @@ static void chtdc_ti_shutdown(struct i2c_client *i2c)
disable_irq(pmic->irq);
}
-static int __maybe_unused chtdc_ti_suspend(struct device *dev)
+static int chtdc_ti_suspend(struct device *dev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
@@ -149,7 +149,7 @@ static int __maybe_unused chtdc_ti_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused chtdc_ti_resume(struct device *dev)
+static int chtdc_ti_resume(struct device *dev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
@@ -158,7 +158,7 @@ static int __maybe_unused chtdc_ti_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(chtdc_ti_pm_ops, chtdc_ti_suspend, chtdc_ti_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(chtdc_ti_pm_ops, chtdc_ti_suspend, chtdc_ti_resume);
static const struct acpi_device_id chtdc_ti_acpi_ids[] = {
{ "INT33F5" },
@@ -169,7 +169,7 @@ MODULE_DEVICE_TABLE(acpi, chtdc_ti_acpi_ids);
static struct i2c_driver chtdc_ti_i2c_driver = {
.driver = {
.name = "intel_soc_pmic_chtdc_ti",
- .pm = &chtdc_ti_pm_ops,
+ .pm = pm_sleep_ptr(&chtdc_ti_pm_ops),
.acpi_match_table = chtdc_ti_acpi_ids,
},
.probe_new = chtdc_ti_probe,
diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c
deleted file mode 100644
index b824e15f4d22..000000000000
--- a/drivers/mfd/intel_soc_pmic_core.c
+++ /dev/null
@@ -1,158 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Intel SoC PMIC MFD Driver
- *
- * Copyright (C) 2013, 2014 Intel Corporation. All rights reserved.
- *
- * Author: Yang, Bin <bin.yang@intel.com>
- * Author: Zhu, Lejun <lejun.zhu@linux.intel.com>
- */
-
-#include <linux/acpi.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/intel_soc_pmic.h>
-#include <linux/platform_data/x86/soc.h>
-#include <linux/pwm.h>
-#include <linux/regmap.h>
-
-#include "intel_soc_pmic_core.h"
-
-/* PWM consumed by the Intel GFX */
-static struct pwm_lookup crc_pwm_lookup[] = {
- PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_pmic_backlight", 0, PWM_POLARITY_NORMAL),
-};
-
-static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
-{
- struct device *dev = &i2c->dev;
- struct intel_soc_pmic_config *config;
- struct intel_soc_pmic *pmic;
- int ret;
-
- if (soc_intel_is_byt())
- config = &intel_soc_pmic_config_byt_crc;
- else
- config = &intel_soc_pmic_config_cht_crc;
-
- pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
- if (!pmic)
- return -ENOMEM;
-
- dev_set_drvdata(dev, pmic);
-
- pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config);
- if (IS_ERR(pmic->regmap))
- return PTR_ERR(pmic->regmap);
-
- pmic->irq = i2c->irq;
-
- ret = regmap_add_irq_chip(pmic->regmap, pmic->irq,
- config->irq_flags | IRQF_ONESHOT,
- 0, config->irq_chip,
- &pmic->irq_chip_data);
- if (ret)
- return ret;
-
- ret = enable_irq_wake(pmic->irq);
- if (ret)
- dev_warn(dev, "Can't enable IRQ as wake source: %d\n", ret);
-
- /* Add lookup table for crc-pwm */
- pwm_add_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup));
-
- /* To distuingish this domain from the GPIO/charger's irqchip domains */
- irq_domain_update_bus_token(regmap_irq_get_domain(pmic->irq_chip_data),
- DOMAIN_BUS_NEXUS);
-
- ret = mfd_add_devices(dev, -1, config->cell_dev,
- config->n_cell_devs, NULL, 0,
- regmap_irq_get_domain(pmic->irq_chip_data));
- if (ret)
- goto err_del_irq_chip;
-
- return 0;
-
-err_del_irq_chip:
- regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data);
- return ret;
-}
-
-static void intel_soc_pmic_i2c_remove(struct i2c_client *i2c)
-{
- struct intel_soc_pmic *pmic = dev_get_drvdata(&i2c->dev);
-
- regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data);
-
- /* remove crc-pwm lookup table */
- pwm_remove_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup));
-
- mfd_remove_devices(&i2c->dev);
-}
-
-static void intel_soc_pmic_shutdown(struct i2c_client *i2c)
-{
- struct intel_soc_pmic *pmic = dev_get_drvdata(&i2c->dev);
-
- disable_irq(pmic->irq);
-
- return;
-}
-
-#if defined(CONFIG_PM_SLEEP)
-static int intel_soc_pmic_suspend(struct device *dev)
-{
- struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
-
- disable_irq(pmic->irq);
-
- return 0;
-}
-
-static int intel_soc_pmic_resume(struct device *dev)
-{
- struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
-
- enable_irq(pmic->irq);
-
- return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(intel_soc_pmic_pm_ops, intel_soc_pmic_suspend,
- intel_soc_pmic_resume);
-
-static const struct i2c_device_id intel_soc_pmic_i2c_id[] = {
- { }
-};
-MODULE_DEVICE_TABLE(i2c, intel_soc_pmic_i2c_id);
-
-#if defined(CONFIG_ACPI)
-static const struct acpi_device_id intel_soc_pmic_acpi_match[] = {
- { "INT33FD" },
- { },
-};
-MODULE_DEVICE_TABLE(acpi, intel_soc_pmic_acpi_match);
-#endif
-
-static struct i2c_driver intel_soc_pmic_i2c_driver = {
- .driver = {
- .name = "intel_soc_pmic_i2c",
- .pm = &intel_soc_pmic_pm_ops,
- .acpi_match_table = ACPI_PTR(intel_soc_pmic_acpi_match),
- },
- .probe = intel_soc_pmic_i2c_probe,
- .remove = intel_soc_pmic_i2c_remove,
- .id_table = intel_soc_pmic_i2c_id,
- .shutdown = intel_soc_pmic_shutdown,
-};
-
-module_i2c_driver(intel_soc_pmic_i2c_driver);
-
-MODULE_DESCRIPTION("I2C driver for Intel SoC PMIC");
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Yang, Bin <bin.yang@intel.com>");
-MODULE_AUTHOR("Zhu, Lejun <lejun.zhu@linux.intel.com>");
diff --git a/drivers/mfd/intel_soc_pmic_core.h b/drivers/mfd/intel_soc_pmic_core.h
deleted file mode 100644
index d490685845eb..000000000000
--- a/drivers/mfd/intel_soc_pmic_core.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Intel SoC PMIC MFD Driver
- *
- * Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
- *
- * Author: Yang, Bin <bin.yang@intel.com>
- * Author: Zhu, Lejun <lejun.zhu@linux.intel.com>
- */
-
-#ifndef __INTEL_SOC_PMIC_CORE_H__
-#define __INTEL_SOC_PMIC_CORE_H__
-
-struct intel_soc_pmic_config {
- unsigned long irq_flags;
- struct mfd_cell *cell_dev;
- int n_cell_devs;
- const struct regmap_config *regmap_config;
- const struct regmap_irq_chip *irq_chip;
-};
-
-extern struct intel_soc_pmic_config intel_soc_pmic_config_byt_crc;
-extern struct intel_soc_pmic_config intel_soc_pmic_config_cht_crc;
-
-#endif /* __INTEL_SOC_PMIC_CORE_H__ */
diff --git a/drivers/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c
index 5bb0367bd974..b1548a933dc3 100644
--- a/drivers/mfd/intel_soc_pmic_crc.c
+++ b/drivers/mfd/intel_soc_pmic_crc.c
@@ -2,18 +2,21 @@
/*
* Device access for Crystal Cove PMIC
*
- * Copyright (C) 2013, 2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012-2014, 2022 Intel Corporation. All rights reserved.
*
* Author: Yang, Bin <bin.yang@intel.com>
* Author: Zhu, Lejun <lejun.zhu@linux.intel.com>
*/
+#include <linux/i2c.h>
#include <linux/interrupt.h>
-#include <linux/regmap.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
#include <linux/mfd/core.h>
#include <linux/mfd/intel_soc_pmic.h>
-
-#include "intel_soc_pmic_core.h"
+#include <linux/platform_data/x86/soc.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
#define CRYSTAL_COVE_MAX_REGISTER 0xC6
@@ -132,7 +135,20 @@ static const struct regmap_irq_chip crystal_cove_irq_chip = {
.mask_base = CRYSTAL_COVE_REG_MIRQLVL1,
};
-struct intel_soc_pmic_config intel_soc_pmic_config_byt_crc = {
+/* PWM consumed by the Intel GFX */
+static struct pwm_lookup crc_pwm_lookup[] = {
+ PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_pmic_backlight", 0, PWM_POLARITY_NORMAL),
+};
+
+struct crystal_cove_config {
+ unsigned long irq_flags;
+ struct mfd_cell *cell_dev;
+ int n_cell_devs;
+ const struct regmap_config *regmap_config;
+ const struct regmap_irq_chip *irq_chip;
+};
+
+static const struct crystal_cove_config crystal_cove_config_byt_crc = {
.irq_flags = IRQF_TRIGGER_RISING,
.cell_dev = crystal_cove_byt_dev,
.n_cell_devs = ARRAY_SIZE(crystal_cove_byt_dev),
@@ -140,10 +156,121 @@ struct intel_soc_pmic_config intel_soc_pmic_config_byt_crc = {
.irq_chip = &crystal_cove_irq_chip,
};
-struct intel_soc_pmic_config intel_soc_pmic_config_cht_crc = {
+static const struct crystal_cove_config crystal_cove_config_cht_crc = {
.irq_flags = IRQF_TRIGGER_RISING,
.cell_dev = crystal_cove_cht_dev,
.n_cell_devs = ARRAY_SIZE(crystal_cove_cht_dev),
.regmap_config = &crystal_cove_regmap_config,
.irq_chip = &crystal_cove_irq_chip,
};
+
+static int crystal_cove_i2c_probe(struct i2c_client *i2c)
+{
+ const struct crystal_cove_config *config;
+ struct device *dev = &i2c->dev;
+ struct intel_soc_pmic *pmic;
+ int ret;
+
+ if (soc_intel_is_byt())
+ config = &crystal_cove_config_byt_crc;
+ else
+ config = &crystal_cove_config_cht_crc;
+
+ pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, pmic);
+
+ pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config);
+ if (IS_ERR(pmic->regmap))
+ return PTR_ERR(pmic->regmap);
+
+ pmic->irq = i2c->irq;
+
+ ret = devm_regmap_add_irq_chip(dev, pmic->regmap, pmic->irq,
+ config->irq_flags | IRQF_ONESHOT,
+ 0, config->irq_chip, &pmic->irq_chip_data);
+ if (ret)
+ return ret;
+
+ ret = enable_irq_wake(pmic->irq);
+ if (ret)
+ dev_warn(dev, "Can't enable IRQ as wake source: %d\n", ret);
+
+ /* Add lookup table for crc-pwm */
+ pwm_add_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup));
+
+ /* To distuingish this domain from the GPIO/charger's irqchip domains */
+ irq_domain_update_bus_token(regmap_irq_get_domain(pmic->irq_chip_data),
+ DOMAIN_BUS_NEXUS);
+
+ ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE, config->cell_dev,
+ config->n_cell_devs, NULL, 0,
+ regmap_irq_get_domain(pmic->irq_chip_data));
+ if (ret)
+ pwm_remove_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup));
+
+ return ret;
+}
+
+static void crystal_cove_i2c_remove(struct i2c_client *i2c)
+{
+ /* remove crc-pwm lookup table */
+ pwm_remove_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup));
+
+ mfd_remove_devices(&i2c->dev);
+}
+
+static void crystal_cove_shutdown(struct i2c_client *i2c)
+{
+ struct intel_soc_pmic *pmic = i2c_get_clientdata(i2c);
+
+ disable_irq(pmic->irq);
+
+ return;
+}
+
+static int crystal_cove_suspend(struct device *dev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
+
+ disable_irq(pmic->irq);
+
+ return 0;
+}
+
+static int crystal_cove_resume(struct device *dev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
+
+ enable_irq(pmic->irq);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(crystal_cove_pm_ops, crystal_cove_suspend, crystal_cove_resume);
+
+static const struct acpi_device_id crystal_cove_acpi_match[] = {
+ { "INT33FD" },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, crystal_cove_acpi_match);
+
+static struct i2c_driver crystal_cove_i2c_driver = {
+ .driver = {
+ .name = "crystal_cove_i2c",
+ .pm = pm_sleep_ptr(&crystal_cove_pm_ops),
+ .acpi_match_table = crystal_cove_acpi_match,
+ },
+ .probe_new = crystal_cove_i2c_probe,
+ .remove = crystal_cove_i2c_remove,
+ .shutdown = crystal_cove_shutdown,
+};
+
+module_i2c_driver(crystal_cove_i2c_driver);
+
+MODULE_DESCRIPTION("I2C driver for Intel SoC PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Yang, Bin <bin.yang@intel.com>");
+MODULE_AUTHOR("Zhu, Lejun <lejun.zhu@linux.intel.com>");
diff --git a/drivers/mfd/lp8788-irq.c b/drivers/mfd/lp8788-irq.c
index 348439a3fbbd..39006297f3d2 100644
--- a/drivers/mfd/lp8788-irq.c
+++ b/drivers/mfd/lp8788-irq.c
@@ -175,6 +175,7 @@ int lp8788_irq_init(struct lp8788 *lp, int irq)
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"lp8788-irq", irqd);
if (ret) {
+ irq_domain_remove(lp->irqdm);
dev_err(lp->dev, "failed to create a thread for IRQ_N\n");
return ret;
}
@@ -188,4 +189,6 @@ void lp8788_irq_exit(struct lp8788 *lp)
{
if (lp->irq)
free_irq(lp->irq, lp->irqdm);
+ if (lp->irqdm)
+ irq_domain_remove(lp->irqdm);
}
diff --git a/drivers/mfd/lp8788.c b/drivers/mfd/lp8788.c
index e7c601bca9ef..724a5712b36b 100644
--- a/drivers/mfd/lp8788.c
+++ b/drivers/mfd/lp8788.c
@@ -195,8 +195,16 @@ static int lp8788_probe(struct i2c_client *cl, const struct i2c_device_id *id)
if (ret)
return ret;
- return mfd_add_devices(lp->dev, -1, lp8788_devs,
- ARRAY_SIZE(lp8788_devs), NULL, 0, NULL);
+ ret = mfd_add_devices(lp->dev, -1, lp8788_devs,
+ ARRAY_SIZE(lp8788_devs), NULL, 0, NULL);
+ if (ret)
+ goto err_exit_irq;
+
+ return 0;
+
+err_exit_irq:
+ lp8788_irq_exit(lp);
+ return ret;
}
static void lp8788_remove(struct i2c_client *cl)
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index 650951f89f1c..7b1c597b6879 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -959,7 +959,7 @@ static int lpc_ich_finalize_wdt_cell(struct pci_dev *dev)
info = &lpc_chipset_info[priv->chipset];
pdata->version = info->iTCO_version;
- strlcpy(pdata->name, info->name, sizeof(pdata->name));
+ strscpy(pdata->name, info->name, sizeof(pdata->name));
cell->platform_data = pdata;
cell->pdata_size = sizeof(*pdata);
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 8b058200d5ad..16d1861e9682 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -105,7 +105,7 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell,
.ids = ids,
};
- strlcpy(ids[0].id, match->pnpid, sizeof(ids[0].id));
+ strscpy(ids[0].id, match->pnpid, sizeof(ids[0].id));
acpi_dev_for_each_child(parent, match_device_ids, &wd);
adev = wd.adev;
} else {
@@ -368,6 +368,7 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
{
struct platform_device *pdev;
const struct mfd_cell *cell;
+ struct mfd_of_node_entry *of_entry, *tmp;
int *level = data;
if (dev->type != &mfd_dev_type)
@@ -382,6 +383,12 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
if (cell->swnode)
device_remove_software_node(&pdev->dev);
+ list_for_each_entry_safe(of_entry, tmp, &mfd_of_node_list, list)
+ if (of_entry->dev == &pdev->dev) {
+ list_del(&of_entry->list);
+ kfree(of_entry);
+ }
+
regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies,
cell->num_parent_supplies);
diff --git a/drivers/mfd/mt6370.c b/drivers/mfd/mt6370.c
new file mode 100644
index 000000000000..cf19cce2fdc0
--- /dev/null
+++ b/drivers/mfd/mt6370.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 Richtek Technology Corp.
+ *
+ * Author: ChiYuan Huang <cy_huang@richtek.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "mt6370.h"
+
+#define MT6370_REG_DEV_INFO 0x100
+#define MT6370_REG_CHG_IRQ1 0x1C0
+#define MT6370_REG_CHG_MASK1 0x1E0
+#define MT6370_REG_MAXADDR 0x1FF
+
+#define MT6370_VENID_MASK GENMASK(7, 4)
+
+#define MT6370_NUM_IRQREGS 16
+#define MT6370_USBC_I2CADDR 0x4E
+#define MT6370_MAX_ADDRLEN 2
+
+#define MT6370_VENID_RT5081 0x8
+#define MT6370_VENID_RT5081A 0xA
+#define MT6370_VENID_MT6370 0xE
+#define MT6370_VENID_MT6371 0xF
+#define MT6370_VENID_MT6372P 0x9
+#define MT6370_VENID_MT6372CP 0xB
+
+static const struct regmap_irq mt6370_irqs[] = {
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHGON, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TREG, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_AICR, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_MIVR, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_PWR_RDY, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FL_CHG_VINOVP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VSYSUV, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VSYSOV, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VBATOV, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VINOVPCHG, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_COLD, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_COOL, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_WARM, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_HOT, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_STATC, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_FAULT, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_STATC, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TMR, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_BATABS, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_ADPBAD, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_RVP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_TSHUTDOWN, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_IINMEAS, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_ICCMEAS, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHGDET_DONE, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_WDTMR, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_SSFINISH, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_RECHG, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TERM, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_IEOC, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_ADC_DONE, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_PUMPX_DONE, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_BATUV, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_MIDOV, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_OLP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_ATTACH, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DETACH, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_STPDONE, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_VBUSDET_DONE, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_DET, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHGDET, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DCDT, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_VGOK, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_WDTMR, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_UC, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_OC, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_OV, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_SWON, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_UVP_D, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_UVP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_OVP_D, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_OVP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_STRBPIN, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_TORPIN, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_TX, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_LVF, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_SHORT, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_SHORT, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_STRB, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_STRB, 8),
+ REGMAP_IRQ_REG_LINE(mT6370_IRQ_FLED2_STRB_TO, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_STRB_TO, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_TOR, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_TOR, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_OTP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_VDDA_OVP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_VDDA_UV, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_LDO_OC, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_BLED_OCP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_BLED_OVP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VNEG_OCP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VPOS_OCP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_BST_OCP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VNEG_SCP, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VPOS_SCP, 8),
+};
+
+static const struct regmap_irq_chip mt6370_irq_chip = {
+ .name = "mt6370-irqs",
+ .status_base = MT6370_REG_CHG_IRQ1,
+ .mask_base = MT6370_REG_CHG_MASK1,
+ .num_regs = MT6370_NUM_IRQREGS,
+ .irqs = mt6370_irqs,
+ .num_irqs = ARRAY_SIZE(mt6370_irqs),
+};
+
+static const struct resource mt6370_regulator_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VPOS_SCP, "db_vpos_scp"),
+ DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VNEG_SCP, "db_vneg_scp"),
+ DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_BST_OCP, "db_vbst_ocp"),
+ DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VPOS_OCP, "db_vpos_ocp"),
+ DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VNEG_OCP, "db_vneg_ocp"),
+ DEFINE_RES_IRQ_NAMED(MT6370_IRQ_LDO_OC, "ldo_oc"),
+};
+
+static const struct mfd_cell mt6370_devices[] = {
+ MFD_CELL_OF("mt6370-adc",
+ NULL, NULL, 0, 0, "mediatek,mt6370-adc"),
+ MFD_CELL_OF("mt6370-charger",
+ NULL, NULL, 0, 0, "mediatek,mt6370-charger"),
+ MFD_CELL_OF("mt6370-flashlight",
+ NULL, NULL, 0, 0, "mediatek,mt6370-flashlight"),
+ MFD_CELL_OF("mt6370-indicator",
+ NULL, NULL, 0, 0, "mediatek,mt6370-indicator"),
+ MFD_CELL_OF("mt6370-tcpc",
+ NULL, NULL, 0, 0, "mediatek,mt6370-tcpc"),
+ MFD_CELL_RES("mt6370-regulator", mt6370_regulator_irqs),
+};
+
+static const struct mfd_cell mt6370_exclusive_devices[] = {
+ MFD_CELL_OF("mt6370-backlight",
+ NULL, NULL, 0, 0, "mediatek,mt6370-backlight"),
+};
+
+static const struct mfd_cell mt6372_exclusive_devices[] = {
+ MFD_CELL_OF("mt6370-backlight",
+ NULL, NULL, 0, 0, "mediatek,mt6372-backlight"),
+};
+
+static int mt6370_check_vendor_info(struct device *dev, struct regmap *rmap,
+ int *vid)
+{
+ unsigned int devinfo;
+ int ret;
+
+ ret = regmap_read(rmap, MT6370_REG_DEV_INFO, &devinfo);
+ if (ret)
+ return ret;
+
+ *vid = FIELD_GET(MT6370_VENID_MASK, devinfo);
+ switch (*vid) {
+ case MT6370_VENID_RT5081:
+ case MT6370_VENID_RT5081A:
+ case MT6370_VENID_MT6370:
+ case MT6370_VENID_MT6371:
+ case MT6370_VENID_MT6372P:
+ case MT6370_VENID_MT6372CP:
+ return 0;
+ default:
+ dev_err(dev, "Unknown Vendor ID 0x%02x\n", devinfo);
+ return -ENODEV;
+ }
+}
+
+static int mt6370_regmap_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf, size_t val_size)
+{
+ struct mt6370_info *info = context;
+ const u8 *u8_buf = reg_buf;
+ u8 bank_idx, bank_addr;
+ int ret;
+
+ bank_idx = u8_buf[0];
+ bank_addr = u8_buf[1];
+
+ ret = i2c_smbus_read_i2c_block_data(info->i2c[bank_idx], bank_addr,
+ val_size, val_buf);
+ if (ret < 0)
+ return ret;
+
+ if (ret != val_size)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt6370_regmap_write(void *context, const void *data, size_t count)
+{
+ struct mt6370_info *info = context;
+ const u8 *u8_buf = data;
+ u8 bank_idx, bank_addr;
+ int len = count - MT6370_MAX_ADDRLEN;
+
+ bank_idx = u8_buf[0];
+ bank_addr = u8_buf[1];
+
+ return i2c_smbus_write_i2c_block_data(info->i2c[bank_idx], bank_addr,
+ len, data + MT6370_MAX_ADDRLEN);
+}
+
+static const struct regmap_bus mt6370_regmap_bus = {
+ .read = mt6370_regmap_read,
+ .write = mt6370_regmap_write,
+};
+
+static const struct regmap_config mt6370_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = MT6370_REG_MAXADDR,
+};
+
+static int mt6370_probe(struct i2c_client *i2c)
+{
+ struct mt6370_info *info;
+ struct i2c_client *usbc_i2c;
+ struct regmap *regmap;
+ struct device *dev = &i2c->dev;
+ int ret, vid;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ usbc_i2c = devm_i2c_new_dummy_device(dev, i2c->adapter,
+ MT6370_USBC_I2CADDR);
+ if (IS_ERR(usbc_i2c))
+ return dev_err_probe(dev, PTR_ERR(usbc_i2c),
+ "Failed to register USBC I2C client\n");
+
+ /* Assign I2C client for PMU and TypeC */
+ info->i2c[MT6370_PMU_I2C] = i2c;
+ info->i2c[MT6370_USBC_I2C] = usbc_i2c;
+
+ regmap = devm_regmap_init(dev, &mt6370_regmap_bus,
+ info, &mt6370_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to init regmap\n");
+
+ ret = mt6370_check_vendor_info(dev, regmap, &vid);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to check vendor info\n");
+
+ ret = devm_regmap_add_irq_chip(dev, regmap, i2c->irq,
+ IRQF_ONESHOT, -1, &mt6370_irq_chip,
+ &info->irq_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add irq chip\n");
+
+ switch (vid) {
+ case MT6370_VENID_MT6372P:
+ case MT6370_VENID_MT6372CP:
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+ mt6372_exclusive_devices,
+ ARRAY_SIZE(mt6372_exclusive_devices),
+ NULL, 0,
+ regmap_irq_get_domain(info->irq_data));
+ break;
+ default:
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+ mt6370_exclusive_devices,
+ ARRAY_SIZE(mt6370_exclusive_devices),
+ NULL, 0,
+ regmap_irq_get_domain(info->irq_data));
+ break;
+ }
+
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add the exclusive devices\n");
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+ mt6370_devices, ARRAY_SIZE(mt6370_devices),
+ NULL, 0,
+ regmap_irq_get_domain(info->irq_data));
+}
+
+static const struct of_device_id mt6370_match_table[] = {
+ { .compatible = "mediatek,mt6370" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt6370_match_table);
+
+static struct i2c_driver mt6370_driver = {
+ .driver = {
+ .name = "mt6370",
+ .of_match_table = mt6370_match_table,
+ },
+ .probe_new = mt6370_probe,
+};
+module_i2c_driver(mt6370_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("MediaTek MT6370 SubPMIC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/mt6370.h b/drivers/mfd/mt6370.h
new file mode 100644
index 000000000000..094e59e4af4e
--- /dev/null
+++ b/drivers/mfd/mt6370.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2022 Richtek Technology Corp.
+ *
+ * Author: ChiYuan Huang <cy_huang@richtek.com>
+ */
+
+#ifndef __MFD_MT6370_H__
+#define __MFD_MT6370_H__
+
+/* IRQ definitions */
+#define MT6370_IRQ_DIRCHGON 0
+#define MT6370_IRQ_CHG_TREG 4
+#define MT6370_IRQ_CHG_AICR 5
+#define MT6370_IRQ_CHG_MIVR 6
+#define MT6370_IRQ_PWR_RDY 7
+#define MT6370_IRQ_FL_CHG_VINOVP 11
+#define MT6370_IRQ_CHG_VSYSUV 12
+#define MT6370_IRQ_CHG_VSYSOV 13
+#define MT6370_IRQ_CHG_VBATOV 14
+#define MT6370_IRQ_CHG_VINOVPCHG 15
+#define MT6370_IRQ_TS_BAT_COLD 20
+#define MT6370_IRQ_TS_BAT_COOL 21
+#define MT6370_IRQ_TS_BAT_WARM 22
+#define MT6370_IRQ_TS_BAT_HOT 23
+#define MT6370_IRQ_TS_STATC 24
+#define MT6370_IRQ_CHG_FAULT 25
+#define MT6370_IRQ_CHG_STATC 26
+#define MT6370_IRQ_CHG_TMR 27
+#define MT6370_IRQ_CHG_BATABS 28
+#define MT6370_IRQ_CHG_ADPBAD 29
+#define MT6370_IRQ_CHG_RVP 30
+#define MT6370_IRQ_TSHUTDOWN 31
+#define MT6370_IRQ_CHG_IINMEAS 32
+#define MT6370_IRQ_CHG_ICCMEAS 33
+#define MT6370_IRQ_CHGDET_DONE 34
+#define MT6370_IRQ_WDTMR 35
+#define MT6370_IRQ_SSFINISH 36
+#define MT6370_IRQ_CHG_RECHG 37
+#define MT6370_IRQ_CHG_TERM 38
+#define MT6370_IRQ_CHG_IEOC 39
+#define MT6370_IRQ_ADC_DONE 40
+#define MT6370_IRQ_PUMPX_DONE 41
+#define MT6370_IRQ_BST_BATUV 45
+#define MT6370_IRQ_BST_MIDOV 46
+#define MT6370_IRQ_BST_OLP 47
+#define MT6370_IRQ_ATTACH 48
+#define MT6370_IRQ_DETACH 49
+#define MT6370_IRQ_HVDCP_STPDONE 51
+#define MT6370_IRQ_HVDCP_VBUSDET_DONE 52
+#define MT6370_IRQ_HVDCP_DET 53
+#define MT6370_IRQ_CHGDET 54
+#define MT6370_IRQ_DCDT 55
+#define MT6370_IRQ_DIRCHG_VGOK 59
+#define MT6370_IRQ_DIRCHG_WDTMR 60
+#define MT6370_IRQ_DIRCHG_UC 61
+#define MT6370_IRQ_DIRCHG_OC 62
+#define MT6370_IRQ_DIRCHG_OV 63
+#define MT6370_IRQ_OVPCTRL_SWON 67
+#define MT6370_IRQ_OVPCTRL_UVP_D 68
+#define MT6370_IRQ_OVPCTRL_UVP 69
+#define MT6370_IRQ_OVPCTRL_OVP_D 70
+#define MT6370_IRQ_OVPCTRL_OVP 71
+#define MT6370_IRQ_FLED_STRBPIN 72
+#define MT6370_IRQ_FLED_TORPIN 73
+#define MT6370_IRQ_FLED_TX 74
+#define MT6370_IRQ_FLED_LVF 75
+#define MT6370_IRQ_FLED2_SHORT 78
+#define MT6370_IRQ_FLED1_SHORT 79
+#define MT6370_IRQ_FLED2_STRB 80
+#define MT6370_IRQ_FLED1_STRB 81
+#define mT6370_IRQ_FLED2_STRB_TO 82
+#define MT6370_IRQ_FLED1_STRB_TO 83
+#define MT6370_IRQ_FLED2_TOR 84
+#define MT6370_IRQ_FLED1_TOR 85
+#define MT6370_IRQ_OTP 93
+#define MT6370_IRQ_VDDA_OVP 94
+#define MT6370_IRQ_VDDA_UV 95
+#define MT6370_IRQ_LDO_OC 103
+#define MT6370_IRQ_BLED_OCP 118
+#define MT6370_IRQ_BLED_OVP 119
+#define MT6370_IRQ_DSV_VNEG_OCP 123
+#define MT6370_IRQ_DSV_VPOS_OCP 124
+#define MT6370_IRQ_DSV_BST_OCP 125
+#define MT6370_IRQ_DSV_VNEG_SCP 126
+#define MT6370_IRQ_DSV_VPOS_SCP 127
+
+enum {
+ MT6370_USBC_I2C = 0,
+ MT6370_PMU_I2C,
+ MT6370_MAX_I2C
+};
+
+struct mt6370_info {
+ struct i2c_client *i2c[MT6370_MAX_I2C];
+ struct regmap_irq_chip_data *irq_data;
+};
+
+#endif /* __MFD_MT6375_H__ */
diff --git a/drivers/mfd/ocelot-spi.c b/drivers/mfd/ocelot-spi.c
index 0f097f4829d1..2ecd271de2fb 100644
--- a/drivers/mfd/ocelot-spi.c
+++ b/drivers/mfd/ocelot-spi.c
@@ -276,6 +276,7 @@ static const struct spi_device_id ocelot_spi_ids[] = {
{ "vsc7512", 0 },
{ }
};
+MODULE_DEVICE_TABLE(spi, ocelot_spi_ids);
static const struct of_device_id ocelot_spi_of_match[] = {
{ .compatible = "mscc,vsc7512" },
diff --git a/drivers/mfd/qcom-spmi-pmic.c b/drivers/mfd/qcom-spmi-pmic.c
index 00003a868d28..7e2cd79d17eb 100644
--- a/drivers/mfd/qcom-spmi-pmic.c
+++ b/drivers/mfd/qcom-spmi-pmic.c
@@ -60,6 +60,7 @@ static const struct of_device_id pmic_spmi_id_table[] = {
{ .compatible = "qcom,pmi8994", .data = N_USIDS(2) },
{ .compatible = "qcom,pmi8998", .data = N_USIDS(2) },
{ .compatible = "qcom,pmk8002", .data = N_USIDS(2) },
+ { .compatible = "qcom,pmp8074", .data = N_USIDS(2) },
{ .compatible = "qcom,smb2351", .data = N_USIDS(2) },
{ .compatible = "qcom,spmi-pmic", .data = N_USIDS(1) },
{ }
diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
index d5d641efa077..e00da7c7e3b1 100644
--- a/drivers/mfd/rk808.c
+++ b/drivers/mfd/rk808.c
@@ -67,6 +67,10 @@ static bool rk817_is_volatile_reg(struct device *dev, unsigned int reg)
case RK817_SECONDS_REG ... RK817_WEEKS_REG:
case RK817_RTC_STATUS_REG:
case RK817_CODEC_DTOP_LPT_SRST:
+ case RK817_GAS_GAUGE_ADC_CONFIG0 ... RK817_GAS_GAUGE_CUR_ADC_K0:
+ case RK817_PMIC_CHRG_STS:
+ case RK817_PMIC_CHRG_OUT:
+ case RK817_PMIC_CHRG_IN:
case RK817_INT_STS_REG0:
case RK817_INT_STS_REG1:
case RK817_INT_STS_REG2:
@@ -74,7 +78,7 @@ static bool rk817_is_volatile_reg(struct device *dev, unsigned int reg)
return true;
}
- return true;
+ return false;
}
static const struct regmap_config rk818_regmap_config = {
@@ -127,6 +131,11 @@ static const struct resource rk817_pwrkey_resources[] = {
DEFINE_RES_IRQ(RK817_IRQ_PWRON_FALL),
};
+static const struct resource rk817_charger_resources[] = {
+ DEFINE_RES_IRQ(RK817_IRQ_PLUG_IN),
+ DEFINE_RES_IRQ(RK817_IRQ_PLUG_OUT),
+};
+
static const struct mfd_cell rk805s[] = {
{ .name = "rk808-clkout", },
{ .name = "rk808-regulator", },
@@ -166,6 +175,11 @@ static const struct mfd_cell rk817s[] = {
.resources = &rk817_rtc_resources[0],
},
{ .name = "rk817-codec",},
+ {
+ .name = "rk817-charger",
+ .num_resources = ARRAY_SIZE(rk817_charger_resources),
+ .resources = &rk817_charger_resources[0],
+ },
};
static const struct mfd_cell rk818s[] = {
diff --git a/drivers/mfd/rt5120.c b/drivers/mfd/rt5120.c
new file mode 100644
index 000000000000..8046e383bc92
--- /dev/null
+++ b/drivers/mfd/rt5120.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 Richtek Technology Corp.
+ * Author: ChiYuan Huang <cy_huang@richtek.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+
+#define RT5120_REG_INTENABLE 0x1D
+#define RT5120_REG_INTSTAT 0x1E
+#define RT5120_REG_FZCMODE 0x44
+
+#define RT5120_INT_HOTDIE 0
+#define RT5120_INT_PWRKEY_REL 5
+#define RT5120_INT_PWRKEY_PRESS 6
+
+static const struct regmap_range rt5120_rd_yes_ranges[] = {
+ regmap_reg_range(0x03, 0x13),
+ regmap_reg_range(0x1c, 0x20),
+ regmap_reg_range(0x44, 0x44),
+};
+
+static const struct regmap_range rt5120_wr_yes_ranges[] = {
+ regmap_reg_range(0x06, 0x13),
+ regmap_reg_range(0x1c, 0x20),
+ regmap_reg_range(0x44, 0x44),
+};
+
+static const struct regmap_access_table rt5120_rd_table = {
+ .yes_ranges = rt5120_rd_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rt5120_rd_yes_ranges),
+};
+
+static const struct regmap_access_table rt5120_wr_table = {
+ .yes_ranges = rt5120_wr_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rt5120_wr_yes_ranges),
+};
+
+static const struct regmap_config rt5120_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = RT5120_REG_FZCMODE,
+
+ .wr_table = &rt5120_wr_table,
+ .rd_table = &rt5120_rd_table,
+};
+
+static const struct regmap_irq rt5120_irqs[] = {
+ REGMAP_IRQ_REG_LINE(RT5120_INT_HOTDIE, 8),
+ REGMAP_IRQ_REG_LINE(RT5120_INT_PWRKEY_REL, 8),
+ REGMAP_IRQ_REG_LINE(RT5120_INT_PWRKEY_PRESS, 8),
+};
+
+static const struct regmap_irq_chip rt5120_irq_chip = {
+ .name = "rt5120-pmic",
+ .status_base = RT5120_REG_INTSTAT,
+ .mask_base = RT5120_REG_INTENABLE,
+ .ack_base = RT5120_REG_INTSTAT,
+ .mask_invert = true,
+ .use_ack = true,
+ .num_regs = 1,
+ .irqs = rt5120_irqs,
+ .num_irqs = ARRAY_SIZE(rt5120_irqs),
+};
+
+static const struct resource rt5120_regulator_resources[] = {
+ DEFINE_RES_IRQ(RT5120_INT_HOTDIE),
+};
+
+static const struct resource rt5120_pwrkey_resources[] = {
+ DEFINE_RES_IRQ_NAMED(RT5120_INT_PWRKEY_PRESS, "pwrkey-press"),
+ DEFINE_RES_IRQ_NAMED(RT5120_INT_PWRKEY_REL, "pwrkey-release"),
+};
+
+static const struct mfd_cell rt5120_devs[] = {
+ MFD_CELL_RES("rt5120-regulator", rt5120_regulator_resources),
+ MFD_CELL_OF("rt5120-pwrkey", rt5120_pwrkey_resources, NULL, 0, 0, "richtek,rt5120-pwrkey"),
+};
+
+static int rt5120_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct regmap *regmap;
+ struct regmap_irq_chip_data *irq_data;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(i2c, &rt5120_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to init regmap\n");
+
+ ret = devm_regmap_add_irq_chip(dev, regmap, i2c->irq, IRQF_ONESHOT, 0,
+ &rt5120_irq_chip, &irq_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add IRQ chip\n");
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, rt5120_devs,
+ ARRAY_SIZE(rt5120_devs), NULL, 0,
+ regmap_irq_get_domain(irq_data));
+}
+
+static const struct of_device_id rt5120_device_match_table[] = {
+ { .compatible = "richtek,rt5120" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt5120_device_match_table);
+
+static struct i2c_driver rt5120_driver = {
+ .driver = {
+ .name = "rt5120",
+ .of_match_table = rt5120_device_match_table,
+ },
+ .probe_new = rt5120_probe,
+};
+module_i2c_driver(rt5120_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("Richtek RT5120 I2C driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index bc0a2c38653e..3ac4508a6742 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -1720,7 +1720,12 @@ static struct platform_driver sm501_plat_driver = {
static int __init sm501_base_init(void)
{
- platform_driver_register(&sm501_plat_driver);
+ int ret;
+
+ ret = platform_driver_register(&sm501_plat_driver);
+ if (ret < 0)
+ return ret;
+
return pci_register_driver(&sm501_pci_driver);
}
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index aeb9ea55f97d..0c4f74197d3e 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -8,14 +8,13 @@
*/
#include <linux/err.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/mfd/core.h>
@@ -30,17 +29,12 @@
* @irq_trigger: IRQ trigger to use for the interrupt to the host
* @autosleep: bool to enable/disable stmpe autosleep
* @autosleep_timeout: inactivity timeout in milliseconds for autosleep
- * @irq_over_gpio: true if gpio is used to get irq
- * @irq_gpio: gpio number over which irq will be requested (significant only if
- * irq_over_gpio is true)
*/
struct stmpe_platform_data {
int id;
unsigned int blocks;
unsigned int irq_trigger;
bool autosleep;
- bool irq_over_gpio;
- int irq_gpio;
int autosleep_timeout;
};
@@ -1349,32 +1343,22 @@ static void stmpe_of_probe(struct stmpe_platform_data *pdata,
if (pdata->id < 0)
pdata->id = -1;
- pdata->irq_gpio = of_get_named_gpio_flags(np, "irq-gpio", 0,
- &pdata->irq_trigger);
- if (gpio_is_valid(pdata->irq_gpio))
- pdata->irq_over_gpio = 1;
- else
- pdata->irq_trigger = IRQF_TRIGGER_NONE;
-
of_property_read_u32(np, "st,autosleep-timeout",
&pdata->autosleep_timeout);
pdata->autosleep = (pdata->autosleep_timeout) ? true : false;
for_each_available_child_of_node(np, child) {
- if (of_node_name_eq(child, "stmpe_gpio")) {
+ if (of_device_is_compatible(child, stmpe_gpio_cell.of_compatible))
pdata->blocks |= STMPE_BLOCK_GPIO;
- } else if (of_node_name_eq(child, "stmpe_keypad")) {
+ else if (of_device_is_compatible(child, stmpe_keypad_cell.of_compatible))
pdata->blocks |= STMPE_BLOCK_KEYPAD;
- } else if (of_node_name_eq(child, "stmpe_touchscreen")) {
+ else if (of_device_is_compatible(child, stmpe_ts_cell.of_compatible))
pdata->blocks |= STMPE_BLOCK_TOUCHSCREEN;
- } else if (of_node_name_eq(child, "stmpe_adc")) {
+ else if (of_device_is_compatible(child, stmpe_adc_cell.of_compatible))
pdata->blocks |= STMPE_BLOCK_ADC;
- } else if (of_node_name_eq(child, "stmpe_pwm")) {
+ else if (of_device_is_compatible(child, stmpe_pwm_cell.of_compatible))
pdata->blocks |= STMPE_BLOCK_PWM;
- } else if (of_node_name_eq(child, "stmpe_rotator")) {
- pdata->blocks |= STMPE_BLOCK_ROTATOR;
- }
}
}
@@ -1384,6 +1368,7 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum)
struct stmpe_platform_data *pdata;
struct device_node *np = ci->dev->of_node;
struct stmpe *stmpe;
+ struct gpio_desc *irq_gpio;
int ret;
u32 val;
@@ -1437,18 +1422,20 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum)
if (ci->init)
ci->init(stmpe);
- if (pdata->irq_over_gpio) {
- ret = devm_gpio_request_one(ci->dev, pdata->irq_gpio,
- GPIOF_DIR_IN, "stmpe");
- if (ret) {
- dev_err(stmpe->dev, "failed to request IRQ GPIO: %d\n",
- ret);
- return ret;
- }
+ irq_gpio = devm_gpiod_get_optional(ci->dev, "irq", GPIOD_ASIS);
+ ret = PTR_ERR_OR_ZERO(irq_gpio);
+ if (ret) {
+ dev_err(stmpe->dev, "failed to request IRQ GPIO: %d\n", ret);
+ return ret;
+ }
- stmpe->irq = gpio_to_irq(pdata->irq_gpio);
+ if (irq_gpio) {
+ stmpe->irq = gpiod_to_irq(irq_gpio);
+ pdata->irq_trigger = gpiod_is_active_low(irq_gpio) ?
+ IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
} else {
stmpe->irq = ci->irq;
+ pdata->irq_trigger = IRQF_TRIGGER_NONE;
}
if (stmpe->irq < 0) {
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index bdb2ce7ff03b..9489e80e905a 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -66,14 +66,6 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
goto err_map;
}
- /* Parse the device's DT node for an endianness specification */
- if (of_property_read_bool(np, "big-endian"))
- syscon_config.val_format_endian = REGMAP_ENDIAN_BIG;
- else if (of_property_read_bool(np, "little-endian"))
- syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE;
- else if (of_property_read_bool(np, "native-endian"))
- syscon_config.val_format_endian = REGMAP_ENDIAN_NATIVE;
-
/*
* search for reg-io-width property in DT. If it is not provided,
* default to 4 bytes. regmap_init_mmio will return an error if values
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 2679c41232e6..f6b4b9d94bbd 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -882,7 +882,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
* SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0.
*
* Also, always enable SmartReflex bit as that's needed for omaps to
- * to do anything over I2C4 for voltage scaling even if SmartReflex
+ * do anything over I2C4 for voltage scaling even if SmartReflex
* is disabled. Without the SmartReflex bit omap sys_clkreq idle
* signal will never trigger for retention idle.
*/
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index 4f576f0160a9..87496c1cb8bc 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -14,6 +14,7 @@
* by syed khasim <x0khasim@ti.com>
*/
+#include <linux/device.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 1aa8323ad9f6..56e70a68679a 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -708,6 +708,12 @@ config CHARGER_BQ256XX
charge management and system power path management devices for single
cell Li-ion and Li-polymer batteries.
+config CHARGER_RK817
+ tristate "Rockchip RK817 PMIC Battery Charger"
+ depends on MFD_RK808
+ help
+ Say Y to include support for Rockchip RK817 Battery Charger.
+
config CHARGER_SMB347
tristate "Summit Microelectronics SMB3XX Battery Charger"
depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 7f02f36aea55..3040a1de81b8 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_CHARGER_BQ2515X) += bq2515x_charger.o
obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
obj-$(CONFIG_CHARGER_BQ25980) += bq25980_charger.o
obj-$(CONFIG_CHARGER_BQ256XX) += bq256xx_charger.o
+obj-$(CONFIG_CHARGER_RK817) += rk817_charger.o
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
diff --git a/drivers/power/supply/rk817_charger.c b/drivers/power/supply/rk817_charger.c
new file mode 100644
index 000000000000..635f051b0821
--- /dev/null
+++ b/drivers/power/supply/rk817_charger.c
@@ -0,0 +1,1211 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Charger Driver for Rockchip rk817
+ *
+ * Copyright (c) 2021 Maya Matuszczyk <maccraft123mc@gmail.com>
+ *
+ * Authors: Maya Matuszczyk <maccraft123mc@gmail.com>
+ * Chris Morgan <macromorgan@hotmail.com>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/devm-helpers.h>
+#include <linux/mfd/rk808.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+
+/* Charging statuses reported by hardware register */
+enum rk817_charge_status {
+ CHRG_OFF,
+ DEAD_CHRG,
+ TRICKLE_CHRG,
+ CC_OR_CV_CHRG,
+ CHARGE_FINISH,
+ USB_OVER_VOL,
+ BAT_TMP_ERR,
+ BAT_TIM_ERR,
+};
+
+/*
+ * Max charging current read to/written from hardware register.
+ * Note how highest value corresponding to 0x7 is the lowest
+ * current, this is per the datasheet.
+ */
+enum rk817_chg_cur {
+ CHG_1A,
+ CHG_1_5A,
+ CHG_2A,
+ CHG_2_5A,
+ CHG_2_75A,
+ CHG_3A,
+ CHG_3_5A,
+ CHG_0_5A,
+};
+
+struct rk817_charger {
+ struct device *dev;
+ struct rk808 *rk808;
+
+ struct power_supply *bat_ps;
+ struct power_supply *chg_ps;
+ bool plugged_in;
+ bool battery_present;
+
+ /*
+ * voltage_k and voltage_b values are used to calibrate the ADC
+ * voltage readings. While they are documented in the BSP kernel and
+ * datasheet as voltage_k and voltage_b, there is no further
+ * information explaining them in more detail.
+ */
+
+ uint32_t voltage_k;
+ uint32_t voltage_b;
+
+ /*
+ * soc - state of charge - like the BSP this is stored as a percentage,
+ * to the thousandth. BSP has a display state of charge (dsoc) and a
+ * remaining state of charge (rsoc). This value will be used for both
+ * purposes here so we don't do any fancy math to try and "smooth" the
+ * charge and just report it as it is. Note for example an soc of 100
+ * is stored as 100000, an soc of 50 is stored as 50000, etc.
+ */
+ int soc;
+
+ /*
+ * Capacity of battery when fully charged, equal or less than design
+ * capacity depending upon wear. BSP kernel saves to nvram in mAh,
+ * so this value is in mAh not the standard uAh.
+ */
+ int fcc_mah;
+
+ /*
+ * Calibrate the SOC on a fully charged battery, this way we can use
+ * the calibrated SOC value to correct for columb counter drift.
+ */
+ bool soc_cal;
+
+ /* Implementation specific immutable properties from device tree */
+ int res_div;
+ int sleep_enter_current_ua;
+ int sleep_filter_current_ua;
+ int bat_charge_full_design_uah;
+ int bat_voltage_min_design_uv;
+ int bat_voltage_max_design_uv;
+
+ /* Values updated periodically by driver for display. */
+ int charge_now_uah;
+ int volt_avg_uv;
+ int cur_avg_ua;
+ int max_chg_cur_ua;
+ int max_chg_volt_uv;
+ int charge_status;
+ int charger_input_volt_avg_uv;
+
+ /* Work queue to periodically update values. */
+ struct delayed_work work;
+};
+
+/* ADC coefficients extracted from BSP kernel */
+#define ADC_TO_CURRENT(adc_value, res_div) \
+ (adc_value * 172 / res_div)
+
+#define CURRENT_TO_ADC(current, samp_res) \
+ (current * samp_res / 172)
+
+#define CHARGE_TO_ADC(capacity, res_div) \
+ (capacity * res_div * 3600 / 172 * 1000)
+
+#define ADC_TO_CHARGE_UAH(adc_value, res_div) \
+ (adc_value / 3600 * 172 / res_div)
+
+static u8 rk817_chg_cur_to_reg(u32 chg_cur_ma)
+{
+ if (chg_cur_ma >= 3500)
+ return CHG_3_5A;
+ else if (chg_cur_ma >= 3000)
+ return CHG_3A;
+ else if (chg_cur_ma >= 2750)
+ return CHG_2_75A;
+ else if (chg_cur_ma >= 2500)
+ return CHG_2_5A;
+ else if (chg_cur_ma >= 2000)
+ return CHG_2A;
+ else if (chg_cur_ma >= 1500)
+ return CHG_1_5A;
+ else if (chg_cur_ma >= 1000)
+ return CHG_1A;
+ else if (chg_cur_ma >= 500)
+ return CHG_0_5A;
+ else
+ return -EINVAL;
+}
+
+static int rk817_chg_cur_from_reg(u8 reg)
+{
+ switch (reg) {
+ case CHG_0_5A:
+ return 500000;
+ case CHG_1A:
+ return 1000000;
+ case CHG_1_5A:
+ return 1500000;
+ case CHG_2A:
+ return 2000000;
+ case CHG_2_5A:
+ return 2500000;
+ case CHG_2_75A:
+ return 2750000;
+ case CHG_3A:
+ return 3000000;
+ case CHG_3_5A:
+ return 3500000;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void rk817_bat_calib_vol(struct rk817_charger *charger)
+{
+ uint32_t vcalib0 = 0;
+ uint32_t vcalib1 = 0;
+ u8 bulk_reg[2];
+
+ /* calibrate voltage */
+ regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_VCALIB0_H,
+ bulk_reg, 2);
+ vcalib0 = get_unaligned_be16(bulk_reg);
+
+ regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_VCALIB1_H,
+ bulk_reg, 2);
+ vcalib1 = get_unaligned_be16(bulk_reg);
+
+ /* values were taken from BSP kernel */
+ charger->voltage_k = (4025 - 2300) * 1000 /
+ ((vcalib1 - vcalib0) ? (vcalib1 - vcalib0) : 1);
+ charger->voltage_b = 4025 - (charger->voltage_k * vcalib1) / 1000;
+}
+
+static void rk817_bat_calib_cur(struct rk817_charger *charger)
+{
+ u8 bulk_reg[2];
+
+ /* calibrate current */
+ regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_IOFFSET_H,
+ bulk_reg, 2);
+ regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_CAL_OFFSET_H,
+ bulk_reg, 2);
+}
+
+/*
+ * note that only the fcc_mah is really used by this driver, the other values
+ * are to ensure we can remain backwards compatible with the BSP kernel.
+ */
+static int rk817_record_battery_nvram_values(struct rk817_charger *charger)
+{
+ u8 bulk_reg[3];
+ int ret, rsoc;
+
+ /*
+ * write the soc value to the nvram location used by the BSP kernel
+ * for the dsoc value.
+ */
+ put_unaligned_le24(charger->soc, bulk_reg);
+ ret = regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_R1,
+ bulk_reg, 3);
+ if (ret < 0)
+ return ret;
+ /*
+ * write the remaining capacity in mah to the nvram location used by
+ * the BSP kernel for the rsoc value.
+ */
+ rsoc = (charger->soc * charger->fcc_mah) / 100000;
+ put_unaligned_le24(rsoc, bulk_reg);
+ ret = regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_DATA0,
+ bulk_reg, 3);
+ if (ret < 0)
+ return ret;
+ /* write the fcc_mah in mAh, just as the BSP kernel does. */
+ put_unaligned_le24(charger->fcc_mah, bulk_reg);
+ ret = regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_DATA3,
+ bulk_reg, 3);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rk817_bat_calib_cap(struct rk817_charger *charger)
+{
+ struct rk808 *rk808 = charger->rk808;
+ int tmp, charge_now, charge_now_adc, volt_avg;
+ u8 bulk_reg[4];
+
+ /* Calibrate the soc and fcc on a fully charged battery */
+
+ if (charger->charge_status == CHARGE_FINISH && (!charger->soc_cal)) {
+ /*
+ * soc should be 100000 and columb counter should show the full
+ * charge capacity. Note that if the device is unplugged for a
+ * period of several days the columb counter will have a large
+ * margin of error, so setting it back to the full charge on
+ * a completed charge cycle should correct this (my device was
+ * showing 33% battery after 3 days unplugged when it should
+ * have been closer to 95% based on voltage and charge
+ * current).
+ */
+
+ charger->soc = 100000;
+ charge_now_adc = CHARGE_TO_ADC(charger->fcc_mah,
+ charger->res_div);
+ put_unaligned_be32(charge_now_adc, bulk_reg);
+ regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_Q_INIT_H3,
+ bulk_reg, 4);
+
+ charger->soc_cal = 1;
+ dev_dbg(charger->dev,
+ "Fully charged. SOC is %d, full capacity is %d\n",
+ charger->soc, charger->fcc_mah * 1000);
+ }
+
+ /*
+ * The columb counter can drift up slightly, so we should correct for
+ * it. But don't correct it until we're at 100% soc.
+ */
+ if (charger->charge_status == CHARGE_FINISH && charger->soc_cal) {
+ regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3,
+ bulk_reg, 4);
+ charge_now_adc = get_unaligned_be32(bulk_reg);
+ if (charge_now_adc < 0)
+ return charge_now_adc;
+ charge_now = ADC_TO_CHARGE_UAH(charge_now_adc,
+ charger->res_div);
+
+ /*
+ * Re-init columb counter with updated values to correct drift.
+ */
+ if (charge_now / 1000 > charger->fcc_mah) {
+ dev_dbg(charger->dev,
+ "Recalibrating columb counter to %d uah\n",
+ charge_now);
+ /*
+ * Order of operations matters here to ensure we keep
+ * enough precision until the last step to keep from
+ * making needless updates to columb counter.
+ */
+ charge_now_adc = CHARGE_TO_ADC(charger->fcc_mah,
+ charger->res_div);
+ put_unaligned_be32(charge_now_adc, bulk_reg);
+ regmap_bulk_write(rk808->regmap,
+ RK817_GAS_GAUGE_Q_INIT_H3,
+ bulk_reg, 4);
+ }
+ }
+
+ /*
+ * Calibrate the fully charged capacity when we previously had a full
+ * battery (soc_cal = 1) and are now empty (at or below minimum design
+ * voltage). If our columb counter is still positive, subtract that
+ * from our fcc value to get a calibrated fcc, and if our columb
+ * counter is negative add that to our fcc (but not to exceed our
+ * design capacity).
+ */
+ regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_VOL_H,
+ bulk_reg, 2);
+ tmp = get_unaligned_be16(bulk_reg);
+ volt_avg = (charger->voltage_k * tmp) + 1000 * charger->voltage_b;
+ if (volt_avg <= charger->bat_voltage_min_design_uv &&
+ charger->soc_cal) {
+ regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3,
+ bulk_reg, 4);
+ charge_now_adc = get_unaligned_be32(bulk_reg);
+ charge_now = ADC_TO_CHARGE_UAH(charge_now_adc,
+ charger->res_div);
+ /*
+ * Note, if charge_now is negative this will add it (what we
+ * want) and if it's positive this will subtract (also what
+ * we want).
+ */
+ charger->fcc_mah = charger->fcc_mah - (charge_now / 1000);
+
+ dev_dbg(charger->dev,
+ "Recalibrating full charge capacity to %d uah\n",
+ charger->fcc_mah * 1000);
+ }
+
+ rk817_record_battery_nvram_values(charger);
+
+ return 0;
+}
+
+static void rk817_read_props(struct rk817_charger *charger)
+{
+ int tmp, reg;
+ u8 bulk_reg[4];
+
+ /*
+ * Recalibrate voltage and current readings if we need to BSP does both
+ * on CUR_CALIB_UPD, ignoring VOL_CALIB_UPD. Curiously enough, both
+ * documentation and the BSP show that you perform an update if bit 7
+ * is 1, but you clear the status by writing a 1 to bit 7.
+ */
+ regmap_read(charger->rk808->regmap, RK817_GAS_GAUGE_ADC_CONFIG1, &reg);
+ if (reg & RK817_VOL_CUR_CALIB_UPD) {
+ rk817_bat_calib_cur(charger);
+ rk817_bat_calib_vol(charger);
+ regmap_write_bits(charger->rk808->regmap,
+ RK817_GAS_GAUGE_ADC_CONFIG1,
+ RK817_VOL_CUR_CALIB_UPD,
+ RK817_VOL_CUR_CALIB_UPD);
+ }
+
+ /* Update reported charge. */
+ regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3,
+ bulk_reg, 4);
+ tmp = get_unaligned_be32(bulk_reg);
+ charger->charge_now_uah = ADC_TO_CHARGE_UAH(tmp, charger->res_div);
+ if (charger->charge_now_uah < 0)
+ charger->charge_now_uah = 0;
+ if (charger->charge_now_uah > charger->fcc_mah * 1000)
+ charger->charge_now_uah = charger->fcc_mah * 1000;
+
+ /* Update soc based on reported charge. */
+ charger->soc = charger->charge_now_uah * 100 / charger->fcc_mah;
+
+ /* Update reported voltage. */
+ regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_VOL_H,
+ bulk_reg, 2);
+ tmp = get_unaligned_be16(bulk_reg);
+ charger->volt_avg_uv = (charger->voltage_k * tmp) + 1000 *
+ charger->voltage_b;
+
+ /*
+ * Update reported current. Note value from registers is a signed 16
+ * bit int.
+ */
+ regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_CUR_H,
+ bulk_reg, 2);
+ tmp = (short int)get_unaligned_be16(bulk_reg);
+ charger->cur_avg_ua = ADC_TO_CURRENT(tmp, charger->res_div);
+
+ /*
+ * Update the max charge current. This value shouldn't change, but we
+ * can read it to report what the PMIC says it is instead of simply
+ * returning the default value.
+ */
+ regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_OUT, &reg);
+ charger->max_chg_cur_ua =
+ rk817_chg_cur_from_reg(reg & RK817_CHRG_CUR_SEL);
+
+ /*
+ * Update max charge voltage. Like the max charge current this value
+ * shouldn't change, but we can report what the PMIC says.
+ */
+ regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_OUT, &reg);
+ charger->max_chg_volt_uv = ((((reg & RK817_CHRG_VOL_SEL) >> 4) *
+ 50000) + 4100000);
+
+ /* Check if battery still present. */
+ regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_STS, &reg);
+ charger->battery_present = (reg & RK817_BAT_EXS);
+
+ /* Get which type of charge we are using (if any). */
+ regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_STS, &reg);
+ charger->charge_status = (reg >> 4) & 0x07;
+
+ /*
+ * Get charger input voltage. Note that on my example hardware (an
+ * Odroid Go Advance) the voltage of the power connector is measured
+ * on the register labelled USB in the datasheet; I don't know if this
+ * is how it is designed or just a quirk of the implementation. I
+ * believe this will also measure the voltage of the USB output when in
+ * OTG mode, if that is the case we may need to change this in the
+ * future to return 0 if the power supply status is offline (I can't
+ * test this with my current implementation. Also, when the voltage
+ * should be zero sometimes the ADC still shows a single bit (which
+ * would register as 20000uv). When this happens set it to 0.
+ */
+ regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_USB_VOL_H,
+ bulk_reg, 2);
+ reg = get_unaligned_be16(bulk_reg);
+ if (reg > 1) {
+ tmp = ((charger->voltage_k * reg / 1000 + charger->voltage_b) *
+ 60 / 46);
+ charger->charger_input_volt_avg_uv = tmp * 1000;
+ } else {
+ charger->charger_input_volt_avg_uv = 0;
+ }
+
+ /* Calibrate battery capacity and soc. */
+ rk817_bat_calib_cap(charger);
+}
+
+static int rk817_bat_get_prop(struct power_supply *ps,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct rk817_charger *charger = power_supply_get_drvdata(ps);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = charger->battery_present;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ if (charger->cur_avg_ua < 0) {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ }
+ switch (charger->charge_status) {
+ case CHRG_OFF:
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ /*
+ * Dead charge is documented, but not explained. I never
+ * observed it but assume it's a pre-charge for a dead
+ * battery.
+ */
+ case DEAD_CHRG:
+ case TRICKLE_CHRG:
+ case CC_OR_CV_CHRG:
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case CHARGE_FINISH:
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ return -EINVAL;
+
+ }
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ switch (charger->charge_status) {
+ case CHRG_OFF:
+ case CHARGE_FINISH:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ case TRICKLE_CHRG:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ case DEAD_CHRG:
+ case CC_OR_CV_CHRG:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ break;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = charger->fcc_mah * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = charger->bat_charge_full_design_uah;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = charger->charge_now_uah;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = charger->bat_voltage_min_design_uv;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ /* Add 500 so that values like 99999 are 100% not 99%. */
+ val->intval = (charger->soc + 500) / 1000;
+ if (val->intval > 100)
+ val->intval = 100;
+ if (val->intval < 0)
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ val->intval = charger->volt_avg_uv;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = charger->cur_avg_ua;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = charger->max_chg_cur_ua;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ val->intval = charger->max_chg_volt_uv;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = charger->bat_voltage_max_design_uv;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rk817_chg_get_prop(struct power_supply *ps,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct rk817_charger *charger = power_supply_get_drvdata(ps);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = charger->plugged_in;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ /* max voltage from datasheet at 5.5v (default 5.0v) */
+ val->intval = 5500000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ /* min voltage from datasheet at 3.8v (default 5.0v) */
+ val->intval = 3800000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ val->intval = charger->charger_input_volt_avg_uv;
+ break;
+ /*
+ * While it's possible that other implementations could use different
+ * USB types, the current implementation for this PMIC (the Odroid Go
+ * Advance) only uses a dedicated charging port with no rx/tx lines.
+ */
+ case POWER_SUPPLY_PROP_USB_TYPE:
+ val->intval = POWER_SUPPLY_USB_TYPE_DCP;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+
+}
+
+static irqreturn_t rk817_plug_in_isr(int irq, void *cg)
+{
+ struct rk817_charger *charger;
+
+ charger = (struct rk817_charger *)cg;
+ charger->plugged_in = 1;
+ power_supply_changed(charger->chg_ps);
+ power_supply_changed(charger->bat_ps);
+ /* try to recalibrate capacity if we hit full charge. */
+ charger->soc_cal = 0;
+
+ rk817_read_props(charger);
+
+ dev_dbg(charger->dev, "Power Cord Inserted\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rk817_plug_out_isr(int irq, void *cg)
+{
+ struct rk817_charger *charger;
+ struct rk808 *rk808;
+
+ charger = (struct rk817_charger *)cg;
+ rk808 = charger->rk808;
+ charger->plugged_in = 0;
+ power_supply_changed(charger->bat_ps);
+ power_supply_changed(charger->chg_ps);
+
+ /*
+ * For some reason the bits of RK817_PMIC_CHRG_IN reset whenever the
+ * power cord is unplugged. This was not documented in the BSP kernel
+ * or the datasheet and only discovered by trial and error. Set minimum
+ * USB input voltage to 4.5v and enable USB voltage input limit.
+ */
+ regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN,
+ RK817_USB_VLIM_SEL, (0x05 << 4));
+ regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_VLIM_EN,
+ (0x01 << 7));
+
+ /*
+ * Set average USB input current limit to 1.5A and enable USB current
+ * input limit.
+ */
+ regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN,
+ RK817_USB_ILIM_SEL, 0x03);
+ regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_ILIM_EN,
+ (0x01 << 3));
+
+ rk817_read_props(charger);
+
+ dev_dbg(charger->dev, "Power Cord Removed\n");
+
+ return IRQ_HANDLED;
+}
+
+static enum power_supply_property rk817_bat_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+};
+
+static enum power_supply_property rk817_chg_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_USB_TYPE,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+};
+
+static enum power_supply_usb_type rk817_usb_type[] = {
+ POWER_SUPPLY_USB_TYPE_DCP,
+ POWER_SUPPLY_USB_TYPE_UNKNOWN,
+};
+
+static const struct power_supply_desc rk817_bat_desc = {
+ .name = "rk817-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = rk817_bat_props,
+ .num_properties = ARRAY_SIZE(rk817_bat_props),
+ .get_property = rk817_bat_get_prop,
+};
+
+static const struct power_supply_desc rk817_chg_desc = {
+ .name = "rk817-charger",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .usb_types = rk817_usb_type,
+ .num_usb_types = ARRAY_SIZE(rk817_usb_type),
+ .properties = rk817_chg_props,
+ .num_properties = ARRAY_SIZE(rk817_chg_props),
+ .get_property = rk817_chg_get_prop,
+};
+
+static int rk817_read_battery_nvram_values(struct rk817_charger *charger)
+{
+ u8 bulk_reg[3];
+ int ret;
+
+ /* Read the nvram data for full charge capacity. */
+ ret = regmap_bulk_read(charger->rk808->regmap,
+ RK817_GAS_GAUGE_DATA3, bulk_reg, 3);
+ if (ret < 0)
+ return ret;
+ charger->fcc_mah = get_unaligned_le24(bulk_reg);
+
+ /*
+ * Sanity checking for values equal to zero or less than would be
+ * practical for this device (BSP Kernel assumes 500mAH or less) for
+ * practicality purposes. Also check if the value is too large and
+ * correct it.
+ */
+ if ((charger->fcc_mah < 500) ||
+ ((charger->fcc_mah * 1000) > charger->bat_charge_full_design_uah)) {
+ dev_info(charger->dev,
+ "Invalid NVRAM max charge, setting to %u uAH\n",
+ charger->bat_charge_full_design_uah);
+ charger->fcc_mah = charger->bat_charge_full_design_uah / 1000;
+ }
+
+ /*
+ * Read the nvram for state of charge. Sanity check for values greater
+ * than 100 (10000). If the value is off it should get corrected
+ * automatically when the voltage drops to the min (soc is 0) or when
+ * the battery is full (soc is 100).
+ */
+ ret = regmap_bulk_read(charger->rk808->regmap,
+ RK817_GAS_GAUGE_BAT_R1, bulk_reg, 3);
+ if (ret < 0)
+ return ret;
+ charger->soc = get_unaligned_le24(bulk_reg);
+ if (charger->soc > 10000)
+ charger->soc = 10000;
+
+ return 0;
+}
+
+static int
+rk817_read_or_set_full_charge_on_boot(struct rk817_charger *charger,
+ struct power_supply_battery_info *bat_info)
+{
+ struct rk808 *rk808 = charger->rk808;
+ u8 bulk_reg[4];
+ u32 boot_voltage, boot_charge_mah, tmp;
+ int ret, reg, off_time;
+ bool first_boot;
+
+ /*
+ * Check if the battery is uninitalized. If it is, the columb counter
+ * needs to be set up.
+ */
+ ret = regmap_read(rk808->regmap, RK817_GAS_GAUGE_GG_STS, &reg);
+ if (ret < 0)
+ return ret;
+ first_boot = reg & RK817_BAT_CON;
+ /*
+ * If the battery is uninitialized, use the poweron voltage and an ocv
+ * lookup to guess our charge. The number won't be very accurate until
+ * we hit either our minimum voltage (0%) or full charge (100%).
+ */
+ if (first_boot) {
+ regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_PWRON_VOL_H,
+ bulk_reg, 2);
+ tmp = get_unaligned_be16(bulk_reg);
+ boot_voltage = (charger->voltage_k * tmp) +
+ 1000 * charger->voltage_b;
+ /*
+ * Since only implementation has no working thermistor, assume
+ * 20C for OCV lookup. If lookup fails, report error with OCV
+ * table.
+ */
+ charger->soc = power_supply_batinfo_ocv2cap(bat_info,
+ boot_voltage,
+ 20) * 1000;
+ if (charger->soc < 0)
+ charger->soc = 0;
+
+ /* Guess that full charge capacity is the design capacity */
+ charger->fcc_mah = charger->bat_charge_full_design_uah / 1000;
+ /*
+ * Set battery as "set up". BSP driver uses this value even
+ * though datasheet claims it's a read-only value.
+ */
+ regmap_write_bits(rk808->regmap, RK817_GAS_GAUGE_GG_STS,
+ RK817_BAT_CON, 0);
+ /* Save nvram values */
+ ret = rk817_record_battery_nvram_values(charger);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = rk817_read_battery_nvram_values(charger);
+ if (ret < 0)
+ return ret;
+
+ regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3,
+ bulk_reg, 4);
+ tmp = get_unaligned_be32(bulk_reg);
+ if (tmp < 0)
+ tmp = 0;
+ boot_charge_mah = ADC_TO_CHARGE_UAH(tmp,
+ charger->res_div) / 1000;
+ /*
+ * Check if the columb counter has been off for more than 300
+ * minutes as it tends to drift downward. If so, re-init soc
+ * with the boot voltage instead. Note the unit values for the
+ * OFF_CNT register appear to be in decaminutes and stops
+ * counting at 2550 (0xFF) minutes. BSP kernel used OCV, but
+ * for me occasionally that would show invalid values. Boot
+ * voltage is only accurate for me on first poweron (not
+ * reboots), but we shouldn't ever encounter an OFF_CNT more
+ * than 0 on a reboot anyway.
+ */
+ regmap_read(rk808->regmap, RK817_GAS_GAUGE_OFF_CNT, &off_time);
+ if (off_time >= 30) {
+ regmap_bulk_read(rk808->regmap,
+ RK817_GAS_GAUGE_PWRON_VOL_H,
+ bulk_reg, 2);
+ tmp = get_unaligned_be16(bulk_reg);
+ boot_voltage = (charger->voltage_k * tmp) +
+ 1000 * charger->voltage_b;
+ charger->soc =
+ power_supply_batinfo_ocv2cap(bat_info,
+ boot_voltage,
+ 20) * 1000;
+ } else {
+ charger->soc = (boot_charge_mah * 1000 * 100 /
+ charger->fcc_mah);
+ }
+ }
+
+ regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_PWRON_VOL_H,
+ bulk_reg, 2);
+ tmp = get_unaligned_be16(bulk_reg);
+ boot_voltage = (charger->voltage_k * tmp) + 1000 * charger->voltage_b;
+ regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3,
+ bulk_reg, 4);
+ tmp = get_unaligned_be32(bulk_reg);
+ if (tmp < 0)
+ tmp = 0;
+ boot_charge_mah = ADC_TO_CHARGE_UAH(tmp, charger->res_div) / 1000;
+ regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_OCV_VOL_H,
+ bulk_reg, 2);
+ tmp = get_unaligned_be16(bulk_reg);
+ boot_voltage = (charger->voltage_k * tmp) + 1000 * charger->voltage_b;
+
+ /*
+ * Now we have our full charge capacity and soc, init the columb
+ * counter.
+ */
+ boot_charge_mah = charger->soc * charger->fcc_mah / 100 / 1000;
+ if (boot_charge_mah > charger->fcc_mah)
+ boot_charge_mah = charger->fcc_mah;
+ tmp = CHARGE_TO_ADC(boot_charge_mah, charger->res_div);
+ put_unaligned_be32(tmp, bulk_reg);
+ ret = regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_Q_INIT_H3,
+ bulk_reg, 4);
+ if (ret < 0)
+ return ret;
+
+ /* Set QMAX value to max design capacity. */
+ tmp = CHARGE_TO_ADC((charger->bat_charge_full_design_uah / 1000),
+ charger->res_div);
+ put_unaligned_be32(tmp, bulk_reg);
+ ret = regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_Q_MAX_H3,
+ bulk_reg, 4);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rk817_battery_init(struct rk817_charger *charger,
+ struct power_supply_battery_info *bat_info)
+{
+ struct rk808 *rk808 = charger->rk808;
+ u32 tmp, max_chg_vol_mv, max_chg_cur_ma;
+ u8 max_chg_vol_reg, chg_term_i_reg, max_chg_cur_reg;
+ int ret, chg_term_ma;
+ u8 bulk_reg[2];
+
+ /* Get initial plug state */
+ regmap_read(rk808->regmap, RK817_SYS_STS, &tmp);
+ charger->plugged_in = (tmp & RK817_PLUG_IN_STS);
+
+ /*
+ * Turn on all ADC functions to measure battery, USB, and sys voltage,
+ * as well as batt temp. Note only tested implementation so far does
+ * not use a battery with a thermistor.
+ */
+ regmap_write(rk808->regmap, RK817_GAS_GAUGE_ADC_CONFIG0, 0xfc);
+
+ /*
+ * Set relax mode voltage sampling interval and ADC offset calibration
+ * interval to 8 minutes to mirror BSP kernel. Set voltage and current
+ * modes to average to mirror BSP kernel.
+ */
+ regmap_write(rk808->regmap, RK817_GAS_GAUGE_GG_CON, 0x04);
+
+ /* Calibrate voltage like the BSP does here. */
+ rk817_bat_calib_vol(charger);
+
+ /* Write relax threshold, derived from sleep enter current. */
+ tmp = CURRENT_TO_ADC(charger->sleep_enter_current_ua,
+ charger->res_div);
+ put_unaligned_be16(tmp, bulk_reg);
+ regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_RELAX_THRE_H,
+ bulk_reg, 2);
+
+ /* Write sleep sample current, derived from sleep filter current. */
+ tmp = CURRENT_TO_ADC(charger->sleep_filter_current_ua,
+ charger->res_div);
+ put_unaligned_be16(tmp, bulk_reg);
+ regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_SLEEP_CON_SAMP_CUR_H,
+ bulk_reg, 2);
+
+ /* Restart battery relax voltage */
+ regmap_write_bits(rk808->regmap, RK817_GAS_GAUGE_GG_STS,
+ RK817_RELAX_VOL_UPD, (0x0 << 2));
+
+ /*
+ * Set OCV Threshold Voltage to 127.5mV. This was hard coded like this
+ * in the BSP.
+ */
+ regmap_write(rk808->regmap, RK817_GAS_GAUGE_OCV_THRE_VOL, 0xff);
+
+ /*
+ * Set maximum charging voltage to battery max voltage. Trying to be
+ * incredibly safe with these value, as setting them wrong could
+ * overcharge the battery, which would be very bad.
+ */
+ max_chg_vol_mv = bat_info->constant_charge_voltage_max_uv / 1000;
+ max_chg_cur_ma = bat_info->constant_charge_current_max_ua / 1000;
+
+ if (max_chg_vol_mv < 4100) {
+ return dev_err_probe(charger->dev, -EINVAL,
+ "invalid max charger voltage, value %u unsupported\n",
+ max_chg_vol_mv * 1000);
+ }
+ if (max_chg_vol_mv > 4450) {
+ dev_info(charger->dev,
+ "Setting max charge voltage to 4450000uv\n");
+ max_chg_vol_mv = 4450;
+ }
+
+ if (max_chg_cur_ma < 500) {
+ return dev_err_probe(charger->dev, -EINVAL,
+ "invalid max charger current, value %u unsupported\n",
+ max_chg_cur_ma * 1000);
+ }
+ if (max_chg_cur_ma > 3500)
+ dev_info(charger->dev,
+ "Setting max charge current to 3500000ua\n");
+
+ /*
+ * Now that the values are sanity checked, if we subtract 4100 from the
+ * max voltage and divide by 50, we conviently get the exact value for
+ * the registers, which are 4.1v, 4.15v, 4.2v, 4.25v, 4.3v, 4.35v,
+ * 4.4v, and 4.45v; these correspond to values 0x00 through 0x07.
+ */
+ max_chg_vol_reg = (max_chg_vol_mv - 4100) / 50;
+
+ max_chg_cur_reg = rk817_chg_cur_to_reg(max_chg_cur_ma);
+
+ if (max_chg_vol_reg < 0 || max_chg_vol_reg > 7) {
+ return dev_err_probe(charger->dev, -EINVAL,
+ "invalid max charger voltage, value %u unsupported\n",
+ max_chg_vol_mv * 1000);
+ }
+ if (max_chg_cur_reg < 0 || max_chg_cur_reg > 7) {
+ return dev_err_probe(charger->dev, -EINVAL,
+ "invalid max charger current, value %u unsupported\n",
+ max_chg_cur_ma * 1000);
+ }
+
+ /*
+ * Write the values to the registers, and deliver an emergency warning
+ * in the event they are not written correctly.
+ */
+ ret = regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_OUT,
+ RK817_CHRG_VOL_SEL, (max_chg_vol_reg << 4));
+ if (ret) {
+ dev_emerg(charger->dev,
+ "Danger, unable to set max charger voltage: %u\n",
+ ret);
+ }
+
+ ret = regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_OUT,
+ RK817_CHRG_CUR_SEL, max_chg_cur_reg);
+ if (ret) {
+ dev_emerg(charger->dev,
+ "Danger, unable to set max charger current: %u\n",
+ ret);
+ }
+
+ /* Set charge finishing mode to analog */
+ regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_TERM,
+ RK817_CHRG_TERM_ANA_DIG, (0x0 << 2));
+
+ /*
+ * Set charge finish current, warn if value not in range and keep
+ * default.
+ */
+ chg_term_ma = bat_info->charge_term_current_ua / 1000;
+ if (chg_term_ma < 150 || chg_term_ma > 400) {
+ dev_warn(charger->dev,
+ "Invalid charge termination %u, keeping default\n",
+ chg_term_ma * 1000);
+ chg_term_ma = 200;
+ }
+
+ /*
+ * Values of 150ma, 200ma, 300ma, and 400ma correspond to 00, 01, 10,
+ * and 11.
+ */
+ chg_term_i_reg = (chg_term_ma - 100) / 100;
+ regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_TERM,
+ RK817_CHRG_TERM_ANA_SEL, chg_term_i_reg);
+
+ ret = rk817_read_or_set_full_charge_on_boot(charger, bat_info);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Set minimum USB input voltage to 4.5v and enable USB voltage input
+ * limit.
+ */
+ regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN,
+ RK817_USB_VLIM_SEL, (0x05 << 4));
+ regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_VLIM_EN,
+ (0x01 << 7));
+
+ /*
+ * Set average USB input current limit to 1.5A and enable USB current
+ * input limit.
+ */
+ regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN,
+ RK817_USB_ILIM_SEL, 0x03);
+ regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_ILIM_EN,
+ (0x01 << 3));
+
+ return 0;
+}
+
+static void rk817_charging_monitor(struct work_struct *work)
+{
+ struct rk817_charger *charger;
+
+ charger = container_of(work, struct rk817_charger, work.work);
+
+ rk817_read_props(charger);
+
+ /* Run every 8 seconds like the BSP driver did. */
+ queue_delayed_work(system_wq, &charger->work, msecs_to_jiffies(8000));
+}
+
+static int rk817_charger_probe(struct platform_device *pdev)
+{
+ struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
+ struct rk817_charger *charger;
+ struct device_node *node;
+ struct power_supply_battery_info *bat_info;
+ struct device *dev = &pdev->dev;
+ struct power_supply_config pscfg = {};
+ int plugin_irq, plugout_irq;
+ int of_value;
+ int ret;
+
+ node = of_get_child_by_name(dev->parent->of_node, "charger");
+ if (!node)
+ return -ENODEV;
+
+ charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
+ if (!charger)
+ return -ENOMEM;
+
+ charger->rk808 = rk808;
+
+ charger->dev = &pdev->dev;
+ platform_set_drvdata(pdev, charger);
+
+ rk817_bat_calib_vol(charger);
+
+ pscfg.drv_data = charger;
+ pscfg.of_node = node;
+
+ /*
+ * Get sample resistor value. Note only values of 10000 or 20000
+ * microohms are allowed. Schematic for my test implementation (an
+ * Odroid Go Advance) shows a 10 milliohm resistor for reference.
+ */
+ ret = of_property_read_u32(node, "rockchip,resistor-sense-micro-ohms",
+ &of_value);
+ if (ret < 0) {
+ return dev_err_probe(dev, ret,
+ "Error reading sample resistor value\n");
+ }
+ /*
+ * Store as a 1 or a 2, since all we really use the value for is as a
+ * divisor in some calculations.
+ */
+ charger->res_div = (of_value == 20000) ? 2 : 1;
+
+ /*
+ * Get sleep enter current value. Not sure what this value is for
+ * other than to help calibrate the relax threshold.
+ */
+ ret = of_property_read_u32(node,
+ "rockchip,sleep-enter-current-microamp",
+ &of_value);
+ if (ret < 0) {
+ return dev_err_probe(dev, ret,
+ "Error reading sleep enter cur value\n");
+ }
+ charger->sleep_enter_current_ua = of_value;
+
+ /* Get sleep filter current value */
+ ret = of_property_read_u32(node,
+ "rockchip,sleep-filter-current-microamp",
+ &of_value);
+ if (ret < 0) {
+ return dev_err_probe(dev, ret,
+ "Error reading sleep filter cur value\n");
+ }
+
+ charger->sleep_filter_current_ua = of_value;
+
+ charger->bat_ps = devm_power_supply_register(&pdev->dev,
+ &rk817_bat_desc, &pscfg);
+
+ charger->chg_ps = devm_power_supply_register(&pdev->dev,
+ &rk817_chg_desc, &pscfg);
+
+ if (IS_ERR(charger->chg_ps))
+ return dev_err_probe(dev, -EINVAL,
+ "Battery failed to probe\n");
+
+ if (IS_ERR(charger->chg_ps))
+ return dev_err_probe(dev, -EINVAL,
+ "Charger failed to probe\n");
+
+ ret = power_supply_get_battery_info(charger->bat_ps,
+ &bat_info);
+ if (ret) {
+ return dev_err_probe(dev, ret,
+ "Unable to get battery info: %d\n", ret);
+ }
+
+ if ((bat_info->charge_full_design_uah <= 0) ||
+ (bat_info->voltage_min_design_uv <= 0) ||
+ (bat_info->voltage_max_design_uv <= 0) ||
+ (bat_info->constant_charge_voltage_max_uv <= 0) ||
+ (bat_info->constant_charge_current_max_ua <= 0) ||
+ (bat_info->charge_term_current_ua <= 0)) {
+ return dev_err_probe(dev, -EINVAL,
+ "Required bat info missing or invalid\n");
+ }
+
+ charger->bat_charge_full_design_uah = bat_info->charge_full_design_uah;
+ charger->bat_voltage_min_design_uv = bat_info->voltage_min_design_uv;
+ charger->bat_voltage_max_design_uv = bat_info->voltage_max_design_uv;
+
+ /*
+ * Has to run after power_supply_get_battery_info as it depends on some
+ * values discovered from that routine.
+ */
+ ret = rk817_battery_init(charger, bat_info);
+ if (ret)
+ return ret;
+
+ power_supply_put_battery_info(charger->bat_ps, bat_info);
+
+ plugin_irq = platform_get_irq(pdev, 0);
+ if (plugin_irq < 0)
+ return plugin_irq;
+
+ plugout_irq = platform_get_irq(pdev, 1);
+ if (plugout_irq < 0)
+ return plugout_irq;
+
+ ret = devm_request_threaded_irq(charger->dev, plugin_irq, NULL,
+ rk817_plug_in_isr,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "rk817_plug_in", charger);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "plug_in_irq request failed!\n");
+ }
+
+ ret = devm_request_threaded_irq(charger->dev, plugout_irq, NULL,
+ rk817_plug_out_isr,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "rk817_plug_out", charger);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "plug_out_irq request failed!\n");
+ }
+
+ ret = devm_delayed_work_autocancel(&pdev->dev, &charger->work,
+ rk817_charging_monitor);
+ if (ret)
+ return ret;
+
+ /* Force the first update immediately. */
+ mod_delayed_work(system_wq, &charger->work, 0);
+
+ return 0;
+}
+
+
+static struct platform_driver rk817_charger_driver = {
+ .probe = rk817_charger_probe,
+ .driver = {
+ .name = "rk817-charger",
+ },
+};
+module_platform_driver(rk817_charger_driver);
+
+MODULE_DESCRIPTION("Battery power supply driver for RK817 PMIC");
+MODULE_AUTHOR("Maya Matuszczyk <maccraft123mc@gmail.com>");
+MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index d663ab9670fe..070e4403c6c2 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -1282,6 +1282,7 @@ config REGULATOR_STW481X_VMMC
config REGULATOR_SY7636A
tristate "Silergy SY7636A voltage regulator"
+ depends on MFD_SY7636A
help
This driver supports Silergy SY3686A voltage regulator.