root/drivers/regulator/max77686-regulator.c
// SPDX-License-Identifier: GPL-2.0+
//
// max77686.c - Regulator driver for the Maxim 77686
//
// Copyright (C) 2012 Samsung Electronics
// Chiwoong Byun <woong.byun@samsung.com>
// Jonghwa Lee <jonghwa3.lee@samsung.com>
//
// This driver is based on max8997.c

#include <linux/kernel.h>
#include <linux/bug.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/mfd/max77686.h>
#include <linux/mfd/max77686-private.h>

#define MAX77686_LDO_MINUV      800000
#define MAX77686_LDO_UVSTEP     50000
#define MAX77686_LDO_LOW_MINUV  800000
#define MAX77686_LDO_LOW_UVSTEP 25000
#define MAX77686_BUCK_MINUV     750000
#define MAX77686_BUCK_UVSTEP    50000
#define MAX77686_BUCK_ENABLE_TIME       40              /* us */
#define MAX77686_DVS_ENABLE_TIME        22              /* us */
#define MAX77686_RAMP_DELAY     100000                  /* uV/us */
#define MAX77686_DVS_RAMP_DELAY 27500                   /* uV/us */
#define MAX77686_DVS_MINUV      600000
#define MAX77686_DVS_UVSTEP     12500

/*
 * Value for configuring buck[89] and LDO{20,21,22} as GPIO control.
 * It is the same as 'off' for other regulators.
 */
#define MAX77686_GPIO_CONTROL           0x0
/*
 * Values used for configuring LDOs and bucks.
 * Forcing low power mode: LDO1, 3-5, 9, 13, 17-26
 */
#define MAX77686_LDO_LOWPOWER           0x1
/*
 * On/off controlled by PWRREQ:
 *  - LDO2, 6-8, 10-12, 14-16
 *  - buck[1234]
 */
#define MAX77686_OFF_PWRREQ             0x1
/* Low power mode controlled by PWRREQ: All LDOs */
#define MAX77686_LDO_LOWPOWER_PWRREQ    0x2
/* Forcing low power mode: buck[234] */
#define MAX77686_BUCK_LOWPOWER          0x2
#define MAX77686_NORMAL                 0x3

#define MAX77686_OPMODE_SHIFT   6
#define MAX77686_OPMODE_BUCK234_SHIFT   4
#define MAX77686_OPMODE_MASK    0x3

#define MAX77686_VSEL_MASK      0x3F
#define MAX77686_DVS_VSEL_MASK  0xFF

#define MAX77686_RAMP_RATE_MASK 0xC0

#define MAX77686_REGULATORS     MAX77686_REG_MAX
#define MAX77686_LDOS           26

struct max77686_data {
        struct device *dev;
        DECLARE_BITMAP(gpio_enabled, MAX77686_REGULATORS);

        /* Array indexed by regulator id */
        unsigned int opmode[MAX77686_REGULATORS];
};

static unsigned int max77686_get_opmode_shift(int id)
{
        switch (id) {
        case MAX77686_BUCK1:
        case MAX77686_BUCK5 ... MAX77686_BUCK9:
                return 0;
        case MAX77686_BUCK2 ... MAX77686_BUCK4:
                return MAX77686_OPMODE_BUCK234_SHIFT;
        default:
                /* all LDOs */
                return MAX77686_OPMODE_SHIFT;
        }
}

/*
 * When regulator is configured for GPIO control then it
 * replaces "normal" mode. Any change from low power mode to normal
 * should actually change to GPIO control.
 * Map normal mode to proper value for such regulators.
 */
static unsigned int max77686_map_normal_mode(struct max77686_data *max77686,
                                             int id)
{
        switch (id) {
        case MAX77686_BUCK8:
        case MAX77686_BUCK9:
        case MAX77686_LDO20 ... MAX77686_LDO22:
                if (test_bit(id, max77686->gpio_enabled))
                        return MAX77686_GPIO_CONTROL;
        }

        return MAX77686_NORMAL;
}

/* Some BUCKs and LDOs supports Normal[ON/OFF] mode during suspend */
static int max77686_set_suspend_disable(struct regulator_dev *rdev)
{
        unsigned int val, shift;
        struct max77686_data *max77686 = rdev_get_drvdata(rdev);
        int ret, id = rdev_get_id(rdev);

        shift = max77686_get_opmode_shift(id);
        val = MAX77686_OFF_PWRREQ;

        ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
                                 rdev->desc->enable_mask, val << shift);
        if (ret)
                return ret;

        max77686->opmode[id] = val;
        return 0;
}

