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, ®vals, 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, ®_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, ®_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, ®_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 = <c2991_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, <c2991_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 <c2991_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