linux/drivers/regulator/bd718x7-regulator.c

// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 ROHM Semiconductors
// bd71837-regulator.c ROHM BD71837MWV/BD71847MWV regulator driver

#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/rohm-bd718x7.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/slab.h>

/* Typical regulator startup times as per data sheet in uS */
#define BD71847_BUCK1_STARTUP_TIME
#define BD71847_BUCK2_STARTUP_TIME
#define BD71847_BUCK3_STARTUP_TIME
#define BD71847_BUCK4_STARTUP_TIME
#define BD71847_BUCK5_STARTUP_TIME
#define BD71847_BUCK6_STARTUP_TIME
#define BD71847_LDO1_STARTUP_TIME
#define BD71847_LDO2_STARTUP_TIME
#define BD71847_LDO3_STARTUP_TIME
#define BD71847_LDO4_STARTUP_TIME
#define BD71847_LDO5_STARTUP_TIME
#define BD71847_LDO6_STARTUP_TIME

#define BD71837_BUCK1_STARTUP_TIME
#define BD71837_BUCK2_STARTUP_TIME
#define BD71837_BUCK3_STARTUP_TIME
#define BD71837_BUCK4_STARTUP_TIME
#define BD71837_BUCK5_STARTUP_TIME
#define BD71837_BUCK6_STARTUP_TIME
#define BD71837_BUCK7_STARTUP_TIME
#define BD71837_BUCK8_STARTUP_TIME
#define BD71837_LDO1_STARTUP_TIME
#define BD71837_LDO2_STARTUP_TIME
#define BD71837_LDO3_STARTUP_TIME
#define BD71837_LDO4_STARTUP_TIME
#define BD71837_LDO5_STARTUP_TIME
#define BD71837_LDO6_STARTUP_TIME
#define BD71837_LDO7_STARTUP_TIME

/*
 * BD718(37/47/50) have two "enable control modes". ON/OFF can either be
 * controlled by software - or by PMIC internal HW state machine. Whether
 * regulator should be under SW or HW control can be defined from device-tree.
 * Let's provide separate ops for regulators to use depending on the "enable
 * control mode".
 */
#define BD718XX_HWOPNAME(swopname)

#define BD718XX_OPS(name, _list_voltage, _map_voltage, _set_voltage_sel, \
		   _get_voltage_sel, _set_voltage_time_sel, _set_ramp_delay, \
		   _set_uvp, _set_ovp)								\

/*
 * BUCK1/2/3/4
 * BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting
 * 00: 10.00mV/usec 10mV 1uS
 * 01: 5.00mV/usec	10mV 2uS
 * 10: 2.50mV/usec	10mV 4uS
 * 11: 1.25mV/usec	10mV 8uS
 */
static const unsigned int bd718xx_ramp_delay[] =;

/* These functions are used when regulators are under HW state machine control.
 * We assume PMIC is in RUN state because SW running and able to query the
 * status. Most of the regulators have fixed ON or OFF state at RUN/IDLE so for
 * them we just return a constant. BD71837 BUCK3 and BUCK4 are exceptions as
 * they support configuring the ON/OFF state for RUN.
 *
 * Note for next hacker - these PMICs have a register where the HW state can be
 * read. If assuming RUN appears to be false in your use-case - you can
 * implement state reading (although that is not going to be atomic) before
 * returning the enable state.
 */
static int always_enabled_by_hwstate(struct regulator_dev *rdev)
{}

static int never_enabled_by_hwstate(struct regulator_dev *rdev)
{}

static int bd71837_get_buck34_enable_hwctrl(struct regulator_dev *rdev)
{}

static void voltage_change_done(struct regulator_dev *rdev, unsigned int sel,
				unsigned int *mask)
{}

static int voltage_change_prepare(struct regulator_dev *rdev, unsigned int sel,
				  unsigned int *mask)
{}

static int bd718xx_set_voltage_sel_restricted(struct regulator_dev *rdev,
						    unsigned int sel)
{}

static int bd718xx_set_voltage_sel_pickable_restricted(
		struct regulator_dev *rdev, unsigned int sel)
{}