/* Some LDOs supports [LPM/Normal]ON mode during suspend state */
static int max77686_set_suspend_mode(struct regulator_dev *rdev,
                                     unsigned int mode)
{
        struct max77686_data *max77686 = rdev_get_drvdata(rdev);
        unsigned int val;
        int ret, id = rdev_get_id(rdev);

        /* BUCK[5-9] doesn't support this feature */
        if (id >= MAX77686_BUCK5)
                return 0;

        switch (mode) {
        case REGULATOR_MODE_IDLE:                       /* ON in LP Mode */
                val = MAX77686_LDO_LOWPOWER_PWRREQ;
                break;
        case REGULATOR_MODE_NORMAL:                     /* ON in Normal Mode */
                val = max77686_map_normal_mode(max77686, id);
                break;
        default:
                pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n",
                        rdev->desc->name, mode);
                return -EINVAL;
        }

        ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
                                  rdev->desc->enable_mask,
                                  val << MAX77686_OPMODE_SHIFT);
        if (ret)
                return ret;

        max77686->opmode[id] = val;
        return 0;
}

/* Some LDOs supports LPM-ON/OFF/Normal-ON mode during suspend state */
static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev,
                                     unsigned int mode)
{
        unsigned int val;
        struct max77686_data *max77686 = rdev_get_drvdata(rdev);
        int ret, id = rdev_get_id(rdev);

        switch (mode) {
        case REGULATOR_MODE_STANDBY:                    /* switch off */
                val = MAX77686_OFF_PWRREQ;
                break;
        case REGULATOR_MODE_IDLE:                       /* ON in LP Mode */
                val = MAX77686_LDO_LOWPOWER_PWRREQ;
                break;
        case REGULATOR_MODE_NORMAL:                     /* ON in Normal Mode */
                val = max77686_map_normal_mode(max77686, id);
                break;
        default:
                pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n",
                        rdev->desc->name, mode);
                return -EINVAL;
        }

        ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
                                 rdev->desc->enable_mask,
                                 val << MAX77686_OPMODE_SHIFT);
        if (ret)
                return ret;

        max77686->opmode[id] = val;
        return 0;
}

static int max77686_enable(struct regulator_dev *rdev)
{
        struct max77686_data *max77686 = rdev_get_drvdata(rdev);
        unsigned int shift;
        int id = rdev_get_id(rdev);

        shift = max77686_get_opmode_shift(id);

        if (max77686->opmode[id] == MAX77686_OFF_PWRREQ)
                max77686->opmode[id] = max77686_map_normal_mode(max77686, id);

        return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
                                  rdev->desc->enable_mask,
                                  max77686->opmode[id] << shift);
}

static int max77686_of_parse_cb(struct device_node *np,
                const struct regulator_desc *desc,
                struct regulator_config *config)
{
        struct max77686_data *max77686 = config->driver_data;
        int ret;

        switch (desc->id) {
        case MAX77686_BUCK8:
        case MAX77686_BUCK9:
        case MAX77686_LDO20 ... MAX77686_LDO22:
                config->ena_gpiod = fwnode_gpiod_get_index(
                                of_fwnode_handle(np),
                                "maxim,ena",
                                0,
                                GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE,
                                "max77686-regulator");
                if (IS_ERR(config->ena_gpiod))
                        config->ena_gpiod = NULL;
                break;
        default:
                return 0;
        }

        if (config->ena_gpiod) {
                set_bit(desc->id, max77686->gpio_enabled);

                ret = regmap_update_bits(config->regmap, desc->enable_reg,
                                         desc->enable_mask,
                                         MAX77686_GPIO_CONTROL);
                if (ret) {
                        gpiod_put(config->ena_gpiod);
                        config->ena_gpiod = NULL;
                }
        }

        return 0;
}

static const unsigned int max77686_buck_dvs_ramp_table[] = {
        13750, 27500, 55000, 100000
};

static const struct regulator_ops max77686_ops = {
        .list_voltage           = regulator_list_voltage_linear,
        .map_voltage            = regulator_map_voltage_linear,
        .is_enabled             = regulator_is_enabled_regmap,
        .enable                 = max77686_enable,
        .disable                = regulator_disable_regmap,
        .get_voltage_sel        = regulator_get_voltage_sel_regmap,
        .set_voltage_sel        = regulator_set_voltage_sel_regmap,
        .set_voltage_time_sel   = regulator_set_voltage_time_sel,
        .set_suspend_mode       = max77686_set_suspend_mode,
};

