linux/drivers/regulator/bd96801-regulator.c

// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2024 ROHM Semiconductors
// bd96801-regulator.c ROHM BD96801 regulator driver

/*
 * This version of the "BD86801 scalable PMIC"'s driver supports only very
 * basic set of the PMIC features. Most notably, there is no support for
 * the ERRB interrupt and the configurations which should be done when the
 * PMIC is in STBY mode.
 *
 * Supporting the ERRB interrupt would require dropping the regmap-IRQ
 * usage or working around (or accepting a presense of) a naming conflict
 * in debugFS IRQs.
 *
 * Being able to reliably do the configurations like changing the
 * regulator safety limits (like limits for the over/under -voltages, over
 * current, thermal protection) would require the configuring driver to be
 * synchronized with entity causing the PMIC state transitions. Eg, one
 * should be able to ensure the PMIC is in STBY state when the
 * configurations are applied to the hardware. How and when the PMIC state
 * transitions are to be done is likely to be very system specific, as will
 * be the need to configure these safety limits. Hence it's not simple to
 * come up with a generic solution.
 *
 * Users who require the ERRB handling and STBY state configurations can
 * have a look at the original RFC:
 * https://lore.kernel.org/all/[email protected]/
 * which implements a workaround to debugFS naming conflict and some of
 * the safety limit configurations - but leaves the state change handling
 * and synchronization to be implemented.
 *
 * It would be great to hear (and receive a patch!) if you implement the
 * STBY configuration support or a proper fix to the debugFS naming
 * conflict in your downstream driver ;)
 */

#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/linear_range.h>
#include <linux/mfd/rohm-generic.h>
#include <linux/mfd/rohm-bd96801.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/coupler.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/slab.h>
#include <linux/timer.h>

enum {};

enum {};

#define BD96801_ALWAYS_ON_REG
#define BD96801_REG_ENABLE
#define BD96801_BUCK1_EN_MASK
#define BD96801_BUCK2_EN_MASK
#define BD96801_BUCK3_EN_MASK
#define BD96801_BUCK4_EN_MASK
#define BD96801_LDO5_EN_MASK
#define BD96801_LDO6_EN_MASK
#define BD96801_LDO7_EN_MASK

#define BD96801_BUCK1_VSEL_REG
#define BD96801_BUCK2_VSEL_REG
#define BD96801_BUCK3_VSEL_REG
#define BD96801_BUCK4_VSEL_REG
#define BD96801_LDO5_VSEL_REG
#define BD96801_LDO6_VSEL_REG
#define BD96801_LDO7_VSEL_REG
#define BD96801_BUCK_VSEL_MASK
#define BD96801_LDO_VSEL_MASK

#define BD96801_MASK_RAMP_DELAY
#define BD96801_INT_VOUT_BASE_REG
#define BD96801_BUCK_INT_VOUT_MASK

#define BD96801_BUCK_VOLTS
#define BD96801_LDO_VOLTS

#define BD96801_OVP_MASK
#define BD96801_MASK_BUCK1_OVP_SHIFT
#define BD96801_MASK_BUCK2_OVP_SHIFT
#define BD96801_MASK_BUCK3_OVP_SHIFT
#define BD96801_MASK_BUCK4_OVP_SHIFT
#define BD96801_MASK_LDO5_OVP_SHIFT
#define BD96801_MASK_LDO6_OVP_SHIFT
#define BD96801_MASK_LDO7_OVP_SHIFT

#define BD96801_PROT_LIMIT_OCP_MIN
#define BD96801_PROT_LIMIT_LOW
#define BD96801_PROT_LIMIT_MID
#define BD96801_PROT_LIMIT_HI

#define BD96801_REG_BUCK1_OCP
#define BD96801_REG_BUCK2_OCP
#define BD96801_REG_BUCK3_OCP
#define BD96801_REG_BUCK4_OCP

#define BD96801_MASK_BUCK1_OCP_SHIFT
#define BD96801_MASK_BUCK2_OCP_SHIFT
#define BD96801_MASK_BUCK3_OCP_SHIFT
#define BD96801_MASK_BUCK4_OCP_SHIFT

#define BD96801_REG_LDO5_OCP
#define BD96801_REG_LDO6_OCP
#define BD96801_REG_LDO7_OCP

#define BD96801_MASK_LDO5_OCP_SHIFT
#define BD96801_MASK_LDO6_OCP_SHIFT
#define BD96801_MASK_LDO7_OCP_SHIFT

#define BD96801_MASK_SHD_INTB
#define BD96801_INTB_FATAL