static int bd71837_set_voltage_sel_pickable_restricted(
		struct regulator_dev *rdev, unsigned int sel)
{}

/*
 * BD71837 BUCK1/2/3/4
 * BD71847 BUCK1/2
 * 0.70 to 1.30V (10mV step)
 */
static const struct linear_range bd718xx_dvs_buck_volts[] =;

/*
 * BD71837 BUCK5
 * 0.7V to 1.35V  (range 0)
 * and
 * 0.675 to 1.325 (range 1)
 */
static const struct linear_range bd71837_buck5_volts[] =;

/*
 * Range selector for first 3 linear ranges is 0x0
 * and 0x1 for last 3 ranges.
 */
static const unsigned int bd71837_buck5_volt_range_sel[] =;

/*
 * BD71847 BUCK3
 */
static const struct linear_range bd71847_buck3_volts[] =;

static const unsigned int bd71847_buck3_volt_range_sel[] =;

static const struct linear_range bd71847_buck4_volts[] =;

static const unsigned int bd71847_buck4_volt_range_sel[] =;

/*
 * BUCK6
 * 3.0V to 3.3V (step 100mV)
 */
static const struct linear_range bd71837_buck6_volts[] =;

/*
 * BD71837 BUCK7
 * BD71847 BUCK5
 * 000 = 1.605V
 * 001 = 1.695V
 * 010 = 1.755V
 * 011 = 1.8V (Initial)
 * 100 = 1.845V
 * 101 = 1.905V
 * 110 = 1.95V
 * 111 = 1.995V
 */
static const unsigned int bd718xx_3rd_nodvs_buck_volts[] =;

/*
 * BUCK8
 * 0.8V to 1.40V (step 10mV)
 */
static const struct linear_range bd718xx_4th_nodvs_buck_volts[] =;

/*
 * LDO1
 * 3.0 to 3.3V (100mV step)
 */
static const struct linear_range bd718xx_ldo1_volts[] =;

static const unsigned int bd718xx_ldo1_volt_range_sel[] =;

/*
 * LDO2
 * 0.8 or 0.9V
 */
static const unsigned int ldo_2_volts[] =;

/*
 * LDO3
 * 1.8 to 3.3V (100mV step)
 */
static const struct linear_range bd718xx_ldo3_volts[] =;

/*
 * LDO4
 * 0.9 to 1.8V (100mV step)
 */
static const struct linear_range bd718xx_ldo4_volts[] =;

/*
 * LDO5 for BD71837
 * 1.8 to 3.3V (100mV step)
 */
static const struct linear_range bd71837_ldo5_volts[] =;

/*
 * LDO5 for BD71837
 * 1.8 to 3.3V (100mV step)
 */
static const struct linear_range bd71847_ldo5_volts[] =;

static const unsigned int bd71847_ldo5_volt_range_sel[] =;

/*
 * LDO6
 * 0.9 to 1.8V (100mV step)
 */
static const struct linear_range bd718xx_ldo6_volts[] =;

/*
 * LDO7
 * 1.8 to 3.3V (100mV step)
 */
static const struct linear_range bd71837_ldo7_volts[] =;

struct reg_init {};
struct bd718xx_regulator_data {};

static int bd718x7_xvp_sanity_check(struct regulator_dev *rdev, int lim_uV,
				    int severity)
{}

static int bd718x7_set_ldo_uvp(struct regulator_dev *rdev, int lim_uV,
			       int severity, bool enable)
{}

static int bd718x7_get_buck_prot_reg(int id, int *reg)
{}

static int bd718x7_get_buck_ovp_info(int id, int *reg, int *bit)
{}

static int bd718x7_get_buck_uvp_info(int id, int *reg, int *bit)
{}

static int bd718x7_set_buck_uvp(struct regulator_dev *rdev, int lim_uV,
				int severity, bool enable)
{}

static int bd718x7_set_buck_ovp(struct regulator_dev *rdev, int lim_uV,
				int severity,
				bool enable)
{}