static const struct regulator_ops max77686_ldo_ops = {
        .list_voltage           = regulator_list_voltage_linear,
        .map_voltage            = regulator_map_voltage_linear,
        .is_enabled             = regulator_is_enabled_regmap,
        .enable                 = max77686_enable,
        .disable                = regulator_disable_regmap,
        .get_voltage_sel        = regulator_get_voltage_sel_regmap,
        .set_voltage_sel        = regulator_set_voltage_sel_regmap,
        .set_voltage_time_sel   = regulator_set_voltage_time_sel,
        .set_suspend_mode       = max77686_ldo_set_suspend_mode,
        .set_suspend_disable    = max77686_set_suspend_disable,
};

static const struct regulator_ops max77686_buck1_ops = {
        .list_voltage           = regulator_list_voltage_linear,
        .map_voltage            = regulator_map_voltage_linear,
        .is_enabled             = regulator_is_enabled_regmap,
        .enable                 = max77686_enable,
        .disable                = regulator_disable_regmap,
        .get_voltage_sel        = regulator_get_voltage_sel_regmap,
        .set_voltage_sel        = regulator_set_voltage_sel_regmap,
        .set_voltage_time_sel   = regulator_set_voltage_time_sel,
        .set_suspend_disable    = max77686_set_suspend_disable,
};

static const struct regulator_ops max77686_buck_dvs_ops = {
        .list_voltage           = regulator_list_voltage_linear,
        .map_voltage            = regulator_map_voltage_linear,
        .is_enabled             = regulator_is_enabled_regmap,
        .enable                 = max77686_enable,
        .disable                = regulator_disable_regmap,
        .get_voltage_sel        = regulator_get_voltage_sel_regmap,
        .set_voltage_sel        = regulator_set_voltage_sel_regmap,
        .set_voltage_time_sel   = regulator_set_voltage_time_sel,
        .set_ramp_delay         = regulator_set_ramp_delay_regmap,
        .set_suspend_disable    = max77686_set_suspend_disable,
};

