brintos

brintos / linux-shallow public Read only

0
0
Text · 5.9 KiB · 8b2c965 Raw
266 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ADM1177 Hot Swap Controller and Digital Power Monitor with Soft Start Pin4 *5 * Copyright 2015-2019 Analog Devices Inc.6 */7 8#include <linux/bits.h>9#include <linux/device.h>10#include <linux/hwmon.h>11#include <linux/i2c.h>12#include <linux/init.h>13#include <linux/module.h>14#include <linux/regulator/consumer.h>15 16/*  Command Byte Operations */17#define ADM1177_CMD_V_CONT	BIT(0)18#define ADM1177_CMD_I_CONT	BIT(2)19#define ADM1177_CMD_VRANGE	BIT(4)20 21/* Extended Register */22#define ADM1177_REG_ALERT_TH	223 24#define ADM1177_BITS		1225 26/**27 * struct adm1177_state - driver instance specific data28 * @client:		pointer to i2c client29 * @r_sense_uohm:	current sense resistor value30 * @alert_threshold_ua:	current limit for shutdown31 * @vrange_high:	internal voltage divider32 */33struct adm1177_state {34	struct i2c_client	*client;35	u32			r_sense_uohm;36	u32			alert_threshold_ua;37	bool			vrange_high;38};39 40static int adm1177_read_raw(struct adm1177_state *st, u8 num, u8 *data)41{42	return i2c_master_recv(st->client, data, num);43}44 45static int adm1177_write_cmd(struct adm1177_state *st, u8 cmd)46{47	return i2c_smbus_write_byte(st->client, cmd);48}49 50static int adm1177_write_alert_thr(struct adm1177_state *st,51				   u32 alert_threshold_ua)52{53	u64 val;54	int ret;55 56	val = 0xFFULL * alert_threshold_ua * st->r_sense_uohm;57	val = div_u64(val, 105840000U);58	val = div_u64(val, 1000U);59	if (val > 0xFF)60		val = 0xFF;61 62	ret = i2c_smbus_write_byte_data(st->client, ADM1177_REG_ALERT_TH,63					val);64	if (ret)65		return ret;66 67	st->alert_threshold_ua = alert_threshold_ua;68	return 0;69}70 71static int adm1177_read(struct device *dev, enum hwmon_sensor_types type,72			u32 attr, int channel, long *val)73{74	struct adm1177_state *st = dev_get_drvdata(dev);75	u8 data[3];76	long dummy;77	int ret;78 79	switch (type) {80	case hwmon_curr:81		switch (attr) {82		case hwmon_curr_input:83			ret = adm1177_read_raw(st, 3, data);84			if (ret < 0)85				return ret;86			dummy = (data[1] << 4) | (data[2] & 0xF);87			/*88			 * convert to milliamperes89			 * ((105.84mV / 4096) x raw) / senseResistor(ohm)90			 */91			*val = div_u64((105840000ull * dummy),92				       4096 * st->r_sense_uohm);93			return 0;94		case hwmon_curr_max_alarm:95			*val = st->alert_threshold_ua;96			return 0;97		default:98			return -EOPNOTSUPP;99		}100	case hwmon_in:101		ret = adm1177_read_raw(st, 3, data);102		if (ret < 0)103			return ret;104		dummy = (data[0] << 4) | (data[2] >> 4);105		/*106		 * convert to millivolts based on resistor devision107		 * (V_fullscale / 4096) * raw108		 */109		if (st->vrange_high)110			dummy *= 26350;111		else112			dummy *= 6650;113 114		*val = DIV_ROUND_CLOSEST(dummy, 4096);115		return 0;116	default:117		return -EOPNOTSUPP;118	}119}120 121static int adm1177_write(struct device *dev, enum hwmon_sensor_types type,122			 u32 attr, int channel, long val)123{124	struct adm1177_state *st = dev_get_drvdata(dev);125 126	switch (type) {127	case hwmon_curr:128		switch (attr) {129		case hwmon_curr_max_alarm:130			adm1177_write_alert_thr(st, val);131			return 0;132		default:133			return -EOPNOTSUPP;134		}135	default:136		return -EOPNOTSUPP;137	}138}139 140static umode_t adm1177_is_visible(const void *data,141				  enum hwmon_sensor_types type,142				  u32 attr, int channel)143{144	const struct adm1177_state *st = data;145 146	switch (type) {147	case hwmon_in:148		switch (attr) {149		case hwmon_in_input:150			return 0444;151		}152		break;153	case hwmon_curr:154		switch (attr) {155		case hwmon_curr_input:156			if (st->r_sense_uohm)157				return 0444;158			return 0;159		case hwmon_curr_max_alarm:160			if (st->r_sense_uohm)161				return 0644;162			return 0;163		}164		break;165	default:166		break;167	}168	return 0;169}170 171static const struct hwmon_channel_info * const adm1177_info[] = {172	HWMON_CHANNEL_INFO(curr,173			   HWMON_C_INPUT | HWMON_C_MAX_ALARM),174	HWMON_CHANNEL_INFO(in,175			   HWMON_I_INPUT),176	NULL177};178 179static const struct hwmon_ops adm1177_hwmon_ops = {180	.is_visible = adm1177_is_visible,181	.read = adm1177_read,182	.write = adm1177_write,183};184 185static const struct hwmon_chip_info adm1177_chip_info = {186	.ops = &adm1177_hwmon_ops,187	.info = adm1177_info,188};189 190static int adm1177_probe(struct i2c_client *client)191{192	struct device *dev = &client->dev;193	struct device *hwmon_dev;194	struct adm1177_state *st;195	u32 alert_threshold_ua;196	int ret;197 198	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);199	if (!st)200		return -ENOMEM;201 202	st->client = client;203 204	ret = devm_regulator_get_enable_optional(&client->dev, "vref");205	if (ret == -EPROBE_DEFER)206		return -EPROBE_DEFER;207 208	if (device_property_read_u32(dev, "shunt-resistor-micro-ohms",209				     &st->r_sense_uohm))210		st->r_sense_uohm = 0;211	if (device_property_read_u32(dev, "adi,shutdown-threshold-microamp",212				     &alert_threshold_ua)) {213		if (st->r_sense_uohm)214			/*215			 * set maximum default value from datasheet based on216			 * shunt-resistor217			 */218			alert_threshold_ua = div_u64(105840000000,219						     st->r_sense_uohm);220		else221			alert_threshold_ua = 0;222	}223	st->vrange_high = device_property_read_bool(dev,224						    "adi,vrange-high-enable");225	if (alert_threshold_ua && st->r_sense_uohm)226		adm1177_write_alert_thr(st, alert_threshold_ua);227 228	ret = adm1177_write_cmd(st, ADM1177_CMD_V_CONT |229				    ADM1177_CMD_I_CONT |230				    (st->vrange_high ? 0 : ADM1177_CMD_VRANGE));231	if (ret)232		return ret;233 234	hwmon_dev =235		devm_hwmon_device_register_with_info(dev, client->name, st,236						     &adm1177_chip_info, NULL);237	return PTR_ERR_OR_ZERO(hwmon_dev);238}239 240static const struct i2c_device_id adm1177_id[] = {241	{"adm1177"},242	{}243};244MODULE_DEVICE_TABLE(i2c, adm1177_id);245 246static const struct of_device_id adm1177_dt_ids[] = {247	{ .compatible = "adi,adm1177" },248	{},249};250MODULE_DEVICE_TABLE(of, adm1177_dt_ids);251 252static struct i2c_driver adm1177_driver = {253	.driver = {254		.name = "adm1177",255		.of_match_table = adm1177_dt_ids,256	},257	.probe = adm1177_probe,258	.id_table = adm1177_id,259};260module_i2c_driver(adm1177_driver);261 262MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>");263MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");264MODULE_DESCRIPTION("Analog Devices ADM1177 ADC driver");265MODULE_LICENSE("GPL v2");266