/*
 * OPS common for BD71847 and BD71850
 */
BD718XX_OPS(bd718xx_pickable_range_ldo_ops,
	    regulator_list_voltage_pickable_linear_range, NULL,
	    bd718xx_set_voltage_sel_pickable_restricted,
	    regulator_get_voltage_sel_pickable_regmap, NULL, NULL,
	    bd718x7_set_ldo_uvp, NULL);

/* BD71847 and BD71850 LDO 5 is by default OFF at RUN state */
static const struct regulator_ops bd718xx_ldo5_ops_hwstate =;

BD718XX_OPS(bd718xx_pickable_range_buck_ops,
	    regulator_list_voltage_pickable_linear_range, NULL,
	    regulator_set_voltage_sel_pickable_regmap,
	    regulator_get_voltage_sel_pickable_regmap,
	    regulator_set_voltage_time_sel, NULL, bd718x7_set_buck_uvp,
	    bd718x7_set_buck_ovp);

BD718XX_OPS(bd718xx_ldo_regulator_ops, regulator_list_voltage_linear_range,
	    NULL, bd718xx_set_voltage_sel_restricted,
	    regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp,
	    NULL);

BD718XX_OPS(bd718xx_ldo_regulator_nolinear_ops, regulator_list_voltage_table,
	    NULL, bd718xx_set_voltage_sel_restricted,
	    regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp,
	    NULL);

BD718XX_OPS(bd718xx_buck_regulator_ops, regulator_list_voltage_linear_range,
	    NULL, regulator_set_voltage_sel_regmap,
	    regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
	    NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp);

BD718XX_OPS(bd718xx_buck_regulator_nolinear_ops, regulator_list_voltage_table,
	    regulator_map_voltage_ascend, regulator_set_voltage_sel_regmap,
	    regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
	    NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp);

/*
 * OPS for BD71837
 */
BD718XX_OPS(bd71837_pickable_range_ldo_ops,
	    regulator_list_voltage_pickable_linear_range, NULL,
	    bd71837_set_voltage_sel_pickable_restricted,
	    regulator_get_voltage_sel_pickable_regmap, NULL, NULL,
	    bd718x7_set_ldo_uvp, NULL);

BD718XX_OPS(bd71837_pickable_range_buck_ops,
	    regulator_list_voltage_pickable_linear_range, NULL,
	    bd71837_set_voltage_sel_pickable_restricted,
	    regulator_get_voltage_sel_pickable_regmap,
	    regulator_set_voltage_time_sel, NULL, bd718x7_set_buck_uvp,
	    bd718x7_set_buck_ovp);

BD718XX_OPS(bd71837_ldo_regulator_ops, regulator_list_voltage_linear_range,
	    NULL, rohm_regulator_set_voltage_sel_restricted,
	    regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp,
	    NULL);

BD718XX_OPS(bd71837_ldo_regulator_nolinear_ops, regulator_list_voltage_table,
	    NULL, rohm_regulator_set_voltage_sel_restricted,
	    regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp,
	    NULL);

BD718XX_OPS(bd71837_buck_regulator_ops, regulator_list_voltage_linear_range,
	    NULL, rohm_regulator_set_voltage_sel_restricted,
	    regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
	    NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp);

BD718XX_OPS(bd71837_buck_regulator_nolinear_ops, regulator_list_voltage_table,
	    regulator_map_voltage_ascend, rohm_regulator_set_voltage_sel_restricted,
	    regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
	    NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp);
/*
 * BD71837 bucks 3 and 4 support defining their enable/disable state also
 * when buck enable state is under HW state machine control. In that case the
 * bit [2] in CTRL register is used to indicate if regulator should be ON.
 */
static const struct regulator_ops bd71837_buck34_ops_hwctrl =;

/*
 * OPS for all of the ICs - BD718(37/47/50)
 */
BD718XX_OPS(bd718xx_dvs_buck_regulator_ops, regulator_list_voltage_linear_range,
	    NULL, regulator_set_voltage_sel_regmap,
	    regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
	    regulator_set_ramp_delay_regmap, bd718x7_set_buck_uvp,
	    bd718x7_set_buck_ovp);



