brintos

brintos / linux-shallow public Read only

0
0
Text · 9.5 KiB · ab105ff Raw
409 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Copyright (c) 2019 five technologies GmbH4// Author: Markus Reichl <m.reichl@fivetechno.de>5 6#include <linux/module.h>7#include <linux/i2c.h>8#include <linux/of.h>9#include <linux/regulator/driver.h>10#include <linux/regmap.h>11 12 13#define VOL_MIN_IDX			0x0014#define VOL_MAX_IDX			0x7ff15 16/* Register definitions */17#define MP8859_VOUT_L_REG		0    //3 lo Bits18#define MP8859_VOUT_H_REG		1    //8 hi Bits19#define MP8859_VOUT_GO_REG		220#define MP8859_IOUT_LIM_REG		321#define MP8859_CTL1_REG			422#define MP8859_CTL2_REG			523#define MP8859_RESERVED1_REG		624#define MP8859_RESERVED2_REG		725#define MP8859_RESERVED3_REG		826#define MP8859_STATUS_REG		927#define MP8859_INTERRUPT_REG		0x0A28#define MP8859_MASK_REG			0x0B29#define MP8859_ID1_REG			0x0C30#define MP8859_MFR_ID_REG		0x2731#define MP8859_DEV_ID_REG		0x2832#define MP8859_IC_REV_REG		0x2933 34#define MP8859_MAX_REG			0x2935 36#define MP8859_GO_BIT			0x0137 38#define MP8859_IOUT_LIM_MASK		0x7f39 40#define MP8859_ENABLE_MASK		0x8041#define MP8859_DISCHG_EN_MASK		0x1042#define MP8859_MODE_MASK		0x0843 44#define MP8859_PG_MASK			0x8045#define MP8859_OTP_MASK			0x4046#define MP8859_OTW_MASK			0x2047#define MP8859_CC_CV_MASK		0x1048 49static int mp8859_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)50{51	int ret;52 53	ret = regmap_write(rdev->regmap, MP8859_VOUT_L_REG, sel & 0x7);54 55	if (ret)56		return ret;57	ret = regmap_write(rdev->regmap, MP8859_VOUT_H_REG, sel >> 3);58 59	if (ret)60		return ret;61	ret = regmap_update_bits(rdev->regmap, MP8859_VOUT_GO_REG,62					MP8859_GO_BIT, 1);63	return ret;64}65 66static int mp8859_get_voltage_sel(struct regulator_dev *rdev)67{68	unsigned int val_tmp;69	unsigned int val;70	int ret;71 72	ret = regmap_read(rdev->regmap, MP8859_VOUT_H_REG, &val_tmp);73 74	if (ret)75		return ret;76	val = val_tmp << 3;77 78	ret = regmap_read(rdev->regmap, MP8859_VOUT_L_REG, &val_tmp);79 80	if (ret)81		return ret;82	val |= val_tmp & 0x07;83	return val;84}85 86static int mp8859_set_voltage_time_sel(struct regulator_dev *rdev,87				       unsigned int from, unsigned int to)88{89	int change;90 91	/* The voltage ramps at 1mV/uS, selectors are 10mV */92	if (from > to)93		change = from - to;94	else95		change = to - from;96 97	return change * 10 * 1000;98}99 100static unsigned int mp8859_get_mode(struct regulator_dev *rdev)101{102	unsigned int val;103	int ret;104 105	ret = regmap_read(rdev->regmap, MP8859_CTL1_REG, &val);106	if (ret != 0) {107		dev_err(&rdev->dev, "Failed to read mode: %d\n", ret);108		return 0;109	}110 111	if (val & MP8859_MODE_MASK)112		return REGULATOR_MODE_FAST;113	else114		return REGULATOR_MODE_NORMAL;115}116 117static int mp8859_set_mode(struct regulator_dev *rdev, unsigned int mode)118{119	unsigned int val;120 121	switch (mode) {122	case REGULATOR_MODE_FAST:123		val = MP8859_MODE_MASK;124		break;125	case REGULATOR_MODE_NORMAL:126		val = 0;127		break;128	default:129		return -EINVAL;130	}131 132	return regmap_update_bits(rdev->regmap, MP8859_CTL1_REG,133				  MP8859_MODE_MASK, val);134}135 136static int mp8859_set_current_limit(struct regulator_dev *rdev,137				    int min_uA, int max_uA)138{139	unsigned int cur_val, new_val;140	int ret, i;141 142	/* Steps of 50mA */143	new_val = max_uA / 50000;144	if (new_val > MP8859_IOUT_LIM_MASK)145		return -EINVAL;146	if (new_val == 0)147		return -EINVAL;148 149	/*150	 * If the regulator is limiting then ramp gradually as per151	 * datasheet, otherwise just set the value directly.152	 */153	ret = regmap_read(rdev->regmap, MP8859_STATUS_REG, &cur_val);154	if (ret != 0)155		return ret;156	if (!(cur_val & MP8859_CC_CV_MASK)) {157		return regmap_update_bits(rdev->regmap, MP8859_IOUT_LIM_REG,158					  MP8859_IOUT_LIM_MASK, new_val);159	}160 161	ret = regmap_read(rdev->regmap, MP8859_IOUT_LIM_REG, &cur_val);162	if (ret != 0)163		return ret;164 165	if (cur_val >= new_val) {166		for (i = cur_val; i >= new_val; i--) {167			ret = regmap_update_bits(rdev->regmap,168						 MP8859_IOUT_LIM_REG,169						 MP8859_IOUT_LIM_MASK,170						 cur_val - i);171			if (ret != 0)172				return ret;173		}174	} else {175		for (i = cur_val; i <= new_val; i++) {176			ret = regmap_update_bits(rdev->regmap,177						 MP8859_IOUT_LIM_REG,178						 MP8859_IOUT_LIM_MASK,179						 cur_val + i);180			if (ret != 0)181				return ret;182		}183	}184 185	return 0;186}187 188static int mp8859_get_status(struct regulator_dev *rdev)189{190	unsigned int val;191	int ret;192 193	/* Output status is only meaingful when enabled */194	ret = regmap_read(rdev->regmap, MP8859_CTL1_REG, &val);195	if (ret != 0)196		return ret;197	if (!(val & MP8859_ENABLE_MASK))198		return REGULATOR_STATUS_UNDEFINED;199 200	ret = regmap_read(rdev->regmap, MP8859_STATUS_REG, &val);201	if (ret != 0)202		return ret;203 204	if (val & MP8859_PG_MASK)205		return REGULATOR_STATUS_ON;206	else207		return REGULATOR_STATUS_ERROR;208}209 210static int mp8859_get_error_flags(struct regulator_dev *rdev,211				  unsigned int *flags)212{213	unsigned int status, enabled;214	int ret;215 216	*flags = 0;217 218	/* Output status is only meaingful when enabled */219	ret = regmap_read(rdev->regmap, MP8859_CTL1_REG, &enabled);220	if (ret != 0)221		return ret;222	enabled &= MP8859_ENABLE_MASK;223 224	ret = regmap_read(rdev->regmap, MP8859_STATUS_REG, &status);225	if (ret != 0)226		return ret;227 228	if (enabled && !(status & MP8859_PG_MASK))229		status |= REGULATOR_ERROR_FAIL;230	if (status & MP8859_OTP_MASK)231		status |= REGULATOR_ERROR_OVER_TEMP;232	if (status & MP8859_OTW_MASK)233		status |= REGULATOR_ERROR_OVER_TEMP_WARN;234	if (status & MP8859_CC_CV_MASK)235		status |= REGULATOR_ERROR_OVER_CURRENT;236 237	return 0;238}239 240static const struct linear_range mp8859_dcdc_ranges[] = {241	REGULATOR_LINEAR_RANGE(0, VOL_MIN_IDX, VOL_MAX_IDX, 10000),242};243 244static bool mp8859_readable(struct device *dev, unsigned int reg)245{246	switch (reg) {247	case MP8859_VOUT_L_REG:248	case MP8859_VOUT_H_REG:249	case MP8859_VOUT_GO_REG:250	case MP8859_IOUT_LIM_REG:251	case MP8859_CTL1_REG:252	case MP8859_CTL2_REG:253	case MP8859_STATUS_REG:254	case MP8859_INTERRUPT_REG:255	case MP8859_MASK_REG:256	case MP8859_ID1_REG:257	case MP8859_MFR_ID_REG:258	case MP8859_DEV_ID_REG:259	case MP8859_IC_REV_REG:260		return true;261	default:262		return false;263	}264}265 266static bool mp8859_volatile(struct device *dev, unsigned int reg)267{268	switch (reg) {269	case MP8859_VOUT_GO_REG:270	case MP8859_STATUS_REG:271	case MP8859_INTERRUPT_REG:272		return true;273	default:274		return false;275	}276}277 278static const struct regmap_config mp8859_regmap = {279	.reg_bits = 8,280	.val_bits = 8,281	.max_register = MP8859_MAX_REG,282	.cache_type = REGCACHE_MAPLE,283	.readable_reg = mp8859_readable,284	.volatile_reg = mp8859_volatile,285};286 287static const struct regulator_ops mp8859_ops = {288	.set_voltage_sel = mp8859_set_voltage_sel,289	.get_voltage_sel = mp8859_get_voltage_sel,290	.list_voltage = regulator_list_voltage_linear_range,291	.set_voltage_time_sel = mp8859_set_voltage_time_sel,292	.enable = regulator_enable_regmap,293	.disable = regulator_disable_regmap,294	.is_enabled = regulator_is_enabled_regmap,295	.set_mode = mp8859_set_mode,296	.get_mode = mp8859_get_mode,297	.set_active_discharge = regulator_set_active_discharge_regmap,298	.set_current_limit = mp8859_set_current_limit,299	.get_status = mp8859_get_status,300	.get_error_flags = mp8859_get_error_flags,301};302 303static const struct regulator_desc mp8859_regulators[] = {304	{305		.id = 0,306		.type = REGULATOR_VOLTAGE,307		.name = "mp8859_dcdc",308		.supply_name = "vin",309		.of_match = of_match_ptr("mp8859_dcdc"),310		.n_voltages = VOL_MAX_IDX + 1,311		.linear_ranges = mp8859_dcdc_ranges,312		.n_linear_ranges = 1,313		.enable_reg = MP8859_CTL1_REG,314		.enable_mask = MP8859_ENABLE_MASK,315		.enable_val = MP8859_ENABLE_MASK,316		.active_discharge_reg = MP8859_CTL1_REG,317		.active_discharge_on = MP8859_DISCHG_EN_MASK,318		.active_discharge_mask = MP8859_DISCHG_EN_MASK,319		.ops = &mp8859_ops,320		.owner = THIS_MODULE,321	},322};323 324static int mp8859_i2c_probe(struct i2c_client *i2c)325{326	int ret;327	struct regulator_config config = {.dev = &i2c->dev};328	struct regmap *regmap = devm_regmap_init_i2c(i2c, &mp8859_regmap);329	struct regulator_dev *rdev;330	unsigned int val, rev;331 332	if (IS_ERR(regmap)) {333		ret = PTR_ERR(regmap);334		dev_err(&i2c->dev, "regmap init failed: %d\n", ret);335		return ret;336	}337 338	ret = regmap_read(regmap, MP8859_MFR_ID_REG, &val);339	if (ret != 0) {340		dev_err(&i2c->dev, "Failed to read manufacturer ID: %d\n", ret);341		return ret;342	}343	if (val != 0x9) {344		dev_err(&i2c->dev, "Manufacturer ID %x != 9\n", val);345		return -EINVAL;346	}347 348	ret = regmap_read(regmap, MP8859_DEV_ID_REG, &val);349	if (ret != 0) {350		dev_err(&i2c->dev, "Failed to read device ID: %d\n", ret);351		return ret;352	}353	if (val != 0x58) {354		dev_err(&i2c->dev, "Manufacturer ID %x != 0x58\n", val);355		return -EINVAL;356	}357 358	ret = regmap_read(regmap, MP8859_IC_REV_REG, &rev);359	if (ret != 0) {360		dev_err(&i2c->dev, "Failed to read device revision: %d\n", ret);361		return ret;362	}363	ret = regmap_read(regmap, MP8859_ID1_REG, &val);364	if (ret != 0) {365		dev_err(&i2c->dev, "Failed to read device ID1: %d\n", ret);366		return ret;367	}368	dev_info(&i2c->dev, "MP8859-%04d revision %d\n", val, rev);369 370	rdev = devm_regulator_register(&i2c->dev, &mp8859_regulators[0],371					&config);372 373	if (IS_ERR(rdev)) {374		ret = PTR_ERR(rdev);375		dev_err(&i2c->dev, "failed to register %s: %d\n",376			mp8859_regulators[0].name, ret);377		return ret;378	}379	return 0;380}381 382static const struct of_device_id mp8859_dt_id[] __maybe_unused = {383	{.compatible =  "mps,mp8859"},384	{},385};386MODULE_DEVICE_TABLE(of, mp8859_dt_id);387 388static const struct i2c_device_id mp8859_i2c_id[] = {389	{ "mp8859", },390	{  },391};392MODULE_DEVICE_TABLE(i2c, mp8859_i2c_id);393 394static struct i2c_driver mp8859_regulator_driver = {395	.driver = {396		.name = "mp8859",397		.probe_type = PROBE_PREFER_ASYNCHRONOUS,398		.of_match_table = of_match_ptr(mp8859_dt_id),399	},400	.probe = mp8859_i2c_probe,401	.id_table = mp8859_i2c_id,402};403 404module_i2c_driver(mp8859_regulator_driver);405 406MODULE_DESCRIPTION("Monolithic Power Systems MP8859 voltage regulator driver");407MODULE_AUTHOR("Markus Reichl <m.reichl@fivetechno.de>");408MODULE_LICENSE("GPL v2");409