162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// max20086-regulator.c - MAX20086-MAX20089 camera power protector driver
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2022 Laurent Pinchart <laurent.pinchart@idesonboard.com>
662306a36Sopenharmony_ci// Copyright (C) 2018 Avnet, Inc.
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1062306a36Sopenharmony_ci#include <linux/i2c.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/regmap.h>
1362306a36Sopenharmony_ci#include <linux/regulator/driver.h>
1462306a36Sopenharmony_ci#include <linux/regulator/machine.h>
1562306a36Sopenharmony_ci#include <linux/regulator/of_regulator.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* Register Offset */
1962306a36Sopenharmony_ci#define MAX20086_REG_MASK		0x00
2062306a36Sopenharmony_ci#define MAX20086_REG_CONFIG		0x01
2162306a36Sopenharmony_ci#define	MAX20086_REG_ID			0x02
2262306a36Sopenharmony_ci#define	MAX20086_REG_STAT1		0x03
2362306a36Sopenharmony_ci#define	MAX20086_REG_STAT2_L		0x04
2462306a36Sopenharmony_ci#define	MAX20086_REG_STAT2_H		0x05
2562306a36Sopenharmony_ci#define	MAX20086_REG_ADC1		0x06
2662306a36Sopenharmony_ci#define	MAX20086_REG_ADC2		0x07
2762306a36Sopenharmony_ci#define	MAX20086_REG_ADC3		0x08
2862306a36Sopenharmony_ci#define	MAX20086_REG_ADC4		0x09
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* DEVICE IDs */
3162306a36Sopenharmony_ci#define MAX20086_DEVICE_ID_MAX20086	0x40
3262306a36Sopenharmony_ci#define MAX20086_DEVICE_ID_MAX20087	0x20
3362306a36Sopenharmony_ci#define MAX20086_DEVICE_ID_MAX20088	0x10
3462306a36Sopenharmony_ci#define MAX20086_DEVICE_ID_MAX20089	0x00
3562306a36Sopenharmony_ci#define DEVICE_ID_MASK			0xf0
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* Register bits */
3862306a36Sopenharmony_ci#define MAX20086_EN_MASK		0x0f
3962306a36Sopenharmony_ci#define MAX20086_EN_OUT1		0x01
4062306a36Sopenharmony_ci#define MAX20086_EN_OUT2		0x02
4162306a36Sopenharmony_ci#define MAX20086_EN_OUT3		0x04
4262306a36Sopenharmony_ci#define MAX20086_EN_OUT4		0x08
4362306a36Sopenharmony_ci#define MAX20086_INT_DISABLE_ALL	0x3f
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define MAX20086_MAX_REGULATORS		4
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct max20086_chip_info {
4862306a36Sopenharmony_ci	u8 id;
4962306a36Sopenharmony_ci	unsigned int num_outputs;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct max20086_regulator {
5362306a36Sopenharmony_ci	struct device_node *of_node;
5462306a36Sopenharmony_ci	struct regulator_init_data *init_data;
5562306a36Sopenharmony_ci	const struct regulator_desc *desc;
5662306a36Sopenharmony_ci	struct regulator_dev *rdev;
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct max20086 {
6062306a36Sopenharmony_ci	struct device *dev;
6162306a36Sopenharmony_ci	struct regmap *regmap;
6262306a36Sopenharmony_ci	struct gpio_desc *ena_gpiod;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	const struct max20086_chip_info *info;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	struct max20086_regulator regulators[MAX20086_MAX_REGULATORS];
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic const struct regulator_ops max20086_buck_ops = {
7062306a36Sopenharmony_ci	.enable = regulator_enable_regmap,
7162306a36Sopenharmony_ci	.disable = regulator_disable_regmap,
7262306a36Sopenharmony_ci	.is_enabled = regulator_is_enabled_regmap,
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define MAX20086_REGULATOR_DESC(n)		\
7662306a36Sopenharmony_ci{						\
7762306a36Sopenharmony_ci	.name = "OUT"#n,			\
7862306a36Sopenharmony_ci	.supply_name = "in",			\
7962306a36Sopenharmony_ci	.id = (n) - 1,				\
8062306a36Sopenharmony_ci	.ops = &max20086_buck_ops,		\
8162306a36Sopenharmony_ci	.type = REGULATOR_VOLTAGE,		\
8262306a36Sopenharmony_ci	.owner = THIS_MODULE,			\
8362306a36Sopenharmony_ci	.enable_reg = MAX20086_REG_CONFIG,	\
8462306a36Sopenharmony_ci	.enable_mask = 1 << ((n) - 1),		\
8562306a36Sopenharmony_ci	.enable_val = 1 << ((n) - 1),		\
8662306a36Sopenharmony_ci	.disable_val = 0,			\
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic const char * const max20086_output_names[] = {
9062306a36Sopenharmony_ci	"OUT1",
9162306a36Sopenharmony_ci	"OUT2",
9262306a36Sopenharmony_ci	"OUT3",
9362306a36Sopenharmony_ci	"OUT4",
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic const struct regulator_desc max20086_regulators[] = {
9762306a36Sopenharmony_ci	MAX20086_REGULATOR_DESC(1),
9862306a36Sopenharmony_ci	MAX20086_REGULATOR_DESC(2),
9962306a36Sopenharmony_ci	MAX20086_REGULATOR_DESC(3),
10062306a36Sopenharmony_ci	MAX20086_REGULATOR_DESC(4),
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int max20086_regulators_register(struct max20086 *chip)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	unsigned int i;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	for (i = 0; i < chip->info->num_outputs; i++) {
10862306a36Sopenharmony_ci		struct max20086_regulator *reg = &chip->regulators[i];
10962306a36Sopenharmony_ci		struct regulator_config config = { };
11062306a36Sopenharmony_ci		struct regulator_dev *rdev;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		config.dev = chip->dev;
11362306a36Sopenharmony_ci		config.init_data = reg->init_data;
11462306a36Sopenharmony_ci		config.driver_data = chip;
11562306a36Sopenharmony_ci		config.of_node = reg->of_node;
11662306a36Sopenharmony_ci		config.regmap = chip->regmap;
11762306a36Sopenharmony_ci		config.ena_gpiod = chip->ena_gpiod;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		rdev = devm_regulator_register(chip->dev, reg->desc, &config);
12062306a36Sopenharmony_ci		if (IS_ERR(rdev)) {
12162306a36Sopenharmony_ci			dev_err(chip->dev,
12262306a36Sopenharmony_ci				"Failed to register regulator output %s\n",
12362306a36Sopenharmony_ci				reg->desc->name);
12462306a36Sopenharmony_ci			return PTR_ERR(rdev);
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		reg->rdev = rdev;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int max20086_parse_regulators_dt(struct max20086 *chip, bool *boot_on)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct of_regulator_match matches[MAX20086_MAX_REGULATORS] = { };
13662306a36Sopenharmony_ci	struct device_node *node;
13762306a36Sopenharmony_ci	unsigned int i;
13862306a36Sopenharmony_ci	int ret;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	node = of_get_child_by_name(chip->dev->of_node, "regulators");
14162306a36Sopenharmony_ci	if (!node) {
14262306a36Sopenharmony_ci		dev_err(chip->dev, "regulators node not found\n");
14362306a36Sopenharmony_ci		return -ENODEV;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	for (i = 0; i < chip->info->num_outputs; ++i)
14762306a36Sopenharmony_ci		matches[i].name = max20086_output_names[i];
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	ret = of_regulator_match(chip->dev, node, matches,
15062306a36Sopenharmony_ci				 chip->info->num_outputs);
15162306a36Sopenharmony_ci	of_node_put(node);
15262306a36Sopenharmony_ci	if (ret < 0) {
15362306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to match regulators\n");
15462306a36Sopenharmony_ci		return -EINVAL;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	*boot_on = false;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	for (i = 0; i < chip->info->num_outputs; i++) {
16062306a36Sopenharmony_ci		struct max20086_regulator *reg = &chip->regulators[i];
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		reg->init_data = matches[i].init_data;
16362306a36Sopenharmony_ci		reg->of_node = matches[i].of_node;
16462306a36Sopenharmony_ci		reg->desc = &max20086_regulators[i];
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		if (reg->init_data) {
16762306a36Sopenharmony_ci			if (reg->init_data->constraints.always_on ||
16862306a36Sopenharmony_ci			    reg->init_data->constraints.boot_on)
16962306a36Sopenharmony_ci				*boot_on = true;
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int max20086_detect(struct max20086 *chip)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	unsigned int data;
17962306a36Sopenharmony_ci	int ret;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	ret = regmap_read(chip->regmap, MAX20086_REG_ID, &data);
18262306a36Sopenharmony_ci	if (ret < 0) {
18362306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to read DEVICE_ID reg: %d\n", ret);
18462306a36Sopenharmony_ci		return ret;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if ((data & DEVICE_ID_MASK) != chip->info->id) {
18862306a36Sopenharmony_ci		dev_err(chip->dev, "Invalid device ID 0x%02x\n", data);
18962306a36Sopenharmony_ci		return -ENXIO;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return 0;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic bool max20086_gen_is_writeable_reg(struct device *dev, unsigned int reg)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	switch (reg) {
19862306a36Sopenharmony_ci	case MAX20086_REG_MASK:
19962306a36Sopenharmony_ci	case MAX20086_REG_CONFIG:
20062306a36Sopenharmony_ci		return true;
20162306a36Sopenharmony_ci	default:
20262306a36Sopenharmony_ci		return false;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic const struct regmap_config max20086_regmap_config = {
20762306a36Sopenharmony_ci	.reg_bits = 8,
20862306a36Sopenharmony_ci	.val_bits = 8,
20962306a36Sopenharmony_ci	.writeable_reg = max20086_gen_is_writeable_reg,
21062306a36Sopenharmony_ci	.max_register = 0x9,
21162306a36Sopenharmony_ci	.cache_type = REGCACHE_NONE,
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic int max20086_i2c_probe(struct i2c_client *i2c)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct max20086 *chip;
21762306a36Sopenharmony_ci	enum gpiod_flags flags;
21862306a36Sopenharmony_ci	bool boot_on;
21962306a36Sopenharmony_ci	int ret;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL);
22262306a36Sopenharmony_ci	if (!chip)
22362306a36Sopenharmony_ci		return -ENOMEM;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	chip->dev = &i2c->dev;
22662306a36Sopenharmony_ci	chip->info = device_get_match_data(chip->dev);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	i2c_set_clientdata(i2c, chip);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	chip->regmap = devm_regmap_init_i2c(i2c, &max20086_regmap_config);
23162306a36Sopenharmony_ci	if (IS_ERR(chip->regmap)) {
23262306a36Sopenharmony_ci		ret = PTR_ERR(chip->regmap);
23362306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to allocate register map: %d\n", ret);
23462306a36Sopenharmony_ci		return ret;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	ret = max20086_parse_regulators_dt(chip, &boot_on);
23862306a36Sopenharmony_ci	if (ret < 0)
23962306a36Sopenharmony_ci		return ret;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	ret = max20086_detect(chip);
24262306a36Sopenharmony_ci	if (ret < 0)
24362306a36Sopenharmony_ci		return ret;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Until IRQ support is added, just disable all interrupts. */
24662306a36Sopenharmony_ci	ret = regmap_update_bits(chip->regmap, MAX20086_REG_MASK,
24762306a36Sopenharmony_ci				 MAX20086_INT_DISABLE_ALL,
24862306a36Sopenharmony_ci				 MAX20086_INT_DISABLE_ALL);
24962306a36Sopenharmony_ci	if (ret < 0) {
25062306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to disable interrupts: %d\n", ret);
25162306a36Sopenharmony_ci		return ret;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/*
25562306a36Sopenharmony_ci	 * Get the enable GPIO. If any of the outputs is marked as being
25662306a36Sopenharmony_ci	 * enabled at boot, request the GPIO with an initial high state to
25762306a36Sopenharmony_ci	 * avoid disabling outputs that may have been turned on by the boot
25862306a36Sopenharmony_ci	 * loader. Otherwise, request it with a low state to enter lower-power
25962306a36Sopenharmony_ci	 * shutdown.
26062306a36Sopenharmony_ci	 */
26162306a36Sopenharmony_ci	flags = boot_on ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW;
26262306a36Sopenharmony_ci	chip->ena_gpiod = devm_gpiod_get(chip->dev, "enable", flags);
26362306a36Sopenharmony_ci	if (IS_ERR(chip->ena_gpiod)) {
26462306a36Sopenharmony_ci		ret = PTR_ERR(chip->ena_gpiod);
26562306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to get enable GPIO: %d\n", ret);
26662306a36Sopenharmony_ci		return ret;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ret = max20086_regulators_register(chip);
27062306a36Sopenharmony_ci	if (ret < 0) {
27162306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to register regulators: %d\n", ret);
27262306a36Sopenharmony_ci		return ret;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic const struct i2c_device_id max20086_i2c_id[] = {
27962306a36Sopenharmony_ci	{ "max20086" },
28062306a36Sopenharmony_ci	{ "max20087" },
28162306a36Sopenharmony_ci	{ "max20088" },
28262306a36Sopenharmony_ci	{ "max20089" },
28362306a36Sopenharmony_ci	{ /* Sentinel */ },
28462306a36Sopenharmony_ci};
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max20086_i2c_id);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic const struct of_device_id max20086_dt_ids[] __maybe_unused = {
28962306a36Sopenharmony_ci	{
29062306a36Sopenharmony_ci		.compatible = "maxim,max20086",
29162306a36Sopenharmony_ci		.data = &(const struct max20086_chip_info) {
29262306a36Sopenharmony_ci			.id = MAX20086_DEVICE_ID_MAX20086,
29362306a36Sopenharmony_ci			.num_outputs = 4,
29462306a36Sopenharmony_ci		}
29562306a36Sopenharmony_ci	}, {
29662306a36Sopenharmony_ci		.compatible = "maxim,max20087",
29762306a36Sopenharmony_ci		.data = &(const struct max20086_chip_info) {
29862306a36Sopenharmony_ci			.id = MAX20086_DEVICE_ID_MAX20087,
29962306a36Sopenharmony_ci			.num_outputs = 4,
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci	}, {
30262306a36Sopenharmony_ci		.compatible = "maxim,max20088",
30362306a36Sopenharmony_ci		.data = &(const struct max20086_chip_info) {
30462306a36Sopenharmony_ci			.id = MAX20086_DEVICE_ID_MAX20088,
30562306a36Sopenharmony_ci			.num_outputs = 2,
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci	}, {
30862306a36Sopenharmony_ci		.compatible = "maxim,max20089",
30962306a36Sopenharmony_ci		.data = &(const struct max20086_chip_info) {
31062306a36Sopenharmony_ci			.id = MAX20086_DEVICE_ID_MAX20089,
31162306a36Sopenharmony_ci			.num_outputs = 2,
31262306a36Sopenharmony_ci		}
31362306a36Sopenharmony_ci	},
31462306a36Sopenharmony_ci	{ /* Sentinel */ },
31562306a36Sopenharmony_ci};
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, max20086_dt_ids);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic struct i2c_driver max20086_regulator_driver = {
32062306a36Sopenharmony_ci	.driver = {
32162306a36Sopenharmony_ci		.name = "max20086",
32262306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
32362306a36Sopenharmony_ci		.of_match_table = of_match_ptr(max20086_dt_ids),
32462306a36Sopenharmony_ci	},
32562306a36Sopenharmony_ci	.probe = max20086_i2c_probe,
32662306a36Sopenharmony_ci	.id_table = max20086_i2c_id,
32762306a36Sopenharmony_ci};
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cimodule_i2c_driver(max20086_regulator_driver);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ciMODULE_AUTHOR("Watson Chow <watson.chow@avnet.com>");
33262306a36Sopenharmony_ciMODULE_DESCRIPTION("MAX20086-MAX20089 Camera Power Protector Driver");
33362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
334