// SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2020, The Linux Foundation. All rights reserved. #include <linux/module.h> #include <linux/of_irq.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/regulator/driver.h> #include <linux/regulator/of_regulator.h> #define REG_PERPH_TYPE … #define QCOM_LAB_TYPE … #define QCOM_IBB_TYPE … #define PMI8998_LAB_REG_BASE … #define PMI8998_IBB_REG_BASE … #define PMI8998_IBB_LAB_REG_OFFSET … #define REG_LABIBB_STATUS1 … #define LABIBB_STATUS1_SC_BIT … #define LABIBB_STATUS1_VREG_OK_BIT … #define REG_LABIBB_INT_SET_TYPE … #define REG_LABIBB_INT_POLARITY_HIGH … #define REG_LABIBB_INT_POLARITY_LOW … #define REG_LABIBB_INT_LATCHED_CLR … #define REG_LABIBB_INT_EN_SET … #define REG_LABIBB_INT_EN_CLR … #define LABIBB_INT_VREG_OK … #define LABIBB_INT_VREG_TYPE_LEVEL … #define REG_LABIBB_VOLTAGE … #define LABIBB_VOLTAGE_OVERRIDE_EN … #define LAB_VOLTAGE_SET_MASK … #define IBB_VOLTAGE_SET_MASK … #define REG_LABIBB_ENABLE_CTL … #define LABIBB_CONTROL_ENABLE … #define REG_LABIBB_PD_CTL … #define LAB_PD_CTL_MASK … #define IBB_PD_CTL_MASK … #define LAB_PD_CTL_STRONG_PULL … #define IBB_PD_CTL_HALF_STRENGTH … #define IBB_PD_CTL_EN … #define REG_LABIBB_CURRENT_LIMIT … #define LAB_CURRENT_LIMIT_MASK … #define IBB_CURRENT_LIMIT_MASK … #define LAB_CURRENT_LIMIT_OVERRIDE_EN … #define LABIBB_CURRENT_LIMIT_EN … #define REG_IBB_PWRUP_PWRDN_CTL_1 … #define IBB_CTL_1_DISCHARGE_EN … #define REG_LABIBB_SOFT_START_CTL … #define REG_LABIBB_SEC_ACCESS … #define LABIBB_SEC_UNLOCK_CODE … #define LAB_ENABLE_CTL_MASK … #define IBB_ENABLE_CTL_MASK … #define LABIBB_OFF_ON_DELAY … #define LAB_ENABLE_TIME … #define IBB_ENABLE_TIME … #define LABIBB_POLL_ENABLED_TIME … #define OCP_RECOVERY_INTERVAL_MS … #define SC_RECOVERY_INTERVAL_MS … #define LABIBB_MAX_OCP_COUNT … #define LABIBB_MAX_SC_COUNT … #define LABIBB_MAX_FATAL_COUNT … struct labibb_current_limits { … }; struct labibb_regulator { … }; struct labibb_regulator_data { … }; static int qcom_labibb_ocp_hw_enable(struct regulator_dev *rdev) { … } static int qcom_labibb_ocp_hw_disable(struct regulator_dev *rdev) { … } /** * qcom_labibb_check_ocp_status - Check the Over-Current Protection status * @vreg: Main driver structure * * This function checks the STATUS1 register for the VREG_OK bit: if it is * set, then there is no Over-Current event. * * Returns: Zero if there is no over-current, 1 if in over-current or * negative number for error */ static int qcom_labibb_check_ocp_status(struct labibb_regulator *vreg) { … } /** * qcom_labibb_ocp_recovery_worker - Handle OCP event * @work: OCP work structure * * This is the worker function to handle the Over Current Protection * hardware event; This will check if the hardware is still * signaling an over-current condition and will eventually stop * the regulator if such condition is still signaled after * LABIBB_MAX_OCP_COUNT times. * * If the driver that is consuming the regulator did not take action * for the OCP condition, or the hardware did not stabilize, a cut * of the LAB and IBB regulators will be forced (regulators will be * disabled). * * As last, if the writes to shut down the LAB/IBB regulators fail * for more than LABIBB_MAX_FATAL_COUNT, then a kernel panic will be * triggered, as a last resort to protect the hardware from burning; * this, however, is expected to never happen, but this is kept to * try to further ensure that we protect the hardware at all costs. */ static void qcom_labibb_ocp_recovery_worker(struct work_struct *work) { … } /** * qcom_labibb_ocp_isr - Interrupt routine for OverCurrent Protection * @irq: Interrupt number * @chip: Main driver structure * * Over Current Protection (OCP) will signal to the client driver * that an over-current event has happened and then will schedule * a recovery worker. * * Disabling and eventually re-enabling the regulator is expected * to be done by the driver, as some hardware may be triggering an * over-current condition only at first initialization or it may * be expected only for a very brief amount of time, after which * the attached hardware may be expected to stabilize its current * draw. * * Returns: IRQ_HANDLED for success or IRQ_NONE for failure. */ static irqreturn_t qcom_labibb_ocp_isr(int irq, void *chip) { … } static int qcom_labibb_set_ocp(struct regulator_dev *rdev, int lim, int severity, bool enable) { … } /** * qcom_labibb_check_sc_status - Check the Short Circuit Protection status * @vreg: Main driver structure * * This function checks the STATUS1 register on both LAB and IBB regulators * for the ShortCircuit bit: if it is set on *any* of them, then we have * experienced a short-circuit event. * * Returns: Zero if there is no short-circuit, 1 if in short-circuit or * negative number for error */ static int qcom_labibb_check_sc_status(struct labibb_regulator *vreg) { … } /** * qcom_labibb_sc_recovery_worker - Handle Short Circuit event * @work: SC work structure * * This is the worker function to handle the Short Circuit Protection * hardware event; This will check if the hardware is still * signaling a short-circuit condition and will eventually never * re-enable the regulator if such condition is still signaled after * LABIBB_MAX_SC_COUNT times. * * If the driver that is consuming the regulator did not take action * for the SC condition, or the hardware did not stabilize, this * worker will stop rescheduling, leaving the regulators disabled * as already done by the Portable Batch System (PBS). * * Returns: IRQ_HANDLED for success or IRQ_NONE for failure. */ static void qcom_labibb_sc_recovery_worker(struct work_struct *work) { … } /** * qcom_labibb_sc_isr - Interrupt routine for Short Circuit Protection * @irq: Interrupt number * @chip: Main driver structure * * Short Circuit Protection (SCP) will signal to the client driver * that a regulation-out event has happened and then will schedule * a recovery worker. * * The LAB and IBB regulators will be automatically disabled by the * Portable Batch System (PBS) and they will be enabled again by * the worker function if the hardware stops signaling the short * circuit event. * * Returns: IRQ_HANDLED for success or IRQ_NONE for failure. */ static irqreturn_t qcom_labibb_sc_isr(int irq, void *chip) { … } static int qcom_labibb_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) { … } static int qcom_labibb_get_current_limit(struct regulator_dev *rdev) { … } static int qcom_labibb_set_soft_start(struct regulator_dev *rdev) { … } static int qcom_labibb_get_table_sel(const int *table, int sz, u32 value) { … } /* IBB discharge resistor values in KOhms */ static const int dischg_resistor_values[] = …; /* Soft start time in microseconds */ static const int soft_start_values[] = …; static int qcom_labibb_of_parse_cb(struct device_node *np, const struct regulator_desc *desc, struct regulator_config *config) { … } static const struct regulator_ops qcom_labibb_ops = …; static const struct regulator_desc pmi8998_lab_desc = …; static const struct regulator_desc pmi8998_ibb_desc = …; static const struct labibb_regulator_data pmi8998_labibb_data[] = …; static const struct of_device_id qcom_labibb_match[] = …; MODULE_DEVICE_TABLE(of, qcom_labibb_match); static int qcom_labibb_regulator_probe(struct platform_device *pdev) { … } static struct platform_driver qcom_labibb_regulator_driver = …; module_platform_driver(…) …; MODULE_DESCRIPTION(…) …; MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_LICENSE(…) …;