18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Regulators driver for Maxim max8649
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Marvell International Ltd.
68c2ecf20Sopenharmony_ci *      Haojian Zhuang <haojian.zhuang@marvell.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci#include <linux/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/regulator/max8649.h>
168c2ecf20Sopenharmony_ci#include <linux/regmap.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define MAX8649_DCDC_VMIN	750000		/* uV */
198c2ecf20Sopenharmony_ci#define MAX8649_DCDC_VMAX	1380000		/* uV */
208c2ecf20Sopenharmony_ci#define MAX8649_DCDC_STEP	10000		/* uV */
218c2ecf20Sopenharmony_ci#define MAX8649_VOL_MASK	0x3f
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* Registers */
248c2ecf20Sopenharmony_ci#define MAX8649_MODE0		0x00
258c2ecf20Sopenharmony_ci#define MAX8649_MODE1		0x01
268c2ecf20Sopenharmony_ci#define MAX8649_MODE2		0x02
278c2ecf20Sopenharmony_ci#define MAX8649_MODE3		0x03
288c2ecf20Sopenharmony_ci#define MAX8649_CONTROL		0x04
298c2ecf20Sopenharmony_ci#define MAX8649_SYNC		0x05
308c2ecf20Sopenharmony_ci#define MAX8649_RAMP		0x06
318c2ecf20Sopenharmony_ci#define MAX8649_CHIP_ID1	0x08
328c2ecf20Sopenharmony_ci#define MAX8649_CHIP_ID2	0x09
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* Bits */
358c2ecf20Sopenharmony_ci#define MAX8649_EN_PD		(1 << 7)
368c2ecf20Sopenharmony_ci#define MAX8649_VID0_PD		(1 << 6)
378c2ecf20Sopenharmony_ci#define MAX8649_VID1_PD		(1 << 5)
388c2ecf20Sopenharmony_ci#define MAX8649_VID_MASK	(3 << 5)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define MAX8649_FORCE_PWM	(1 << 7)
418c2ecf20Sopenharmony_ci#define MAX8649_SYNC_EXTCLK	(1 << 6)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define MAX8649_EXT_MASK	(3 << 6)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define MAX8649_RAMP_MASK	(7 << 5)
468c2ecf20Sopenharmony_ci#define MAX8649_RAMP_DOWN	(1 << 1)
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct max8649_regulator_info {
498c2ecf20Sopenharmony_ci	struct device		*dev;
508c2ecf20Sopenharmony_ci	struct regmap		*regmap;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	unsigned	mode:2;	/* bit[1:0] = VID1, VID0 */
538c2ecf20Sopenharmony_ci	unsigned	extclk_freq:2;
548c2ecf20Sopenharmony_ci	unsigned	extclk:1;
558c2ecf20Sopenharmony_ci	unsigned	ramp_timing:3;
568c2ecf20Sopenharmony_ci	unsigned	ramp_down:1;
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int max8649_enable_time(struct regulator_dev *rdev)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
628c2ecf20Sopenharmony_ci	int voltage, rate, ret;
638c2ecf20Sopenharmony_ci	unsigned int val;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* get voltage */
668c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, rdev->desc->vsel_reg, &val);
678c2ecf20Sopenharmony_ci	if (ret != 0)
688c2ecf20Sopenharmony_ci		return ret;
698c2ecf20Sopenharmony_ci	val &= MAX8649_VOL_MASK;
708c2ecf20Sopenharmony_ci	voltage = regulator_list_voltage_linear(rdev, (unsigned char)val);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* get rate */
738c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, MAX8649_RAMP, &val);
748c2ecf20Sopenharmony_ci	if (ret != 0)
758c2ecf20Sopenharmony_ci		return ret;
768c2ecf20Sopenharmony_ci	ret = (val & MAX8649_RAMP_MASK) >> 5;
778c2ecf20Sopenharmony_ci	rate = (32 * 1000) >> ret;	/* uV/uS */
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return DIV_ROUND_UP(voltage, rate);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int max8649_set_mode(struct regulator_dev *rdev, unsigned int mode)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	switch (mode) {
878c2ecf20Sopenharmony_ci	case REGULATOR_MODE_FAST:
888c2ecf20Sopenharmony_ci		regmap_update_bits(info->regmap, rdev->desc->vsel_reg,
898c2ecf20Sopenharmony_ci				   MAX8649_FORCE_PWM, MAX8649_FORCE_PWM);
908c2ecf20Sopenharmony_ci		break;
918c2ecf20Sopenharmony_ci	case REGULATOR_MODE_NORMAL:
928c2ecf20Sopenharmony_ci		regmap_update_bits(info->regmap, rdev->desc->vsel_reg,
938c2ecf20Sopenharmony_ci				   MAX8649_FORCE_PWM, 0);
948c2ecf20Sopenharmony_ci		break;
958c2ecf20Sopenharmony_ci	default:
968c2ecf20Sopenharmony_ci		return -EINVAL;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic unsigned int max8649_get_mode(struct regulator_dev *rdev)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
1048c2ecf20Sopenharmony_ci	unsigned int val;
1058c2ecf20Sopenharmony_ci	int ret;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, rdev->desc->vsel_reg, &val);
1088c2ecf20Sopenharmony_ci	if (ret != 0)
1098c2ecf20Sopenharmony_ci		return ret;
1108c2ecf20Sopenharmony_ci	if (val & MAX8649_FORCE_PWM)
1118c2ecf20Sopenharmony_ci		return REGULATOR_MODE_FAST;
1128c2ecf20Sopenharmony_ci	return REGULATOR_MODE_NORMAL;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic const struct regulator_ops max8649_dcdc_ops = {
1168c2ecf20Sopenharmony_ci	.set_voltage_sel = regulator_set_voltage_sel_regmap,
1178c2ecf20Sopenharmony_ci	.get_voltage_sel = regulator_get_voltage_sel_regmap,
1188c2ecf20Sopenharmony_ci	.list_voltage	= regulator_list_voltage_linear,
1198c2ecf20Sopenharmony_ci	.map_voltage	= regulator_map_voltage_linear,
1208c2ecf20Sopenharmony_ci	.enable		= regulator_enable_regmap,
1218c2ecf20Sopenharmony_ci	.disable	= regulator_disable_regmap,
1228c2ecf20Sopenharmony_ci	.is_enabled	= regulator_is_enabled_regmap,
1238c2ecf20Sopenharmony_ci	.enable_time	= max8649_enable_time,
1248c2ecf20Sopenharmony_ci	.set_mode	= max8649_set_mode,
1258c2ecf20Sopenharmony_ci	.get_mode	= max8649_get_mode,
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic struct regulator_desc dcdc_desc = {
1308c2ecf20Sopenharmony_ci	.name		= "max8649",
1318c2ecf20Sopenharmony_ci	.ops		= &max8649_dcdc_ops,
1328c2ecf20Sopenharmony_ci	.type		= REGULATOR_VOLTAGE,
1338c2ecf20Sopenharmony_ci	.n_voltages	= 1 << 6,
1348c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1358c2ecf20Sopenharmony_ci	.vsel_mask	= MAX8649_VOL_MASK,
1368c2ecf20Sopenharmony_ci	.min_uV		= MAX8649_DCDC_VMIN,
1378c2ecf20Sopenharmony_ci	.uV_step	= MAX8649_DCDC_STEP,
1388c2ecf20Sopenharmony_ci	.enable_reg	= MAX8649_CONTROL,
1398c2ecf20Sopenharmony_ci	.enable_mask	= MAX8649_EN_PD,
1408c2ecf20Sopenharmony_ci	.enable_is_inverted = true,
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic const struct regmap_config max8649_regmap_config = {
1448c2ecf20Sopenharmony_ci	.reg_bits = 8,
1458c2ecf20Sopenharmony_ci	.val_bits = 8,
1468c2ecf20Sopenharmony_ci};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int max8649_regulator_probe(struct i2c_client *client,
1498c2ecf20Sopenharmony_ci					     const struct i2c_device_id *id)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct max8649_platform_data *pdata = dev_get_platdata(&client->dev);
1528c2ecf20Sopenharmony_ci	struct max8649_regulator_info *info = NULL;
1538c2ecf20Sopenharmony_ci	struct regulator_dev *regulator;
1548c2ecf20Sopenharmony_ci	struct regulator_config config = { };
1558c2ecf20Sopenharmony_ci	unsigned int val;
1568c2ecf20Sopenharmony_ci	unsigned char data;
1578c2ecf20Sopenharmony_ci	int ret;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	info = devm_kzalloc(&client->dev, sizeof(struct max8649_regulator_info),
1608c2ecf20Sopenharmony_ci			    GFP_KERNEL);
1618c2ecf20Sopenharmony_ci	if (!info)
1628c2ecf20Sopenharmony_ci		return -ENOMEM;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	info->regmap = devm_regmap_init_i2c(client, &max8649_regmap_config);
1658c2ecf20Sopenharmony_ci	if (IS_ERR(info->regmap)) {
1668c2ecf20Sopenharmony_ci		ret = PTR_ERR(info->regmap);
1678c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to allocate register map: %d\n", ret);
1688c2ecf20Sopenharmony_ci		return ret;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	info->dev = &client->dev;
1728c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, info);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	info->mode = pdata->mode;
1758c2ecf20Sopenharmony_ci	switch (info->mode) {
1768c2ecf20Sopenharmony_ci	case 0:
1778c2ecf20Sopenharmony_ci		dcdc_desc.vsel_reg = MAX8649_MODE0;
1788c2ecf20Sopenharmony_ci		break;
1798c2ecf20Sopenharmony_ci	case 1:
1808c2ecf20Sopenharmony_ci		dcdc_desc.vsel_reg = MAX8649_MODE1;
1818c2ecf20Sopenharmony_ci		break;
1828c2ecf20Sopenharmony_ci	case 2:
1838c2ecf20Sopenharmony_ci		dcdc_desc.vsel_reg = MAX8649_MODE2;
1848c2ecf20Sopenharmony_ci		break;
1858c2ecf20Sopenharmony_ci	case 3:
1868c2ecf20Sopenharmony_ci		dcdc_desc.vsel_reg = MAX8649_MODE3;
1878c2ecf20Sopenharmony_ci		break;
1888c2ecf20Sopenharmony_ci	default:
1898c2ecf20Sopenharmony_ci		break;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, MAX8649_CHIP_ID1, &val);
1938c2ecf20Sopenharmony_ci	if (ret != 0) {
1948c2ecf20Sopenharmony_ci		dev_err(info->dev, "Failed to detect ID of MAX8649:%d\n",
1958c2ecf20Sopenharmony_ci			ret);
1968c2ecf20Sopenharmony_ci		return ret;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci	dev_info(info->dev, "Detected MAX8649 (ID:%x)\n", val);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/* enable VID0 & VID1 */
2018c2ecf20Sopenharmony_ci	regmap_update_bits(info->regmap, MAX8649_CONTROL, MAX8649_VID_MASK, 0);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* enable/disable external clock synchronization */
2048c2ecf20Sopenharmony_ci	info->extclk = pdata->extclk;
2058c2ecf20Sopenharmony_ci	data = (info->extclk) ? MAX8649_SYNC_EXTCLK : 0;
2068c2ecf20Sopenharmony_ci	regmap_update_bits(info->regmap, dcdc_desc.vsel_reg,
2078c2ecf20Sopenharmony_ci			   MAX8649_SYNC_EXTCLK, data);
2088c2ecf20Sopenharmony_ci	if (info->extclk) {
2098c2ecf20Sopenharmony_ci		/* set external clock frequency */
2108c2ecf20Sopenharmony_ci		info->extclk_freq = pdata->extclk_freq;
2118c2ecf20Sopenharmony_ci		regmap_update_bits(info->regmap, MAX8649_SYNC, MAX8649_EXT_MASK,
2128c2ecf20Sopenharmony_ci				   info->extclk_freq << 6);
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (pdata->ramp_timing) {
2168c2ecf20Sopenharmony_ci		info->ramp_timing = pdata->ramp_timing;
2178c2ecf20Sopenharmony_ci		regmap_update_bits(info->regmap, MAX8649_RAMP, MAX8649_RAMP_MASK,
2188c2ecf20Sopenharmony_ci				   info->ramp_timing << 5);
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	info->ramp_down = pdata->ramp_down;
2228c2ecf20Sopenharmony_ci	if (info->ramp_down) {
2238c2ecf20Sopenharmony_ci		regmap_update_bits(info->regmap, MAX8649_RAMP, MAX8649_RAMP_DOWN,
2248c2ecf20Sopenharmony_ci				   MAX8649_RAMP_DOWN);
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	config.dev = &client->dev;
2288c2ecf20Sopenharmony_ci	config.init_data = pdata->regulator;
2298c2ecf20Sopenharmony_ci	config.driver_data = info;
2308c2ecf20Sopenharmony_ci	config.regmap = info->regmap;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	regulator = devm_regulator_register(&client->dev, &dcdc_desc,
2338c2ecf20Sopenharmony_ci						  &config);
2348c2ecf20Sopenharmony_ci	if (IS_ERR(regulator)) {
2358c2ecf20Sopenharmony_ci		dev_err(info->dev, "failed to register regulator %s\n",
2368c2ecf20Sopenharmony_ci			dcdc_desc.name);
2378c2ecf20Sopenharmony_ci		return PTR_ERR(regulator);
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return 0;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic const struct i2c_device_id max8649_id[] = {
2448c2ecf20Sopenharmony_ci	{ "max8649", 0 },
2458c2ecf20Sopenharmony_ci	{ }
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max8649_id);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic struct i2c_driver max8649_driver = {
2508c2ecf20Sopenharmony_ci	.probe		= max8649_regulator_probe,
2518c2ecf20Sopenharmony_ci	.driver		= {
2528c2ecf20Sopenharmony_ci		.name	= "max8649",
2538c2ecf20Sopenharmony_ci	},
2548c2ecf20Sopenharmony_ci	.id_table	= max8649_id,
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int __init max8649_init(void)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	return i2c_add_driver(&max8649_driver);
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_cisubsys_initcall(max8649_init);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic void __exit max8649_exit(void)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	i2c_del_driver(&max8649_driver);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_cimodule_exit(max8649_exit);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci/* Module information */
2708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAXIM 8649 voltage regulator driver");
2718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
2728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
273