summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt230
-rw-r--r--drivers/regulator/Kconfig9
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/qpnp-oledb-regulator.c1193
4 files changed, 1433 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
new file mode 100644
index 000000000000..5d80a04c0b88
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
@@ -0,0 +1,230 @@
+QPNP OLEDB (AMOLED AVDD Bias) Regulator
+
+QPNP OLEDB module provides AVDD voltage bias to the AMOLED display panel.
+The supported voltage range is 5V to 8.1V.
+
+This document describes the bindings for QPNP OLEDB module.
+
+=======================
+Required Node Structure
+=======================
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: should be "qcom,qpnp-oledb-regulator".
+
+- reg
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Base address of the OLEDB SPMI peripheral.
+
+- label
+ Usage: required
+ Value type: <string>
+ Definition: A string used to describe the bias type(oledb).
+
+- regulator-name
+ Usage: required
+ Value type: <string>
+ Definition: A string used to describe the regulator.
+
+- regulator-min-microvolt
+ Usage: required
+ Value type: <u32>
+ Definition: Minimum voltage (in uV) supported by the bias (5000000uV).
+
+- regulator-max-microvolt
+ Usage: required
+ Value type: <u32>
+ Definition: Maximum voltage (in uV) supported by the bias (8100000uV).
+
+- qcom,swire-control
+ Usage: optional
+ Value type: <bool>
+ Definition: Enables the voltage programming through SWIRE signal.
+
+ qcom,ext-pin-control
+ Usage: optional
+ Value type: <bool>
+ Definition: Configures the OLED module to be enabled by a external pin.
+
+ qcom,dynamic-ext-pinctl-config
+ Usage: optional
+ Value type: <bool>
+ Definition: Used to dynamically enable/disable the OLEDB module
+ using external pin to avoid the glitches on the voltage
+ rail. This property is applicable only if qcom,ext-pin-ctl
+ property is specified and it is specific to PM660A.
+
+ qcom,pbs-control
+ Usage: optional
+ Value type: <bool>
+ Definition: PMIC PBS logic directly configures the output voltage update
+ and pull down control.
+
+ qcom,oledb-init-voltage-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: Sets the AVDD bias voltage (in mV) when the module is
+ already enabled. Applicable only if the qcom,swire-control
+ property is not specified. Supported values are from 5.0V
+ to 8.1V with a step of 100mV.
+
+qcom,oledb-default-voltage-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: Sets the default AVDD bias voltage (in mV) before module
+ enable. Supported values are from 5.0V to 8.1V with the
+ step of 100mV.
+
+qcom,bias-gen-warmup-delay-ns
+ Usage: optional
+ Value type: <u32>
+ Definition: Bias generator warm-up time (ns). Supported values are
+ 6700, 13300, 267000, 534000.
+
+qcom,peak-curr-limit-ma
+ Usage: optional
+ Value type: <u32>
+ Definition: Peak current limit (in mA). Supported values are 115, 265,
+ 415, 570, 720, 870, 1020, 1170.
+
+qcom,pull-down-enable
+ Usage: optional
+ Value type: <u32>
+ Definition: Pull down configuration of OLEDB.
+ 1 - Enable pull-down
+ 0 - Disable pull-down
+
+qcom,negative-curr-limit-enable
+ Usage: optional
+ Value type: <u32>
+ Definition: negative current limit enable/disable.
+ 1 = enable negative current limit
+ 0 = disable negative current limit
+
+qcom,negative-curr-limit-ma
+ Usage: optional
+ Value type: <u32>
+ Definition: Negative current limit (in mA). Supported values are
+ 170, 300, 420, 550.
+
+qcom,enable-short-circuit
+ Usage: optional
+ Value type: <u32>
+ Definition: Short circuit protection enable/disable.
+ 1 = enable short circuit protection
+ 0 = disable short circuit protection
+
+qcom,short-circuit-dbnc-time
+ usage: optional
+ Value type: <u32>
+ Definitioan: Short circuit debounce time (in Fsw). Supported
+ values are 2, 4, 8, 16.
+
+Fast precharge properties:
+-------------------------
+
+qcom,fast-precharge-ppulse-enable
+ usage: optional
+ Value type: <u32>
+ Definitioan: Fast precharge pfet pulsing enable/disable.
+ 1 = enable fast precharge pfet pulsing
+ 0 = disable fast precharge pfet pulsing
+
+qcom,precharge-debounce-time-ms
+ usage: optional
+ Value type: <u32>
+ Definitioan: Fast precharge debounce time (in ms). Supported
+ values are 1, 2, 4, 8.
+
+qcom,precharge-pulse-period-us
+ usage: optional
+ Value type: <u32>
+ Definitioan: Fast precharge pulse period (in us). Supported
+ values are 3, 6, 9, 12.
+
+qcom,precharge-pulse-on-time-us
+ usage: optional
+ Value type: <u32>
+ Definitioan: Fast precharge pulse on time (in ns). Supported
+ values are 1200, 1800, 2400, 3000.
+
+Pulse Skip Modulation (PSM) properties:
+--------------------------------------
+
+qcom,psm-enable
+ Usage: optional
+ Value type: <u32>
+ Definition: Pulse Skip Modulation mode.
+ 1 - Enable PSM mode
+ 0 - Disable PSM mode
+
+qcom,psm-hys-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: PSM hysterysis voltage (in mV).
+ Supported values are 13mV and 26mV.
+
+qcom,psm-vref-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: Reference voltage(in mV) control for PSM comparator.
+ Supported values are 440, 510, 580, 650, 715, 780, 850,
+ and 920.
+
+Pulse Frequency Modulation (PFM) properties:
+-------------------------------------------
+
+qcom,pfm-enable
+ Usage: optional
+ Value type: <u32>
+ Definition: Pulse Frequency Modulation mode.
+ 1 - Enable PFM mode
+ 0 - Disable PFM mode
+
+qcom,pfm-hys-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: PFM hysterysis voltage (in mV).
+ Supported values are 13mV and 26mV.
+
+qcom,pfm-curr-limit-ma
+ Usage: optional
+ Value type: <u32>
+ Definition: PFM current limit (in mA).
+ Supported values are 130, 200, 270, 340.
+
+qcom,pfm-off-time-ns
+ Usage: optional
+ Value type: <u32>
+ Definition: NFET off time at PFM (in ns).
+ Supported values are 110, 240, 350, 480.
+
+=======
+Example
+=======
+
+pm660a_oledb: qpnp-oledb@e000 {
+ compatible = "qcom,qpnp-oledb-regulator";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xe000 0x100>;
+
+ label = "oledb";
+ regulator-name = "regulator-oledb";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <8100000>;
+
+ qcom,swire-control;
+ qcom,ext-pin-control;
+
+ qcom,oledb-default-voltage-mv = <5000>;
+ qcom,bias-gen-warmup-delay-ns = <6700>;
+ qcom,pull-down-enable = <1>;
+ qcom,peak-curr-limit-ma = <570>;
+
+ qcom, enable-psm = <1>;
+ qcom,psm-hys-mv = <13>;
+};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index bd2c1a8e7540..8d54ece776e2 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -835,6 +835,15 @@ config REGULATOR_QPNP_LCDB
negative voltage bias for the LCD display panel. It also
allows configurability for the various bias-voltage parameters.
+config REGULATOR_QPNP_OLEDB
+ depends on SPMI
+ tristate "Qualcomm Technologies, Inc QPNP OLEDB regulator support"
+ help
+ This driver supports the OLEDB(AVDD bias) signal for AMOLED panel in Qualcomm
+ Technologies, Inc QPNP PMIC. It exposes the OLED voltage configuration
+ via the regulator framework. The configurable range of this bias is
+ 5V to 8.1V.
+
config REGULATOR_SPM
bool "SPM regulator driver"
depends on SPMI
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index abd116d3d8af..20cf304a4714 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -117,6 +117,7 @@ obj-$(CONFIG_REGULATOR_CPRH_KBSS) += cprh-kbss-regulator.o
obj-$(CONFIG_REGULATOR_CPR4_MMSS_LDO) += cpr4-mmss-ldo-regulator.o
obj-$(CONFIG_REGULATOR_QPNP_LABIBB) += qpnp-labibb-regulator.o
obj-$(CONFIG_REGULATOR_QPNP_LCDB) += qpnp-lcdb-regulator.o
+obj-$(CONFIG_REGULATOR_QPNP_OLEDB) += qpnp-oledb-regulator.o
obj-$(CONFIG_REGULATOR_STUB) += stub-regulator.o
obj-$(CONFIG_REGULATOR_KRYO) += kryo-regulator.o
obj-$(CONFIG_REGULATOR_CPR2_GFX) += cpr2-gfx-regulator.o
diff --git a/drivers/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c
new file mode 100644
index 000000000000..dd52b74b11b6
--- /dev/null
+++ b/drivers/regulator/qpnp-oledb-regulator.c
@@ -0,0 +1,1193 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "OLEDB: %s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#define QPNP_OLEDB_REGULATOR_DRIVER_NAME "qcom,qpnp-oledb-regulator"
+#define OLEDB_VOUT_STEP_MV 100
+#define OLEDB_VOUT_MIN_MV 5000
+#define OLEDB_VOUT_MAX_MV 8100
+#define OLEDB_VOUT_HW_DEFAULT 6400
+
+#define OLEDB_MODULE_RDY 0x45
+#define OLEDB_MODULE_RDY_BIT BIT(7)
+
+#define OLEDB_MODULE_ENABLE 0x46
+#define OLEDB_MODULE_ENABLE_BIT BIT(7)
+
+#define OLEDB_EXT_PIN_CTL 0x47
+#define OLEDB_EXT_PIN_CTL_BIT BIT(7)
+
+#define OLEDB_SWIRE_CONTROL 0x48
+#define OLEDB_EN_SWIRE_VOUT_UPD_BIT BIT(6)
+#define OLEDB_EN_SWIRE_PD_UPD_BIT BIT(7)
+
+#define OLEDB_VOUT_PGM 0x49
+#define OLEDB_VOUT_PGM_MASK GENMASK(4, 0)
+
+#define OLEDB_VOUT_DEFAULT 0x4A
+#define OLEDB_VOUT_DEFAULT_MASK GENMASK(4, 0)
+
+#define OLEDB_PD_CTL 0x4B
+
+#define OLEDB_ILIM_NFET 0x4E
+#define OLEDB_ILIMIT_NFET_MASK GENMASK(2, 0)
+
+#define OLEDB_BIAS_GEN_WARMUP_DELAY 0x52
+#define OLEDB_BIAS_GEN_WARMUP_DELAY_MASK GENMASK(1, 0)
+
+#define OLEDB_SHORT_PROTECT 0x59
+#define OLEDB_ENABLE_SC_DETECTION_BIT BIT(7)
+#define OLEDB_DBNC_SHORT_DETECTION_MASK GENMASK(1, 0)
+
+#define OLEDB_FAST_PRECHARGE 0x5A
+#define OLEDB_FAST_PRECHG_PPULSE_EN_BIT BIT(7)
+#define OLEDB_DBNC_PRECHARGE_MASK GENMASK(5, 4)
+#define OLEDB_DBNC_PRECHARGE_SHIFT 4
+#define OLEDB_PRECHARGE_PULSE_PERIOD_MASK GENMASK(3, 2)
+#define OLEDB_PRECHARGE_PULSE_PERIOD_SHIFT 2
+#define OLEDB_PRECHARGE_PULSE_TON_MASK GENMASK(1, 0)
+
+#define OLEDB_EN_PSM 0x5B
+#define OLEDB_PSM_ENABLE_BIT BIT(7)
+
+#define OLEDB_PSM_CTL 0x5C
+#define OLEDB_PSM_HYSTERYSIS_CTL_BIT BIT(3)
+#define OLEDB_PSM_HYSTERYSIS_CTL_BIT_SHIFT 3
+#define OLEDB_VREF_PSM_MASK GENMASK(2, 0)
+
+#define OLEDB_PFM_CTL 0x5D
+#define OLEDB_PFM_ENABLE_BIT BIT(7)
+#define OLEDB_PFM_HYSTERYSIS_CTRL_BIT_MASK BIT(4)
+#define OLEDB_PFM_HYSTERYSIS_CTL_BIT_SHIFT 4
+#define OLEDB_PFM_CURR_LIMIT_MASK GENMASK(3, 2)
+#define OLEDB_PFM_CURR_LIMIT_SHIFT 2
+#define OLEDB_PFM_OFF_TIME_NS_MASK GENMASK(1, 0)
+
+#define OLEDB_NLIMIT 0x64
+#define OLEDB_ENABLE_NLIMIT_BIT BIT(7)
+#define OLEDB_ENABLE_NLIMIT_BIT_SHIFT 7
+#define OLEDB_NLIMIT_PGM_MASK GENMASK(1, 0)
+
+#define OLEDB_PSM_HYS_CTRL_MIN 13
+#define OLEDB_PSM_HYS_CTRL_MAX 26
+
+#define OLEDB_PFM_HYS_CTRL_MIN 13
+#define OLEDB_PFM_HYS_CTRL_MAX 26
+
+#define OLEDB_PFM_OFF_TIME_MIN 110
+#define OLEDB_PFM_OFF_TIME_MAX 480
+
+#define OLEDB_PRECHG_TIME_MIN 1
+#define OLEDB_PRECHG_TIME_MAX 8
+
+#define OLEDB_PRECHG_PULSE_PERIOD_MIN 3
+#define OLEDB_PRECHG_PULSE_PERIOD_MAX 12
+
+#define OLEDB_MIN_SC_DBNC_TIME_FSW 2
+#define OLEDB_MAX_SC_DBNC_TIME_FSW 16
+
+#define OLEDB_PRECHG_PULSE_ON_TIME_MIN 1200
+#define OLEDB_PRECHG_PULSE_ON_TIME_MAX 3000
+
+#define PSM_HYSTERYSIS_MV_TO_VAL(val_mv) ((val_mv/13) - 1)
+#define PFM_HYSTERYSIS_MV_TO_VAL(val_mv) ((val_mv/13) - 1)
+#define PFM_OFF_TIME_NS_TO_VAL(val_ns) ((val_ns/110) - 1)
+#define PRECHG_DEBOUNCE_TIME_MS_TO_VAL(val_ms) ((val_ms/2) - \
+ (val_ms/8))
+#define PRECHG_PULSE_PERIOD_US_TO_VAL(val_us) ((val_us/3) - 1)
+#define PRECHG_PULSE_ON_TIME_NS_TO_VAL(val_ns) (val_ns/600 - 2)
+#define SHORT_CIRCUIT_DEBOUNCE_TIME_TO_VAL(val) ((val/4) - (val/16))
+
+struct qpnp_oledb_psm_ctl {
+ int psm_enable;
+ int psm_hys_ctl;
+ int psm_vref;
+};
+
+struct qpnp_oledb_pfm_ctl {
+ int pfm_enable;
+ int pfm_hys_ctl;
+ int pfm_curr_limit;
+ int pfm_off_time;
+};
+
+struct qpnp_oledb_fast_precharge_ctl {
+ int fast_prechg_ppulse_en;
+ int prechg_debounce_time;
+ int prechg_pulse_period;
+ int prechg_pulse_on_time;
+};
+
+struct qpnp_oledb {
+ struct platform_device *pdev;
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_desc rdesc;
+ struct regulator_dev *rdev;
+ struct qpnp_oledb_psm_ctl psm_ctl;
+ struct qpnp_oledb_pfm_ctl pfm_ctl;
+ struct qpnp_oledb_fast_precharge_ctl fast_prechg_ctl;
+
+ u32 base;
+ int current_voltage;
+ int default_voltage;
+ int vout_mv;
+ int warmup_delay;
+ int peak_curr_limit;
+ int pd_ctl;
+ int negative_curr_limit;
+ int nlimit_enable;
+ int sc_en;
+ int sc_dbnc_time;
+ bool mod_enable;
+ bool swire_control;
+ bool ext_pin_control;
+ bool ext_pinctl_state;
+ bool dynamic_ext_pinctl_config;
+ bool pbs_control;
+};
+
+static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400};
+static const u16 oledb_peak_curr_limit_ma[] = {115, 265, 415, 570,
+ 720, 870, 1020, 1170};
+static const u16 oledb_psm_vref_mv[] = {440, 510, 580, 650, 715,
+ 780, 850, 920};
+static const u16 oledb_pfm_curr_limit_ma[] = {130, 200, 270, 340};
+static const u16 oledb_nlimit_ma[] = {170, 300, 420, 550};
+
+static int qpnp_oledb_read(struct qpnp_oledb *oledb, u32 address,
+ u8 *val, int count)
+{
+ int rc = 0;
+ struct platform_device *pdev = oledb->pdev;
+
+ rc = regmap_bulk_read(oledb->regmap, address, val, count);
+ if (rc)
+ pr_err("Failed to read address=0x%02x sid=0x%02x rc=%d\n",
+ address, to_spmi_device(pdev->dev.parent)->usid, rc);
+
+ return rc;
+}
+
+static int qpnp_oledb_masked_write(struct qpnp_oledb *oledb,
+ u32 address, u8 mask, u8 val)
+{
+ int rc;
+
+ rc = regmap_update_bits(oledb->regmap, address, mask, val);
+ if (rc < 0)
+ pr_err("Failed to write address 0x%04X, rc = %d\n",
+ address, rc);
+ else
+ pr_debug("Wrote 0x%02X to addr 0x%04X\n",
+ val, address);
+
+ return rc;
+}
+
+static int qpnp_oledb_write(struct qpnp_oledb *oledb, u16 address, u8 *val,
+ int count)
+{
+ int rc = 0;
+ struct platform_device *pdev = oledb->pdev;
+
+ rc = regmap_bulk_write(oledb->regmap, address, val, count);
+ if (rc)
+ pr_err("Failed to write address=0x%02x sid=0x%02x rc=%d\n",
+ address, to_spmi_device(pdev->dev.parent)->usid, rc);
+ else
+ pr_debug("Wrote 0x%02X to addr 0x%04X\n",
+ *val, address);
+
+ return 0;
+}
+
+static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ u8 val = 0;
+
+ struct qpnp_oledb *oledb = rdev_get_drvdata(rdev);
+
+ if (oledb->ext_pin_control) {
+ rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to read EXT_PIN_CTL rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * Enable ext-pin-ctl after display-supply is turned on.
+ * This is to avoid glitches on the external pin.
+ */
+ if (!(val & OLEDB_EXT_PIN_CTL_BIT) &&
+ oledb->dynamic_ext_pinctl_config) {
+ val = OLEDB_EXT_PIN_CTL_BIT;
+ rc = qpnp_oledb_write(oledb, oledb->base +
+ OLEDB_EXT_PIN_CTL, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write EXT_PIN_CTL rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ pr_debug("ext-pin-ctrl mode enabled\n");
+ } else {
+ val = OLEDB_MODULE_ENABLE_BIT;
+ rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_MODULE_ENABLE,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write MODULE_ENABLE rc=%d\n", rc);
+ return rc;
+ }
+
+ ndelay(oledb->warmup_delay);
+ pr_debug("register-control mode, module enabled\n");
+ }
+
+ oledb->mod_enable = true;
+ if (oledb->pbs_control) {
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_SWIRE_CONTROL, OLEDB_EN_SWIRE_PD_UPD_BIT |
+ OLEDB_EN_SWIRE_VOUT_UPD_BIT, 0);
+ if (rc < 0)
+ pr_err("Failed to write SWIRE_CTL for pbs mode rc=%d\n",
+ rc);
+ }
+
+ return rc;
+}
+
+static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+
+ struct qpnp_oledb *oledb = rdev_get_drvdata(rdev);
+
+ /*
+ * Disable ext-pin-ctl after display-supply is turned off. This is to
+ * avoid glitches on the external pin.
+ */
+ if (oledb->ext_pin_control && oledb->dynamic_ext_pinctl_config) {
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_EXT_PIN_CTL, OLEDB_EXT_PIN_CTL_BIT, 0);
+ if (rc < 0) {
+ pr_err("Failed to write EXT_PIN_CTL rc=%d\n", rc);
+ return rc;
+ }
+ pr_debug("ext-pin-ctrl mode disabled\n");
+ } else {
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_MODULE_ENABLE,
+ OLEDB_MODULE_ENABLE_BIT, 0);
+ if (rc < 0) {
+ pr_err("Failed to write MODULE_ENABLE rc=%d\n", rc);
+ return rc;
+ }
+ pr_debug("Register-control mode, module disabled\n");
+ }
+
+ oledb->mod_enable = false;
+
+ return rc;
+}
+
+static int qpnp_oledb_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct qpnp_oledb *oledb = rdev_get_drvdata(rdev);
+
+ return oledb->mod_enable;
+}
+
+static int qpnp_oledb_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ u8 val;
+ int rc = 0;
+
+ struct qpnp_oledb *oledb = rdev_get_drvdata(rdev);
+
+ if (oledb->swire_control)
+ return 0;
+
+ val = DIV_ROUND_UP(min_uV - OLEDB_VOUT_MIN_MV, OLEDB_VOUT_STEP_MV);
+
+ rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_VOUT_PGM,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write VOUT_PGM rc=%d\n", rc);
+ return rc;
+ }
+
+ oledb->current_voltage = min_uV;
+ pr_debug("register-control mode, current voltage %d\n",
+ oledb->current_voltage);
+
+ return 0;
+}
+
+static int qpnp_oledb_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct qpnp_oledb *oledb = rdev_get_drvdata(rdev);
+
+ if (oledb->swire_control)
+ return 0;
+
+ return oledb->current_voltage;
+}
+
+static struct regulator_ops qpnp_oledb_ops = {
+ .enable = qpnp_oledb_regulator_enable,
+ .disable = qpnp_oledb_regulator_disable,
+ .is_enabled = qpnp_oledb_regulator_is_enabled,
+ .set_voltage = qpnp_oledb_regulator_set_voltage,
+ .get_voltage = qpnp_oledb_regulator_get_voltage,
+};
+
+static int qpnp_oledb_register_regulator(struct qpnp_oledb *oledb)
+{
+ int rc = 0;
+ struct platform_device *pdev = oledb->pdev;
+ struct regulator_init_data *init_data;
+ struct regulator_desc *rdesc = &oledb->rdesc;
+ struct regulator_config cfg = {};
+
+ init_data = of_get_regulator_init_data(&pdev->dev,
+ pdev->dev.of_node, rdesc);
+ if (!init_data) {
+ pr_err("Unable to get OLEDB regulator init data\n");
+ return -ENOMEM;
+ }
+
+ if (init_data->constraints.name) {
+ rdesc->owner = THIS_MODULE;
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->ops = &qpnp_oledb_ops;
+ rdesc->name = init_data->constraints.name;
+
+ cfg.dev = &pdev->dev;
+ cfg.init_data = init_data;
+ cfg.driver_data = oledb;
+ cfg.of_node = pdev->dev.of_node;
+
+ if (of_get_property(pdev->dev.of_node, "parent-supply",
+ NULL))
+ init_data->supply_regulator = "parent";
+
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS;
+
+ oledb->rdev = devm_regulator_register(oledb->dev, rdesc, &cfg);
+ if (IS_ERR(oledb->rdev)) {
+ rc = PTR_ERR(oledb->rdev);
+ oledb->rdev = NULL;
+ pr_err("Unable to register OLEDB regulator, rc = %d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ pr_err("OLEDB regulator name missing\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qpnp_oledb_get_curr_voltage(struct qpnp_oledb *oledb,
+ u16 *current_voltage)
+{
+ int rc = 0;
+ u8 val;
+
+ if (!(oledb->mod_enable || oledb->ext_pinctl_state)) {
+ rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_VOUT_DEFAULT,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to read VOUT_DEFAULT rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = qpnp_oledb_read(oledb, oledb->base +
+ OLEDB_VOUT_PGM, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to read VOUT_PGM rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ *current_voltage = (val * OLEDB_VOUT_STEP_MV) + OLEDB_VOUT_MIN_MV;
+
+ return rc;
+}
+
+static int qpnp_oledb_init_nlimit(struct qpnp_oledb *oledb)
+{
+ int rc = 0, i = 0;
+ u32 val, mask = 0;
+
+ if (oledb->nlimit_enable != -EINVAL) {
+ val = oledb->nlimit_enable <<
+ OLEDB_ENABLE_NLIMIT_BIT_SHIFT;
+ mask = OLEDB_ENABLE_NLIMIT_BIT;
+ if (oledb->negative_curr_limit != -EINVAL) {
+ for (i = 0; i < ARRAY_SIZE(oledb_nlimit_ma); i++) {
+ if (oledb->negative_curr_limit ==
+ oledb_nlimit_ma[i])
+ break;
+ }
+ val |= i;
+ mask |= OLEDB_NLIMIT_PGM_MASK;
+ }
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_NLIMIT, mask, val);
+ if (rc < 0)
+ pr_err("Failed to write NLIMT rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+static int qpnp_oledb_init_psm(struct qpnp_oledb *oledb)
+{
+ int rc = 0, i = 0;
+ u32 val = 0, mask = 0, temp = 0;
+ struct qpnp_oledb_psm_ctl *psm_ctl = &oledb->psm_ctl;
+
+ if (psm_ctl->psm_enable == -EINVAL)
+ return rc;
+
+ if (psm_ctl->psm_enable) {
+ val = OLEDB_PSM_ENABLE_BIT;
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_EN_PSM, OLEDB_PSM_ENABLE_BIT, val);
+ if (rc < 0) {
+ pr_err("Failed to write PSM_EN rc=%d\n", rc);
+ return rc;
+ }
+
+ val = 0;
+ if (psm_ctl->psm_vref != -EINVAL) {
+ for (i = 0; i < ARRAY_SIZE(oledb_psm_vref_mv); i++) {
+ if (psm_ctl->psm_vref ==
+ oledb_psm_vref_mv[i])
+ break;
+ }
+ val = i;
+ mask = OLEDB_VREF_PSM_MASK;
+ }
+
+ if (psm_ctl->psm_hys_ctl != -EINVAL) {
+ temp = PSM_HYSTERYSIS_MV_TO_VAL(psm_ctl->psm_hys_ctl);
+ val |= (temp << OLEDB_PSM_HYSTERYSIS_CTL_BIT_SHIFT);
+ mask |= OLEDB_PSM_HYSTERYSIS_CTL_BIT;
+ }
+ if (val) {
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_PSM_CTL, mask, val);
+ if (rc < 0)
+ pr_err("Failed to write PSM_CTL rc=%d\n", rc);
+ }
+ } else {
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_EN_PSM, OLEDB_PSM_ENABLE_BIT, 0);
+ if (rc < 0)
+ pr_err("Failed to write PSM_CTL rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+static int qpnp_oledb_init_pfm(struct qpnp_oledb *oledb)
+{
+ int rc = 0, i = 0;
+ u32 val = 0, temp = 0, mask = 0;
+ struct qpnp_oledb_pfm_ctl *pfm_ctl = &oledb->pfm_ctl;
+
+ if (pfm_ctl->pfm_enable == -EINVAL)
+ return rc;
+
+ if (pfm_ctl->pfm_enable) {
+ mask = val = OLEDB_PFM_ENABLE_BIT;
+ if (pfm_ctl->pfm_hys_ctl != -EINVAL) {
+ temp = PFM_HYSTERYSIS_MV_TO_VAL(pfm_ctl->pfm_hys_ctl);
+ val |= temp <<
+ OLEDB_PFM_HYSTERYSIS_CTL_BIT_SHIFT;
+ mask |= OLEDB_PFM_HYSTERYSIS_CTRL_BIT_MASK;
+ }
+
+ if (pfm_ctl->pfm_curr_limit != -EINVAL) {
+ for (i = 0; i < ARRAY_SIZE(oledb_pfm_curr_limit_ma);
+ i++) {
+ if (pfm_ctl->pfm_curr_limit ==
+ oledb_pfm_curr_limit_ma[i])
+ break;
+ }
+ val |= (i << OLEDB_PFM_CURR_LIMIT_SHIFT);
+ mask |= OLEDB_PFM_CURR_LIMIT_MASK;
+ }
+
+ if (pfm_ctl->pfm_off_time != -EINVAL) {
+ val |= PFM_OFF_TIME_NS_TO_VAL(pfm_ctl->pfm_off_time);
+ mask |= OLEDB_PFM_OFF_TIME_NS_MASK;
+ }
+
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_PFM_CTL, mask, val);
+ if (rc < 0)
+ pr_err("Failed to write PFM_CTL rc=%d\n", rc);
+ } else {
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_PFM_CTL, OLEDB_PFM_ENABLE_BIT, 0);
+ if (rc < 0)
+ pr_err("Failed to write PFM_CTL rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+static int qpnp_oledb_init_fast_precharge(struct qpnp_oledb *oledb)
+{
+ int rc = 0;
+ u32 val = 0, temp = 0, mask = 0;
+ struct qpnp_oledb_fast_precharge_ctl *prechg_ctl =
+ &oledb->fast_prechg_ctl;
+
+ if (prechg_ctl->fast_prechg_ppulse_en == -EINVAL)
+ return rc;
+
+ if (prechg_ctl->fast_prechg_ppulse_en) {
+ mask = val = OLEDB_FAST_PRECHG_PPULSE_EN_BIT;
+ if (prechg_ctl->prechg_debounce_time != -EINVAL) {
+ temp = PRECHG_DEBOUNCE_TIME_MS_TO_VAL(
+ prechg_ctl->prechg_debounce_time);
+ val |= temp << OLEDB_DBNC_PRECHARGE_SHIFT;
+ mask |= OLEDB_DBNC_PRECHARGE_MASK;
+ }
+
+ if (prechg_ctl->prechg_pulse_period != -EINVAL) {
+ temp = PRECHG_PULSE_PERIOD_US_TO_VAL(
+ prechg_ctl->prechg_pulse_period);
+ val |= temp << OLEDB_PRECHARGE_PULSE_PERIOD_SHIFT;
+ mask |= OLEDB_PRECHARGE_PULSE_PERIOD_MASK;
+ }
+
+ if (prechg_ctl->prechg_pulse_on_time != -EINVAL) {
+ val |= PRECHG_PULSE_ON_TIME_NS_TO_VAL(
+ prechg_ctl->prechg_pulse_on_time);
+ mask |= OLEDB_PRECHARGE_PULSE_TON_MASK;
+ }
+
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_FAST_PRECHARGE, mask, val);
+ if (rc < 0)
+ pr_err("Failed to write FAST_PRECHARGE rc=%d\n", rc);
+ } else {
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_FAST_PRECHARGE,
+ OLEDB_FAST_PRECHG_PPULSE_EN_BIT, 0);
+ if (rc < 0)
+ pr_err("Failed to write FAST_PRECHARGE rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+static int qpnp_oledb_hw_init(struct qpnp_oledb *oledb)
+{
+ int rc, i = 0;
+ u8 val = 0, mask = 0;
+ u16 current_voltage;
+
+ if (oledb->default_voltage != -EINVAL) {
+ val = (oledb->default_voltage - OLEDB_VOUT_MIN_MV) /
+ OLEDB_VOUT_STEP_MV;
+ rc = qpnp_oledb_write(oledb, oledb->base +
+ OLEDB_VOUT_DEFAULT, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write VOUT_DEFAULT rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_MODULE_ENABLE,
+ (u8 *)&oledb->mod_enable, 1);
+ if (rc < 0) {
+ pr_err("Failed to read MODULE_ENABLE rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL,
+ (u8 *)&oledb->ext_pinctl_state, 1);
+ if (rc < 0) {
+ pr_err("Failed to read EXT_PIN_CTL rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_oledb_get_curr_voltage(oledb, &current_voltage);
+ if (rc < 0)
+ return rc;
+
+ if (!((val & OLEDB_EXT_PIN_CTL_BIT) || oledb->mod_enable)) {
+ if (oledb->warmup_delay != -EINVAL) {
+ for (i = 0; i < ARRAY_SIZE(oledb_warmup_dly_ns); i++) {
+ if (oledb->warmup_delay ==
+ oledb_warmup_dly_ns[i])
+ break;
+ }
+ val = i;
+ rc = qpnp_oledb_masked_write(oledb,
+ oledb->base + OLEDB_BIAS_GEN_WARMUP_DELAY,
+ OLEDB_BIAS_GEN_WARMUP_DELAY_MASK, val);
+ if (rc < 0) {
+ pr_err("Failed to write WARMUP_DELAY rc=%d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ rc = qpnp_oledb_read(oledb, oledb->base +
+ OLEDB_BIAS_GEN_WARMUP_DELAY,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to read WARMUP_DELAY rc=%d\n",
+ rc);
+ return rc;
+ }
+ oledb->warmup_delay = oledb_warmup_dly_ns[val];
+ }
+
+ if (oledb->peak_curr_limit != -EINVAL) {
+ for (i = 0; i < ARRAY_SIZE(oledb_peak_curr_limit_ma);
+ i++) {
+ if (oledb->peak_curr_limit ==
+ oledb_peak_curr_limit_ma[i])
+ break;
+ }
+ val = i;
+ rc = qpnp_oledb_write(oledb,
+ oledb->base + OLEDB_ILIM_NFET,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write ILIM_NEFT rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (oledb->pd_ctl != -EINVAL) {
+ val = oledb->pd_ctl;
+ rc = qpnp_oledb_write(oledb, oledb->base +
+ OLEDB_PD_CTL, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write PD_CTL rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (oledb->sc_en != -EINVAL) {
+ val = oledb->sc_en ? OLEDB_ENABLE_SC_DETECTION_BIT : 0;
+ mask = OLEDB_ENABLE_SC_DETECTION_BIT;
+ if (oledb->sc_dbnc_time != -EINVAL) {
+ val |= SHORT_CIRCUIT_DEBOUNCE_TIME_TO_VAL(
+ oledb->sc_dbnc_time);
+ mask |= OLEDB_DBNC_PRECHARGE_MASK;
+ }
+
+ rc = qpnp_oledb_write(oledb, oledb->base +
+ OLEDB_SHORT_PROTECT, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write SHORT_PROTECT rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ rc = qpnp_oledb_init_nlimit(oledb);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_oledb_init_psm(oledb);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_oledb_init_pfm(oledb);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_oledb_init_fast_precharge(oledb);
+ if (rc < 0)
+ return rc;
+
+ if (oledb->swire_control) {
+ val = OLEDB_EN_SWIRE_PD_UPD_BIT |
+ OLEDB_EN_SWIRE_VOUT_UPD_BIT;
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_SWIRE_CONTROL, OLEDB_EN_SWIRE_PD_UPD_BIT |
+ OLEDB_EN_SWIRE_VOUT_UPD_BIT, val);
+ if (rc < 0)
+ return rc;
+ }
+
+ rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_MODULE_RDY,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to read MODULE_RDY rc=%d\n", rc);
+ return rc;
+ }
+
+ if (!(val & OLEDB_MODULE_RDY_BIT)) {
+ val = OLEDB_MODULE_RDY_BIT;
+ rc = qpnp_oledb_write(oledb, oledb->base +
+ OLEDB_MODULE_RDY, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write MODULE_RDY rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (!oledb->dynamic_ext_pinctl_config) {
+ if (oledb->ext_pin_control) {
+ val = OLEDB_EXT_PIN_CTL_BIT;
+ rc = qpnp_oledb_write(oledb, oledb->base +
+ OLEDB_EXT_PIN_CTL, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write EXT_PIN_CTL rc=%d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ val = OLEDB_MODULE_ENABLE_BIT;
+ rc = qpnp_oledb_write(oledb, oledb->base +
+ OLEDB_MODULE_ENABLE, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write MODULE_ENABLE rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ ndelay(oledb->warmup_delay);
+ }
+
+ oledb->mod_enable = true;
+ if (oledb->pbs_control) {
+ rc = qpnp_oledb_masked_write(oledb,
+ oledb->base + OLEDB_SWIRE_CONTROL,
+ OLEDB_EN_SWIRE_PD_UPD_BIT |
+ OLEDB_EN_SWIRE_VOUT_UPD_BIT, 0);
+ if (rc < 0) {
+ pr_err("Failed to write SWIRE_CTL rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ oledb->current_voltage = current_voltage;
+ } else {
+ /* module is enabled */
+ if (oledb->current_voltage == -EINVAL) {
+ oledb->current_voltage = current_voltage;
+ } else if (!oledb->swire_control) {
+ if (oledb->current_voltage < OLEDB_VOUT_MIN_MV) {
+ pr_err("current_voltage %d is less than min_volt %d\n",
+ oledb->current_voltage, OLEDB_VOUT_MIN_MV);
+ return -EINVAL;
+ }
+ val = DIV_ROUND_UP(oledb->current_voltage -
+ OLEDB_VOUT_MIN_MV, OLEDB_VOUT_STEP_MV);
+ rc = qpnp_oledb_write(oledb, oledb->base +
+ OLEDB_VOUT_PGM, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write VOUT_PGM rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ oledb->mod_enable = true;
+ }
+
+ return rc;
+}
+
+static int qpnp_oledb_parse_nlimit(struct qpnp_oledb *oledb)
+{
+ int rc = 0;
+ struct device_node *of_node = oledb->dev->of_node;
+
+ oledb->nlimit_enable = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,negative-curr-limit-enable",
+ &oledb->nlimit_enable);
+ if (!rc) {
+ oledb->negative_curr_limit = -EINVAL;
+ rc = of_property_read_u32(of_node,
+ "qcom,negative-curr-limit-ma",
+ &oledb->negative_curr_limit);
+ if (!rc) {
+ u16 min_curr_limit = oledb_nlimit_ma[0];
+ u16 max_curr_limit = oledb_nlimit_ma[ARRAY_SIZE(
+ oledb_nlimit_ma) - 1];
+ if (oledb->negative_curr_limit < min_curr_limit ||
+ oledb->negative_curr_limit > max_curr_limit) {
+ pr_err("Invalid value in qcom,negative-curr-limit-ma\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int qpnp_oledb_parse_psm(struct qpnp_oledb *oledb)
+{
+ int rc = 0;
+ struct qpnp_oledb_psm_ctl *psm_ctl = &oledb->psm_ctl;
+ struct device_node *of_node = oledb->dev->of_node;
+
+ psm_ctl->psm_enable = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,psm-enable",
+ &psm_ctl->psm_enable);
+ if (!rc) {
+ psm_ctl->psm_hys_ctl = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,psm-hys-mv",
+ &psm_ctl->psm_hys_ctl);
+ if (!rc) {
+ if (psm_ctl->psm_hys_ctl < OLEDB_PSM_HYS_CTRL_MIN ||
+ psm_ctl->psm_hys_ctl > OLEDB_PSM_HYS_CTRL_MAX) {
+ pr_err("Invalid value in qcom,psm-hys-mv\n");
+ return -EINVAL;
+ }
+ }
+
+ psm_ctl->psm_vref = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,psm-vref-mv",
+ &psm_ctl->psm_vref);
+ if (!rc) {
+ u16 min_vref = oledb_psm_vref_mv[0];
+ u16 max_vref = oledb_psm_vref_mv[ARRAY_SIZE(
+ oledb_psm_vref_mv) - 1];
+ if (psm_ctl->psm_vref < min_vref ||
+ psm_ctl->psm_vref > max_vref) {
+ pr_err("Invalid value in qcom,psm-vref-mv\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int qpnp_oledb_parse_pfm(struct qpnp_oledb *oledb)
+{
+ int rc = 0;
+ struct qpnp_oledb_pfm_ctl *pfm_ctl = &oledb->pfm_ctl;
+ struct device_node *of_node = oledb->dev->of_node;
+
+ pfm_ctl->pfm_enable = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,pfm-enable",
+ &pfm_ctl->pfm_enable);
+ if (!rc) {
+ pfm_ctl->pfm_hys_ctl = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,pfm-hys-mv",
+ &pfm_ctl->pfm_hys_ctl);
+ if (!rc) {
+ if (pfm_ctl->pfm_hys_ctl < OLEDB_PFM_HYS_CTRL_MIN ||
+ pfm_ctl->pfm_hys_ctl > OLEDB_PFM_HYS_CTRL_MAX) {
+ pr_err("Invalid value in qcom,pfm-hys-mv\n");
+ return -EINVAL;
+ }
+ }
+
+ pfm_ctl->pfm_curr_limit = -EINVAL;
+ rc = of_property_read_u32(of_node,
+ "qcom,pfm-curr-limit-ma", &pfm_ctl->pfm_curr_limit);
+ if (!rc) {
+ u16 min_limit = oledb_pfm_curr_limit_ma[0];
+ u16 max_limit = oledb_pfm_curr_limit_ma[ARRAY_SIZE(
+ oledb_pfm_curr_limit_ma) - 1];
+ if (pfm_ctl->pfm_curr_limit < min_limit ||
+ pfm_ctl->pfm_curr_limit > max_limit) {
+ pr_err("Invalid value in qcom,pfm-curr-limit-ma\n");
+ return -EINVAL;
+ }
+ }
+
+ pfm_ctl->pfm_off_time = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,pfm-off-time-ns",
+ &pfm_ctl->pfm_off_time);
+ if (!rc) {
+ if (pfm_ctl->pfm_off_time < OLEDB_PFM_OFF_TIME_MIN ||
+ pfm_ctl->pfm_off_time > OLEDB_PFM_OFF_TIME_MAX) {
+ pr_err("Invalid value in qcom,pfm-off-time-ns\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int qpnp_oledb_parse_fast_precharge(struct qpnp_oledb *oledb)
+{
+ int rc = 0;
+ struct device_node *of_node = oledb->dev->of_node;
+ struct qpnp_oledb_fast_precharge_ctl *fast_prechg =
+ &oledb->fast_prechg_ctl;
+
+ fast_prechg->fast_prechg_ppulse_en = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,fast-precharge-ppulse-enable",
+ &fast_prechg->fast_prechg_ppulse_en);
+ if (!rc) {
+ fast_prechg->prechg_debounce_time = -EINVAL;
+ rc = of_property_read_u32(of_node,
+ "qcom,precharge-debounce-time-ms",
+ &fast_prechg->prechg_debounce_time);
+ if (!rc) {
+ int dbnc_time = fast_prechg->prechg_debounce_time;
+
+ if (dbnc_time < OLEDB_PRECHG_TIME_MIN || dbnc_time >
+ OLEDB_PRECHG_TIME_MAX) {
+ pr_err("Invalid value in qcom,precharge-debounce-time-ms\n");
+ return -EINVAL;
+ }
+ }
+
+ fast_prechg->prechg_pulse_period = -EINVAL;
+ rc = of_property_read_u32(of_node,
+ "qcom,precharge-pulse-period-us",
+ &fast_prechg->prechg_pulse_period);
+ if (!rc) {
+ int pulse_period = fast_prechg->prechg_pulse_period;
+
+ if (pulse_period < OLEDB_PRECHG_PULSE_PERIOD_MIN ||
+ pulse_period > OLEDB_PRECHG_PULSE_PERIOD_MAX) {
+ pr_err("Invalid value in qcom,precharge-pulse-period-us\n");
+ return -EINVAL;
+ }
+ }
+
+ fast_prechg->prechg_pulse_on_time = -EINVAL;
+ rc = of_property_read_u32(of_node,
+ "qcom,precharge-pulse-on-time-ns",
+ &fast_prechg->prechg_pulse_on_time);
+ if (!rc) {
+ int pulse_on_time = fast_prechg->prechg_pulse_on_time;
+
+ if (pulse_on_time < OLEDB_PRECHG_PULSE_ON_TIME_MIN ||
+ pulse_on_time > OLEDB_PRECHG_PULSE_ON_TIME_MAX) {
+ pr_err("Invalid value in qcom,precharge-pulse-on-time-ns\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
+{
+ int rc = 0;
+ struct device_node *of_node = oledb->dev->of_node;
+
+ oledb->swire_control =
+ of_property_read_bool(of_node, "qcom,swire-control");
+
+ oledb->ext_pin_control =
+ of_property_read_bool(of_node, "qcom,ext-pin-control");
+
+ if (oledb->ext_pin_control)
+ oledb->dynamic_ext_pinctl_config =
+ of_property_read_bool(of_node,
+ "qcom,dynamic-ext-pinctl-config");
+ oledb->pbs_control =
+ of_property_read_bool(of_node, "qcom,pbs-control");
+
+ oledb->current_voltage = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,oledb-init-voltage-mv",
+ &oledb->current_voltage);
+ if (!rc && (oledb->current_voltage < OLEDB_VOUT_MIN_MV ||
+ oledb->current_voltage > OLEDB_VOUT_MAX_MV)) {
+ pr_err("Invalid value in qcom,oledb-init-voltage-mv\n");
+ return -EINVAL;
+ }
+
+ oledb->default_voltage = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,oledb-default-voltage-mv",
+ &oledb->default_voltage);
+ if (!rc && (oledb->default_voltage < OLEDB_VOUT_MIN_MV ||
+ oledb->default_voltage > OLEDB_VOUT_MAX_MV)) {
+ pr_err("Invalid value in qcom,oledb-default-voltage-mv\n");
+ return -EINVAL;
+ }
+
+ oledb->warmup_delay = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,bias-gen-warmup-delay-ns",
+ &oledb->warmup_delay);
+ if (!rc) {
+ u16 min_delay = oledb_warmup_dly_ns[0];
+ u16 max_delay = oledb_warmup_dly_ns[ARRAY_SIZE(
+ oledb_warmup_dly_ns) - 1];
+ if (oledb->warmup_delay < min_delay ||
+ oledb->warmup_delay > max_delay) {
+ pr_err("Invalid value in qcom,bias-gen-warmup-delay-ns\n");
+ return -EINVAL;
+ }
+ }
+
+ oledb->peak_curr_limit = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,peak-curr-limit-ma",
+ &oledb->peak_curr_limit);
+ if (!rc) {
+ u16 min_limit = oledb_peak_curr_limit_ma[0];
+ u16 max_limit = oledb_peak_curr_limit_ma[ARRAY_SIZE(
+ oledb_peak_curr_limit_ma) - 1];
+ if (oledb->peak_curr_limit < min_limit ||
+ oledb->peak_curr_limit > max_limit) {
+ pr_err("Invalid value in qcom,peak-curr-limit-ma\n");
+ return -EINVAL;
+ }
+ }
+
+ oledb->pd_ctl = -EINVAL;
+ of_property_read_u32(of_node, "qcom,pull-down-enable", &oledb->pd_ctl);
+
+ oledb->sc_en = -EINVAL;
+ rc = of_property_read_u32(of_node, "qcom,enable-short-circuit",
+ &oledb->sc_en);
+ if (!rc) {
+ oledb->sc_dbnc_time = -EINVAL;
+ rc = of_property_read_u32(of_node,
+ "qcom,short-circuit-dbnc-time", &oledb->sc_dbnc_time);
+ if (!rc) {
+ if (oledb->sc_dbnc_time < OLEDB_MIN_SC_DBNC_TIME_FSW ||
+ oledb->sc_dbnc_time > OLEDB_MAX_SC_DBNC_TIME_FSW) {
+ pr_err("Invalid value in qcom,short-circuit-dbnc-time\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ rc = qpnp_oledb_parse_nlimit(oledb);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_oledb_parse_psm(oledb);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_oledb_parse_pfm(oledb);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_oledb_parse_fast_precharge(oledb);
+
+ return rc;
+}
+
+static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ u32 val;
+ struct qpnp_oledb *oledb;
+ struct device_node *of_node = pdev->dev.of_node;
+
+ oledb = devm_kzalloc(&pdev->dev,
+ sizeof(struct qpnp_oledb), GFP_KERNEL);
+ if (!oledb)
+ return -ENOMEM;
+
+ oledb->pdev = pdev;
+ oledb->dev = &pdev->dev;
+ oledb->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ dev_set_drvdata(&pdev->dev, oledb);
+ if (!oledb->regmap) {
+ pr_err("Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(of_node, "reg", &val);
+ if (rc < 0) {
+ pr_err("Couldn't find reg in node, rc = %d\n", rc);
+ return rc;
+ }
+
+ oledb->base = val;
+ rc = qpnp_oledb_parse_dt(oledb);
+ if (rc < 0) {
+ pr_err("Failed to parse common OLEDB device tree\n");
+ return rc;
+ }
+
+ rc = qpnp_oledb_hw_init(oledb);
+ if (rc < 0) {
+ pr_err("Failed to initialize OLEDB, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_oledb_register_regulator(oledb);
+ if (!rc)
+ pr_info("OLEDB registered successfully, ext_pin_en=%d mod_en=%d cuurent_voltage=%d mV\n",
+ oledb->ext_pin_control, oledb->mod_enable,
+ oledb->current_voltage);
+
+ return rc;
+}
+
+static int qpnp_oledb_regulator_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+const struct of_device_id qpnp_oledb_regulator_match_table[] = {
+ { .compatible = QPNP_OLEDB_REGULATOR_DRIVER_NAME,},
+ { },
+};
+
+static struct platform_driver qpnp_oledb_regulator_driver = {
+ .driver = {
+ .name = QPNP_OLEDB_REGULATOR_DRIVER_NAME,
+ .of_match_table = qpnp_oledb_regulator_match_table,
+ },
+ .probe = qpnp_oledb_regulator_probe,
+ .remove = qpnp_oledb_regulator_remove,
+};
+
+static int __init qpnp_oledb_regulator_init(void)
+{
+ return platform_driver_register(&qpnp_oledb_regulator_driver);
+}
+arch_initcall(qpnp_oledb_regulator_init);
+
+static void __exit qpnp_oledb_regulator_exit(void)
+{
+ platform_driver_unregister(&qpnp_oledb_regulator_driver);
+}
+module_exit(qpnp_oledb_regulator_exit);
+
+MODULE_DESCRIPTION("QPNP OLEDB driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("qpnp-oledb-regulator");