/*
 * There is a HW quirk in BD71837. The shutdown sequence timings for
 * bucks/LDOs which are controlled via register interface are changed.
 * At PMIC poweroff the voltage for BUCK6/7 is cut immediately at the
 * beginning of shut-down sequence. As bucks 6 and 7 are parent
 * supplies for LDO5 and LDO6 - this causes LDO5/6 voltage
 * monitoring to errorneously detect under voltage and force PMIC to
 * emergency state instead of poweroff. In order to avoid this we
 * disable voltage monitoring for LDO5 and LDO6
 */
static const struct reg_init bd71837_ldo5_inits[] =;

static const struct reg_init bd71837_ldo6_inits[] =;

static int buck_set_hw_dvs_levels(struct device_node *np,
			    const struct regulator_desc *desc,
			    struct regulator_config *cfg)
{}

static const struct regulator_ops *bd71847_swcontrol_ops[] =;

static const struct regulator_ops *bd71847_hwcontrol_ops[] =;

static struct bd718xx_regulator_data bd71847_regulators[] =;

static const struct regulator_ops *bd71837_swcontrol_ops[] =;

static const struct regulator_ops *bd71837_hwcontrol_ops[] =;

static struct bd718xx_regulator_data bd71837_regulators[] =;

static void mark_hw_controlled(struct device *dev, struct device_node *np,
			       struct bd718xx_regulator_data *reg_data,
			       unsigned int num_reg_data, int *info)
{}

/*
 * Setups where regulator (especially the buck8) output voltage is scaled
 * by adding external connection where some other regulator output is connected
 * to feedback-pin (over suitable resistors) is getting popular amongst users
 * of BD71837. (This allows for example scaling down the buck8 voltages to suit
 * lover GPU voltages for projects where buck8 is (ab)used to supply power
 * for GPU. Additionally some setups do allow DVS for buck8 but as this do
 * produce voltage spikes the HW must be evaluated to be able to survive this
 * - hence I keep the DVS disabled for non DVS bucks by default. I don't want
 * to help you burn your proto board)
 *
 * So we allow describing this external connection from DT and scale the
 * voltages accordingly. This is what the connection should look like:
 *
 * |------------|
 * |	buck 8  |-------+----->Vout
 * |		|	|
 * |------------|	|
 *	| FB pin	|
 *	|		|
 *	+-------+--R2---+
 *		|
 *		R1
 *		|
 *	V FB-pull-up
 *
 *	Here the buck output is sifted according to formula:
 *
 * Vout_o = Vo - (Vpu - Vo)*R2/R1
 * Linear_step = step_orig*(R1+R2)/R1
 *
 * where:
 * Vout_o is adjusted voltage output at vsel reg value 0
 * Vo is original voltage output at vsel reg value 0
 * Vpu is the pull-up voltage V FB-pull-up in the picture
 * R1 and R2 are resistor values.
 *
 * As a real world example for buck8 and a specific GPU:
 * VLDO = 1.6V (used as FB-pull-up)
 * R1 = 1000ohms
 * R2 = 150ohms
 * VSEL 0x0 => 0.8V – (VLDO – 0.8) * R2 / R1 = 0.68V
 * Linear Step = 10mV * (R1 + R2) / R1 = 11.5mV
 */
static int setup_feedback_loop(struct device *dev, struct device_node *np,
			       struct bd718xx_regulator_data *reg_data,
			       unsigned int num_reg_data, int fb_uv)
{}

static int get_special_regulators(struct device *dev,
				  struct bd718xx_regulator_data *reg_data,
				  unsigned int num_reg_data, int *info)
{}

static int bd718xx_probe(struct platform_device *pdev)
{}

static const struct platform_device_id bd718x7_pmic_id[] =;
MODULE_DEVICE_TABLE(platform, bd718x7_pmic_id);

static struct platform_driver bd718xx_regulator =;

module_platform_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();
MODULE_ALIAS();