18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright (c) 2019 five technologies GmbH
48c2ecf20Sopenharmony_ci// Author: Markus Reichl <m.reichl@fivetechno.de>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/i2c.h>
88c2ecf20Sopenharmony_ci#include <linux/of.h>
98c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h>
108c2ecf20Sopenharmony_ci#include <linux/regmap.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define VOL_MIN_IDX			0x00
148c2ecf20Sopenharmony_ci#define VOL_MAX_IDX			0x7ff
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* Register definitions */
178c2ecf20Sopenharmony_ci#define MP8859_VOUT_L_REG		0    //3 lo Bits
188c2ecf20Sopenharmony_ci#define MP8859_VOUT_H_REG		1    //8 hi Bits
198c2ecf20Sopenharmony_ci#define MP8859_VOUT_GO_REG		2
208c2ecf20Sopenharmony_ci#define MP8859_IOUT_LIM_REG		3
218c2ecf20Sopenharmony_ci#define MP8859_CTL1_REG			4
228c2ecf20Sopenharmony_ci#define MP8859_CTL2_REG			5
238c2ecf20Sopenharmony_ci#define MP8859_RESERVED1_REG		6
248c2ecf20Sopenharmony_ci#define MP8859_RESERVED2_REG		7
258c2ecf20Sopenharmony_ci#define MP8859_RESERVED3_REG		8
268c2ecf20Sopenharmony_ci#define MP8859_STATUS_REG		9
278c2ecf20Sopenharmony_ci#define MP8859_INTERRUPT_REG		0x0A
288c2ecf20Sopenharmony_ci#define MP8859_MASK_REG			0x0B
298c2ecf20Sopenharmony_ci#define MP8859_ID1_REG			0x0C
308c2ecf20Sopenharmony_ci#define MP8859_MFR_ID_REG		0x27
318c2ecf20Sopenharmony_ci#define MP8859_DEV_ID_REG		0x28
328c2ecf20Sopenharmony_ci#define MP8859_IC_REV_REG		0x29
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define MP8859_MAX_REG			0x29
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define MP8859_GO_BIT			0x01
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int mp8859_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	int ret;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	ret = regmap_write(rdev->regmap, MP8859_VOUT_L_REG, sel & 0x7);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (ret)
468c2ecf20Sopenharmony_ci		return ret;
478c2ecf20Sopenharmony_ci	ret = regmap_write(rdev->regmap, MP8859_VOUT_H_REG, sel >> 3);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (ret)
508c2ecf20Sopenharmony_ci		return ret;
518c2ecf20Sopenharmony_ci	ret = regmap_update_bits(rdev->regmap, MP8859_VOUT_GO_REG,
528c2ecf20Sopenharmony_ci					MP8859_GO_BIT, 1);
538c2ecf20Sopenharmony_ci	return ret;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int mp8859_get_voltage_sel(struct regulator_dev *rdev)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	unsigned int val_tmp;
598c2ecf20Sopenharmony_ci	unsigned int val;
608c2ecf20Sopenharmony_ci	int ret;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	ret = regmap_read(rdev->regmap, MP8859_VOUT_H_REG, &val_tmp);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (ret)
658c2ecf20Sopenharmony_ci		return ret;
668c2ecf20Sopenharmony_ci	val = val_tmp << 3;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	ret = regmap_read(rdev->regmap, MP8859_VOUT_L_REG, &val_tmp);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (ret)
718c2ecf20Sopenharmony_ci		return ret;
728c2ecf20Sopenharmony_ci	val |= val_tmp & 0x07;
738c2ecf20Sopenharmony_ci	return val;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic const struct linear_range mp8859_dcdc_ranges[] = {
778c2ecf20Sopenharmony_ci	REGULATOR_LINEAR_RANGE(0, VOL_MIN_IDX, VOL_MAX_IDX, 10000),
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic const struct regmap_config mp8859_regmap = {
818c2ecf20Sopenharmony_ci	.reg_bits = 8,
828c2ecf20Sopenharmony_ci	.val_bits = 8,
838c2ecf20Sopenharmony_ci	.max_register = MP8859_MAX_REG,
848c2ecf20Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic const struct regulator_ops mp8859_ops = {
888c2ecf20Sopenharmony_ci	.set_voltage_sel = mp8859_set_voltage_sel,
898c2ecf20Sopenharmony_ci	.get_voltage_sel = mp8859_get_voltage_sel,
908c2ecf20Sopenharmony_ci	.list_voltage = regulator_list_voltage_linear_range,
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic const struct regulator_desc mp8859_regulators[] = {
948c2ecf20Sopenharmony_ci	{
958c2ecf20Sopenharmony_ci		.id = 0,
968c2ecf20Sopenharmony_ci		.type = REGULATOR_VOLTAGE,
978c2ecf20Sopenharmony_ci		.name = "mp8859_dcdc",
988c2ecf20Sopenharmony_ci		.supply_name = "vin",
998c2ecf20Sopenharmony_ci		.of_match = of_match_ptr("mp8859_dcdc"),
1008c2ecf20Sopenharmony_ci		.n_voltages = VOL_MAX_IDX + 1,
1018c2ecf20Sopenharmony_ci		.linear_ranges = mp8859_dcdc_ranges,
1028c2ecf20Sopenharmony_ci		.n_linear_ranges = 1,
1038c2ecf20Sopenharmony_ci		.ops = &mp8859_ops,
1048c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
1058c2ecf20Sopenharmony_ci	},
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int mp8859_i2c_probe(struct i2c_client *i2c)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	int ret;
1118c2ecf20Sopenharmony_ci	struct regulator_config config = {.dev = &i2c->dev};
1128c2ecf20Sopenharmony_ci	struct regmap *regmap = devm_regmap_init_i2c(i2c, &mp8859_regmap);
1138c2ecf20Sopenharmony_ci	struct regulator_dev *rdev;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (IS_ERR(regmap)) {
1168c2ecf20Sopenharmony_ci		ret = PTR_ERR(regmap);
1178c2ecf20Sopenharmony_ci		dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
1188c2ecf20Sopenharmony_ci		return ret;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci	rdev = devm_regulator_register(&i2c->dev, &mp8859_regulators[0],
1218c2ecf20Sopenharmony_ci					&config);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (IS_ERR(rdev)) {
1248c2ecf20Sopenharmony_ci		ret = PTR_ERR(rdev);
1258c2ecf20Sopenharmony_ci		dev_err(&i2c->dev, "failed to register %s: %d\n",
1268c2ecf20Sopenharmony_ci			mp8859_regulators[0].name, ret);
1278c2ecf20Sopenharmony_ci		return ret;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	return 0;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic const struct of_device_id mp8859_dt_id[] = {
1338c2ecf20Sopenharmony_ci	{.compatible =  "mps,mp8859"},
1348c2ecf20Sopenharmony_ci	{},
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mp8859_dt_id);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic const struct i2c_device_id mp8859_i2c_id[] = {
1398c2ecf20Sopenharmony_ci	{ "mp8859", },
1408c2ecf20Sopenharmony_ci	{  },
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mp8859_i2c_id);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic struct i2c_driver mp8859_regulator_driver = {
1458c2ecf20Sopenharmony_ci	.driver = {
1468c2ecf20Sopenharmony_ci		.name = "mp8859",
1478c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(mp8859_dt_id),
1488c2ecf20Sopenharmony_ci	},
1498c2ecf20Sopenharmony_ci	.probe_new = mp8859_i2c_probe,
1508c2ecf20Sopenharmony_ci	.id_table = mp8859_i2c_id,
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cimodule_i2c_driver(mp8859_regulator_driver);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Monolithic Power Systems MP8859 voltage regulator driver");
1568c2ecf20Sopenharmony_ciMODULE_AUTHOR("Markus Reichl <m.reichl@fivetechno.de>");
1578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
158