#define BD96801_NUM_REGULATORS
#define BD96801_NUM_LDOS

/*
 * Ramp rates for bucks are controlled by bits [7:6] as follows:
 * 00 => 1 mV/uS
 * 01 => 5 mV/uS
 * 10 => 10 mV/uS
 * 11 => 20 mV/uS
 */
static const unsigned int buck_ramp_table[] =;

/*
 * This is a voltage range that get's appended to selected
 * bd96801_buck_init_volts value. The range from 0x0 to 0xF is actually
 * bd96801_buck_init_volts + 0 ... bd96801_buck_init_volts + 150mV
 * and the range from 0x10 to 0x1f is bd96801_buck_init_volts - 150mV ...
 * bd96801_buck_init_volts - 0. But as the members of linear_range
 * are all unsigned I will apply offset of -150 mV to value in
 * linear_range - which should increase these ranges with
 * 150 mV getting all the values to >= 0.
 */
static const struct linear_range bd96801_tune_volts[] =;

static const struct linear_range bd96801_buck_init_volts[] =;

static const struct linear_range bd96801_ldo_int_volts[] =;

#define BD96801_LDO_SD_VOLT_MASK
#define BD96801_LDO_MODE_MASK
#define BD96801_LDO_MODE_INT
#define BD96801_LDO_MODE_SD
#define BD96801_LDO_MODE_DDR

static int ldo_ddr_volt_table[] =;
static int ldo_sd_volt_table[] =;

/* Constant IRQ initialization data (templates) */
struct bd96801_irqinfo {};

#define BD96801_IRQINFO(_type, _name, _irqoff_ms, _irqname)

static const struct bd96801_irqinfo buck1_irqinfo[] =;

static const struct bd96801_irqinfo buck2_irqinfo[] =;

static const struct bd96801_irqinfo buck3_irqinfo[] =;

static const struct bd96801_irqinfo buck4_irqinfo[] =;

static const struct bd96801_irqinfo ldo5_irqinfo[] =;

static const struct bd96801_irqinfo ldo6_irqinfo[] =;

static const struct bd96801_irqinfo ldo7_irqinfo[] =;

struct bd96801_irq_desc {};

struct bd96801_regulator_data {};

struct bd96801_pmic_data {};

static int ldo_map_notif(int irq, struct regulator_irq_data *rid,
			 unsigned long *dev_mask)
{}

static int bd96801_list_voltage_lr(struct regulator_dev *rdev,
				   unsigned int selector)
{}


static const struct regulator_ops bd96801_ldo_table_ops =;

static const struct regulator_ops bd96801_buck_ops =;

static const struct regulator_ops bd96801_ldo_ops =;

static int buck_get_initial_voltage(struct regmap *regmap, struct device *dev,
				    struct bd96801_regulator_data *data)
{}

static int get_ldo_initial_voltage(struct regmap *regmap,
				   struct device *dev,
				   struct bd96801_regulator_data *data)
{}

static int get_initial_voltage(struct device *dev, struct regmap *regmap,
			struct bd96801_regulator_data *data)
{}

static int bd96801_walk_regulator_dt(struct device *dev, struct regmap *regmap,
				     struct bd96801_regulator_data *data,
				     int num)
{}

/*
 * Template for regulator data. Probe will allocate dynamic / driver instance
 * struct so we should be on a safe side even if there were multiple PMICs to
 * control. Note that there is a plan to allow multiple PMICs to be used so
 * systems can scale better. I am however still slightly unsure how the
 * multi-PMIC case will be handled. I don't know if the processor will have I2C
 * acces to all of the PMICs or only the first one. I'd guess there will be
 * access provided to all PMICs for voltage scaling - but the errors will only
 * be informed via the master PMIC. Eg, we should prepare to support multiple
 * driver instances - either with or without the IRQs... Well, let's first
 * just support the simple and clear single-PMIC setup and ponder the multi PMIC
 * case later. What we can easly do for preparing is to not use static global
 * data for regulators though.
 */
static const struct bd96801_pmic_data bd96801_data =;

static int initialize_pmic_data(struct device *dev,
				struct bd96801_pmic_data *pdata)
{}

static int bd96801_rdev_intb_irqs(struct platform_device *pdev,
				  struct bd96801_pmic_data *pdata,
				  struct bd96801_irqinfo *iinfo,
				  struct regulator_dev *rdev)
{}



static int bd96801_probe(struct platform_device *pdev)
{}

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

static struct platform_driver bd96801_regulator =;

module_platform_driver();

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