/* Copyright (c) 2014-2017, 2019, 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) "%s: " fmt, __func__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define QPNP_LABIBB_REGULATOR_DRIVER_NAME "qcom,qpnp-labibb-regulator" #define REG_REVISION_2 0x01 #define REG_PERPH_TYPE 0x04 #define REG_INT_RT_STS 0x10 #define QPNP_LAB_TYPE 0x24 #define QPNP_IBB_TYPE 0x20 /* Common register value for LAB/IBB */ #define REG_LAB_IBB_LCD_MODE 0x0 #define REG_LAB_IBB_AMOLED_MODE BIT(7) #define REG_LAB_IBB_SEC_ACCESS 0xD0 #define REG_LAB_IBB_SEC_UNLOCK_CODE 0xA5 /* LAB register offset definitions */ #define REG_LAB_STATUS1 0x08 #define REG_LAB_SWIRE_PGM_CTL 0x40 #define REG_LAB_VOLTAGE 0x41 #define REG_LAB_RING_SUPPRESSION_CTL 0x42 #define REG_LAB_LCD_AMOLED_SEL 0x44 #define REG_LAB_MODULE_RDY 0x45 #define REG_LAB_ENABLE_CTL 0x46 #define REG_LAB_PD_CTL 0x47 #define REG_LAB_CLK_DIV 0x48 #define REG_LAB_IBB_EN_RDY 0x49 #define REG_LAB_CURRENT_LIMIT 0x4B #define REG_LAB_CURRENT_SENSE 0x4C #define REG_LAB_PS_CTL 0x50 #define REG_LAB_RDSON_MNGMNT 0x53 #define REG_LAB_PRECHARGE_CTL 0x5E #define REG_LAB_SOFT_START_CTL 0x5F #define REG_LAB_SPARE_CTL 0x60 #define REG_LAB_MISC_CTL 0x60 /* PMI8998/PM660A */ #define REG_LAB_PFM_CTL 0x62 /* LAB registers for PM660A */ #define REG_LAB_VOUT_DEFAULT 0x44 #define REG_LAB_SW_HIGH_PSRR_CTL 0x70 #define REG_LAB_LDO_PD_CTL 0x78 #define REG_LAB_VPH_ENVELOP_CTL 0x7E /* LAB register bits definitions */ /* REG_LAB_STATUS1 */ #define LAB_STATUS1_VREG_OK_BIT BIT(7) #define LAB_STATUS1_SC_DETECT_BIT BIT(6) /* REG_LAB_SWIRE_PGM_CTL */ #define LAB_EN_SWIRE_PGM_VOUT BIT(7) #define LAB_EN_SWIRE_PGM_PD BIT(6) /* REG_LAB_VOLTAGE */ #define LAB_VOLTAGE_OVERRIDE_EN BIT(7) #define LAB_VOLTAGE_SET_MASK GENMASK(3, 0) /* REG_LAB_RING_SUPPRESSION_CTL */ #define LAB_RING_SUPPRESSION_CTL_EN BIT(7) /* REG_LAB_MODULE_RDY */ #define LAB_MODULE_RDY_EN BIT(7) /* REG_LAB_ENABLE_CTL */ #define LAB_ENABLE_CTL_EN BIT(7) /* REG_LAB_PD_CTL */ #define LAB_PD_CTL_STRONG_PULL BIT(0) #define LAB_PD_CTL_STRENGTH_MASK BIT(0) #define LAB_PD_CTL_DISABLE_PD BIT(1) #define LAB_PD_CTL_EN_MASK BIT(1) /* REG_LAB_IBB_EN_RDY */ #define LAB_IBB_EN_RDY_EN BIT(7) /* REG_LAB_CURRENT_LIMIT */ #define LAB_CURRENT_LIMIT_MASK GENMASK(2, 0) #define LAB_CURRENT_LIMIT_EN_BIT BIT(7) #define LAB_OVERRIDE_CURRENT_MAX_BIT BIT(3) /* REG_LAB_CURRENT_SENSE */ #define LAB_CURRENT_SENSE_GAIN_MASK GENMASK(1, 0) /* REG_LAB_PS_CTL */ #define LAB_PS_THRESH_MASK GENMASK(1, 0) #define LAB_PS_CTL_EN BIT(7) /* REG_LAB_RDSON_MNGMNT */ #define LAB_RDSON_MNGMNT_NFET_SLEW_EN BIT(5) #define LAB_RDSON_MNGMNT_PFET_SLEW_EN BIT(4) #define LAB_RDSON_MNGMNT_NFET_MASK GENMASK(3, 2) #define LAB_RDSON_MNGMNT_NFET_SHIFT 2 #define LAB_RDSON_MNGMNT_PFET_MASK GENMASK(1, 0) #define LAB_RDSON_NFET_SW_SIZE_QUARTER 0x0 #define LAB_RDSON_PFET_SW_SIZE_QUARTER 0x0 /* REG_LAB_PRECHARGE_CTL */ #define LAB_FAST_PRECHARGE_CTL_EN BIT(2) #define LAB_MAX_PRECHARGE_TIME_MASK GENMASK(1, 0) /* REG_LAB_SOFT_START_CTL */ #define LAB_SOFT_START_CTL_MASK GENMASK(1, 0) /* REG_LAB_SPARE_CTL */ #define LAB_SPARE_TOUCH_WAKE_BIT BIT(3) #define LAB_SPARE_DISABLE_SCP_BIT BIT(0) /* REG_LAB_MISC_CTL */ #define LAB_AUTO_GM_BIT BIT(4) /* REG_LAB_PFM_CTL */ #define LAB_PFM_EN_BIT BIT(7) /* REG_LAB_SW_HIGH_PSRR_CTL */ #define LAB_EN_SW_HIGH_PSRR_MODE BIT(7) #define LAB_SW_HIGH_PSRR_REQ BIT(0) /* REG_LAB_VPH_ENVELOP_CTL */ #define LAB_VREF_HIGH_PSRR_SEL_MASK GENMASK(7, 6) #define LAB_SEL_HW_HIGH_PSRR_SRC_MASK GENMASK(1, 0) #define LAB_SEL_HW_HIGH_PSRR_SRC_SHIFT 6 /* IBB register offset definitions */ #define REG_IBB_REVISION4 0x03 #define REG_IBB_STATUS1 0x08 #define REG_IBB_VOLTAGE 0x41 #define REG_IBB_RING_SUPPRESSION_CTL 0x42 #define REG_IBB_LCD_AMOLED_SEL 0x44 #define REG_IBB_MODULE_RDY 0x45 #define REG_IBB_ENABLE_CTL 0x46 #define REG_IBB_PD_CTL 0x47 #define REG_IBB_CLK_DIV 0x48 #define REG_IBB_CURRENT_LIMIT 0x4B #define REG_IBB_PS_CTL 0x50 #define REG_IBB_RDSON_MNGMNT 0x53 #define REG_IBB_NONOVERLAP_TIME_1 0x56 #define REG_IBB_NONOVERLAP_TIME_2 0x57 #define REG_IBB_PWRUP_PWRDN_CTL_1 0x58 #define REG_IBB_PWRUP_PWRDN_CTL_2 0x59 #define REG_IBB_SOFT_START_CTL 0x5F #define REG_IBB_SWIRE_CTL 0x5A #define REG_IBB_OUTPUT_SLEW_CTL 0x5D #define REG_IBB_SPARE_CTL 0x60 #define REG_IBB_NLIMIT_DAC 0x61 /* IBB registers for PM660A */ #define REG_IBB_DEFAULT_VOLTAGE 0x40 #define REG_IBB_FLOAT_CTL 0x43 #define REG_IBB_VREG_OK_CTL 0x55 #define REG_IBB_VOUT_MIN_MAGNITUDE 0x5C #define REG_IBB_PFM_CTL 0x62 #define REG_IBB_SMART_PS_CTL 0x65 #define REG_IBB_ADAPT_DEAD_TIME 0x67 /* IBB register bits definition */ /* REG_IBB_STATUS1 */ #define IBB_STATUS1_VREG_OK_BIT BIT(7) #define IBB_STATUS1_SC_DETECT_BIT BIT(6) /* REG_IBB_VOLTAGE */ #define IBB_VOLTAGE_OVERRIDE_EN BIT(7) #define IBB_VOLTAGE_SET_MASK GENMASK(5, 0) /* REG_IBB_CLK_DIV */ #define IBB_CLK_DIV_OVERRIDE_EN BIT(7) #define IBB_CLK_DIV_MASK GENMASK(3, 0) /* REG_IBB_RING_SUPPRESSION_CTL */ #define IBB_RING_SUPPRESSION_CTL_EN BIT(7) /* REG_IBB_FLOAT_CTL */ #define IBB_FLOAT_EN BIT(0) #define IBB_SMART_FLOAT_EN BIT(7) /* REG_IBB_MIN_MAGNITUDE */ #define IBB_MIN_VOLTAGE_0P8_V BIT(3) /* REG_IBB_MODULE_RDY */ #define IBB_MODULE_RDY_EN BIT(7) /* REG_IBB_ENABLE_CTL */ #define IBB_ENABLE_CTL_MASK (BIT(7) | BIT(6)) #define IBB_ENABLE_CTL_SWIRE_RDY BIT(6) #define IBB_ENABLE_CTL_MODULE_EN BIT(7) /* REG_IBB_PD_CTL */ #define IBB_PD_CTL_HALF_STRENGTH BIT(0) #define IBB_PD_CTL_STRENGTH_MASK BIT(0) #define IBB_PD_CTL_EN BIT(7) #define IBB_SWIRE_PD_UPD BIT(1) #define IBB_PD_CTL_EN_MASK BIT(7) /* REG_IBB_CURRENT_LIMIT */ #define IBB_CURRENT_LIMIT_MASK GENMASK(4, 0) #define IBB_CURRENT_LIMIT_DEBOUNCE_SHIFT 5 #define IBB_CURRENT_LIMIT_DEBOUNCE_MASK GENMASK(6, 5) #define IBB_CURRENT_LIMIT_EN BIT(7) #define IBB_ILIMIT_COUNT_CYC8 0 #define IBB_CURRENT_MAX_500MA 0xA /* REG_IBB_PS_CTL */ #define IBB_PS_CTL_EN 0x85 /* REG_IBB_SMART_PS_CTL */ #define IBB_SMART_PS_CTL_EN BIT(7) #define IBB_NUM_SWIRE_PULSE_WAIT 0x5 /* REG_IBB_OUTPUT_SLEW_CTL */ #define IBB_SLEW_CTL_EN BIT(7) #define IBB_SLEW_RATE_SPEED_FAST_EN BIT(6) #define IBB_SLEW_RATE_TRANS_TIME_FAST_SHIFT 3 #define IBB_SLEW_RATE_TRANS_TIME_FAST_MASK GENMASK(5, 3) #define IBB_SLEW_RATE_TRANS_TIME_SLOW_MASK GENMASK(2, 0) /* REG_IBB_VREG_OK_CTL */ #define IBB_VREG_OK_EN_OVERLOAD_BLANK BIT(7) #define IBB_VREG_OK_OVERLOAD_DEB_SHIFT 5 #define IBB_VREG_OK_OVERLOAD_DEB_MASK GENMASK(6, 5) /* REG_IBB_RDSON_MNGMNT */ #define IBB_NFET_SLEW_EN BIT(7) #define IBB_PFET_SLEW_EN BIT(6) #define IBB_OVERRIDE_NFET_SW_SIZE BIT(5) #define IBB_OVERRIDE_PFET_SW_SIZE BIT(2) #define IBB_NFET_SW_SIZE_MASK GENMASK(3, 2) #define IBB_PFET_SW_SIZE_MASK GENMASK(1, 0) /* REG_IBB_NONOVERLAP_TIME_1 */ #define IBB_OVERRIDE_NONOVERLAP BIT(6) #define IBB_NONOVERLAP_NFET_MASK GENMASK(2, 0) #define IBB_NFET_GATE_DELAY_2 0x3 /* REG_IBB_NONOVERLAP_TIME_2 */ #define IBB_N2P_MUX_SEL BIT(0) /* REG_IBB_SOFT_START_CTL */ #define IBB_SOFT_START_CHARGING_RESISTOR_16K 0x3 /* REG_IBB_SPARE_CTL */ #define IBB_BYPASS_PWRDN_DLY2_BIT BIT(5) #define IBB_POFF_CTL_MASK BIT(4) #define IBB_FASTER_PFET_OFF BIT(4) #define IBB_FAST_STARTUP BIT(3) /* REG_IBB_SWIRE_CTL */ #define IBB_SWIRE_VOUT_UPD_EN BIT(6) #define IBB_OUTPUT_VOLTAGE_AT_ONE_PULSE_MASK GENMASK(5, 0) #define MAX_OUTPUT_EDGE_VOLTAGE_MV 6300 #define MAX_OUTPUT_PULSE_VOLTAGE_MV 7700 #define MIN_OUTPUT_PULSE_VOLTAGE_MV 1400 #define OUTPUT_VOLTAGE_STEP_MV 100 /* REG_IBB_NLIMIT_DAC */ #define IBB_DEFAULT_NLIMIT_DAC 0x5 /* REG_IBB_PFM_CTL */ #define IBB_PFM_ENABLE BIT(7) #define IBB_PFM_PEAK_CURRENT_BIT_SHIFT 1 #define IBB_PFM_PEAK_CURRENT_MASK GENMASK(3, 1) #define IBB_PFM_HYSTERESIS_BIT_SHIFT 4 #define IBB_PFM_HYSTERESIS_MASK GENMASK(5, 4) /* REG_IBB_PWRUP_PWRDN_CTL_1 */ #define IBB_PWRUP_PWRDN_CTL_1_DLY1_BITS 2 #define IBB_PWRUP_PWRDN_CTL_1_DLY1_MASK GENMASK(5, 4) #define IBB_PWRUP_PWRDN_CTL_1_DLY1_SHIFT 4 #define IBB_PWRUP_PWRDN_CTL_1_EN_DLY2 BIT(3) #define IBB_PWRUP_PWRDN_CTL_1_DLY2_MASK GENMASK(1, 0) #define IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK BIT(7) #define IBB_PWRUP_PWRDN_CTL_1_EN_DLY1 BIT(6) #define PWRUP_PWRDN_CTL_1_DISCHARGE_EN BIT(2) /* REG_IBB_PWRUP_PWRDN_CTL_2 */ #define IBB_DIS_DLY_MASK GENMASK(1, 0) #define IBB_WAIT_MBG_OK BIT(2) /* Constants */ #define SWIRE_DEFAULT_2ND_CMD_DLY_MS 20 #define SWIRE_DEFAULT_IBB_PS_ENABLE_DLY_MS 200 #define IBB_HW_DEFAULT_SLEW_RATE 12000 /** * enum qpnp_labibb_mode - working mode of LAB/IBB regulators * %QPNP_LABIBB_LCD_MODE: configure LAB and IBB regulators * together to provide power supply for LCD * %QPNP_LABIBB_AMOLED_MODE: configure LAB and IBB regulators * together to provide power supply for AMOLED * %QPNP_LABIBB_MAX_MODE max number of configureable modes * supported by qpnp_labibb_regulator */ enum qpnp_labibb_mode { QPNP_LABIBB_LCD_MODE, QPNP_LABIBB_AMOLED_MODE, QPNP_LABIBB_MAX_MODE, }; /** * IBB_SW_CONTROL_EN: Specifies IBB is enabled through software. * IBB_SW_CONTROL_DIS: Specifies IBB is disabled through software. * IBB_HW_CONTROL: Specifies IBB is controlled through SWIRE (hardware). */ enum ibb_mode { IBB_SW_CONTROL_EN, IBB_SW_CONTROL_DIS, IBB_HW_CONTROL, IBB_HW_SW_CONTROL, }; static const int ibb_dischg_res_table[] = { 300, 64, 32, 16, }; static const int ibb_pwrup_dly_table[] = { 1000, 2000, 4000, 8000, }; static const int ibb_pwrdn_dly_table[] = { 1000, 2000, 4000, 8000, }; static const int lab_clk_div_table[] = { 3200, 2740, 2400, 2130, 1920, 1750, 1600, 1480, 1370, 1280, 1200, 1130, 1070, 1010, 960, 910, }; static const int ibb_clk_div_table[] = { 3200, 2740, 2400, 2130, 1920, 1750, 1600, 1480, 1370, 1280, 1200, 1130, 1070, 1010, 960, 910, }; static const int lab_current_limit_table[] = { 200, 400, 600, 800, 1000, 1200, 1400, 1600, }; static const char * const lab_current_sense_table[] = { "0.5x", "1x", "1.5x", "2x" }; static const int ibb_current_limit_table[] = { 0, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, }; static const int ibb_output_slew_ctl_table[] = { 100, 200, 500, 1000, 2000, 10000, 12000, 15000 }; static const int ibb_debounce_table[] = { 8, 16, 32, 64, }; static const int ibb_overload_debounce_table[] = { 1, 2, 4, 8 }; static const int ibb_vreg_ok_deb_table[] = { 4, 8, 16, 32 }; static const int lab_ps_thresh_table_v1[] = { 20, 30, 40, 50, }; static const int lab_ps_thresh_table_v2[] = { 50, 60, 70, 80, }; static const int lab_soft_start_table[] = { 200, 400, 600, 800, }; static const int lab_rdson_nfet_table[] = { 25, 50, 75, 100, }; static const int lab_rdson_pfet_table[] = { 25, 50, 75, 100, }; static const int lab_max_precharge_table[] = { 200, 300, 400, 500, }; static const int ibb_pfm_peak_curr_table[] = { 150, 200, 250, 300, 350, 400, 450, 500 }; static const int ibb_pfm_hysteresis_table[] = { 0, 25, 50, 0 }; static const int lab_vref_high_psrr_table[] = { 350, 400, 450, 500 }; struct lab_regulator { struct regulator_desc rdesc; struct regulator_dev *rdev; struct mutex lab_mutex; int lab_vreg_ok_irq; int lab_sc_irq; int curr_volt; int min_volt; int step_size; int slew_rate; int soft_start; int sc_wait_time_ms; int vreg_enabled; }; struct ibb_regulator { struct regulator_desc rdesc; struct regulator_dev *rdev; struct mutex ibb_mutex; int ibb_sc_irq; int curr_volt; int min_volt; int step_size; int slew_rate; int soft_start; u32 pwrup_dly; u32 pwrdn_dly; int vreg_enabled; int num_swire_trans; }; struct qpnp_labibb { struct device *dev; struct platform_device *pdev; struct regmap *regmap; struct class labibb_class; struct pmic_revid_data *pmic_rev_id; u16 lab_base; u16 ibb_base; u8 lab_dig_major; u8 ibb_dig_major; struct lab_regulator lab_vreg; struct ibb_regulator ibb_vreg; const struct ibb_ver_ops *ibb_ver_ops; const struct lab_ver_ops *lab_ver_ops; struct mutex bus_mutex; enum qpnp_labibb_mode mode; struct work_struct lab_vreg_ok_work; struct delayed_work sc_err_recovery_work; struct hrtimer sc_err_check_timer; int sc_err_count; bool standalone; bool ttw_en; bool in_ttw_mode; bool ibb_settings_saved; bool swire_control; bool pbs_control; bool ttw_force_lab_on; bool skip_2nd_swire_cmd; bool pfm_enable; bool notify_lab_vreg_ok_sts; bool detect_lab_sc; bool sc_detected; /* Tracks the secure UI mode entry/exit */ bool secure_mode; u32 swire_2nd_cmd_delay; u32 swire_ibb_ps_enable_delay; }; static RAW_NOTIFIER_HEAD(labibb_notifier); struct ibb_ver_ops { int (*set_default_voltage)(struct qpnp_labibb *labibb, bool use_default); int (*set_voltage)(struct qpnp_labibb *labibb, int min_uV, int max_uV); int (*sel_mode)(struct qpnp_labibb *labibb, bool is_ibb); int (*get_mode)(struct qpnp_labibb *labibb); int (*set_clk_div)(struct qpnp_labibb *labibb, u8 val); int (*smart_ps_config)(struct qpnp_labibb *labibb, bool enable, int num_swire_trans, int neg_curr_limit); int (*soft_start_ctl)(struct qpnp_labibb *labibb, struct device_node *of_node); int (*voltage_at_one_pulse)(struct qpnp_labibb *labibb, u32 volt); }; struct lab_ver_ops { const char *ver_str; int (*set_default_voltage)(struct qpnp_labibb *labibb, bool default_pres); int (*ps_ctl)(struct qpnp_labibb *labibb, u32 thresh, bool enable); }; enum ibb_settings_index { IBB_PD_CTL = 0, IBB_CURRENT_LIMIT, IBB_RDSON_MNGMNT, IBB_PWRUP_PWRDN_CTL_1, IBB_PWRUP_PWRDN_CTL_2, IBB_NLIMIT_DAC, IBB_PS_CTL, IBB_SOFT_START_CTL, IBB_SETTINGS_MAX, }; enum lab_settings_index { LAB_SOFT_START_CTL = 0, LAB_PS_CTL, LAB_RDSON_MNGMNT, LAB_SETTINGS_MAX, }; struct settings { u16 address; u8 value; bool sec_access; }; #define SETTING(_id, _sec_access) \ [_id] = { \ .address = REG_##_id, \ .sec_access = _sec_access, \ } static struct settings ibb_settings[IBB_SETTINGS_MAX] = { SETTING(IBB_PD_CTL, false), SETTING(IBB_CURRENT_LIMIT, true), SETTING(IBB_RDSON_MNGMNT, false), SETTING(IBB_PWRUP_PWRDN_CTL_1, true), SETTING(IBB_PWRUP_PWRDN_CTL_2, true), SETTING(IBB_NLIMIT_DAC, false), SETTING(IBB_PS_CTL, false), SETTING(IBB_SOFT_START_CTL, false), }; static struct settings lab_settings[LAB_SETTINGS_MAX] = { SETTING(LAB_SOFT_START_CTL, false), SETTING(LAB_PS_CTL, false), SETTING(LAB_RDSON_MNGMNT, false), }; static int qpnp_labibb_read(struct qpnp_labibb *labibb, u16 address, u8 *val, int count) { int rc = 0; struct platform_device *pdev = labibb->pdev; mutex_lock(&(labibb->bus_mutex)); rc = regmap_bulk_read(labibb->regmap, address, val, count); if (rc < 0) pr_err("SPMI read failed address=0x%02x sid=0x%02x rc=%d\n", address, to_spmi_device(pdev->dev.parent)->usid, rc); mutex_unlock(&(labibb->bus_mutex)); return rc; } static int qpnp_labibb_write(struct qpnp_labibb *labibb, u16 address, u8 *val, int count) { int rc = 0; struct platform_device *pdev = labibb->pdev; mutex_lock(&(labibb->bus_mutex)); if (address == 0) { pr_err("address cannot be zero address=0x%02x sid=0x%02x rc=%d\n", address, to_spmi_device(pdev->dev.parent)->usid, rc); rc = -EINVAL; goto error; } rc = regmap_bulk_write(labibb->regmap, address, val, count); if (rc < 0) pr_err("write failed address=0x%02x sid=0x%02x rc=%d\n", address, to_spmi_device(pdev->dev.parent)->usid, rc); error: mutex_unlock(&(labibb->bus_mutex)); return rc; } static int qpnp_labibb_masked_write(struct qpnp_labibb *labibb, u16 address, u8 mask, u8 val) { int rc = 0; struct platform_device *pdev = labibb->pdev; mutex_lock(&(labibb->bus_mutex)); if (address == 0) { pr_err("address cannot be zero address=0x%02x sid=0x%02x\n", address, to_spmi_device(pdev->dev.parent)->usid); rc = -EINVAL; goto error; } rc = regmap_update_bits(labibb->regmap, address, mask, val); if (rc < 0) pr_err("spmi write failed: addr=%03X, rc=%d\n", address, rc); error: mutex_unlock(&(labibb->bus_mutex)); return rc; } static int qpnp_labibb_sec_write(struct qpnp_labibb *labibb, u16 base, u8 offset, u8 val) { int rc = 0; u8 sec_val = REG_LAB_IBB_SEC_UNLOCK_CODE; struct platform_device *pdev = labibb->pdev; mutex_lock(&(labibb->bus_mutex)); if (base == 0) { pr_err("base cannot be zero base=0x%02x sid=0x%02x\n", base, to_spmi_device(pdev->dev.parent)->usid); rc = -EINVAL; goto error; } rc = regmap_write(labibb->regmap, base + REG_LAB_IBB_SEC_ACCESS, sec_val); if (rc < 0) { pr_err("register %x failed rc = %d\n", base + REG_LAB_IBB_SEC_ACCESS, rc); goto error; } rc = regmap_write(labibb->regmap, base + offset, val); if (rc < 0) pr_err("failed: addr=%03X, rc=%d\n", base + offset, rc); error: mutex_unlock(&(labibb->bus_mutex)); return rc; } static int qpnp_labibb_sec_masked_write(struct qpnp_labibb *labibb, u16 base, u8 offset, u8 mask, u8 val) { int rc = 0; u8 sec_val = REG_LAB_IBB_SEC_UNLOCK_CODE; struct platform_device *pdev = labibb->pdev; mutex_lock(&(labibb->bus_mutex)); if (base == 0) { pr_err("base cannot be zero base=0x%02x sid=0x%02x\n", base, to_spmi_device(pdev->dev.parent)->usid); rc = -EINVAL; goto error; } rc = regmap_write(labibb->regmap, base + REG_LAB_IBB_SEC_ACCESS, sec_val); if (rc < 0) { pr_err("register %x failed rc = %d\n", base + REG_LAB_IBB_SEC_ACCESS, rc); goto error; } rc = regmap_update_bits(labibb->regmap, base + offset, mask, val); if (rc < 0) pr_err("spmi write failed: addr=%03X, rc=%d\n", base, rc); error: mutex_unlock(&(labibb->bus_mutex)); return rc; } static int qpnp_ibb_smart_ps_config_v1(struct qpnp_labibb *labibb, bool enable, int num_swire_trans, int neg_curr_limit) { return 0; } static int qpnp_ibb_smart_ps_config_v2(struct qpnp_labibb *labibb, bool enable, int num_swire_trans, int neg_curr_limit) { u8 val; int rc = 0; if (enable) { val = IBB_NUM_SWIRE_PULSE_WAIT; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_PS_CTL, &val, 1); if (rc < 0) { pr_err("write register %x failed rc = %d\n", REG_IBB_PS_CTL, rc); return rc; } } val = enable ? IBB_SMART_PS_CTL_EN : IBB_NUM_SWIRE_PULSE_WAIT; if (num_swire_trans) val |= num_swire_trans; else val |= IBB_NUM_SWIRE_PULSE_WAIT; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_SMART_PS_CTL, &val, 1); if (rc < 0) { pr_err("write register %x failed rc = %d\n", REG_IBB_SMART_PS_CTL, rc); return rc; } val = enable ? (neg_curr_limit ? neg_curr_limit : IBB_DEFAULT_NLIMIT_DAC) : IBB_DEFAULT_NLIMIT_DAC; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_NLIMIT_DAC, &val, 1); if (rc < 0) pr_err("write register %x failed rc = %d\n", REG_IBB_NLIMIT_DAC, rc); return rc; } static int qpnp_labibb_sel_mode_v1(struct qpnp_labibb *labibb, bool is_ibb) { int rc = 0; u8 val; u16 base; val = (labibb->mode == QPNP_LABIBB_LCD_MODE) ? REG_LAB_IBB_LCD_MODE : REG_LAB_IBB_AMOLED_MODE; base = is_ibb ? labibb->ibb_base : labibb->lab_base; rc = qpnp_labibb_sec_write(labibb, base, REG_LAB_LCD_AMOLED_SEL, val); if (rc < 0) pr_err("register %x failed rc = %d\n", REG_LAB_LCD_AMOLED_SEL, rc); return rc; } static int qpnp_labibb_sel_mode_v2(struct qpnp_labibb *labibb, bool is_ibb) { return 0; } static int qpnp_ibb_get_mode_v1(struct qpnp_labibb *labibb) { int rc = 0; u8 val; rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_LCD_AMOLED_SEL, &val, 1); if (rc < 0) return rc; if (val == REG_LAB_IBB_AMOLED_MODE) labibb->mode = QPNP_LABIBB_AMOLED_MODE; else labibb->mode = QPNP_LABIBB_LCD_MODE; return 0; } static int qpnp_ibb_get_mode_v2(struct qpnp_labibb *labibb) { labibb->mode = QPNP_LABIBB_AMOLED_MODE; return 0; } static int qpnp_ibb_set_clk_div_v1(struct qpnp_labibb *labibb, u8 val) { int rc = 0; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_CLK_DIV, &val, 1); return rc; } static int qpnp_ibb_set_clk_div_v2(struct qpnp_labibb *labibb, u8 val) { int rc = 0; val |= IBB_CLK_DIV_OVERRIDE_EN; rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_CLK_DIV, IBB_CLK_DIV_MASK | IBB_CLK_DIV_OVERRIDE_EN, val); return rc; } static int qpnp_ibb_soft_start_ctl_v1(struct qpnp_labibb *labibb, struct device_node *of_node) { int rc = 0; u8 val; u32 tmp; rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-soft-start", &(labibb->ibb_vreg.soft_start)); if (rc < 0) { pr_err("qcom,qpnp-ibb-soft-start is missing, rc = %d\n", rc); return rc; } rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-discharge-resistor", &tmp); if (!rc) { for (val = 0; val < ARRAY_SIZE(ibb_dischg_res_table); val++) { if (ibb_dischg_res_table[val] == tmp) break; } if (val == ARRAY_SIZE(ibb_dischg_res_table)) { pr_err("Invalid value in qcom,qpnp-ibb-discharge-resistor\n"); return -EINVAL; } rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_SOFT_START_CTL, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_IBB_SOFT_START_CTL, rc); return rc; } } return 0; } static int qpnp_ibb_soft_start_ctl_v2(struct qpnp_labibb *labibb, struct device_node *of_node) { return 0; } static int qpnp_ibb_vreg_ok_ctl(struct qpnp_labibb *labibb, struct device_node *of_node) { u8 val = 0; int rc = 0, i = 0; u32 tmp; if (labibb->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE) return rc; val |= IBB_VREG_OK_EN_OVERLOAD_BLANK; rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-overload-debounce", &tmp); if (rc < 0) { pr_err("failed to read qcom,qpnp-ibb-overload-debounce rc=%d\n", rc); return rc; } for (i = 0; i < ARRAY_SIZE(ibb_overload_debounce_table); i++) if (ibb_overload_debounce_table[i] == tmp) break; if (i == ARRAY_SIZE(ibb_overload_debounce_table)) { pr_err("Invalid value in qcom,qpnp-ibb-overload-debounce\n"); return -EINVAL; } val |= i << IBB_VREG_OK_OVERLOAD_DEB_SHIFT; rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-vreg-ok-debounce", &tmp); if (rc < 0) { pr_err("failed to read qcom,qpnp-ibb-vreg-ok-debounce rc=%d\n", rc); return rc; } for (i = 0; i < ARRAY_SIZE(ibb_vreg_ok_deb_table); i++) if (ibb_vreg_ok_deb_table[i] == tmp) break; if (i == ARRAY_SIZE(ibb_vreg_ok_deb_table)) { pr_err("Invalid value in qcom,qpnp-ibb-vreg-ok-debounce\n"); return -EINVAL; } val |= i; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_VREG_OK_CTL, &val, 1); if (rc < 0) pr_err("write to register %x failed rc = %d\n", REG_IBB_VREG_OK_CTL, rc); return rc; } static int qpnp_ibb_set_default_voltage_v1(struct qpnp_labibb *labibb, bool use_default) { u8 val; int rc = 0; if (!use_default) { if (labibb->ibb_vreg.curr_volt < labibb->ibb_vreg.min_volt) { pr_err("qcom,qpnp-ibb-init-voltage %d is less than the the minimum voltage %d", labibb->ibb_vreg.curr_volt, labibb->ibb_vreg.min_volt); return -EINVAL; } val = DIV_ROUND_UP(labibb->ibb_vreg.curr_volt - labibb->ibb_vreg.min_volt, labibb->ibb_vreg.step_size); if (val > IBB_VOLTAGE_SET_MASK) { pr_err("qcom,qpnp-lab-init-voltage %d is larger than the max supported voltage %ld", labibb->ibb_vreg.curr_volt, labibb->ibb_vreg.min_volt + labibb->ibb_vreg.step_size * IBB_VOLTAGE_SET_MASK); return -EINVAL; } labibb->ibb_vreg.curr_volt = val * labibb->ibb_vreg.step_size + labibb->ibb_vreg.min_volt; val |= IBB_VOLTAGE_OVERRIDE_EN; } else { val = 0; } rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_VOLTAGE, IBB_VOLTAGE_SET_MASK | IBB_VOLTAGE_OVERRIDE_EN, val); if (rc < 0) pr_err("write to register %x failed rc = %d\n", REG_IBB_VOLTAGE, rc); return rc; } static int qpnp_ibb_set_default_voltage_v2(struct qpnp_labibb *labibb, bool use_default) { int rc = 0; u8 val; val = DIV_ROUND_UP(labibb->ibb_vreg.curr_volt, labibb->ibb_vreg.step_size); if (val > IBB_VOLTAGE_SET_MASK) { pr_err("Invalid qcom,qpnp-ibb-init-voltage property %d", labibb->ibb_vreg.curr_volt); return -EINVAL; } labibb->ibb_vreg.curr_volt = val * labibb->ibb_vreg.step_size; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_DEFAULT_VOLTAGE, &val, 1); if (rc < 0) pr_err("write to register %x failed rc = %d\n", REG_IBB_DEFAULT_VOLTAGE, rc); return rc; } static int qpnp_ibb_set_voltage_v1(struct qpnp_labibb *labibb, int min_uV, int max_uV) { int rc, new_uV; u8 val; if (min_uV < labibb->ibb_vreg.min_volt) { pr_err("min_uV %d is less than min_volt %d", min_uV, labibb->ibb_vreg.min_volt); return -EINVAL; } val = DIV_ROUND_UP(min_uV - labibb->ibb_vreg.min_volt, labibb->ibb_vreg.step_size); new_uV = val * labibb->ibb_vreg.step_size + labibb->ibb_vreg.min_volt; if (new_uV > max_uV) { pr_err("unable to set voltage %d (min:%d max:%d)\n", new_uV, min_uV, max_uV); return -EINVAL; } rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_VOLTAGE, IBB_VOLTAGE_SET_MASK | IBB_VOLTAGE_OVERRIDE_EN, val | IBB_VOLTAGE_OVERRIDE_EN); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_IBB_VOLTAGE, rc); return rc; } if (new_uV > labibb->ibb_vreg.curr_volt) { val = DIV_ROUND_UP(new_uV - labibb->ibb_vreg.curr_volt, labibb->ibb_vreg.step_size); udelay(val * labibb->ibb_vreg.slew_rate); } labibb->ibb_vreg.curr_volt = new_uV; return 0; } static int qpnp_ibb_set_voltage_v2(struct qpnp_labibb *labibb, int min_uV, int max_uV) { int rc, new_uV; u8 val; val = DIV_ROUND_UP(min_uV, labibb->ibb_vreg.step_size); new_uV = val * labibb->ibb_vreg.step_size; if (new_uV > max_uV) { pr_err("unable to set voltage %d (min:%d max:%d)\n", new_uV, min_uV, max_uV); return -EINVAL; } rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_VOLTAGE, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_IBB_VOLTAGE, rc); return rc; } if (new_uV > labibb->ibb_vreg.curr_volt) { val = DIV_ROUND_UP(new_uV - labibb->ibb_vreg.curr_volt, labibb->ibb_vreg.step_size); udelay(val * labibb->ibb_vreg.slew_rate); } labibb->ibb_vreg.curr_volt = new_uV; return 0; } static int qpnp_ibb_output_voltage_at_one_pulse_v1(struct qpnp_labibb *labibb, u32 volt) { int rc = 0; u8 val; /* * Set the output voltage 100mV lower as the IBB HW module * counts one pulse less in SWIRE mode. */ val = DIV_ROUND_UP((volt - MIN_OUTPUT_PULSE_VOLTAGE_MV), OUTPUT_VOLTAGE_STEP_MV) - 1; rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_SWIRE_CTL, IBB_OUTPUT_VOLTAGE_AT_ONE_PULSE_MASK, val); if (rc < 0) pr_err("write register %x failed rc = %d\n", REG_IBB_SWIRE_CTL, rc); return rc; } static int qpnp_ibb_output_voltage_at_one_pulse_v2(struct qpnp_labibb *labibb, u32 volt) { int rc = 0; u8 val; val = DIV_ROUND_UP(volt, OUTPUT_VOLTAGE_STEP_MV); rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_SWIRE_CTL, IBB_OUTPUT_VOLTAGE_AT_ONE_PULSE_MASK, val); if (rc < 0) pr_err("qpnp_labiibb_write register %x failed rc = %d\n", REG_IBB_SWIRE_CTL, rc); return rc; } /* For PMI8998 and earlier PMICs */ static const struct ibb_ver_ops ibb_ops_v1 = { .set_default_voltage = qpnp_ibb_set_default_voltage_v1, .set_voltage = qpnp_ibb_set_voltage_v1, .sel_mode = qpnp_labibb_sel_mode_v1, .get_mode = qpnp_ibb_get_mode_v1, .set_clk_div = qpnp_ibb_set_clk_div_v1, .smart_ps_config = qpnp_ibb_smart_ps_config_v1, .soft_start_ctl = qpnp_ibb_soft_start_ctl_v1, .voltage_at_one_pulse = qpnp_ibb_output_voltage_at_one_pulse_v1, }; /* For PM660A and later PMICs */ static const struct ibb_ver_ops ibb_ops_v2 = { .set_default_voltage = qpnp_ibb_set_default_voltage_v2, .set_voltage = qpnp_ibb_set_voltage_v2, .sel_mode = qpnp_labibb_sel_mode_v2, .get_mode = qpnp_ibb_get_mode_v2, .set_clk_div = qpnp_ibb_set_clk_div_v2, .smart_ps_config = qpnp_ibb_smart_ps_config_v2, .soft_start_ctl = qpnp_ibb_soft_start_ctl_v2, .voltage_at_one_pulse = qpnp_ibb_output_voltage_at_one_pulse_v2, }; static int qpnp_lab_set_default_voltage_v1(struct qpnp_labibb *labibb, bool default_pres) { u8 val; int rc = 0; if (!default_pres) { if (labibb->lab_vreg.curr_volt < labibb->lab_vreg.min_volt) { pr_err("qcom,qpnp-lab-init-voltage %d is less than the the minimum voltage %d", labibb->lab_vreg.curr_volt, labibb->lab_vreg.min_volt); return -EINVAL; } val = DIV_ROUND_UP(labibb->lab_vreg.curr_volt - labibb->lab_vreg.min_volt, labibb->lab_vreg.step_size); if (val > LAB_VOLTAGE_SET_MASK) { pr_err("qcom,qpnp-lab-init-voltage %d is larger than the max supported voltage %ld", labibb->lab_vreg.curr_volt, labibb->lab_vreg.min_volt + labibb->lab_vreg.step_size * LAB_VOLTAGE_SET_MASK); return -EINVAL; } labibb->lab_vreg.curr_volt = val * labibb->lab_vreg.step_size + labibb->lab_vreg.min_volt; val |= LAB_VOLTAGE_OVERRIDE_EN; } else { val = 0; } rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_VOLTAGE, LAB_VOLTAGE_SET_MASK | LAB_VOLTAGE_OVERRIDE_EN, val); if (rc < 0) pr_err("write to register %x failed rc = %d\n", REG_LAB_VOLTAGE, rc); return rc; } static int qpnp_lab_set_default_voltage_v2(struct qpnp_labibb *labibb, bool default_pres) { int rc = 0; u8 val; val = DIV_ROUND_UP((labibb->lab_vreg.curr_volt - labibb->lab_vreg.min_volt), labibb->lab_vreg.step_size); rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_VOUT_DEFAULT, &val, 1); if (rc < 0) pr_err("write to register %x failed rc = %d\n", REG_LAB_VOUT_DEFAULT, rc); return rc; } static int qpnp_lab_ps_ctl_v1(struct qpnp_labibb *labibb, u32 thresh, bool enable) { int rc = 0; u8 val; if (enable) { for (val = 0; val < ARRAY_SIZE(lab_ps_thresh_table_v1); val++) if (lab_ps_thresh_table_v1[val] == thresh) break; if (val == ARRAY_SIZE(lab_ps_thresh_table_v1)) { pr_err("Invalid value in qcom,qpnp-lab-ps-threshold\n"); return -EINVAL; } val |= LAB_PS_CTL_EN; } else { val = 0; } rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_PS_CTL, &val, 1); if (rc < 0) pr_err("write register %x failed rc = %d\n", REG_LAB_PS_CTL, rc); return rc; } static int qpnp_lab_ps_ctl_v2(struct qpnp_labibb *labibb, u32 thresh, bool enable) { int rc = 0; u8 val, mask; mask = LAB_PS_CTL_EN; if (enable) { for (val = 0; val < ARRAY_SIZE(lab_ps_thresh_table_v2); val++) if (lab_ps_thresh_table_v2[val] == thresh) break; if (val == ARRAY_SIZE(lab_ps_thresh_table_v2)) { pr_err("Invalid value in qcom,qpnp-lab-ps-threshold\n"); return -EINVAL; } val |= LAB_PS_CTL_EN; mask |= LAB_PS_THRESH_MASK; } else { val = 0; } rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_PS_CTL, mask, val); if (rc < 0) pr_err("write register %x failed rc = %d\n", REG_LAB_PS_CTL, rc); return rc; } /* For PMI8996 and earlier PMICs */ static const struct lab_ver_ops lab_ops_v1 = { .set_default_voltage = qpnp_lab_set_default_voltage_v1, .ps_ctl = qpnp_lab_ps_ctl_v1, }; static const struct lab_ver_ops pmi8998_lab_ops = { .set_default_voltage = qpnp_lab_set_default_voltage_v1, .ps_ctl = qpnp_lab_ps_ctl_v2, }; static const struct lab_ver_ops pm660_lab_ops = { .set_default_voltage = qpnp_lab_set_default_voltage_v2, .ps_ctl = qpnp_lab_ps_ctl_v2, }; static int qpnp_labibb_get_matching_idx(const char *val) { int i; for (i = 0; i < ARRAY_SIZE(lab_current_sense_table); i++) if (!strcmp(lab_current_sense_table[i], val)) return i; return -EINVAL; } static int qpnp_ibb_set_mode(struct qpnp_labibb *labibb, enum ibb_mode mode) { int rc; u8 val; if (mode == IBB_SW_CONTROL_EN) val = IBB_ENABLE_CTL_MODULE_EN; else if (mode == IBB_HW_CONTROL) val = IBB_ENABLE_CTL_SWIRE_RDY; else if (mode == IBB_HW_SW_CONTROL) val = IBB_ENABLE_CTL_MODULE_EN | IBB_ENABLE_CTL_SWIRE_RDY; else if (mode == IBB_SW_CONTROL_DIS) val = 0; else return -EINVAL; rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_ENABLE_CTL, IBB_ENABLE_CTL_MASK, val); if (rc < 0) pr_err("Unable to configure IBB_ENABLE_CTL rc=%d\n", rc); return rc; } static int qpnp_ibb_ps_config(struct qpnp_labibb *labibb, bool enable) { u8 val; int rc; val = enable ? IBB_PS_CTL_EN : IBB_NUM_SWIRE_PULSE_WAIT; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_PS_CTL, &val, 1); if (rc < 0) { pr_err("write register %x failed rc = %d\n", REG_IBB_PS_CTL, rc); return rc; } val = enable ? 0 : IBB_DEFAULT_NLIMIT_DAC; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_NLIMIT_DAC, &val, 1); if (rc < 0) pr_err("write register %x failed rc = %d\n", REG_IBB_NLIMIT_DAC, rc); return rc; } static int qpnp_lab_dt_init(struct qpnp_labibb *labibb, struct device_node *of_node) { int rc = 0; u8 i, val, mask; u32 tmp; /* * Do not configure LCD_AMOLED_SEL for pmi8998 as it will be done by * GPIO selector. */ if (labibb->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE) { rc = labibb->ibb_ver_ops->sel_mode(labibb, 0); if (rc < 0) return rc; } val = 0; if (of_property_read_bool(of_node, "qcom,qpnp-lab-full-pull-down")) val |= LAB_PD_CTL_STRONG_PULL; if (!of_property_read_bool(of_node, "qcom,qpnp-lab-pull-down-enable")) val |= LAB_PD_CTL_DISABLE_PD; mask = LAB_PD_CTL_EN_MASK | LAB_PD_CTL_STRENGTH_MASK; rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_PD_CTL, mask, val); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_PD_CTL, rc); return rc; } rc = of_property_read_u32(of_node, "qcom,qpnp-lab-switching-clock-frequency", &tmp); if (!rc) { for (val = 0; val < ARRAY_SIZE(lab_clk_div_table); val++) if (lab_clk_div_table[val] == tmp) break; if (val == ARRAY_SIZE(lab_clk_div_table)) { pr_err("Invalid value in qpnp-lab-switching-clock-frequency\n"); return -EINVAL; } rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_CLK_DIV, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_CLK_DIV, rc); return rc; } } if (of_property_read_bool(of_node, "qcom,qpnp-lab-limit-max-current-enable")) { val = LAB_CURRENT_LIMIT_EN_BIT; rc = of_property_read_u32(of_node, "qcom,qpnp-lab-limit-maximum-current", &tmp); if (rc < 0) { pr_err("get qcom,qpnp-lab-limit-maximum-current failed rc = %d\n", rc); return rc; } for (i = 0; i < ARRAY_SIZE(lab_current_limit_table); i++) if (lab_current_limit_table[i] == tmp) break; if (i == ARRAY_SIZE(lab_current_limit_table)) { pr_err("Invalid value in qcom,qpnp-lab-limit-maximum-current\n"); return -EINVAL; } val |= i; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_CURRENT_LIMIT, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_CURRENT_LIMIT, rc); return rc; } } if (of_property_read_bool(of_node, "qcom,qpnp-lab-ring-suppression-enable")) { val = LAB_RING_SUPPRESSION_CTL_EN; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_RING_SUPPRESSION_CTL, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_RING_SUPPRESSION_CTL, rc); return rc; } } if (of_property_read_bool(of_node, "qcom,qpnp-lab-ps-enable")) { rc = of_property_read_u32(of_node, "qcom,qpnp-lab-ps-threshold", &tmp); if (rc < 0) { pr_err("get qcom,qpnp-lab-ps-threshold failed rc = %d\n", rc); return rc; } rc = labibb->lab_ver_ops->ps_ctl(labibb, tmp, true); if (rc < 0) return rc; } else { rc = labibb->lab_ver_ops->ps_ctl(labibb, tmp, false); if (rc < 0) return rc; } val = 0; mask = 0; rc = of_property_read_u32(of_node, "qcom,qpnp-lab-pfet-size", &tmp); if (!rc) { for (val = 0; val < ARRAY_SIZE(lab_rdson_pfet_table); val++) if (tmp == lab_rdson_pfet_table[val]) break; if (val == ARRAY_SIZE(lab_rdson_pfet_table)) { pr_err("Invalid value in qcom,qpnp-lab-pfet-size\n"); return -EINVAL; } val |= LAB_RDSON_MNGMNT_PFET_SLEW_EN; mask |= LAB_RDSON_MNGMNT_PFET_MASK | LAB_RDSON_MNGMNT_PFET_SLEW_EN; } rc = of_property_read_u32(of_node, "qcom,qpnp-lab-nfet-size", &tmp); if (!rc) { for (i = 0; i < ARRAY_SIZE(lab_rdson_nfet_table); i++) if (tmp == lab_rdson_nfet_table[i]) break; if (i == ARRAY_SIZE(lab_rdson_nfet_table)) { pr_err("Invalid value in qcom,qpnp-lab-nfet-size\n"); return -EINVAL; } val |= i << LAB_RDSON_MNGMNT_NFET_SHIFT; val |= LAB_RDSON_MNGMNT_NFET_SLEW_EN; mask |= LAB_RDSON_MNGMNT_NFET_MASK | LAB_RDSON_MNGMNT_NFET_SLEW_EN; } rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_RDSON_MNGMNT, mask, val); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_RDSON_MNGMNT, rc); return rc; } rc = of_property_read_u32(of_node, "qcom,qpnp-lab-init-voltage", &(labibb->lab_vreg.curr_volt)); if (rc < 0) { pr_err("get qcom,qpnp-lab-init-voltage failed, rc = %d\n", rc); return rc; } if (of_property_read_bool(of_node, "qcom,qpnp-lab-use-default-voltage")) rc = labibb->lab_ver_ops->set_default_voltage(labibb, true); else rc = labibb->lab_ver_ops->set_default_voltage(labibb, false); if (rc < 0) return rc; if (of_property_read_bool(of_node, "qcom,qpnp-lab-enable-sw-high-psrr")) { val = LAB_EN_SW_HIGH_PSRR_MODE; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_SW_HIGH_PSRR_CTL, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_SW_HIGH_PSRR_CTL, rc); return rc; } } rc = of_property_read_u32(of_node, "qcom,qpnp-lab-ldo-pulldown-enable", (u32 *)&val); if (!rc) { rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_LDO_PD_CTL, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_LDO_PD_CTL, rc); return rc; } } rc = of_property_read_u32(of_node, "qcom,qpnp-lab-high-psrr-src-select", &tmp); if (!rc) { val = tmp; rc = of_property_read_u32(of_node, "qcom,qpnp-lab-vref-high-psrr-select", &tmp); if (rc < 0) { pr_err("get qcom,qpnp-lab-vref-high-psrr-select failed rc = %d\n", rc); return rc; } for (i = 0; i < ARRAY_SIZE(lab_vref_high_psrr_table); i++) if (lab_vref_high_psrr_table[i] == tmp) break; if (i == ARRAY_SIZE(lab_vref_high_psrr_table)) { pr_err("Invalid value in qpnp-lab-vref-high-psrr-selct\n"); return -EINVAL; } val |= (i << LAB_SEL_HW_HIGH_PSRR_SRC_SHIFT); rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_VPH_ENVELOP_CTL, LAB_VREF_HIGH_PSRR_SEL_MASK | LAB_SEL_HW_HIGH_PSRR_SRC_MASK, val); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_VPH_ENVELOP_CTL, rc); return rc; } } if (labibb->swire_control) { rc = qpnp_ibb_set_mode(labibb, IBB_HW_CONTROL); if (rc < 0) { pr_err("Unable to set SWIRE_RDY rc=%d\n", rc); return rc; } } return 0; } #define LAB_CURRENT_MAX_1600MA 0x7 #define LAB_CURRENT_MAX_400MA 0x1 static int qpnp_lab_pfm_disable(struct qpnp_labibb *labibb) { int rc = 0; u8 val, mask; mutex_lock(&(labibb->lab_vreg.lab_mutex)); if (!labibb->pfm_enable) { pr_debug("PFM already disabled\n"); goto out; } val = 0; mask = LAB_PFM_EN_BIT; rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_PFM_CTL, mask, val); if (rc < 0) { pr_err("Write register %x failed rc = %d\n", REG_LAB_PFM_CTL, rc); goto out; } val = LAB_CURRENT_MAX_1600MA; mask = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_LIMIT_MASK; rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_CURRENT_LIMIT, mask, val); if (rc < 0) { pr_err("Write register %x failed rc = %d\n", REG_LAB_CURRENT_LIMIT, rc); goto out; } labibb->pfm_enable = false; out: mutex_unlock(&(labibb->lab_vreg.lab_mutex)); return rc; } static int qpnp_lab_pfm_enable(struct qpnp_labibb *labibb) { int rc = 0; u8 val, mask; mutex_lock(&(labibb->lab_vreg.lab_mutex)); if (labibb->pfm_enable) { pr_debug("PFM already enabled\n"); goto out; } /* Wait for ~100uS */ usleep_range(100, 105); val = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_MAX_400MA; mask = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_LIMIT_MASK; rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_CURRENT_LIMIT, mask, val); if (rc < 0) { pr_err("Write register %x failed rc = %d\n", REG_LAB_CURRENT_LIMIT, rc); goto out; } /* Wait for ~100uS */ usleep_range(100, 105); val = LAB_PFM_EN_BIT; mask = LAB_PFM_EN_BIT; rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_PFM_CTL, mask, val); if (rc < 0) { pr_err("Write register %x failed rc = %d\n", REG_LAB_PFM_CTL, rc); goto out; } labibb->pfm_enable = true; out: mutex_unlock(&(labibb->lab_vreg.lab_mutex)); return rc; } static int qpnp_labibb_restore_settings(struct qpnp_labibb *labibb) { int rc, i; for (i = 0; i < ARRAY_SIZE(ibb_settings); i++) { if (ibb_settings[i].sec_access) rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base, ibb_settings[i].address, ibb_settings[i].value); else rc = qpnp_labibb_write(labibb, labibb->ibb_base + ibb_settings[i].address, &ibb_settings[i].value, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", ibb_settings[i].address, rc); return rc; } } for (i = 0; i < ARRAY_SIZE(lab_settings); i++) { if (lab_settings[i].sec_access) rc = qpnp_labibb_sec_write(labibb, labibb->lab_base, lab_settings[i].address, lab_settings[i].value); else rc = qpnp_labibb_write(labibb, labibb->lab_base + lab_settings[i].address, &lab_settings[i].value, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", lab_settings[i].address, rc); return rc; } } return 0; } static int qpnp_labibb_save_settings(struct qpnp_labibb *labibb) { int rc, i; for (i = 0; i < ARRAY_SIZE(ibb_settings); i++) { rc = qpnp_labibb_read(labibb, labibb->ibb_base + ibb_settings[i].address, &ibb_settings[i].value, 1); if (rc < 0) { pr_err("read register %x failed rc = %d\n", ibb_settings[i].address, rc); return rc; } } for (i = 0; i < ARRAY_SIZE(lab_settings); i++) { rc = qpnp_labibb_read(labibb, labibb->lab_base + lab_settings[i].address, &lab_settings[i].value, 1); if (rc < 0) { pr_err("read register %x failed rc = %d\n", lab_settings[i].address, rc); return rc; } } return 0; } static int qpnp_labibb_ttw_enter_ibb_common(struct qpnp_labibb *labibb) { int rc = 0; u8 val, mask; val = 0; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_PD_CTL, &val, 1); if (rc < 0) { pr_err("read register %x failed rc = %d\n", REG_IBB_PD_CTL, rc); return rc; } val = 0; rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base, REG_IBB_PWRUP_PWRDN_CTL_1, val); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_IBB_PWRUP_PWRDN_CTL_1, rc); return rc; } if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) { val = 0; mask = IBB_DIS_DLY_MASK; } else { val = IBB_WAIT_MBG_OK; mask = IBB_DIS_DLY_MASK | IBB_WAIT_MBG_OK; } rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base, REG_IBB_PWRUP_PWRDN_CTL_2, mask, val); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_IBB_PWRUP_PWRDN_CTL_2, rc); return rc; } val = IBB_NFET_SLEW_EN | IBB_PFET_SLEW_EN | IBB_OVERRIDE_NFET_SW_SIZE | IBB_OVERRIDE_PFET_SW_SIZE; rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_RDSON_MNGMNT, 0xFF, val); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_IBB_RDSON_MNGMNT, rc); return rc; } val = IBB_CURRENT_LIMIT_EN | IBB_CURRENT_MAX_500MA | (IBB_ILIMIT_COUNT_CYC8 << IBB_CURRENT_LIMIT_DEBOUNCE_SHIFT); rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base, REG_IBB_CURRENT_LIMIT, val); if (rc < 0) pr_err("write to register %x failed rc = %d\n", REG_IBB_CURRENT_LIMIT, rc); return rc; } static int qpnp_labibb_ttw_enter_ibb_pmi8996(struct qpnp_labibb *labibb) { int rc; u8 val; val = IBB_BYPASS_PWRDN_DLY2_BIT | IBB_FAST_STARTUP; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_SPARE_CTL, &val, 1); if (rc < 0) pr_err("write to register %x failed rc = %d\n", REG_IBB_SPARE_CTL, rc); return rc; } static int qpnp_labibb_ttw_enter_ibb_pmi8950(struct qpnp_labibb *labibb) { int rc; u8 val; rc = qpnp_ibb_ps_config(labibb, true); if (rc < 0) { pr_err("Failed to enable ibb_ps_config rc=%d\n", rc); return rc; } val = IBB_SOFT_START_CHARGING_RESISTOR_16K; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_SOFT_START_CTL, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_IBB_SOFT_START_CTL, rc); return rc; } val = IBB_MODULE_RDY_EN; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_IBB_MODULE_RDY, &val, 1); if (rc < 0) pr_err("write to register %x failed rc = %d\n", REG_IBB_MODULE_RDY, rc); return rc; } static int qpnp_labibb_regulator_ttw_mode_enter(struct qpnp_labibb *labibb) { int rc = 0; u8 val, reg; /* Save the IBB settings before they get modified for TTW mode */ if (!labibb->ibb_settings_saved) { rc = qpnp_labibb_save_settings(labibb); if (rc) { pr_err("Error in storing IBB setttings, rc=%d\n", rc); return rc; } labibb->ibb_settings_saved = true; } if (labibb->ttw_force_lab_on) { val = LAB_MODULE_RDY_EN; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_MODULE_RDY, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_MODULE_RDY, rc); return rc; } /* Prevents LAB being turned off by IBB */ val = LAB_ENABLE_CTL_EN; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_ENABLE_CTL, rc); return rc; } val = LAB_RDSON_MNGMNT_NFET_SLEW_EN | LAB_RDSON_MNGMNT_PFET_SLEW_EN | LAB_RDSON_NFET_SW_SIZE_QUARTER | LAB_RDSON_PFET_SW_SIZE_QUARTER; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_RDSON_MNGMNT, &val, 1); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_RDSON_MNGMNT, rc); return rc; } rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_PS_CTL, LAB_PS_CTL_EN, LAB_PS_CTL_EN); if (rc < 0) { pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_LAB_PS_CTL, rc); return rc; } } else { val = LAB_PD_CTL_DISABLE_PD; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_PD_CTL, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_LAB_PD_CTL, rc); return rc; } val = LAB_SPARE_DISABLE_SCP_BIT; if (labibb->pmic_rev_id->pmic_subtype != PMI8950_SUBTYPE) val |= LAB_SPARE_TOUCH_WAKE_BIT; if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) { reg = REG_LAB_MISC_CTL; val |= LAB_AUTO_GM_BIT; } else { reg = REG_LAB_SPARE_CTL; } rc = qpnp_labibb_write(labibb, labibb->lab_base + reg, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_LAB_SPARE_CTL, rc); return rc; } val = 0; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_SOFT_START_CTL, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_LAB_SOFT_START_CTL, rc); return rc; } } rc = qpnp_labibb_ttw_enter_ibb_common(labibb); if (rc) { pr_err("Failed to apply TTW ibb common settings rc=%d\n", rc); return rc; } switch (labibb->pmic_rev_id->pmic_subtype) { case PMI8996_SUBTYPE: rc = qpnp_labibb_ttw_enter_ibb_pmi8996(labibb); break; case PMI8950_SUBTYPE: rc = qpnp_labibb_ttw_enter_ibb_pmi8950(labibb); break; case PMI8998_SUBTYPE: rc = labibb->lab_ver_ops->ps_ctl(labibb, 70, true); if (rc < 0) break; rc = qpnp_ibb_ps_config(labibb, true); break; } if (rc < 0) { pr_err("Failed to configure TTW-enter for IBB rc=%d\n", rc); return rc; } rc = qpnp_ibb_set_mode(labibb, IBB_HW_CONTROL); if (rc < 0) { pr_err("Unable to set SWIRE_RDY rc = %d\n", rc); return rc; } labibb->in_ttw_mode = true; return 0; } static int qpnp_labibb_ttw_exit_ibb_common(struct qpnp_labibb *labibb) { int rc; u8 val; val = IBB_FASTER_PFET_OFF; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_SPARE_CTL, &val, 1); if (rc < 0) pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_IBB_SPARE_CTL, rc); return rc; } static int qpnp_labibb_regulator_ttw_mode_exit(struct qpnp_labibb *labibb) { int rc = 0; u8 val, reg; if (!labibb->ibb_settings_saved) { pr_err("IBB settings are not saved!\n"); return -EINVAL; } /* Restore the IBB settings back to switch back to normal mode */ rc = qpnp_labibb_restore_settings(labibb); if (rc < 0) { pr_err("Error in restoring IBB setttings, rc=%d\n", rc); return rc; } if (labibb->ttw_force_lab_on) { val = 0; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_LAB_ENABLE_CTL, rc); return rc; } } else { val = LAB_PD_CTL_STRONG_PULL; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_PD_CTL, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_LAB_PD_CTL, rc); return rc; } val = 0; if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) { reg = REG_LAB_MISC_CTL; val |= LAB_AUTO_GM_BIT; } else { reg = REG_LAB_SPARE_CTL; } rc = qpnp_labibb_write(labibb, labibb->lab_base + reg, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_LAB_SPARE_CTL, rc); return rc; } } switch (labibb->pmic_rev_id->pmic_subtype) { case PMI8996_SUBTYPE: case PMI8994_SUBTYPE: case PMI8950_SUBTYPE: rc = qpnp_labibb_ttw_exit_ibb_common(labibb); break; } if (rc < 0) { pr_err("Failed to configure TTW-exit for IBB rc=%d\n", rc); return rc; } labibb->in_ttw_mode = false; return rc; } static void qpnp_lab_vreg_notifier_work(struct work_struct *work) { int rc = 0; u16 retries = 1000, dly = 5000; u8 val; struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb, lab_vreg_ok_work); if (labibb->lab_vreg.sc_wait_time_ms != -EINVAL) retries = labibb->lab_vreg.sc_wait_time_ms / 5; while (retries) { rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1, &val, 1); if (rc < 0) { pr_err("read register %x failed rc = %d\n", REG_LAB_STATUS1, rc); return; } if (val & LAB_STATUS1_VREG_OK_BIT) { raw_notifier_call_chain(&labibb_notifier, LAB_VREG_OK, NULL); break; } usleep_range(dly, dly + 100); retries--; } if (!retries) { if (labibb->detect_lab_sc) { pr_crit("short circuit detected on LAB rail.. disabling the LAB/IBB/OLEDB modules\n"); /* Disable LAB module */ val = 0; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_MODULE_RDY, &val, 1); if (rc < 0) { pr_err("write register %x failed rc = %d\n", REG_LAB_MODULE_RDY, rc); return; } raw_notifier_call_chain(&labibb_notifier, LAB_VREG_NOT_OK, NULL); labibb->sc_detected = true; labibb->lab_vreg.vreg_enabled = 0; labibb->ibb_vreg.vreg_enabled = 0; } else { pr_err("LAB_VREG_OK not set, failed to notify\n"); } } } static int qpnp_lab_enable_standalone(struct qpnp_labibb *labibb) { int rc; u8 val; val = LAB_ENABLE_CTL_EN; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); if (rc < 0) { pr_err("Write register %x failed rc = %d\n", REG_LAB_ENABLE_CTL, rc); return rc; } udelay(labibb->lab_vreg.soft_start); rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1, &val, 1); if (rc < 0) { pr_err("Read register %x failed rc = %d\n", REG_LAB_STATUS1, rc); return rc; } if (!(val & LAB_STATUS1_VREG_OK_BIT)) { pr_err("Can't enable LAB standalone\n"); return -EINVAL; } return 0; } static int qpnp_ibb_enable_standalone(struct qpnp_labibb *labibb) { int rc, delay, retries = 10; u8 val; rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN); if (rc < 0) { pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); return rc; } delay = labibb->ibb_vreg.soft_start; while (retries--) { /* Wait for a small period before reading IBB_STATUS1 */ usleep_range(delay, delay + 100); rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_STATUS1, &val, 1); if (rc < 0) { pr_err("Read register %x failed rc = %d\n", REG_IBB_STATUS1, rc); return rc; } if (val & IBB_STATUS1_VREG_OK_BIT) break; } if (!(val & IBB_STATUS1_VREG_OK_BIT)) { pr_err("Can't enable IBB standalone\n"); return -EINVAL; } return 0; } static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) { int rc; u8 val; int dly; int retries; bool enabled = false; if (labibb->ttw_en && !labibb->ibb_vreg.vreg_enabled && labibb->in_ttw_mode) { rc = qpnp_labibb_regulator_ttw_mode_exit(labibb); if (rc) { pr_err("Error in exiting TTW mode rc = %d\n", rc); return rc; } } rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN); if (rc) { pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); return rc; } /* total delay time */ dly = labibb->lab_vreg.soft_start + labibb->ibb_vreg.soft_start + labibb->ibb_vreg.pwrup_dly; usleep_range(dly, dly + 100); /* after this delay, lab should be enabled */ rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1, &val, 1); if (rc < 0) { pr_err("read register %x failed rc = %d\n", REG_LAB_STATUS1, rc); goto err_out; } pr_debug("soft=%d %d up=%d dly=%d\n", labibb->lab_vreg.soft_start, labibb->ibb_vreg.soft_start, labibb->ibb_vreg.pwrup_dly, dly); if (!(val & LAB_STATUS1_VREG_OK_BIT)) { pr_err("failed for LAB %x\n", val); goto err_out; } /* poll IBB_STATUS to make sure ibb had been enabled */ dly = labibb->ibb_vreg.soft_start + labibb->ibb_vreg.pwrup_dly; retries = 10; while (retries--) { rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_STATUS1, &val, 1); if (rc < 0) { pr_err("read register %x failed rc = %d\n", REG_IBB_STATUS1, rc); goto err_out; } if (val & IBB_STATUS1_VREG_OK_BIT) { enabled = true; break; } usleep_range(dly, dly + 100); } if (!enabled) { pr_err("failed for IBB %x\n", val); goto err_out; } labibb->lab_vreg.vreg_enabled = 1; labibb->ibb_vreg.vreg_enabled = 1; return 0; err_out: rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_DIS); if (rc < 0) { pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); return rc; } return -EINVAL; } static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb) { int rc; u8 val; int dly; int retries; bool disabled = false; /* * When TTW mode is enabled and LABIBB regulators are disabled, it is * recommended not to disable IBB through IBB_ENABLE_CTL when switching * to SWIRE control on entering TTW mode. Hence, just enter TTW mode * and mark the regulators disabled. When we exit TTW mode, normal * mode settings will be restored anyways and regulators will be * enabled as before. */ if (labibb->ttw_en && !labibb->in_ttw_mode) { rc = qpnp_labibb_regulator_ttw_mode_enter(labibb); if (rc < 0) { pr_err("Error in entering TTW mode rc = %d\n", rc); return rc; } labibb->lab_vreg.vreg_enabled = 0; labibb->ibb_vreg.vreg_enabled = 0; return 0; } rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_DIS); if (rc < 0) { pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); return rc; } /* poll IBB_STATUS to make sure ibb had been disabled */ dly = labibb->ibb_vreg.pwrdn_dly; retries = 2; while (retries--) { usleep_range(dly, dly + 100); rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_STATUS1, &val, 1); if (rc < 0) { pr_err("read register %x failed rc = %d\n", REG_IBB_STATUS1, rc); return rc; } if (!(val & IBB_STATUS1_VREG_OK_BIT)) { disabled = true; break; } } if (!disabled) { pr_err("failed for IBB %x\n", val); return -EINVAL; } if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE && labibb->mode == QPNP_LABIBB_LCD_MODE) { rc = qpnp_lab_pfm_disable(labibb); if (rc < 0) { pr_err("Error in disabling PFM, rc=%d\n", rc); return rc; } } labibb->lab_vreg.vreg_enabled = 0; labibb->ibb_vreg.vreg_enabled = 0; return 0; } static int qpnp_lab_regulator_enable(struct regulator_dev *rdev) { int rc; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->secure_mode) return 0; if (labibb->sc_detected) { pr_info("Short circuit detected: disabled LAB/IBB rails\n"); return 0; } if (labibb->skip_2nd_swire_cmd) { rc = qpnp_ibb_ps_config(labibb, false); if (rc < 0) { pr_err("Failed to disable IBB PS rc=%d\n", rc); return rc; } } if (!labibb->lab_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); rc = qpnp_lab_enable_standalone(labibb); if (rc) { pr_err("enable lab standalone failed, rc=%d\n", rc); return rc; } labibb->lab_vreg.vreg_enabled = 1; } if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc) schedule_work(&labibb->lab_vreg_ok_work); return 0; } static int qpnp_lab_regulator_disable(struct regulator_dev *rdev) { int rc; u8 val; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->secure_mode) return 0; if (labibb->lab_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_disable(labibb); val = 0; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); if (rc < 0) { pr_err("qpnp_lab_regulator_enable write register %x failed rc = %d\n", REG_LAB_ENABLE_CTL, rc); return rc; } labibb->lab_vreg.vreg_enabled = 0; } return 0; } static int qpnp_lab_regulator_is_enabled(struct regulator_dev *rdev) { struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->swire_control) return 0; return labibb->lab_vreg.vreg_enabled; } static int qpnp_labibb_force_enable(struct qpnp_labibb *labibb) { int rc; if (labibb->skip_2nd_swire_cmd) { rc = qpnp_ibb_ps_config(labibb, false); if (rc < 0) { pr_err("Failed to disable IBB PS rc=%d\n", rc); return rc; } } if (!labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); rc = qpnp_ibb_enable_standalone(labibb); if (rc < 0) { pr_err("enable ibb standalone failed, rc=%d\n", rc); return rc; } labibb->ibb_vreg.vreg_enabled = 1; rc = qpnp_lab_enable_standalone(labibb); if (rc < 0) { pr_err("enable lab standalone failed, rc=%d\n", rc); return rc; } labibb->lab_vreg.vreg_enabled = 1; } return 0; } #define SC_ERR_RECOVERY_DELAY_MS 250 #define SC_ERR_COUNT_INTERVAL_SEC 1 #define POLLING_SCP_DONE_COUNT 2 #define POLLING_SCP_DONE_INTERVAL_MS 5 static irqreturn_t labibb_sc_err_handler(int irq, void *_labibb) { int rc; u16 reg; u8 sc_err_mask, val; char *str; struct qpnp_labibb *labibb = (struct qpnp_labibb *)_labibb; bool in_sc_err, lab_en, ibb_en, scp_done = false; int count; if (irq == labibb->lab_vreg.lab_sc_irq) { reg = labibb->lab_base + REG_LAB_STATUS1; sc_err_mask = LAB_STATUS1_SC_DETECT_BIT; str = "LAB"; } else if (irq == labibb->ibb_vreg.ibb_sc_irq) { reg = labibb->ibb_base + REG_IBB_STATUS1; sc_err_mask = IBB_STATUS1_SC_DETECT_BIT; str = "IBB"; } else { return IRQ_HANDLED; } rc = qpnp_labibb_read(labibb, reg, &val, 1); if (rc < 0) { pr_err("Read 0x%x failed, rc=%d\n", reg, rc); return IRQ_HANDLED; } pr_debug("%s SC error triggered! %s_STATUS1 = %d\n", str, str, val); in_sc_err = !!(val & sc_err_mask); /* * The SC fault would trigger PBS to disable regulators * for protection. This would cause the SC_DETECT status being * cleared so that it's not able to get the SC fault status. * Check if LAB/IBB regulators are enabled in the driver but * disabled in hardware, this means a SC fault had happened * and SCP handling is completed by PBS. */ if (!in_sc_err) { count = POLLING_SCP_DONE_COUNT; do { reg = labibb->lab_base + REG_LAB_ENABLE_CTL; rc = qpnp_labibb_read(labibb, reg, &val, 1); if (rc < 0) { pr_err("Read 0x%x failed, rc=%d\n", reg, rc); return IRQ_HANDLED; } lab_en = !!(val & LAB_ENABLE_CTL_EN); reg = labibb->ibb_base + REG_IBB_ENABLE_CTL; rc = qpnp_labibb_read(labibb, reg, &val, 1); if (rc < 0) { pr_err("Read 0x%x failed, rc=%d\n", reg, rc); return IRQ_HANDLED; } ibb_en = !!(val & IBB_ENABLE_CTL_MODULE_EN); if (lab_en || ibb_en) msleep(POLLING_SCP_DONE_INTERVAL_MS); else break; } while ((lab_en || ibb_en) && count--); if (labibb->lab_vreg.vreg_enabled && labibb->ibb_vreg.vreg_enabled && !lab_en && !ibb_en) { pr_debug("LAB/IBB has been disabled by SCP\n"); scp_done = true; } } if (in_sc_err || scp_done) { if (hrtimer_active(&labibb->sc_err_check_timer) || hrtimer_callback_running(&labibb->sc_err_check_timer)) { labibb->sc_err_count++; } else { labibb->sc_err_count = 1; hrtimer_start(&labibb->sc_err_check_timer, ktime_set(SC_ERR_COUNT_INTERVAL_SEC, 0), HRTIMER_MODE_REL); } schedule_delayed_work(&labibb->sc_err_recovery_work, msecs_to_jiffies(SC_ERR_RECOVERY_DELAY_MS)); } return IRQ_HANDLED; } #define SC_FAULT_COUNT_MAX 4 static enum hrtimer_restart labibb_check_sc_err_count(struct hrtimer *timer) { struct qpnp_labibb *labibb = container_of(timer, struct qpnp_labibb, sc_err_check_timer); /* * if SC fault triggers more than 4 times in 1 second, * then disable the IRQs and leave as it. */ if (labibb->sc_err_count > SC_FAULT_COUNT_MAX) { disable_irq(labibb->lab_vreg.lab_sc_irq); disable_irq(labibb->ibb_vreg.ibb_sc_irq); } return HRTIMER_NORESTART; } static void labibb_sc_err_recovery_work(struct work_struct *work) { struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb, sc_err_recovery_work.work); int rc; labibb->ibb_vreg.vreg_enabled = 0; labibb->lab_vreg.vreg_enabled = 0; rc = qpnp_labibb_force_enable(labibb); if (rc < 0) pr_err("force enable labibb failed, rc=%d\n", rc); } static int qpnp_lab_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned int *selector) { int rc, new_uV; u8 val; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->swire_control || labibb->secure_mode) return 0; if (min_uV < labibb->lab_vreg.min_volt) { pr_err("min_uV %d is less than min_volt %d", min_uV, labibb->lab_vreg.min_volt); return -EINVAL; } val = DIV_ROUND_UP(min_uV - labibb->lab_vreg.min_volt, labibb->lab_vreg.step_size); new_uV = val * labibb->lab_vreg.step_size + labibb->lab_vreg.min_volt; if (new_uV > max_uV) { pr_err("unable to set voltage %d (min:%d max:%d)\n", new_uV, min_uV, max_uV); return -EINVAL; } rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_VOLTAGE, LAB_VOLTAGE_SET_MASK | LAB_VOLTAGE_OVERRIDE_EN, val | LAB_VOLTAGE_OVERRIDE_EN); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_LAB_VOLTAGE, rc); return rc; } if (new_uV > labibb->lab_vreg.curr_volt) { val = DIV_ROUND_UP(new_uV - labibb->lab_vreg.curr_volt, labibb->lab_vreg.step_size); udelay(val * labibb->lab_vreg.slew_rate); } labibb->lab_vreg.curr_volt = new_uV; return 0; } static int qpnp_skip_swire_command(struct qpnp_labibb *labibb) { int rc = 0, retry = 50, dly; u8 reg; do { /* poll for ibb vreg_ok */ rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_STATUS1, ®, 1); if (rc < 0) { pr_err("Failed to read ibb_status1 reg rc=%d\n", rc); return rc; } if (reg & IBB_STATUS1_VREG_OK_BIT) break; /* poll delay */ usleep_range(500, 600); } while (--retry); if (!retry) { pr_err("ibb vreg_ok failed to turn-on\n"); return -EBUSY; } /* move to SW control */ rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN); if (rc < 0) { pr_err("Failed switch to IBB_SW_CONTROL rc=%d\n", rc); return rc; } /* delay to skip the second swire command */ dly = labibb->swire_2nd_cmd_delay * 1000; while (dly / 20000) { usleep_range(20000, 20010); dly -= 20000; } if (dly) usleep_range(dly, dly + 10); rc = qpnp_ibb_set_mode(labibb, IBB_HW_SW_CONTROL); if (rc < 0) { pr_err("Failed switch to IBB_HW_SW_CONTROL rc=%d\n", rc); return rc; } /* delay for SPMI to SWIRE transition */ usleep_range(1000, 1100); /* Move back to SWIRE control */ rc = qpnp_ibb_set_mode(labibb, IBB_HW_CONTROL); if (rc < 0) pr_err("Failed switch to IBB_HW_CONTROL rc=%d\n", rc); /* delay before enabling the PS mode */ msleep(labibb->swire_ibb_ps_enable_delay); rc = qpnp_ibb_ps_config(labibb, true); if (rc < 0) pr_err("Unable to enable IBB PS rc=%d\n", rc); return rc; } static irqreturn_t lab_vreg_ok_handler(int irq, void *_labibb) { struct qpnp_labibb *labibb = _labibb; int rc; if (labibb->skip_2nd_swire_cmd && labibb->lab_dig_major < 2) { rc = qpnp_skip_swire_command(labibb); if (rc < 0) pr_err("Failed in 'qpnp_skip_swire_command' rc=%d\n", rc); } else if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE && labibb->mode == QPNP_LABIBB_LCD_MODE) { rc = qpnp_lab_pfm_enable(labibb); if (rc < 0) pr_err("Failed to config PFM, rc=%d\n", rc); } return IRQ_HANDLED; } static int qpnp_lab_regulator_get_voltage(struct regulator_dev *rdev) { struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->swire_control) return 0; return labibb->lab_vreg.curr_volt; } static bool is_lab_vreg_ok_irq_available(struct qpnp_labibb *labibb) { /* * LAB VREG_OK interrupt is used only to skip 2nd SWIRE command in * dig_major < 2 targets. For pmi8998, it is used to enable PFM in * LCD mode. */ if (labibb->skip_2nd_swire_cmd && labibb->lab_dig_major < 2) return true; if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE && labibb->mode == QPNP_LABIBB_LCD_MODE) { if (labibb->ttw_en) return false; return true; } return false; } static struct regulator_ops qpnp_lab_ops = { .enable = qpnp_lab_regulator_enable, .disable = qpnp_lab_regulator_disable, .is_enabled = qpnp_lab_regulator_is_enabled, .set_voltage = qpnp_lab_regulator_set_voltage, .get_voltage = qpnp_lab_regulator_get_voltage, }; static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, struct device_node *of_node) { int rc = 0; struct regulator_init_data *init_data; struct regulator_desc *rdesc = &labibb->lab_vreg.rdesc; struct regulator_config cfg = {}; u8 val, mask; const char *current_sense_str; bool config_current_sense = false; u32 tmp; if (!of_node) { dev_err(labibb->dev, "qpnp lab regulator device tree node is missing\n"); return -EINVAL; } init_data = of_get_regulator_init_data(labibb->dev, of_node, rdesc); if (!init_data) { pr_err("unable to get regulator init data for qpnp lab regulator\n"); return -ENOMEM; } rc = of_property_read_u32(of_node, "qcom,qpnp-lab-min-voltage", &(labibb->lab_vreg.min_volt)); if (rc < 0) { pr_err("qcom,qpnp-lab-min-voltage is missing, rc = %d\n", rc); return rc; } rc = of_property_read_u32(of_node, "qcom,qpnp-lab-step-size", &(labibb->lab_vreg.step_size)); if (rc < 0) { pr_err("qcom,qpnp-lab-step-size is missing, rc = %d\n", rc); return rc; } rc = of_property_read_u32(of_node, "qcom,qpnp-lab-slew-rate", &(labibb->lab_vreg.slew_rate)); if (rc < 0) { pr_err("qcom,qpnp-lab-slew-rate is missing, rc = %d\n", rc); return rc; } labibb->notify_lab_vreg_ok_sts = of_property_read_bool(of_node, "qcom,notify-lab-vreg-ok-sts"); labibb->lab_vreg.sc_wait_time_ms = -EINVAL; if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE && labibb->detect_lab_sc) of_property_read_u32(of_node, "qcom,qpnp-lab-sc-wait-time-ms", &labibb->lab_vreg.sc_wait_time_ms); rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start", &(labibb->lab_vreg.soft_start)); if (!rc) { for (val = 0; val < ARRAY_SIZE(lab_soft_start_table); val++) if (lab_soft_start_table[val] == labibb->lab_vreg.soft_start) break; if (val == ARRAY_SIZE(lab_soft_start_table)) val = ARRAY_SIZE(lab_soft_start_table) - 1; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_SOFT_START_CTL, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_LAB_SOFT_START_CTL, rc); return rc; } labibb->lab_vreg.soft_start = lab_soft_start_table [val & LAB_SOFT_START_CTL_MASK]; } val = 0; mask = 0; rc = of_property_read_u32(of_node, "qcom,qpnp-lab-max-precharge-time", &tmp); if (!rc) { for (val = 0; val < ARRAY_SIZE(lab_max_precharge_table); val++) if (lab_max_precharge_table[val] == tmp) break; if (val == ARRAY_SIZE(lab_max_precharge_table)) { pr_err("Invalid value in qcom,qpnp-lab-max-precharge-time\n"); return -EINVAL; } mask = LAB_MAX_PRECHARGE_TIME_MASK; } if (of_property_read_bool(of_node, "qcom,qpnp-lab-max-precharge-enable")) { val |= LAB_FAST_PRECHARGE_CTL_EN; mask |= LAB_FAST_PRECHARGE_CTL_EN; } rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_PRECHARGE_CTL, mask, val); if (rc < 0) { pr_err("qpnp_lab_dt_init write register %x failed rc = %d\n", REG_LAB_PRECHARGE_CTL, rc); return rc; } if (labibb->mode == QPNP_LABIBB_AMOLED_MODE && labibb->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE) { /* * default to 1.5 times current gain if * user doesn't specify the current-sense * dt parameter */ current_sense_str = "1.5x"; val = qpnp_labibb_get_matching_idx(current_sense_str); config_current_sense = true; } if (of_find_property(of_node, "qcom,qpnp-lab-current-sense", NULL)) { config_current_sense = true; rc = of_property_read_string(of_node, "qcom,qpnp-lab-current-sense", ¤t_sense_str); if (!rc) { val = qpnp_labibb_get_matching_idx( current_sense_str); } else { pr_err("qcom,qpnp-lab-current-sense configured incorrectly rc = %d\n", rc); return rc; } } if (config_current_sense) { rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_CURRENT_SENSE, LAB_CURRENT_SENSE_GAIN_MASK, val); if (rc < 0) { pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_LAB_CURRENT_SENSE, rc); return rc; } } val = (labibb->standalone) ? 0 : LAB_IBB_EN_RDY_EN; rc = qpnp_labibb_sec_write(labibb, labibb->lab_base, REG_LAB_IBB_EN_RDY, val); if (rc < 0) { pr_err("qpnp_lab_sec_write register %x failed rc = %d\n", REG_LAB_IBB_EN_RDY, rc); return rc; } rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_ENABLE_CTL, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_read register %x failed rc = %d\n", REG_IBB_ENABLE_CTL, rc); return rc; } if (!(val & (IBB_ENABLE_CTL_SWIRE_RDY | IBB_ENABLE_CTL_MODULE_EN))) { /* SWIRE_RDY and IBB_MODULE_EN not enabled */ rc = qpnp_lab_dt_init(labibb, of_node); if (rc < 0) { pr_err("qpnp-lab: wrong DT parameter specified: rc = %d\n", rc); return rc; } } else { rc = labibb->ibb_ver_ops->get_mode(labibb); rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_VOLTAGE, &val, 1); if (rc < 0) { pr_err("qpnp_lab_read read register %x failed rc = %d\n", REG_LAB_VOLTAGE, rc); return rc; } labibb->lab_vreg.curr_volt = (val & LAB_VOLTAGE_SET_MASK) * labibb->lab_vreg.step_size + labibb->lab_vreg.min_volt; if (labibb->mode == QPNP_LABIBB_LCD_MODE) { rc = of_property_read_u32(of_node, "qcom,qpnp-lab-init-lcd-voltage", &(labibb->lab_vreg.curr_volt)); if (rc < 0) { pr_err("get qcom,qpnp-lab-init-lcd-voltage failed, rc = %d\n", rc); return rc; } } else if (!(val & LAB_VOLTAGE_OVERRIDE_EN)) { rc = of_property_read_u32(of_node, "qcom,qpnp-lab-init-amoled-voltage", &(labibb->lab_vreg.curr_volt)); if (rc < 0) { pr_err("get qcom,qpnp-lab-init-amoled-voltage failed, rc = %d\n", rc); return rc; } } labibb->lab_vreg.vreg_enabled = 1; } if (is_lab_vreg_ok_irq_available(labibb)) { irq_set_status_flags(labibb->lab_vreg.lab_vreg_ok_irq, IRQ_DISABLE_UNLAZY); rc = devm_request_threaded_irq(labibb->dev, labibb->lab_vreg.lab_vreg_ok_irq, NULL, lab_vreg_ok_handler, IRQF_ONESHOT | IRQF_TRIGGER_RISING, "lab-vreg-ok", labibb); if (rc) { pr_err("Failed to register 'lab-vreg-ok' irq rc=%d\n", rc); return rc; } } if (labibb->lab_vreg.lab_sc_irq != -EINVAL) { irq_set_status_flags(labibb->lab_vreg.lab_sc_irq, IRQ_DISABLE_UNLAZY); rc = devm_request_threaded_irq(labibb->dev, labibb->lab_vreg.lab_sc_irq, NULL, labibb_sc_err_handler, IRQF_ONESHOT | IRQF_TRIGGER_RISING, "lab-sc-err", labibb); if (rc) { pr_err("Failed to register 'lab-sc-err' irq rc=%d\n", rc); return rc; } } rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_MODULE_RDY, &val, 1); if (rc < 0) { pr_err("qpnp_lab_read read register %x failed rc = %d\n", REG_LAB_MODULE_RDY, rc); return rc; } if (!(val & LAB_MODULE_RDY_EN)) { val = LAB_MODULE_RDY_EN; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_MODULE_RDY, &val, 1); if (rc < 0) { pr_err("qpnp_lab_dt_init write register %x failed rc = %d\n", REG_LAB_MODULE_RDY, rc); return rc; } } if (init_data->constraints.name) { rdesc->owner = THIS_MODULE; rdesc->type = REGULATOR_VOLTAGE; rdesc->ops = &qpnp_lab_ops; rdesc->name = init_data->constraints.name; cfg.dev = labibb->dev; cfg.init_data = init_data; cfg.driver_data = labibb; cfg.of_node = of_node; if (of_get_property(labibb->dev->of_node, "parent-supply", NULL)) init_data->supply_regulator = "parent"; init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS; labibb->lab_vreg.rdev = regulator_register(rdesc, &cfg); if (IS_ERR(labibb->lab_vreg.rdev)) { rc = PTR_ERR(labibb->lab_vreg.rdev); labibb->lab_vreg.rdev = NULL; pr_err("unable to get regulator init data for qpnp lab regulator, rc = %d\n", rc); return rc; } } else { dev_err(labibb->dev, "qpnp lab regulator name missing\n"); return -EINVAL; } return 0; } static int qpnp_ibb_pfm_mode_enable(struct qpnp_labibb *labibb, struct device_node *of_node) { int rc = 0; u32 i, tmp = 0; u8 val = IBB_PFM_ENABLE; rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-pfm-peak-curr", &tmp); if (rc < 0) { pr_err("qcom,qpnp-ibb-pfm-peak-curr is missing, rc = %d\n", rc); return rc; } for (i = 0; i < ARRAY_SIZE(ibb_pfm_peak_curr_table); i++) if (ibb_pfm_peak_curr_table[i] == tmp) break; if (i == ARRAY_SIZE(ibb_pfm_peak_curr_table)) { pr_err("Invalid value in qcom,qpnp-ibb-pfm-peak-curr\n"); return -EINVAL; } val |= (i << IBB_PFM_PEAK_CURRENT_BIT_SHIFT); rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-pfm-hysteresis", &tmp); if (rc < 0) { pr_err("qcom,qpnp-ibb-pfm-hysteresis is missing, rc = %d\n", rc); return rc; } for (i = 0; i < ARRAY_SIZE(ibb_pfm_hysteresis_table); i++) if (ibb_pfm_hysteresis_table[i] == tmp) break; if (i == ARRAY_SIZE(ibb_pfm_hysteresis_table)) { pr_err("Invalid value in qcom,qpnp-ibb-pfm-hysteresis\n"); return -EINVAL; } val |= (i << IBB_PFM_HYSTERESIS_BIT_SHIFT); rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_PFM_CTL, &val, 1); if (rc < 0) pr_err("qpnp_ibb_pfm_ctl write register %x failed rc = %d\n", REG_IBB_PFM_CTL, rc); return rc; } static int qpnp_labibb_pbs_mode_enable(struct qpnp_labibb *labibb, struct device_node *of_node) { int rc = 0; rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_SWIRE_CTL, IBB_SWIRE_VOUT_UPD_EN, 0); if (rc < 0) { pr_err("qpnp_ibb_swire_ctl write register %x failed rc = %d\n", REG_IBB_SWIRE_CTL, rc); return rc; } rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_PD_CTL, IBB_SWIRE_PD_UPD, 0); if (rc < 0) { pr_err("qpnp_ibb_pd_ctl write register %x failed rc = %d\n", REG_IBB_PD_CTL, rc); return rc; } rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_SWIRE_PGM_CTL, LAB_EN_SWIRE_PGM_VOUT | LAB_EN_SWIRE_PGM_PD, 0); if (rc < 0) pr_err("qpnp_lab_swire_pgm_ctl write register %x failed rc = %d\n", REG_LAB_SWIRE_PGM_CTL, rc); return rc; } static int qpnp_ibb_slew_rate_config(struct qpnp_labibb *labibb, struct device_node *of_node) { int rc = 0; u32 i, tmp = 0; u8 val = 0, mask = 0; rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-fast-slew-rate", &tmp); if (!rc) { for (i = 0; i < ARRAY_SIZE(ibb_output_slew_ctl_table); i++) if (ibb_output_slew_ctl_table[i] == tmp) break; if (i == ARRAY_SIZE(ibb_output_slew_ctl_table)) { pr_err("Invalid value in qcom,qpnp-ibb-fast-slew-rate\n"); return -EINVAL; } labibb->ibb_vreg.slew_rate = tmp; val |= (i << IBB_SLEW_RATE_TRANS_TIME_FAST_SHIFT) | IBB_SLEW_RATE_SPEED_FAST_EN | IBB_SLEW_CTL_EN; mask = IBB_SLEW_RATE_SPEED_FAST_EN | IBB_SLEW_RATE_TRANS_TIME_FAST_MASK | IBB_SLEW_CTL_EN; } rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-slow-slew-rate", &tmp); if (!rc) { for (i = 0; i < ARRAY_SIZE(ibb_output_slew_ctl_table); i++) if (ibb_output_slew_ctl_table[i] == tmp) break; if (i == ARRAY_SIZE(ibb_output_slew_ctl_table)) { pr_err("Invalid value in qcom,qpnp-ibb-slow-slew-rate\n"); return -EINVAL; } labibb->ibb_vreg.slew_rate = tmp; val |= (i | IBB_SLEW_CTL_EN); mask |= IBB_SLEW_RATE_SPEED_FAST_EN | IBB_SLEW_RATE_TRANS_TIME_SLOW_MASK | IBB_SLEW_CTL_EN; } rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_OUTPUT_SLEW_CTL, mask, val); if (rc < 0) pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_IBB_OUTPUT_SLEW_CTL, rc); return rc; } static bool qpnp_ibb_poff_ctl_required(struct qpnp_labibb *labibb) { if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) return false; return true; } static int qpnp_ibb_dt_init(struct qpnp_labibb *labibb, struct device_node *of_node) { int rc = 0; u32 i = 0, tmp = 0; u8 val, mask; /* * Do not configure LCD_AMOLED_SEL for pmi8998 as it will be done by * GPIO selector. Override the labibb->mode with what was configured * by the bootloader. */ if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) { rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_LCD_AMOLED_SEL, &val, 1); if (rc) { pr_err("qpnp_labibb_read register %x failed rc = %d\n", REG_IBB_LCD_AMOLED_SEL, rc); return rc; } if (val == REG_LAB_IBB_AMOLED_MODE) labibb->mode = QPNP_LABIBB_AMOLED_MODE; else labibb->mode = QPNP_LABIBB_LCD_MODE; } else { rc = labibb->ibb_ver_ops->sel_mode(labibb, 1); if (rc < 0) { pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n", REG_IBB_LCD_AMOLED_SEL, rc); return rc; } } val = 0; mask = 0; rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-lab-pwrdn-delay", &tmp); if (!rc) { if (tmp > 0) { for (i = 0; i < ARRAY_SIZE(ibb_pwrdn_dly_table); i++) { if (ibb_pwrdn_dly_table[i] == tmp) break; } if (i == ARRAY_SIZE(ibb_pwrdn_dly_table)) { pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrdn-delay\n"); return -EINVAL; } } labibb->ibb_vreg.pwrdn_dly = tmp; if (tmp > 0) val = i | IBB_PWRUP_PWRDN_CTL_1_EN_DLY2; mask |= IBB_PWRUP_PWRDN_CTL_1_EN_DLY2; } rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-lab-pwrup-delay", &tmp); if (!rc) { if (tmp > 0) { for (i = 0; i < ARRAY_SIZE(ibb_pwrup_dly_table); i++) { if (ibb_pwrup_dly_table[i] == tmp) break; } if (i == ARRAY_SIZE(ibb_pwrup_dly_table)) { pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrup-delay\n"); return -EINVAL; } } labibb->ibb_vreg.pwrup_dly = tmp; if (tmp > 0) val |= IBB_PWRUP_PWRDN_CTL_1_EN_DLY1; val |= (i << IBB_PWRUP_PWRDN_CTL_1_DLY1_SHIFT); val |= IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK; mask |= (IBB_PWRUP_PWRDN_CTL_1_EN_DLY1 | IBB_PWRUP_PWRDN_CTL_1_DLY1_MASK | IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK); } if (of_property_read_bool(of_node, "qcom,qpnp-ibb-en-discharge")) { val |= PWRUP_PWRDN_CTL_1_DISCHARGE_EN; mask |= PWRUP_PWRDN_CTL_1_DISCHARGE_EN; } rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base, REG_IBB_PWRUP_PWRDN_CTL_1, mask, val); if (rc < 0) { pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n", REG_IBB_PWRUP_PWRDN_CTL_1, rc); return rc; } if (of_property_read_bool(of_node, "qcom,qpnp-ibb-slew-rate-config")) { rc = qpnp_ibb_slew_rate_config(labibb, of_node); if (rc < 0) return rc; } val = 0; if (!of_property_read_bool(of_node, "qcom,qpnp-ibb-full-pull-down")) val = IBB_PD_CTL_HALF_STRENGTH; if (of_property_read_bool(of_node, "qcom,qpnp-ibb-pull-down-enable")) val |= IBB_PD_CTL_EN; mask = IBB_PD_CTL_STRENGTH_MASK | IBB_PD_CTL_EN; rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_PD_CTL, mask, val); if (rc < 0) { pr_err("qpnp_lab_dt_init write register %x failed rc = %d\n", REG_IBB_PD_CTL, rc); return rc; } rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-switching-clock-frequency", &tmp); if (!rc) { for (val = 0; val < ARRAY_SIZE(ibb_clk_div_table); val++) if (ibb_clk_div_table[val] == tmp) break; if (val == ARRAY_SIZE(ibb_clk_div_table)) { pr_err("Invalid value in qpnp-ibb-switching-clock-frequency\n"); return -EINVAL; } rc = labibb->ibb_ver_ops->set_clk_div(labibb, val); if (rc < 0) { pr_err("write register %x failed rc = %d\n", REG_IBB_CLK_DIV, rc); return rc; } } val = 0; mask = 0; rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-limit-maximum-current", &tmp); if (!rc) { for (val = 0; val < ARRAY_SIZE(ibb_current_limit_table); val++) if (ibb_current_limit_table[val] == tmp) break; if (val == ARRAY_SIZE(ibb_current_limit_table)) { pr_err("Invalid value in qcom,qpnp-ibb-limit-maximum-current\n"); return -EINVAL; } mask = IBB_CURRENT_LIMIT_MASK; } rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-debounce-cycle", &tmp); if (!rc) { for (i = 0; i < ARRAY_SIZE(ibb_debounce_table); i++) if (ibb_debounce_table[i] == tmp) break; if (i == ARRAY_SIZE(ibb_debounce_table)) { pr_err("Invalid value in qcom,qpnp-ibb-debounce-cycle\n"); return -EINVAL; } val |= (i << IBB_CURRENT_LIMIT_DEBOUNCE_SHIFT); mask |= IBB_CURRENT_LIMIT_DEBOUNCE_MASK; } if (of_property_read_bool(of_node, "qcom,qpnp-ibb-limit-max-current-enable")) { val |= IBB_CURRENT_LIMIT_EN; mask |= IBB_CURRENT_LIMIT_EN; } rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base, REG_IBB_CURRENT_LIMIT, mask, val); if (rc < 0) { pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n", REG_IBB_CURRENT_LIMIT, rc); return rc; } if (of_property_read_bool(of_node, "qcom,qpnp-ibb-ring-suppression-enable")) { val = IBB_RING_SUPPRESSION_CTL_EN; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_RING_SUPPRESSION_CTL, &val, 1); if (rc < 0) { pr_err("write register %x failed rc = %d\n", REG_IBB_RING_SUPPRESSION_CTL, rc); return rc; } } if (of_property_read_bool(of_node, "qcom,qpnp-ibb-ps-enable")) { rc = qpnp_ibb_ps_config(labibb, true); if (rc < 0) { pr_err("PS enable failed rc=%d\n", rc); return rc; } } else { rc = qpnp_ibb_ps_config(labibb, false); if (rc < 0) { pr_err("PS disable failed rc=%d\n", rc); return rc; } } if (of_property_read_bool(of_node, "qcom,qpnp-ibb-smart-ps-enable")){ of_property_read_u32(of_node, "qcom,qpnp-ibb-num-swire-trans", &labibb->ibb_vreg.num_swire_trans); of_property_read_u32(of_node, "qcom,qpnp-ibb-neg-curr-limit", &tmp); rc = labibb->ibb_ver_ops->smart_ps_config(labibb, true, labibb->ibb_vreg.num_swire_trans, tmp); if (rc < 0) { pr_err("smart PS enable failed rc=%d\n", rc); return rc; } } rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-init-voltage", &(labibb->ibb_vreg.curr_volt)); if (rc < 0) { pr_err("get qcom,qpnp-ibb-init-voltage failed, rc = %d\n", rc); return rc; } if (of_property_read_bool(of_node, "qcom,qpnp-ibb-use-default-voltage")) rc = labibb->ibb_ver_ops->set_default_voltage(labibb, true); else rc = labibb->ibb_ver_ops->set_default_voltage(labibb, false); if (rc < 0) return rc; if (of_property_read_bool(of_node, "qcom,qpnp-ibb-overload-blank")) { rc = qpnp_ibb_vreg_ok_ctl(labibb, of_node); if (rc < 0) return rc; } return 0; } static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev) { int rc = 0; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->secure_mode) return 0; if (labibb->sc_detected) { pr_info("Short circuit detected: disabled LAB/IBB rails\n"); return 0; } if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); rc = qpnp_ibb_enable_standalone(labibb); if (rc < 0) { pr_err("enable ibb standalone failed, rc=%d\n", rc); return rc; } labibb->ibb_vreg.vreg_enabled = 1; } return 0; } static int qpnp_ibb_regulator_disable(struct regulator_dev *rdev) { int rc; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->secure_mode) return 0; if (labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_disable(labibb); rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_DIS); if (rc < 0) { pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); return rc; } labibb->ibb_vreg.vreg_enabled = 0; } return 0; } static int qpnp_ibb_regulator_is_enabled(struct regulator_dev *rdev) { struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->swire_control) return 0; return labibb->ibb_vreg.vreg_enabled; } static int qpnp_ibb_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned int *selector) { int rc = 0; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->swire_control || labibb->secure_mode) return 0; rc = labibb->ibb_ver_ops->set_voltage(labibb, min_uV, max_uV); return rc; } static int qpnp_ibb_regulator_get_voltage(struct regulator_dev *rdev) { struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->swire_control) return 0; return labibb->ibb_vreg.curr_volt; } static struct regulator_ops qpnp_ibb_ops = { .enable = qpnp_ibb_regulator_enable, .disable = qpnp_ibb_regulator_disable, .is_enabled = qpnp_ibb_regulator_is_enabled, .set_voltage = qpnp_ibb_regulator_set_voltage, .get_voltage = qpnp_ibb_regulator_get_voltage, }; static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, struct device_node *of_node) { int rc = 0; struct regulator_init_data *init_data; struct regulator_desc *rdesc = &labibb->ibb_vreg.rdesc; struct regulator_config cfg = {}; u8 val, ibb_enable_ctl, index; u32 tmp; if (!of_node) { dev_err(labibb->dev, "qpnp ibb regulator device tree node is missing\n"); return -EINVAL; } init_data = of_get_regulator_init_data(labibb->dev, of_node, rdesc); if (!init_data) { pr_err("unable to get regulator init data for qpnp ibb regulator\n"); return -ENOMEM; } rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-min-voltage", &(labibb->ibb_vreg.min_volt)); if (rc < 0) { pr_err("qcom,qpnp-ibb-min-voltage is missing, rc = %d\n", rc); return rc; } rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-step-size", &(labibb->ibb_vreg.step_size)); if (rc < 0) { pr_err("qcom,qpnp-ibb-step-size is missing, rc = %d\n", rc); return rc; } rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-slew-rate", &(labibb->ibb_vreg.slew_rate)); if (rc < 0) labibb->ibb_vreg.slew_rate = IBB_HW_DEFAULT_SLEW_RATE; rc = labibb->ibb_ver_ops->soft_start_ctl(labibb, of_node); if (rc < 0) { pr_err("qpnp_labibb_write register %x failed rc = %d\n", REG_IBB_SOFT_START_CTL, rc); return rc; } if (of_find_property(of_node, "qcom,output-voltage-one-pulse", NULL)) { if (!labibb->swire_control) { pr_err("output-voltage-one-pulse valid for SWIRE only\n"); return -EINVAL; } rc = of_property_read_u32(of_node, "qcom,output-voltage-one-pulse", &tmp); if (rc < 0) { pr_err("failed to read qcom,output-voltage-one-pulse rc=%d\n", rc); return rc; } if (tmp > MAX_OUTPUT_PULSE_VOLTAGE_MV || tmp < MIN_OUTPUT_PULSE_VOLTAGE_MV) { pr_err("Invalid one-pulse voltage range %d\n", tmp); return -EINVAL; } rc = labibb->ibb_ver_ops->voltage_at_one_pulse(labibb, tmp); if (rc < 0) return rc; } rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_ENABLE_CTL, &ibb_enable_ctl, 1); if (rc < 0) { pr_err("qpnp_ibb_read register %x failed rc = %d\n", REG_IBB_ENABLE_CTL, rc); return rc; } /* * For pmi8998, override swire_control with what was configured * before by the bootloader. */ if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) labibb->swire_control = ibb_enable_ctl & IBB_ENABLE_CTL_SWIRE_RDY; if (ibb_enable_ctl & (IBB_ENABLE_CTL_SWIRE_RDY | IBB_ENABLE_CTL_MODULE_EN)) { rc = labibb->ibb_ver_ops->get_mode(labibb); if (rc < 0) { pr_err("qpnp_labibb_read register %x failed rc = %d\n", REG_IBB_LCD_AMOLED_SEL, rc); return rc; } rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_VOLTAGE, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_read read register %x failed rc = %d\n", REG_IBB_VOLTAGE, rc); return rc; } labibb->ibb_vreg.curr_volt = (val & IBB_VOLTAGE_SET_MASK) * labibb->ibb_vreg.step_size + labibb->ibb_vreg.min_volt; if (labibb->mode == QPNP_LABIBB_LCD_MODE) { rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-init-lcd-voltage", &(labibb->ibb_vreg.curr_volt)); if (rc < 0) { pr_err("get qcom,qpnp-ibb-init-lcd-voltage failed, rc = %d\n", rc); return rc; } } else if (!(val & IBB_VOLTAGE_OVERRIDE_EN)) { rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-init-amoled-voltage", &(labibb->ibb_vreg.curr_volt)); if (rc < 0) { pr_err("get qcom,qpnp-ibb-init-amoled-voltage failed, rc = %d\n", rc); return rc; } } rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_PWRUP_PWRDN_CTL_1, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_config_init read register %x failed rc = %d\n", REG_IBB_PWRUP_PWRDN_CTL_1, rc); return rc; } index = (val & IBB_PWRUP_PWRDN_CTL_1_DLY1_MASK) >> IBB_PWRUP_PWRDN_CTL_1_DLY1_SHIFT; labibb->ibb_vreg.pwrup_dly = ibb_pwrup_dly_table[index]; index = val & IBB_PWRUP_PWRDN_CTL_1_DLY2_MASK; labibb->ibb_vreg.pwrdn_dly = ibb_pwrdn_dly_table[index]; labibb->ibb_vreg.vreg_enabled = 1; } else { /* SWIRE_RDY and IBB_MODULE_EN not enabled */ rc = qpnp_ibb_dt_init(labibb, of_node); if (rc < 0) { pr_err("qpnp-ibb: wrong DT parameter specified: rc = %d\n", rc); return rc; } } if (labibb->mode == QPNP_LABIBB_AMOLED_MODE && qpnp_ibb_poff_ctl_required(labibb)) { val = IBB_OVERRIDE_NONOVERLAP | IBB_NFET_GATE_DELAY_2; rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base, REG_IBB_NONOVERLAP_TIME_1, IBB_OVERRIDE_NONOVERLAP | IBB_NONOVERLAP_NFET_MASK, val); if (rc < 0) { pr_err("qpnp_labibb_sec_masked_write register %x failed rc = %d\n", REG_IBB_NONOVERLAP_TIME_1, rc); return rc; } val = IBB_N2P_MUX_SEL; rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base, REG_IBB_NONOVERLAP_TIME_2, val); if (rc < 0) { pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n", REG_IBB_NONOVERLAP_TIME_2, rc); return rc; } val = IBB_FASTER_PFET_OFF; rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + REG_IBB_SPARE_CTL, IBB_POFF_CTL_MASK, val); if (rc < 0) { pr_err("write to register %x failed rc = %d\n", REG_IBB_SPARE_CTL, rc); return rc; } } if (labibb->standalone) { val = 0; rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base, REG_IBB_PWRUP_PWRDN_CTL_1, val); if (rc < 0) { pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n", REG_IBB_PWRUP_PWRDN_CTL_1, rc); return rc; } labibb->ibb_vreg.pwrup_dly = 0; labibb->ibb_vreg.pwrdn_dly = 0; } if (labibb->ibb_vreg.ibb_sc_irq != -EINVAL) { irq_set_status_flags(labibb->ibb_vreg.ibb_sc_irq, IRQ_DISABLE_UNLAZY); rc = devm_request_threaded_irq(labibb->dev, labibb->ibb_vreg.ibb_sc_irq, NULL, labibb_sc_err_handler, IRQF_ONESHOT | IRQF_TRIGGER_RISING, "ibb-sc-err", labibb); if (rc) { pr_err("Failed to register 'ibb-sc-err' irq rc=%d\n", rc); return rc; } } rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_MODULE_RDY, &val, 1); if (rc < 0) { pr_err("qpnp_ibb_read read register %x failed rc = %d\n", REG_IBB_MODULE_RDY, rc); return rc; } if (!(val & IBB_MODULE_RDY_EN)) { val = IBB_MODULE_RDY_EN; rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_MODULE_RDY, &val, 1); if (rc < 0) { pr_err("qpnp_ibb_dt_init write register %x failed rc = %d\n", REG_IBB_MODULE_RDY, rc); return rc; } } if (of_property_read_bool(of_node, "qcom,qpnp-ibb-enable-pfm-mode")) { rc = qpnp_ibb_pfm_mode_enable(labibb, of_node); if (rc < 0) return rc; } if (labibb->pbs_control) { rc = qpnp_labibb_pbs_mode_enable(labibb, of_node); if (rc < 0) return rc; } if (init_data->constraints.name) { rdesc->owner = THIS_MODULE; rdesc->type = REGULATOR_VOLTAGE; rdesc->ops = &qpnp_ibb_ops; rdesc->name = init_data->constraints.name; cfg.dev = labibb->dev; cfg.init_data = init_data; cfg.driver_data = labibb; cfg.of_node = of_node; if (of_get_property(labibb->dev->of_node, "parent-supply", NULL)) init_data->supply_regulator = "parent"; init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS; labibb->ibb_vreg.rdev = regulator_register(rdesc, &cfg); if (IS_ERR(labibb->ibb_vreg.rdev)) { rc = PTR_ERR(labibb->ibb_vreg.rdev); labibb->ibb_vreg.rdev = NULL; pr_err("unable to get regulator init data for qpnp ibb regulator, rc = %d\n", rc); return rc; } } else { dev_err(labibb->dev, "qpnp ibb regulator name missing\n"); return -EINVAL; } return 0; } static int qpnp_lab_register_irq(struct device_node *child, struct qpnp_labibb *labibb) { int rc = 0; if (is_lab_vreg_ok_irq_available(labibb)) { rc = of_irq_get_byname(child, "lab-vreg-ok"); if (rc < 0) { pr_err("Invalid lab-vreg-ok irq\n"); return rc; } labibb->lab_vreg.lab_vreg_ok_irq = rc; } labibb->lab_vreg.lab_sc_irq = -EINVAL; rc = of_irq_get_byname(child, "lab-sc-err"); if (rc < 0) pr_debug("Unable to get lab-sc-err, rc = %d\n", rc); else labibb->lab_vreg.lab_sc_irq = rc; return 0; } static int qpnp_ibb_register_irq(struct device_node *child, struct qpnp_labibb *labibb) { int rc; labibb->ibb_vreg.ibb_sc_irq = -EINVAL; rc = of_irq_get_byname(child, "ibb-sc-err"); if (rc < 0) pr_debug("Unable to get ibb-sc-err, rc = %d\n", rc); else labibb->ibb_vreg.ibb_sc_irq = rc; return 0; } static int qpnp_labibb_check_ttw_supported(struct qpnp_labibb *labibb) { int rc = 0; u8 val; switch (labibb->pmic_rev_id->pmic_subtype) { case PMI8996_SUBTYPE: rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_REVISION4, &val, 1); if (rc < 0) { pr_err("qpnp_labibb_read register %x failed rc = %d\n", REG_IBB_REVISION4, rc); return rc; } /* PMI8996 has revision 1 */ if (val < 1) { pr_err("TTW feature cannot be enabled for revision %d\n", val); labibb->ttw_en = false; } /* FORCE_LAB_ON in TTW is not required for PMI8996 */ labibb->ttw_force_lab_on = false; break; case PMI8950_SUBTYPE: /* TTW supported for all revisions */ break; case PMI8998_SUBTYPE: /* TTW supported for all revisions */ break; default: pr_info("TTW mode not supported for PMIC-subtype = %d\n", labibb->pmic_rev_id->pmic_subtype); labibb->ttw_en = false; break; } return rc; } static ssize_t secure_mode_store(struct class *c, struct class_attribute *attr, const char *buf, size_t count) { struct qpnp_labibb *labibb = container_of(c, struct qpnp_labibb, labibb_class); int val, rc; rc = kstrtouint(buf, 0, &val); if (rc < 0) return rc; if (val != 0 && val != 1) return count; /* Disable irqs */ if (val == 1 && !labibb->secure_mode) { if (labibb->lab_vreg.lab_vreg_ok_irq > 0) disable_irq(labibb->lab_vreg.lab_vreg_ok_irq); if (labibb->lab_vreg.lab_sc_irq > 0) disable_irq(labibb->lab_vreg.lab_sc_irq); if (labibb->ibb_vreg.ibb_sc_irq > 0) disable_irq(labibb->ibb_vreg.ibb_sc_irq); labibb->secure_mode = true; } else if (val == 0 && labibb->secure_mode) { if (labibb->lab_vreg.lab_vreg_ok_irq > 0) enable_irq(labibb->lab_vreg.lab_vreg_ok_irq); if (labibb->lab_vreg.lab_sc_irq > 0) enable_irq(labibb->lab_vreg.lab_sc_irq); if (labibb->ibb_vreg.ibb_sc_irq > 0) enable_irq(labibb->ibb_vreg.ibb_sc_irq); labibb->secure_mode = false; } return count; } static CLASS_ATTR_WO(secure_mode); static struct attribute *labibb_attrs[] = { &class_attr_secure_mode.attr, NULL, }; ATTRIBUTE_GROUPS(labibb); static int qpnp_labibb_regulator_probe(struct platform_device *pdev) { struct qpnp_labibb *labibb; unsigned int base; struct device_node *child, *revid_dev_node; const char *mode_name; u8 type, revision; int rc = 0; labibb = devm_kzalloc(&pdev->dev, sizeof(*labibb), GFP_KERNEL); if (labibb == NULL) return -ENOMEM; labibb->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!labibb->regmap) { dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); return -EINVAL; } labibb->dev = &(pdev->dev); labibb->pdev = pdev; mutex_init(&(labibb->lab_vreg.lab_mutex)); mutex_init(&(labibb->ibb_vreg.ibb_mutex)); mutex_init(&(labibb->bus_mutex)); revid_dev_node = of_parse_phandle(labibb->dev->of_node, "qcom,pmic-revid", 0); if (!revid_dev_node) { pr_err("Missing qcom,pmic-revid property - driver failed\n"); return -EINVAL; } labibb->pmic_rev_id = get_revid_data(revid_dev_node); if (IS_ERR(labibb->pmic_rev_id)) { pr_debug("Unable to get revid data\n"); return -EPROBE_DEFER; } if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) { labibb->ibb_ver_ops = &ibb_ops_v2; labibb->lab_ver_ops = &pm660_lab_ops; } else if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) { labibb->ibb_ver_ops = &ibb_ops_v1; labibb->lab_ver_ops = &pmi8998_lab_ops; } else { labibb->ibb_ver_ops = &ibb_ops_v1; labibb->lab_ver_ops = &lab_ops_v1; } if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) { labibb->mode = QPNP_LABIBB_AMOLED_MODE; /* Enable polling for LAB short circuit detection for PM660A */ labibb->detect_lab_sc = true; } else { rc = of_property_read_string(labibb->dev->of_node, "qcom,qpnp-labibb-mode", &mode_name); if (!rc) { if (strcmp("lcd", mode_name) == 0) { labibb->mode = QPNP_LABIBB_LCD_MODE; } else if (strcmp("amoled", mode_name) == 0) { labibb->mode = QPNP_LABIBB_AMOLED_MODE; } else { pr_err("Invalid device property in qcom,qpnp-labibb-mode: %s\n", mode_name); return -EINVAL; } } else { pr_err("qpnp_labibb: qcom,qpnp-labibb-mode is missing.\n"); return rc; } } labibb->standalone = of_property_read_bool(labibb->dev->of_node, "qcom,labibb-standalone"); labibb->ttw_en = of_property_read_bool(labibb->dev->of_node, "qcom,labibb-touch-to-wake-en"); if (labibb->ttw_en && labibb->mode != QPNP_LABIBB_LCD_MODE) { pr_err("Invalid mode for TTW\n"); return -EINVAL; } labibb->ttw_force_lab_on = of_property_read_bool( labibb->dev->of_node, "qcom,labibb-ttw-force-lab-on"); labibb->swire_control = of_property_read_bool(labibb->dev->of_node, "qcom,swire-control"); labibb->pbs_control = of_property_read_bool(labibb->dev->of_node, "qcom,pbs-control"); if (labibb->swire_control && labibb->mode != QPNP_LABIBB_AMOLED_MODE) { pr_err("Invalid mode for SWIRE control\n"); return -EINVAL; } if (labibb->swire_control) { labibb->skip_2nd_swire_cmd = of_property_read_bool(labibb->dev->of_node, "qcom,skip-2nd-swire-cmd"); rc = of_property_read_u32(labibb->dev->of_node, "qcom,swire-2nd-cmd-delay", &labibb->swire_2nd_cmd_delay); if (rc < 0) labibb->swire_2nd_cmd_delay = SWIRE_DEFAULT_2ND_CMD_DLY_MS; rc = of_property_read_u32(labibb->dev->of_node, "qcom,swire-ibb-ps-enable-delay", &labibb->swire_ibb_ps_enable_delay); if (rc < 0) labibb->swire_ibb_ps_enable_delay = SWIRE_DEFAULT_IBB_PS_ENABLE_DLY_MS; } if (of_get_available_child_count(pdev->dev.of_node) == 0) { pr_err("no child nodes\n"); return -ENXIO; } for_each_available_child_of_node(pdev->dev.of_node, child) { rc = of_property_read_u32(child, "reg", &base); if (rc < 0) { dev_err(&pdev->dev, "Couldn't find reg in node = %s rc = %d\n", child->full_name, rc); return rc; } rc = qpnp_labibb_read(labibb, base + REG_REVISION_2, &revision, 1); if (rc < 0) { pr_err("Reading REVISION_2 failed rc=%d\n", rc); goto fail_registration; } rc = qpnp_labibb_read(labibb, base + REG_PERPH_TYPE, &type, 1); if (rc < 0) { pr_err("Peripheral type read failed rc=%d\n", rc); goto fail_registration; } switch (type) { case QPNP_LAB_TYPE: labibb->lab_base = base; labibb->lab_dig_major = revision; rc = qpnp_lab_register_irq(child, labibb); if (rc) { pr_err("Failed to register LAB IRQ rc=%d\n", rc); goto fail_registration; } rc = register_qpnp_lab_regulator(labibb, child); if (rc < 0) goto fail_registration; break; case QPNP_IBB_TYPE: labibb->ibb_base = base; labibb->ibb_dig_major = revision; qpnp_ibb_register_irq(child, labibb); rc = register_qpnp_ibb_regulator(labibb, child); if (rc < 0) goto fail_registration; break; default: pr_err("qpnp_labibb: unknown peripheral type %x\n", type); rc = -EINVAL; goto fail_registration; } } if (labibb->ttw_en) { rc = qpnp_labibb_check_ttw_supported(labibb); if (rc < 0) { pr_err("pmic revision check failed for TTW rc=%d\n", rc); goto fail_registration; } } INIT_WORK(&labibb->lab_vreg_ok_work, qpnp_lab_vreg_notifier_work); INIT_DELAYED_WORK(&labibb->sc_err_recovery_work, labibb_sc_err_recovery_work); hrtimer_init(&labibb->sc_err_check_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); labibb->sc_err_check_timer.function = labibb_check_sc_err_count; dev_set_drvdata(&pdev->dev, labibb); labibb->labibb_class.name = "lcd_bias"; labibb->labibb_class.owner = THIS_MODULE; labibb->labibb_class.class_groups = labibb_groups; rc = class_register(&labibb->labibb_class); if (rc < 0) { pr_err("Failed to register labibb class rc=%d\n", rc); return rc; } pr_info("LAB/IBB registered successfully, lab_vreg enable=%d ibb_vreg enable=%d swire_control=%d\n", labibb->lab_vreg.vreg_enabled, labibb->ibb_vreg.vreg_enabled, labibb->swire_control); return 0; fail_registration: if (labibb->lab_vreg.rdev) regulator_unregister(labibb->lab_vreg.rdev); if (labibb->ibb_vreg.rdev) regulator_unregister(labibb->ibb_vreg.rdev); return rc; } int qpnp_labibb_notifier_register(struct notifier_block *nb) { return raw_notifier_chain_register(&labibb_notifier, nb); } EXPORT_SYMBOL(qpnp_labibb_notifier_register); int qpnp_labibb_notifier_unregister(struct notifier_block *nb) { return raw_notifier_chain_unregister(&labibb_notifier, nb); } EXPORT_SYMBOL(qpnp_labibb_notifier_unregister); static int qpnp_labibb_regulator_remove(struct platform_device *pdev) { struct qpnp_labibb *labibb = dev_get_drvdata(&pdev->dev); if (labibb) { if (labibb->lab_vreg.rdev) regulator_unregister(labibb->lab_vreg.rdev); if (labibb->ibb_vreg.rdev) regulator_unregister(labibb->ibb_vreg.rdev); cancel_work_sync(&labibb->lab_vreg_ok_work); } return 0; } static const struct of_device_id spmi_match_table[] = { { .compatible = QPNP_LABIBB_REGULATOR_DRIVER_NAME, }, { }, }; static struct platform_driver qpnp_labibb_regulator_driver = { .driver = { .name = QPNP_LABIBB_REGULATOR_DRIVER_NAME, .of_match_table = spmi_match_table, }, .probe = qpnp_labibb_regulator_probe, .remove = qpnp_labibb_regulator_remove, }; static int __init qpnp_labibb_regulator_init(void) { return platform_driver_register(&qpnp_labibb_regulator_driver); } arch_initcall(qpnp_labibb_regulator_init); static void __exit qpnp_labibb_regulator_exit(void) { platform_driver_unregister(&qpnp_labibb_regulator_driver); } module_exit(qpnp_labibb_regulator_exit); MODULE_DESCRIPTION("QPNP labibb driver"); MODULE_LICENSE("GPL v2");