#define regulator_desc_ldo(num)         {                               \
        .name           = "LDO"#num,                                    \
        .of_match       = of_match_ptr("LDO"#num),                      \
        .regulators_node        = of_match_ptr("voltage-regulators"),   \
        .of_parse_cb    = max77686_of_parse_cb,                         \
        .id             = MAX77686_LDO##num,                            \
        .ops            = &max77686_ops,                                \
        .type           = REGULATOR_VOLTAGE,                            \
        .owner          = THIS_MODULE,                                  \
        .min_uV         = MAX77686_LDO_MINUV,                           \
        .uV_step        = MAX77686_LDO_UVSTEP,                          \
        .ramp_delay     = MAX77686_RAMP_DELAY,                          \
        .n_voltages     = MAX77686_VSEL_MASK + 1,                       \
        .vsel_reg       = MAX77686_REG_LDO1CTRL1 + num - 1,             \
        .vsel_mask      = MAX77686_VSEL_MASK,                           \
        .enable_reg     = MAX77686_REG_LDO1CTRL1 + num - 1,             \
        .enable_mask    = MAX77686_OPMODE_MASK                          \
                        << MAX77686_OPMODE_SHIFT,                       \
}
#define regulator_desc_lpm_ldo(num)     {                               \
        .name           = "LDO"#num,                                    \
        .of_match       = of_match_ptr("LDO"#num),                      \
        .regulators_node        = of_match_ptr("voltage-regulators"),   \
        .id             = MAX77686_LDO##num,                            \
        .ops            = &max77686_ldo_ops,                            \
        .type           = REGULATOR_VOLTAGE,                            \
        .owner          = THIS_MODULE,                                  \
        .min_uV         = MAX77686_LDO_MINUV,                           \
        .uV_step        = MAX77686_LDO_UVSTEP,                          \
        .ramp_delay     = MAX77686_RAMP_DELAY,                          \
        .n_voltages     = MAX77686_VSEL_MASK + 1,                       \
        .vsel_reg       = MAX77686_REG_LDO1CTRL1 + num - 1,             \
        .vsel_mask      = MAX77686_VSEL_MASK,                           \
        .enable_reg     = MAX77686_REG_LDO1CTRL1 + num - 1,             \
        .enable_mask    = MAX77686_OPMODE_MASK                          \
                        << MAX77686_OPMODE_SHIFT,                       \
}
#define regulator_desc_ldo_low(num)             {                       \
        .name           = "LDO"#num,                                    \
        .of_match       = of_match_ptr("LDO"#num),                      \
        .regulators_node        = of_match_ptr("voltage-regulators"),   \
        .id             = MAX77686_LDO##num,                            \
        .ops            = &max77686_ldo_ops,                            \
        .type           = REGULATOR_VOLTAGE,                            \
        .owner          = THIS_MODULE,                                  \
        .min_uV         = MAX77686_LDO_LOW_MINUV,                       \
        .uV_step        = MAX77686_LDO_LOW_UVSTEP,                      \
        .ramp_delay     = MAX77686_RAMP_DELAY,                          \
        .n_voltages     = MAX77686_VSEL_MASK + 1,                       \
        .vsel_reg       = MAX77686_REG_LDO1CTRL1 + num - 1,             \
        .vsel_mask      = MAX77686_VSEL_MASK,                           \
        .enable_reg     = MAX77686_REG_LDO1CTRL1 + num - 1,             \
        .enable_mask    = MAX77686_OPMODE_MASK                          \
                        << MAX77686_OPMODE_SHIFT,                       \
}
#define regulator_desc_ldo1_low(num)            {                       \
        .name           = "LDO"#num,                                    \
        .of_match       = of_match_ptr("LDO"#num),                      \
        .regulators_node        = of_match_ptr("voltage-regulators"),   \
        .id             = MAX77686_LDO##num,                            \
        .ops            = &max77686_ops,                                \
        .type           = REGULATOR_VOLTAGE,                            \
        .owner          = THIS_MODULE,                                  \
        .min_uV         = MAX77686_LDO_LOW_MINUV,                       \
        .uV_step        = MAX77686_LDO_LOW_UVSTEP,                      \
        .ramp_delay     = MAX77686_RAMP_DELAY,                          \
        .n_voltages     = MAX77686_VSEL_MASK + 1,                       \
        .vsel_reg       = MAX77686_REG_LDO1CTRL1 + num - 1,             \
        .vsel_mask      = MAX77686_VSEL_MASK,                           \
        .enable_reg     = MAX77686_REG_LDO1CTRL1 + num - 1,             \
        .enable_mask    = MAX77686_OPMODE_MASK                          \
                        << MAX77686_OPMODE_SHIFT,                       \
}
#define regulator_desc_buck(num)                {                       \
        .name           = "BUCK"#num,                                   \
        .of_match       = of_match_ptr("BUCK"#num),                     \
        .regulators_node        = of_match_ptr("voltage-regulators"),   \
        .of_parse_cb    = max77686_of_parse_cb,                         \
        .id             = MAX77686_BUCK##num,                           \
        .ops            = &max77686_ops,                                \
        .type           = REGULATOR_VOLTAGE,                            \
        .owner          = THIS_MODULE,                                  \
        .min_uV         = MAX77686_BUCK_MINUV,                          \
        .uV_step        = MAX77686_BUCK_UVSTEP,                         \
        .ramp_delay     = MAX77686_RAMP_DELAY,                          \
        .enable_time    = MAX77686_BUCK_ENABLE_TIME,                    \
        .n_voltages     = MAX77686_VSEL_MASK + 1,                       \
        .vsel_reg       = MAX77686_REG_BUCK5OUT + (num - 5) * 2,        \
        .vsel_mask      = MAX77686_VSEL_MASK,                           \
        .enable_reg     = MAX77686_REG_BUCK5CTRL + (num - 5) * 2,       \
        .enable_mask    = MAX77686_OPMODE_MASK,                         \
}
#define regulator_desc_buck1(num)               {                       \
        .name           = "BUCK"#num,                                   \
        .of_match       = of_match_ptr("BUCK"#num),                     \
        .regulators_node        = of_match_ptr("voltage-regulators"),   \
        .id             = MAX77686_BUCK##num,                           \
        .ops            = &max77686_buck1_ops,                          \
        .type           = REGULATOR_VOLTAGE,                            \
        .owner          = THIS_MODULE,                                  \
        .min_uV         = MAX77686_BUCK_MINUV,                          \
        .uV_step        = MAX77686_BUCK_UVSTEP,                         \
        .ramp_delay     = MAX77686_RAMP_DELAY,                          \
        .enable_time    = MAX77686_BUCK_ENABLE_TIME,                    \
        .n_voltages     = MAX77686_VSEL_MASK + 1,                       \
        .vsel_reg       = MAX77686_REG_BUCK1OUT,                        \
        .vsel_mask      = MAX77686_VSEL_MASK,                           \
        .enable_reg     = MAX77686_REG_BUCK1CTRL,                       \
        .enable_mask    = MAX77686_OPMODE_MASK,                         \
}
#define regulator_desc_buck_dvs(num)            {                       \
        .name           = "BUCK"#num,                                   \
        .of_match       = of_match_ptr("BUCK"#num),                     \
        .regulators_node        = of_match_ptr("voltage-regulators"),   \
        .id             = MAX77686_BUCK##num,                           \
        .ops            = &max77686_buck_dvs_ops,                       \
        .type           = REGULATOR_VOLTAGE,                            \
        .owner          = THIS_MODULE,                                  \
        .min_uV         = MAX77686_DVS_MINUV,                           \
        .uV_step        = MAX77686_DVS_UVSTEP,                          \
        .ramp_delay     = MAX77686_DVS_RAMP_DELAY,                      \
        .enable_time    = MAX77686_DVS_ENABLE_TIME,                     \
        .n_voltages     = MAX77686_DVS_VSEL_MASK + 1,                   \
        .vsel_reg       = MAX77686_REG_BUCK2DVS1 + (num - 2) * 10,      \
        .vsel_mask      = MAX77686_DVS_VSEL_MASK,                       \
        .enable_reg     = MAX77686_REG_BUCK2CTRL1 + (num - 2) * 10,     \
        .enable_mask    = MAX77686_OPMODE_MASK                          \
                        << MAX77686_OPMODE_BUCK234_SHIFT,               \
        .ramp_reg       = MAX77686_REG_BUCK2CTRL1 + (num - 2) * 10,     \
        .ramp_mask      = MAX77686_RAMP_RATE_MASK,                      \
        .ramp_delay_table = max77686_buck_dvs_ramp_table,               \
        .n_ramp_values  = ARRAY_SIZE(max77686_buck_dvs_ramp_table),     \
}

