brintos

brintos / linux-shallow public Read only

0
0
Text · 9.4 KiB · 7ca139e Raw
431 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2023 Analog Devices, Inc.4 * Author: Antoniu Miclaus <antoniu.miclaus@analog.com>5 */6 7#include <linux/bitops.h>8#include <linux/err.h>9#include <linux/hwmon.h>10#include <linux/i2c.h>11#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/property.h>14#include <linux/regmap.h>15#include <linux/regulator/consumer.h>16 17#define LTC2991_STATUS_LOW		0x0018#define LTC2991_CH_EN_TRIGGER		0x0119#define LTC2991_V1_V4_CTRL		0x0620#define LTC2991_V5_V8_CTRL		0x0721#define LTC2991_PWM_TH_LSB_T_INT	0x0822#define LTC2991_PWM_TH_MSB		0x0923#define LTC2991_CHANNEL_V_MSB(x)	(0x0A + ((x) * 2))24#define LTC2991_CHANNEL_T_MSB(x)	(0x0A + ((x) * 4))25#define LTC2991_CHANNEL_C_MSB(x)	(0x0C + ((x) * 4))26#define LTC2991_T_INT_MSB		0x1A27#define LTC2991_VCC_MSB			0x1C28 29#define LTC2991_V7_V8_EN		BIT(7)30#define LTC2991_V5_V6_EN		BIT(6)31#define LTC2991_V3_V4_EN		BIT(5)32#define LTC2991_V1_V2_EN		BIT(4)33#define LTC2991_T_INT_VCC_EN		BIT(3)34 35#define LTC2991_V3_V4_FILT_EN		BIT(7)36#define LTC2991_V3_V4_TEMP_EN		BIT(5)37#define LTC2991_V3_V4_DIFF_EN		BIT(4)38#define LTC2991_V1_V2_FILT_EN		BIT(3)39#define LTC2991_V1_V2_TEMP_EN		BIT(1)40#define LTC2991_V1_V2_DIFF_EN		BIT(0)41 42#define LTC2991_V7_V8_FILT_EN		BIT(7)43#define LTC2991_V7_V8_TEMP_EN		BIT(5)44#define LTC2991_V7_V8_DIFF_EN		BIT(4)45#define LTC2991_V5_V6_FILT_EN		BIT(3)46#define LTC2991_V5_V6_TEMP_EN		BIT(1)47#define LTC2991_V5_V6_DIFF_EN		BIT(0)48 49#define LTC2991_REPEAT_ACQ_EN		BIT(4)50#define LTC2991_T_INT_FILT_EN		BIT(3)51 52#define LTC2991_MAX_CHANNEL		453#define LTC2991_T_INT_CH_NR		454#define LTC2991_VCC_CH_NR		055 56struct ltc2991_state {57	struct regmap		*regmap;58	u32			r_sense_uohm[LTC2991_MAX_CHANNEL];59	bool			temp_en[LTC2991_MAX_CHANNEL];60};61 62static int ltc2991_read_reg(struct ltc2991_state *st, u8 addr, u8 reg_len,63			    int *val)64{65	__be16 regvals;66	int ret;67 68	if (reg_len < 2)69		return regmap_read(st->regmap, addr, val);70 71	ret = regmap_bulk_read(st->regmap, addr, &regvals, reg_len);72	if (ret)73		return ret;74 75	*val = be16_to_cpu(regvals);76 77	return 0;78}79 80static int ltc2991_get_voltage(struct ltc2991_state *st, u32 reg, long *val)81{82	int reg_val, ret, offset = 0;83 84	ret = ltc2991_read_reg(st, reg, 2, &reg_val);85	if (ret)86		return ret;87 88	if (reg == LTC2991_VCC_MSB)89		/* Vcc 2.5V offset */90		offset = 2500;91 92	/* Vx, 305.18uV/LSB */93	*val = DIV_ROUND_CLOSEST(sign_extend32(reg_val, 14) * 30518,94				 1000 * 100) + offset;95 96	return 0;97}98 99static int ltc2991_read_in(struct device *dev, u32 attr, int channel, long *val)100{101	struct ltc2991_state *st = dev_get_drvdata(dev);102	u32 reg;103 104	switch (attr) {105	case hwmon_in_input:106		if (channel == LTC2991_VCC_CH_NR)107			reg = LTC2991_VCC_MSB;108		else109			reg = LTC2991_CHANNEL_V_MSB(channel - 1);110 111		return ltc2991_get_voltage(st, reg, val);112	default:113		return -EOPNOTSUPP;114	}115}116 117static int ltc2991_get_curr(struct ltc2991_state *st, u32 reg, int channel,118			    long *val)119{120	int reg_val, ret;121 122	ret = ltc2991_read_reg(st, reg, 2, &reg_val);123	if (ret)124		return ret;125 126	/* Vx-Vy, 19.075uV/LSB */127	*val = DIV_ROUND_CLOSEST(sign_extend32(reg_val, 14) * 19075,128				 st->r_sense_uohm[channel]);129 130	return 0;131}132 133static int ltc2991_read_curr(struct device *dev, u32 attr, int channel,134			     long *val)135{136	struct ltc2991_state *st = dev_get_drvdata(dev);137	u32 reg;138 139	switch (attr) {140	case hwmon_curr_input:141		reg = LTC2991_CHANNEL_C_MSB(channel);142		return ltc2991_get_curr(st, reg, channel, val);143	default:144		return -EOPNOTSUPP;145	}146}147 148static int ltc2991_get_temp(struct ltc2991_state *st, u32 reg, int channel,149			    long *val)150{151	int reg_val, ret;152 153	ret = ltc2991_read_reg(st, reg, 2, &reg_val);154	if (ret)155		return ret;156 157	/* Temp LSB = 0.0625 Degrees */158	*val = DIV_ROUND_CLOSEST(sign_extend32(reg_val, 12) * 1000, 16);159 160	return 0;161}162 163static int ltc2991_read_temp(struct device *dev, u32 attr, int channel,164			     long *val)165{166	struct ltc2991_state *st = dev_get_drvdata(dev);167	u32 reg;168 169	switch (attr) {170	case hwmon_temp_input:171		if (channel == LTC2991_T_INT_CH_NR)172			reg = LTC2991_T_INT_MSB;173		else174			reg = LTC2991_CHANNEL_T_MSB(channel);175 176		return ltc2991_get_temp(st, reg, channel, val);177	default:178		return -EOPNOTSUPP;179	}180}181 182static int ltc2991_read(struct device *dev, enum hwmon_sensor_types type,183			u32 attr, int channel, long *val)184{185	switch (type) {186	case hwmon_in:187		return ltc2991_read_in(dev, attr, channel, val);188	case hwmon_curr:189		return ltc2991_read_curr(dev, attr, channel, val);190	case hwmon_temp:191		return ltc2991_read_temp(dev, attr, channel, val);192	default:193		return -EOPNOTSUPP;194	}195}196 197static umode_t ltc2991_is_visible(const void *data,198				  enum hwmon_sensor_types type, u32 attr,199				  int channel)200{201	const struct ltc2991_state *st = data;202 203	switch (type) {204	case hwmon_in:205		switch (attr) {206		case hwmon_in_input:207			if (channel == LTC2991_VCC_CH_NR)208				return 0444;209			if (st->temp_en[(channel - 1) / 2])210				break;211			if (channel % 2)212				return 0444;213			if (!st->r_sense_uohm[(channel - 1) / 2])214				return 0444;215		}216		break;217	case hwmon_curr:218		switch (attr) {219		case hwmon_curr_input:220			if (st->r_sense_uohm[channel])221				return 0444;222			break;223		}224		break;225	case hwmon_temp:226		switch (attr) {227		case hwmon_temp_input:228			if (channel == LTC2991_T_INT_CH_NR ||229			    st->temp_en[channel])230				return 0444;231			break;232		}233		break;234	default:235		break;236	}237 238	return 0;239}240 241static const struct hwmon_ops ltc2991_hwmon_ops = {242	.is_visible = ltc2991_is_visible,243	.read = ltc2991_read,244};245 246static const struct hwmon_channel_info *ltc2991_info[] = {247	HWMON_CHANNEL_INFO(temp,248			   HWMON_T_INPUT,249			   HWMON_T_INPUT,250			   HWMON_T_INPUT,251			   HWMON_T_INPUT,252			   HWMON_T_INPUT253			   ),254	HWMON_CHANNEL_INFO(curr,255			   HWMON_C_INPUT,256			   HWMON_C_INPUT,257			   HWMON_C_INPUT,258			   HWMON_C_INPUT259			   ),260	HWMON_CHANNEL_INFO(in,261			   HWMON_I_INPUT,262			   HWMON_I_INPUT,263			   HWMON_I_INPUT,264			   HWMON_I_INPUT,265			   HWMON_I_INPUT,266			   HWMON_I_INPUT,267			   HWMON_I_INPUT,268			   HWMON_I_INPUT,269			   HWMON_I_INPUT270			   ),271	NULL272};273 274static const struct hwmon_chip_info ltc2991_chip_info = {275	.ops = &ltc2991_hwmon_ops,276	.info = ltc2991_info,277};278 279static const struct regmap_config ltc2991_regmap_config = {280	.reg_bits = 8,281	.val_bits = 8,282	.max_register = 0x1D,283};284 285static int ltc2991_init(struct ltc2991_state *st, struct device *dev)286{287	int ret;288	u32 val, addr;289	u8 v5_v8_reg_data = 0, v1_v4_reg_data = 0;290 291	ret = devm_regulator_get_enable(dev, "vcc");292	if (ret)293		return dev_err_probe(dev, ret,294				     "failed to enable regulator\n");295 296	device_for_each_child_node_scoped(dev, child) {297		ret = fwnode_property_read_u32(child, "reg", &addr);298		if (ret < 0)299			return ret;300 301		if (addr > 3)302			return -EINVAL;303 304		ret = fwnode_property_read_u32(child,305					       "shunt-resistor-micro-ohms",306					       &val);307		if (!ret) {308			if (!val)309				return dev_err_probe(dev, -EINVAL,310						     "shunt resistor value cannot be zero\n");311 312			st->r_sense_uohm[addr] = val;313 314			switch (addr) {315			case 0:316				v1_v4_reg_data |= LTC2991_V1_V2_DIFF_EN;317				break;318			case 1:319				v1_v4_reg_data |= LTC2991_V3_V4_DIFF_EN;320				break;321			case 2:322				v5_v8_reg_data |= LTC2991_V5_V6_DIFF_EN;323				break;324			case 3:325				v5_v8_reg_data |= LTC2991_V7_V8_DIFF_EN;326				break;327			default:328				break;329			}330		}331 332		ret = fwnode_property_read_bool(child,333						"adi,temperature-enable");334		if (ret) {335			st->temp_en[addr] = ret;336 337			switch (addr) {338			case 0:339				v1_v4_reg_data |= LTC2991_V1_V2_TEMP_EN;340				break;341			case 1:342				v1_v4_reg_data |= LTC2991_V3_V4_TEMP_EN;343				break;344			case 2:345				v5_v8_reg_data |= LTC2991_V5_V6_TEMP_EN;346				break;347			case 3:348				v5_v8_reg_data |= LTC2991_V7_V8_TEMP_EN;349				break;350			default:351				break;352			}353		}354	}355 356	ret = regmap_write(st->regmap, LTC2991_V5_V8_CTRL, v5_v8_reg_data);357	if (ret)358		return dev_err_probe(dev, ret,359				     "Error: Failed to set V5-V8 CTRL reg.\n");360 361	ret = regmap_write(st->regmap, LTC2991_V1_V4_CTRL, v1_v4_reg_data);362	if (ret)363		return dev_err_probe(dev, ret,364				     "Error: Failed to set V1-V4 CTRL reg.\n");365 366	ret = regmap_write(st->regmap, LTC2991_PWM_TH_LSB_T_INT,367			   LTC2991_REPEAT_ACQ_EN);368	if (ret)369		return dev_err_probe(dev, ret,370				     "Error: Failed to set continuous mode.\n");371 372	/* Enable all channels and trigger conversions */373	return regmap_write(st->regmap, LTC2991_CH_EN_TRIGGER,374			    LTC2991_V7_V8_EN | LTC2991_V5_V6_EN |375			    LTC2991_V3_V4_EN | LTC2991_V1_V2_EN |376			    LTC2991_T_INT_VCC_EN);377}378 379static int ltc2991_i2c_probe(struct i2c_client *client)380{381	int ret;382	struct device *hwmon_dev;383	struct ltc2991_state *st;384 385	st = devm_kzalloc(&client->dev, sizeof(*st), GFP_KERNEL);386	if (!st)387		return -ENOMEM;388 389	st->regmap = devm_regmap_init_i2c(client, &ltc2991_regmap_config);390	if (IS_ERR(st->regmap))391		return PTR_ERR(st->regmap);392 393	ret = ltc2991_init(st, &client->dev);394	if (ret)395		return ret;396 397	hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,398							 client->name, st,399							 &ltc2991_chip_info,400							 NULL);401 402	return PTR_ERR_OR_ZERO(hwmon_dev);403}404 405static const struct of_device_id ltc2991_of_match[] = {406	{ .compatible = "adi,ltc2991" },407	{ }408};409MODULE_DEVICE_TABLE(of, ltc2991_of_match);410 411static const struct i2c_device_id ltc2991_i2c_id[] = {412	{ "ltc2991" },413	{}414};415MODULE_DEVICE_TABLE(i2c, ltc2991_i2c_id);416 417static struct i2c_driver ltc2991_i2c_driver = {418	.driver = {419		.name = "ltc2991",420		.of_match_table = ltc2991_of_match,421	},422	.probe = ltc2991_i2c_probe,423	.id_table = ltc2991_i2c_id,424};425 426module_i2c_driver(ltc2991_i2c_driver);427 428MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");429MODULE_DESCRIPTION("Analog Devices LTC2991 HWMON Driver");430MODULE_LICENSE("GPL");431