162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * isl6271a-regulator.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Support for Intersil ISL6271A voltage regulator
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/regulator/driver.h>
1662306a36Sopenharmony_ci#include <linux/i2c.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define	ISL6271A_VOLTAGE_MIN	850000
2062306a36Sopenharmony_ci#define	ISL6271A_VOLTAGE_MAX	1600000
2162306a36Sopenharmony_ci#define	ISL6271A_VOLTAGE_STEP	50000
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* PMIC details */
2462306a36Sopenharmony_cistruct isl_pmic {
2562306a36Sopenharmony_ci	struct i2c_client	*client;
2662306a36Sopenharmony_ci	struct mutex		mtx;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int isl6271a_get_voltage_sel(struct regulator_dev *dev)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct isl_pmic *pmic = rdev_get_drvdata(dev);
3262306a36Sopenharmony_ci	int idx;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	mutex_lock(&pmic->mtx);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	idx = i2c_smbus_read_byte(pmic->client);
3762306a36Sopenharmony_ci	if (idx < 0)
3862306a36Sopenharmony_ci		dev_err(&pmic->client->dev, "Error getting voltage\n");
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	mutex_unlock(&pmic->mtx);
4162306a36Sopenharmony_ci	return idx;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int isl6271a_set_voltage_sel(struct regulator_dev *dev,
4562306a36Sopenharmony_ci				    unsigned selector)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct isl_pmic *pmic = rdev_get_drvdata(dev);
4862306a36Sopenharmony_ci	int err;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	mutex_lock(&pmic->mtx);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	err = i2c_smbus_write_byte(pmic->client, selector);
5362306a36Sopenharmony_ci	if (err < 0)
5462306a36Sopenharmony_ci		dev_err(&pmic->client->dev, "Error setting voltage\n");
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	mutex_unlock(&pmic->mtx);
5762306a36Sopenharmony_ci	return err;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic const struct regulator_ops isl_core_ops = {
6162306a36Sopenharmony_ci	.get_voltage_sel = isl6271a_get_voltage_sel,
6262306a36Sopenharmony_ci	.set_voltage_sel = isl6271a_set_voltage_sel,
6362306a36Sopenharmony_ci	.list_voltage	= regulator_list_voltage_linear,
6462306a36Sopenharmony_ci	.map_voltage	= regulator_map_voltage_linear,
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic const struct regulator_ops isl_fixed_ops = {
6862306a36Sopenharmony_ci	.list_voltage	= regulator_list_voltage_linear,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic const struct regulator_desc isl_rd[] = {
7262306a36Sopenharmony_ci	{
7362306a36Sopenharmony_ci		.name		= "Core Buck",
7462306a36Sopenharmony_ci		.id		= 0,
7562306a36Sopenharmony_ci		.n_voltages	= 16,
7662306a36Sopenharmony_ci		.ops		= &isl_core_ops,
7762306a36Sopenharmony_ci		.type		= REGULATOR_VOLTAGE,
7862306a36Sopenharmony_ci		.owner		= THIS_MODULE,
7962306a36Sopenharmony_ci		.min_uV		= ISL6271A_VOLTAGE_MIN,
8062306a36Sopenharmony_ci		.uV_step	= ISL6271A_VOLTAGE_STEP,
8162306a36Sopenharmony_ci	}, {
8262306a36Sopenharmony_ci		.name		= "LDO1",
8362306a36Sopenharmony_ci		.id		= 1,
8462306a36Sopenharmony_ci		.n_voltages	= 1,
8562306a36Sopenharmony_ci		.ops		= &isl_fixed_ops,
8662306a36Sopenharmony_ci		.type		= REGULATOR_VOLTAGE,
8762306a36Sopenharmony_ci		.owner		= THIS_MODULE,
8862306a36Sopenharmony_ci		.min_uV		= 1100000,
8962306a36Sopenharmony_ci	}, {
9062306a36Sopenharmony_ci		.name		= "LDO2",
9162306a36Sopenharmony_ci		.id		= 2,
9262306a36Sopenharmony_ci		.n_voltages	= 1,
9362306a36Sopenharmony_ci		.ops		= &isl_fixed_ops,
9462306a36Sopenharmony_ci		.type		= REGULATOR_VOLTAGE,
9562306a36Sopenharmony_ci		.owner		= THIS_MODULE,
9662306a36Sopenharmony_ci		.min_uV		= 1300000,
9762306a36Sopenharmony_ci	},
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int isl6271a_probe(struct i2c_client *i2c)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
10362306a36Sopenharmony_ci	struct regulator_dev *rdev;
10462306a36Sopenharmony_ci	struct regulator_config config = { };
10562306a36Sopenharmony_ci	struct regulator_init_data *init_data	= dev_get_platdata(&i2c->dev);
10662306a36Sopenharmony_ci	struct isl_pmic *pmic;
10762306a36Sopenharmony_ci	int i;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
11062306a36Sopenharmony_ci		return -EIO;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	pmic = devm_kzalloc(&i2c->dev, sizeof(struct isl_pmic), GFP_KERNEL);
11362306a36Sopenharmony_ci	if (!pmic)
11462306a36Sopenharmony_ci		return -ENOMEM;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	pmic->client = i2c;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	mutex_init(&pmic->mtx);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
12162306a36Sopenharmony_ci		config.dev = &i2c->dev;
12262306a36Sopenharmony_ci		if (i == 0)
12362306a36Sopenharmony_ci			config.init_data = init_data;
12462306a36Sopenharmony_ci		else
12562306a36Sopenharmony_ci			config.init_data = NULL;
12662306a36Sopenharmony_ci		config.driver_data = pmic;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		rdev = devm_regulator_register(&i2c->dev, &isl_rd[i], &config);
12962306a36Sopenharmony_ci		if (IS_ERR(rdev)) {
13062306a36Sopenharmony_ci			dev_err(&i2c->dev, "failed to register %s\n", id->name);
13162306a36Sopenharmony_ci			return PTR_ERR(rdev);
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	i2c_set_clientdata(i2c, pmic);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return 0;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic const struct i2c_device_id isl6271a_id[] = {
14162306a36Sopenharmony_ci	{.name = "isl6271a", 0 },
14262306a36Sopenharmony_ci	{ },
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, isl6271a_id);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic struct i2c_driver isl6271a_i2c_driver = {
14862306a36Sopenharmony_ci	.driver = {
14962306a36Sopenharmony_ci		.name = "isl6271a",
15062306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
15162306a36Sopenharmony_ci	},
15262306a36Sopenharmony_ci	.probe = isl6271a_probe,
15362306a36Sopenharmony_ci	.id_table = isl6271a_id,
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int __init isl6271a_init(void)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	return i2c_add_driver(&isl6271a_i2c_driver);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void __exit isl6271a_cleanup(void)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	i2c_del_driver(&isl6271a_i2c_driver);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cisubsys_initcall(isl6271a_init);
16762306a36Sopenharmony_cimodule_exit(isl6271a_cleanup);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ciMODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
17062306a36Sopenharmony_ciMODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver");
17162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
172