static const struct regulator_desc regulators[] = {
        regulator_desc_ldo1_low(1),
        regulator_desc_ldo_low(2),
        regulator_desc_ldo(3),
        regulator_desc_ldo(4),
        regulator_desc_ldo(5),
        regulator_desc_ldo_low(6),
        regulator_desc_ldo_low(7),
        regulator_desc_ldo_low(8),
        regulator_desc_ldo(9),
        regulator_desc_lpm_ldo(10),
        regulator_desc_lpm_ldo(11),
        regulator_desc_lpm_ldo(12),
        regulator_desc_ldo(13),
        regulator_desc_lpm_ldo(14),
        regulator_desc_ldo_low(15),
        regulator_desc_lpm_ldo(16),
        regulator_desc_ldo(17),
        regulator_desc_ldo(18),
        regulator_desc_ldo(19),
        regulator_desc_ldo(20),
        regulator_desc_ldo(21),
        regulator_desc_ldo(22),
        regulator_desc_ldo(23),
        regulator_desc_ldo(24),
        regulator_desc_ldo(25),
        regulator_desc_ldo(26),
        regulator_desc_buck1(1),
        regulator_desc_buck_dvs(2),
        regulator_desc_buck_dvs(3),
        regulator_desc_buck_dvs(4),
        regulator_desc_buck(5),
        regulator_desc_buck(6),
        regulator_desc_buck(7),
        regulator_desc_buck(8),
        regulator_desc_buck(9),
};

static int max77686_pmic_probe(struct platform_device *pdev)
{
        struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
        struct max77686_data *max77686;
        int i;
        struct regulator_config config = { };

        dev_dbg(&pdev->dev, "%s\n", __func__);

        max77686 = devm_kzalloc(&pdev->dev, sizeof(struct max77686_data),
                                GFP_KERNEL);
        if (!max77686)
                return -ENOMEM;

        max77686->dev = &pdev->dev;
        config.dev = iodev->dev;
        config.regmap = iodev->regmap;
        config.driver_data = max77686;
        platform_set_drvdata(pdev, max77686);

        for (i = 0; i < MAX77686_REGULATORS; i++) {
                struct regulator_dev *rdev;
                int id = regulators[i].id;

                max77686->opmode[id] = MAX77686_NORMAL;
                rdev = devm_regulator_register(&pdev->dev,
                                                &regulators[i], &config);
                if (IS_ERR(rdev)) {
                        int ret = PTR_ERR(rdev);
                        dev_err(&pdev->dev,
                                "regulator init failed for %d: %d\n", i, ret);
                        return ret;
                }
        }

        return 0;
}

static const struct platform_device_id max77686_pmic_id[] = {
        {"max77686-pmic", 0},
        { },
};
MODULE_DEVICE_TABLE(platform, max77686_pmic_id);

static struct platform_driver max77686_pmic_driver = {
        .driver = {
                .name = "max77686-pmic",
                .probe_type = PROBE_PREFER_ASYNCHRONOUS,
        },
        .probe = max77686_pmic_probe,
        .id_table = max77686_pmic_id,
};

module_platform_driver(max77686_pmic_driver);

MODULE_DESCRIPTION("MAXIM 77686 Regulator Driver");
MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
MODULE_LICENSE("GPL");