mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +02:00
The driver reuses the OF node of the parent multi-function device but
fails to take another reference to balance the one dropped by the
platform bus code when unbinding the MFD and deregistering the child
devices.
Fix this by using the intended helper for reusing OF nodes.
Fixes: bcc61f1c44 ("regulator: max77650: add regulator support")
Cc: stable@vger.kernel.org # 5.1
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Johan Hovold <johan@kernel.org>
Link: https://patch.msgid.link/20260408073055.5183-4-johan@kernel.org
Signed-off-by: Mark Brown <broonie@kernel.org>
406 lines
13 KiB
C
406 lines
13 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
//
|
|
// Copyright (C) 2018 BayLibre SAS
|
|
// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
|
|
//
|
|
// Regulator driver for MAXIM 77650/77651 charger/power-supply.
|
|
|
|
#include <linux/of.h>
|
|
#include <linux/mfd/max77650.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/regulator/driver.h>
|
|
|
|
#define MAX77650_REGULATOR_EN_CTRL_MASK GENMASK(3, 0)
|
|
#define MAX77650_REGULATOR_EN_CTRL_BITS(_reg) \
|
|
((_reg) & MAX77650_REGULATOR_EN_CTRL_MASK)
|
|
#define MAX77650_REGULATOR_ENABLED GENMASK(2, 1)
|
|
#define MAX77650_REGULATOR_DISABLED BIT(2)
|
|
|
|
#define MAX77650_REGULATOR_V_LDO_MASK GENMASK(6, 0)
|
|
#define MAX77650_REGULATOR_V_SBB_MASK GENMASK(5, 0)
|
|
#define MAX77651_REGULATOR_V_SBB1_MASK GENMASK(5, 2)
|
|
#define MAX77651_REGULATOR_V_SBB1_RANGE_MASK GENMASK(1, 0)
|
|
|
|
#define MAX77650_REGULATOR_AD_MASK BIT(3)
|
|
#define MAX77650_REGULATOR_AD_DISABLED 0x00
|
|
#define MAX77650_REGULATOR_AD_ENABLED BIT(3)
|
|
|
|
#define MAX77650_REGULATOR_CURR_LIM_MASK GENMASK(7, 6)
|
|
|
|
enum {
|
|
MAX77650_REGULATOR_ID_LDO = 0,
|
|
MAX77650_REGULATOR_ID_SBB0,
|
|
MAX77650_REGULATOR_ID_SBB1,
|
|
MAX77650_REGULATOR_ID_SBB2,
|
|
MAX77650_REGULATOR_NUM_REGULATORS,
|
|
};
|
|
|
|
struct max77650_regulator_desc {
|
|
struct regulator_desc desc;
|
|
unsigned int regA;
|
|
unsigned int regB;
|
|
};
|
|
|
|
static const unsigned int max77651_sbb1_volt_range_sel[] = {
|
|
0x0, 0x1, 0x2, 0x3
|
|
};
|
|
|
|
static const struct linear_range max77651_sbb1_volt_ranges[] = {
|
|
/* range index 0 */
|
|
REGULATOR_LINEAR_RANGE(2400000, 0x00, 0x0f, 50000),
|
|
/* range index 1 */
|
|
REGULATOR_LINEAR_RANGE(3200000, 0x00, 0x0f, 50000),
|
|
/* range index 2 */
|
|
REGULATOR_LINEAR_RANGE(4000000, 0x00, 0x0f, 50000),
|
|
/* range index 3 */
|
|
REGULATOR_LINEAR_RANGE(4800000, 0x00, 0x09, 50000),
|
|
};
|
|
|
|
static const unsigned int max77650_current_limit_table[] = {
|
|
1000000, 866000, 707000, 500000,
|
|
};
|
|
|
|
static int max77650_regulator_is_enabled(struct regulator_dev *rdev)
|
|
{
|
|
const struct max77650_regulator_desc *rdesc;
|
|
struct regmap *map;
|
|
int val, rv, en;
|
|
|
|
rdesc = container_of_const(rdev->desc, struct max77650_regulator_desc, desc);
|
|
map = rdev_get_regmap(rdev);
|
|
|
|
rv = regmap_read(map, rdesc->regB, &val);
|
|
if (rv)
|
|
return rv;
|
|
|
|
en = MAX77650_REGULATOR_EN_CTRL_BITS(val);
|
|
|
|
return en != MAX77650_REGULATOR_DISABLED;
|
|
}
|
|
|
|
static int max77650_regulator_enable(struct regulator_dev *rdev)
|
|
{
|
|
const struct max77650_regulator_desc *rdesc;
|
|
struct regmap *map;
|
|
|
|
rdesc = container_of_const(rdev->desc, struct max77650_regulator_desc, desc);
|
|
map = rdev_get_regmap(rdev);
|
|
|
|
return regmap_update_bits(map, rdesc->regB,
|
|
MAX77650_REGULATOR_EN_CTRL_MASK,
|
|
MAX77650_REGULATOR_ENABLED);
|
|
}
|
|
|
|
static int max77650_regulator_disable(struct regulator_dev *rdev)
|
|
{
|
|
const struct max77650_regulator_desc *rdesc;
|
|
struct regmap *map;
|
|
|
|
rdesc = container_of_const(rdev->desc, struct max77650_regulator_desc, desc);
|
|
map = rdev_get_regmap(rdev);
|
|
|
|
return regmap_update_bits(map, rdesc->regB,
|
|
MAX77650_REGULATOR_EN_CTRL_MASK,
|
|
MAX77650_REGULATOR_DISABLED);
|
|
}
|
|
|
|
static const struct regulator_ops max77650_regulator_LDO_ops = {
|
|
.is_enabled = max77650_regulator_is_enabled,
|
|
.enable = max77650_regulator_enable,
|
|
.disable = max77650_regulator_disable,
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
.map_voltage = regulator_map_voltage_linear,
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
.set_active_discharge = regulator_set_active_discharge_regmap,
|
|
};
|
|
|
|
static const struct regulator_ops max77650_regulator_SBB_ops = {
|
|
.is_enabled = max77650_regulator_is_enabled,
|
|
.enable = max77650_regulator_enable,
|
|
.disable = max77650_regulator_disable,
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
.map_voltage = regulator_map_voltage_linear,
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
.get_current_limit = regulator_get_current_limit_regmap,
|
|
.set_current_limit = regulator_set_current_limit_regmap,
|
|
.set_active_discharge = regulator_set_active_discharge_regmap,
|
|
};
|
|
|
|
/* Special case for max77651 SBB1 - pickable linear-range voltage mapping. */
|
|
static const struct regulator_ops max77651_SBB1_regulator_ops = {
|
|
.is_enabled = max77650_regulator_is_enabled,
|
|
.enable = max77650_regulator_enable,
|
|
.disable = max77650_regulator_disable,
|
|
.list_voltage = regulator_list_voltage_pickable_linear_range,
|
|
.get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_pickable_regmap,
|
|
.get_current_limit = regulator_get_current_limit_regmap,
|
|
.set_current_limit = regulator_set_current_limit_regmap,
|
|
.set_active_discharge = regulator_set_active_discharge_regmap,
|
|
};
|
|
|
|
static const struct max77650_regulator_desc max77650_LDO_desc = {
|
|
.desc = {
|
|
.name = "ldo",
|
|
.of_match = of_match_ptr("ldo"),
|
|
.regulators_node = of_match_ptr("regulators"),
|
|
.supply_name = "in-ldo",
|
|
.id = MAX77650_REGULATOR_ID_LDO,
|
|
.ops = &max77650_regulator_LDO_ops,
|
|
.min_uV = 1350000,
|
|
.uV_step = 12500,
|
|
.n_voltages = 128,
|
|
.vsel_step = 1,
|
|
.vsel_mask = MAX77650_REGULATOR_V_LDO_MASK,
|
|
.vsel_reg = MAX77650_REG_CNFG_LDO_A,
|
|
.active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
|
|
.active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
|
|
.active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
|
|
.active_discharge_reg = MAX77650_REG_CNFG_LDO_B,
|
|
.enable_time = 100,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.regA = MAX77650_REG_CNFG_LDO_A,
|
|
.regB = MAX77650_REG_CNFG_LDO_B,
|
|
};
|
|
|
|
static const struct max77650_regulator_desc max77650_SBB0_desc = {
|
|
.desc = {
|
|
.name = "sbb0",
|
|
.of_match = of_match_ptr("sbb0"),
|
|
.regulators_node = of_match_ptr("regulators"),
|
|
.supply_name = "in-sbb0",
|
|
.id = MAX77650_REGULATOR_ID_SBB0,
|
|
.ops = &max77650_regulator_SBB_ops,
|
|
.min_uV = 800000,
|
|
.uV_step = 25000,
|
|
.n_voltages = 64,
|
|
.vsel_step = 1,
|
|
.vsel_mask = MAX77650_REGULATOR_V_SBB_MASK,
|
|
.vsel_reg = MAX77650_REG_CNFG_SBB0_A,
|
|
.active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
|
|
.active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
|
|
.active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
|
|
.active_discharge_reg = MAX77650_REG_CNFG_SBB0_B,
|
|
.enable_time = 100,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.owner = THIS_MODULE,
|
|
.csel_reg = MAX77650_REG_CNFG_SBB0_A,
|
|
.csel_mask = MAX77650_REGULATOR_CURR_LIM_MASK,
|
|
.curr_table = max77650_current_limit_table,
|
|
.n_current_limits = ARRAY_SIZE(max77650_current_limit_table),
|
|
},
|
|
.regA = MAX77650_REG_CNFG_SBB0_A,
|
|
.regB = MAX77650_REG_CNFG_SBB0_B,
|
|
};
|
|
|
|
static const struct max77650_regulator_desc max77650_SBB1_desc = {
|
|
.desc = {
|
|
.name = "sbb1",
|
|
.of_match = of_match_ptr("sbb1"),
|
|
.regulators_node = of_match_ptr("regulators"),
|
|
.supply_name = "in-sbb1",
|
|
.id = MAX77650_REGULATOR_ID_SBB1,
|
|
.ops = &max77650_regulator_SBB_ops,
|
|
.min_uV = 800000,
|
|
.uV_step = 12500,
|
|
.n_voltages = 64,
|
|
.vsel_step = 1,
|
|
.vsel_mask = MAX77650_REGULATOR_V_SBB_MASK,
|
|
.vsel_reg = MAX77650_REG_CNFG_SBB1_A,
|
|
.active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
|
|
.active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
|
|
.active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
|
|
.active_discharge_reg = MAX77650_REG_CNFG_SBB1_B,
|
|
.enable_time = 100,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.owner = THIS_MODULE,
|
|
.csel_reg = MAX77650_REG_CNFG_SBB1_A,
|
|
.csel_mask = MAX77650_REGULATOR_CURR_LIM_MASK,
|
|
.curr_table = max77650_current_limit_table,
|
|
.n_current_limits = ARRAY_SIZE(max77650_current_limit_table),
|
|
},
|
|
.regA = MAX77650_REG_CNFG_SBB1_A,
|
|
.regB = MAX77650_REG_CNFG_SBB1_B,
|
|
};
|
|
|
|
static const struct max77650_regulator_desc max77651_SBB1_desc = {
|
|
.desc = {
|
|
.name = "sbb1",
|
|
.of_match = of_match_ptr("sbb1"),
|
|
.regulators_node = of_match_ptr("regulators"),
|
|
.supply_name = "in-sbb1",
|
|
.id = MAX77650_REGULATOR_ID_SBB1,
|
|
.ops = &max77651_SBB1_regulator_ops,
|
|
.linear_range_selectors_bitfield = max77651_sbb1_volt_range_sel,
|
|
.linear_ranges = max77651_sbb1_volt_ranges,
|
|
.n_linear_ranges = ARRAY_SIZE(max77651_sbb1_volt_ranges),
|
|
.n_voltages = 58,
|
|
.vsel_step = 1,
|
|
.vsel_range_mask = MAX77651_REGULATOR_V_SBB1_RANGE_MASK,
|
|
.vsel_range_reg = MAX77650_REG_CNFG_SBB1_A,
|
|
.vsel_mask = MAX77651_REGULATOR_V_SBB1_MASK,
|
|
.vsel_reg = MAX77650_REG_CNFG_SBB1_A,
|
|
.active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
|
|
.active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
|
|
.active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
|
|
.active_discharge_reg = MAX77650_REG_CNFG_SBB1_B,
|
|
.enable_time = 100,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.owner = THIS_MODULE,
|
|
.csel_reg = MAX77650_REG_CNFG_SBB1_A,
|
|
.csel_mask = MAX77650_REGULATOR_CURR_LIM_MASK,
|
|
.curr_table = max77650_current_limit_table,
|
|
.n_current_limits = ARRAY_SIZE(max77650_current_limit_table),
|
|
},
|
|
.regA = MAX77650_REG_CNFG_SBB1_A,
|
|
.regB = MAX77650_REG_CNFG_SBB1_B,
|
|
};
|
|
|
|
static const struct max77650_regulator_desc max77650_SBB2_desc = {
|
|
.desc = {
|
|
.name = "sbb2",
|
|
.of_match = of_match_ptr("sbb2"),
|
|
.regulators_node = of_match_ptr("regulators"),
|
|
.supply_name = "in-sbb0",
|
|
.id = MAX77650_REGULATOR_ID_SBB2,
|
|
.ops = &max77650_regulator_SBB_ops,
|
|
.min_uV = 800000,
|
|
.uV_step = 50000,
|
|
.n_voltages = 64,
|
|
.vsel_step = 1,
|
|
.vsel_mask = MAX77650_REGULATOR_V_SBB_MASK,
|
|
.vsel_reg = MAX77650_REG_CNFG_SBB2_A,
|
|
.active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
|
|
.active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
|
|
.active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
|
|
.active_discharge_reg = MAX77650_REG_CNFG_SBB2_B,
|
|
.enable_time = 100,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.owner = THIS_MODULE,
|
|
.csel_reg = MAX77650_REG_CNFG_SBB2_A,
|
|
.csel_mask = MAX77650_REGULATOR_CURR_LIM_MASK,
|
|
.curr_table = max77650_current_limit_table,
|
|
.n_current_limits = ARRAY_SIZE(max77650_current_limit_table),
|
|
},
|
|
.regA = MAX77650_REG_CNFG_SBB2_A,
|
|
.regB = MAX77650_REG_CNFG_SBB2_B,
|
|
};
|
|
|
|
static const struct max77650_regulator_desc max77651_SBB2_desc = {
|
|
.desc = {
|
|
.name = "sbb2",
|
|
.of_match = of_match_ptr("sbb2"),
|
|
.regulators_node = of_match_ptr("regulators"),
|
|
.supply_name = "in-sbb0",
|
|
.id = MAX77650_REGULATOR_ID_SBB2,
|
|
.ops = &max77650_regulator_SBB_ops,
|
|
.min_uV = 2400000,
|
|
.uV_step = 50000,
|
|
.n_voltages = 64,
|
|
.vsel_step = 1,
|
|
.vsel_mask = MAX77650_REGULATOR_V_SBB_MASK,
|
|
.vsel_reg = MAX77650_REG_CNFG_SBB2_A,
|
|
.active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
|
|
.active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
|
|
.active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
|
|
.active_discharge_reg = MAX77650_REG_CNFG_SBB2_B,
|
|
.enable_time = 100,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.owner = THIS_MODULE,
|
|
.csel_reg = MAX77650_REG_CNFG_SBB2_A,
|
|
.csel_mask = MAX77650_REGULATOR_CURR_LIM_MASK,
|
|
.curr_table = max77650_current_limit_table,
|
|
.n_current_limits = ARRAY_SIZE(max77650_current_limit_table),
|
|
},
|
|
.regA = MAX77650_REG_CNFG_SBB2_A,
|
|
.regB = MAX77650_REG_CNFG_SBB2_B,
|
|
};
|
|
|
|
static int max77650_regulator_probe(struct platform_device *pdev)
|
|
{
|
|
const struct max77650_regulator_desc **rdescs;
|
|
const struct max77650_regulator_desc *rdesc;
|
|
struct regulator_config config = { };
|
|
struct device *dev, *parent;
|
|
struct regulator_dev *rdev;
|
|
struct regmap *map;
|
|
unsigned int val;
|
|
int i, rv;
|
|
|
|
dev = &pdev->dev;
|
|
parent = dev->parent;
|
|
|
|
if (!dev->of_node)
|
|
device_set_of_node_from_dev(dev, parent);
|
|
|
|
rdescs = devm_kcalloc(dev, MAX77650_REGULATOR_NUM_REGULATORS,
|
|
sizeof(*rdescs), GFP_KERNEL);
|
|
if (!rdescs)
|
|
return -ENOMEM;
|
|
|
|
map = dev_get_regmap(parent, NULL);
|
|
if (!map)
|
|
return -ENODEV;
|
|
|
|
rv = regmap_read(map, MAX77650_REG_CID, &val);
|
|
if (rv)
|
|
return rv;
|
|
|
|
rdescs[MAX77650_REGULATOR_ID_LDO] = &max77650_LDO_desc;
|
|
rdescs[MAX77650_REGULATOR_ID_SBB0] = &max77650_SBB0_desc;
|
|
|
|
switch (MAX77650_CID_BITS(val)) {
|
|
case MAX77650_CID_77650A:
|
|
case MAX77650_CID_77650C:
|
|
rdescs[MAX77650_REGULATOR_ID_SBB1] = &max77650_SBB1_desc;
|
|
rdescs[MAX77650_REGULATOR_ID_SBB2] = &max77650_SBB2_desc;
|
|
break;
|
|
case MAX77650_CID_77651A:
|
|
case MAX77650_CID_77651B:
|
|
rdescs[MAX77650_REGULATOR_ID_SBB1] = &max77651_SBB1_desc;
|
|
rdescs[MAX77650_REGULATOR_ID_SBB2] = &max77651_SBB2_desc;
|
|
break;
|
|
default:
|
|
return -ENODEV;
|
|
}
|
|
|
|
config.dev = parent;
|
|
|
|
for (i = 0; i < MAX77650_REGULATOR_NUM_REGULATORS; i++) {
|
|
rdesc = rdescs[i];
|
|
|
|
rdev = devm_regulator_register(dev, &rdesc->desc, &config);
|
|
if (IS_ERR(rdev))
|
|
return PTR_ERR(rdev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id max77650_regulator_of_match[] = {
|
|
{ .compatible = "maxim,max77650-regulator" },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, max77650_regulator_of_match);
|
|
|
|
static struct platform_driver max77650_regulator_driver = {
|
|
.driver = {
|
|
.name = "max77650-regulator",
|
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
|
.of_match_table = max77650_regulator_of_match,
|
|
},
|
|
.probe = max77650_regulator_probe,
|
|
};
|
|
module_platform_driver(max77650_regulator_driver);
|
|
|
|
MODULE_DESCRIPTION("MAXIM 77650/77651 regulator driver");
|
|
MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:max77650-regulator");
|