1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Analog Devices LTC2947 high precision power and energy monitor
- *
- * Copyright 2019 Analog Devices Inc.
- */
- #include <linux/bitfield.h>
- #include <linux/bits.h>
- #include <linux/clk.h>
- #include <linux/device.h>
- #include <linux/hwmon.h>
- #include <linux/hwmon-sysfs.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/regmap.h>
- #include "ltc2947.h"
- /* register's */
- #define LTC2947_REG_PAGE_CTRL 0xFF
- #define LTC2947_REG_CTRL 0xF0
- #define LTC2947_REG_TBCTL 0xE9
- #define LTC2947_CONT_MODE_MASK BIT(3)
- #define LTC2947_CONT_MODE(x) FIELD_PREP(LTC2947_CONT_MODE_MASK, x)
- #define LTC2947_PRE_MASK GENMASK(2, 0)
- #define LTC2947_PRE(x) FIELD_PREP(LTC2947_PRE_MASK, x)
- #define LTC2947_DIV_MASK GENMASK(7, 3)
- #define LTC2947_DIV(x) FIELD_PREP(LTC2947_DIV_MASK, x)
- #define LTC2947_SHUTDOWN_MASK BIT(0)
- #define LTC2947_REG_ACCUM_POL 0xE1
- #define LTC2947_ACCUM_POL_1_MASK GENMASK(1, 0)
- #define LTC2947_ACCUM_POL_1(x) FIELD_PREP(LTC2947_ACCUM_POL_1_MASK, x)
- #define LTC2947_ACCUM_POL_2_MASK GENMASK(3, 2)
- #define LTC2947_ACCUM_POL_2(x) FIELD_PREP(LTC2947_ACCUM_POL_2_MASK, x)
- #define LTC2947_REG_ACCUM_DEADBAND 0xE4
- #define LTC2947_REG_GPIOSTATCTL 0x67
- #define LTC2947_GPIO_EN_MASK BIT(0)
- #define LTC2947_GPIO_EN(x) FIELD_PREP(LTC2947_GPIO_EN_MASK, x)
- #define LTC2947_GPIO_FAN_EN_MASK BIT(6)
- #define LTC2947_GPIO_FAN_EN(x) FIELD_PREP(LTC2947_GPIO_FAN_EN_MASK, x)
- #define LTC2947_GPIO_FAN_POL_MASK BIT(7)
- #define LTC2947_GPIO_FAN_POL(x) FIELD_PREP(LTC2947_GPIO_FAN_POL_MASK, x)
- #define LTC2947_REG_GPIO_ACCUM 0xE3
- /* 200Khz */
- #define LTC2947_CLK_MIN 200000
- /* 25Mhz */
- #define LTC2947_CLK_MAX 25000000
- #define LTC2947_PAGE0 0
- #define LTC2947_PAGE1 1
- /* Voltage registers */
- #define LTC2947_REG_VOLTAGE 0xA0
- #define LTC2947_REG_VOLTAGE_MAX 0x50
- #define LTC2947_REG_VOLTAGE_MIN 0x52
- #define LTC2947_REG_VOLTAGE_THRE_H 0x90
- #define LTC2947_REG_VOLTAGE_THRE_L 0x92
- #define LTC2947_REG_DVCC 0xA4
- #define LTC2947_REG_DVCC_MAX 0x58
- #define LTC2947_REG_DVCC_MIN 0x5A
- #define LTC2947_REG_DVCC_THRE_H 0x98
- #define LTC2947_REG_DVCC_THRE_L 0x9A
- #define LTC2947_VOLTAGE_GEN_CHAN 0
- #define LTC2947_VOLTAGE_DVCC_CHAN 1
- /* in mV */
- #define VOLTAGE_MAX 15500
- #define VOLTAGE_MIN -300
- #define VDVCC_MAX 15000
- #define VDVCC_MIN 4750
- /* Current registers */
- #define LTC2947_REG_CURRENT 0x90
- #define LTC2947_REG_CURRENT_MAX 0x40
- #define LTC2947_REG_CURRENT_MIN 0x42
- #define LTC2947_REG_CURRENT_THRE_H 0x80
- #define LTC2947_REG_CURRENT_THRE_L 0x82
- /* in mA */
- #define CURRENT_MAX 30000
- #define CURRENT_MIN -30000
- /* Power registers */
- #define LTC2947_REG_POWER 0x93
- #define LTC2947_REG_POWER_MAX 0x44
- #define LTC2947_REG_POWER_MIN 0x46
- #define LTC2947_REG_POWER_THRE_H 0x84
- #define LTC2947_REG_POWER_THRE_L 0x86
- /* in uW */
- #define POWER_MAX 450000000
- #define POWER_MIN -450000000
- /* Temperature registers */
- #define LTC2947_REG_TEMP 0xA2
- #define LTC2947_REG_TEMP_MAX 0x54
- #define LTC2947_REG_TEMP_MIN 0x56
- #define LTC2947_REG_TEMP_THRE_H 0x94
- #define LTC2947_REG_TEMP_THRE_L 0x96
- #define LTC2947_REG_TEMP_FAN_THRE_H 0x9C
- #define LTC2947_REG_TEMP_FAN_THRE_L 0x9E
- #define LTC2947_TEMP_FAN_CHAN 1
- /* in millidegress Celsius */
- #define TEMP_MAX 85000
- #define TEMP_MIN -40000
- /* Energy registers */
- #define LTC2947_REG_ENERGY1 0x06
- #define LTC2947_REG_ENERGY2 0x16
- /* Status/Alarm/Overflow registers */
- #define LTC2947_REG_STATUS 0x80
- #define LTC2947_REG_STATVT 0x81
- #define LTC2947_REG_STATIP 0x82
- #define LTC2947_REG_STATVDVCC 0x87
- #define LTC2947_ALERTS_SIZE (LTC2947_REG_STATVDVCC - LTC2947_REG_STATUS)
- #define LTC2947_MAX_VOLTAGE_MASK BIT(0)
- #define LTC2947_MIN_VOLTAGE_MASK BIT(1)
- #define LTC2947_MAX_CURRENT_MASK BIT(0)
- #define LTC2947_MIN_CURRENT_MASK BIT(1)
- #define LTC2947_MAX_POWER_MASK BIT(2)
- #define LTC2947_MIN_POWER_MASK BIT(3)
- #define LTC2947_MAX_TEMP_MASK BIT(2)
- #define LTC2947_MIN_TEMP_MASK BIT(3)
- #define LTC2947_MAX_TEMP_FAN_MASK BIT(4)
- #define LTC2947_MIN_TEMP_FAN_MASK BIT(5)
- struct ltc2947_data {
- struct regmap *map;
- struct device *dev;
- /*
- * The mutex is needed because the device has 2 memory pages. When
- * reading/writing the correct page needs to be set so that, the
- * complete sequence select_page->read/write needs to be protected.
- */
- struct mutex lock;
- u32 lsb_energy;
- bool gpio_out;
- };
- static int __ltc2947_val_read16(const struct ltc2947_data *st, const u8 reg,
- u64 *val)
- {
- __be16 __val = 0;
- int ret;
- ret = regmap_bulk_read(st->map, reg, &__val, 2);
- if (ret)
- return ret;
- *val = be16_to_cpu(__val);
- return 0;
- }
- static int __ltc2947_val_read24(const struct ltc2947_data *st, const u8 reg,
- u64 *val)
- {
- __be32 __val = 0;
- int ret;
- ret = regmap_bulk_read(st->map, reg, &__val, 3);
- if (ret)
- return ret;
- *val = be32_to_cpu(__val) >> 8;
- return 0;
- }
- static int __ltc2947_val_read64(const struct ltc2947_data *st, const u8 reg,
- u64 *val)
- {
- __be64 __val = 0;
- int ret;
- ret = regmap_bulk_read(st->map, reg, &__val, 6);
- if (ret)
- return ret;
- *val = be64_to_cpu(__val) >> 16;
- return 0;
- }
- static int ltc2947_val_read(struct ltc2947_data *st, const u8 reg,
- const u8 page, const size_t size, s64 *val)
- {
- int ret;
- u64 __val = 0;
- mutex_lock(&st->lock);
- ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page);
- if (ret) {
- mutex_unlock(&st->lock);
- return ret;
- }
- dev_dbg(st->dev, "Read val, reg:%02X, p:%d sz:%zu\n", reg, page,
- size);
- switch (size) {
- case 2:
- ret = __ltc2947_val_read16(st, reg, &__val);
- break;
- case 3:
- ret = __ltc2947_val_read24(st, reg, &__val);
- break;
- case 6:
- ret = __ltc2947_val_read64(st, reg, &__val);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- mutex_unlock(&st->lock);
- if (ret)
- return ret;
- *val = sign_extend64(__val, (8 * size) - 1);
- dev_dbg(st->dev, "Got s:%lld, u:%016llX\n", *val, __val);
- return 0;
- }
- static int __ltc2947_val_write64(const struct ltc2947_data *st, const u8 reg,
- const u64 val)
- {
- __be64 __val;
- __val = cpu_to_be64(val << 16);
- return regmap_bulk_write(st->map, reg, &__val, 6);
- }
- static int __ltc2947_val_write16(const struct ltc2947_data *st, const u8 reg,
- const u16 val)
- {
- __be16 __val;
- __val = cpu_to_be16(val);
- return regmap_bulk_write(st->map, reg, &__val, 2);
- }
- static int ltc2947_val_write(struct ltc2947_data *st, const u8 reg,
- const u8 page, const size_t size, const u64 val)
- {
- int ret;
- mutex_lock(&st->lock);
- /* set device on correct page */
- ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page);
- if (ret) {
- mutex_unlock(&st->lock);
- return ret;
- }
- dev_dbg(st->dev, "Write val, r:%02X, p:%d, sz:%zu, val:%016llX\n",
- reg, page, size, val);
- switch (size) {
- case 2:
- ret = __ltc2947_val_write16(st, reg, val);
- break;
- case 6:
- ret = __ltc2947_val_write64(st, reg, val);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- mutex_unlock(&st->lock);
- return ret;
- }
- static int ltc2947_reset_history(struct ltc2947_data *st, const u8 reg_h,
- const u8 reg_l)
- {
- int ret;
- /*
- * let's reset the tracking register's. Tracking register's have all
- * 2 bytes size
- */
- ret = ltc2947_val_write(st, reg_h, LTC2947_PAGE0, 2, 0x8000U);
- if (ret)
- return ret;
- return ltc2947_val_write(st, reg_l, LTC2947_PAGE0, 2, 0x7FFFU);
- }
- static int ltc2947_alarm_read(struct ltc2947_data *st, const u8 reg,
- const u32 mask, long *val)
- {
- u8 offset = reg - LTC2947_REG_STATUS;
- /* +1 to include status reg */
- char alarms[LTC2947_ALERTS_SIZE + 1];
- int ret = 0;
- memset(alarms, 0, sizeof(alarms));
- mutex_lock(&st->lock);
- ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, LTC2947_PAGE0);
- if (ret)
- goto unlock;
- dev_dbg(st->dev, "Read alarm, reg:%02X, mask:%02X\n", reg, mask);
- /*
- * As stated in the datasheet, when Threshold and Overflow registers
- * are used, the status and all alert registers must be read in one
- * multi-byte transaction.
- */
- ret = regmap_bulk_read(st->map, LTC2947_REG_STATUS, alarms,
- sizeof(alarms));
- if (ret)
- goto unlock;
- /* get the alarm */
- *val = !!(alarms[offset] & mask);
- unlock:
- mutex_unlock(&st->lock);
- return ret;
- }
- static ssize_t ltc2947_show_value(struct device *dev,
- struct device_attribute *da, char *buf)
- {
- struct ltc2947_data *st = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- int ret;
- s64 val = 0;
- ret = ltc2947_val_read(st, attr->index, LTC2947_PAGE0, 6, &val);
- if (ret)
- return ret;
- /* value in microJoule. st->lsb_energy was multiplied by 10E9 */
- val = div_s64(val * st->lsb_energy, 1000);
- return sprintf(buf, "%lld\n", val);
- }
- static int ltc2947_read_temp(struct device *dev, const u32 attr, long *val,
- const int channel)
- {
- int ret;
- struct ltc2947_data *st = dev_get_drvdata(dev);
- s64 __val = 0;
- switch (attr) {
- case hwmon_temp_input:
- ret = ltc2947_val_read(st, LTC2947_REG_TEMP, LTC2947_PAGE0,
- 2, &__val);
- break;
- case hwmon_temp_highest:
- ret = ltc2947_val_read(st, LTC2947_REG_TEMP_MAX, LTC2947_PAGE0,
- 2, &__val);
- break;
- case hwmon_temp_lowest:
- ret = ltc2947_val_read(st, LTC2947_REG_TEMP_MIN, LTC2947_PAGE0,
- 2, &__val);
- break;
- case hwmon_temp_max_alarm:
- if (channel == LTC2947_TEMP_FAN_CHAN)
- return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
- LTC2947_MAX_TEMP_FAN_MASK,
- val);
- return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
- LTC2947_MAX_TEMP_MASK, val);
- case hwmon_temp_min_alarm:
- if (channel == LTC2947_TEMP_FAN_CHAN)
- return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
- LTC2947_MIN_TEMP_FAN_MASK,
- val);
- return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
- LTC2947_MIN_TEMP_MASK, val);
- case hwmon_temp_max:
- if (channel == LTC2947_TEMP_FAN_CHAN)
- ret = ltc2947_val_read(st, LTC2947_REG_TEMP_FAN_THRE_H,
- LTC2947_PAGE1, 2, &__val);
- else
- ret = ltc2947_val_read(st, LTC2947_REG_TEMP_THRE_H,
- LTC2947_PAGE1, 2, &__val);
- break;
- case hwmon_temp_min:
- if (channel == LTC2947_TEMP_FAN_CHAN)
- ret = ltc2947_val_read(st, LTC2947_REG_TEMP_FAN_THRE_L,
- LTC2947_PAGE1, 2, &__val);
- else
- ret = ltc2947_val_read(st, LTC2947_REG_TEMP_THRE_L,
- LTC2947_PAGE1, 2, &__val);
- break;
- default:
- return -ENOTSUPP;
- }
- if (ret)
- return ret;
- /* in milidegrees celcius, temp is given by: */
- *val = (__val * 204) + 5500;
- return 0;
- }
- static int ltc2947_read_power(struct device *dev, const u32 attr, long *val)
- {
- struct ltc2947_data *st = dev_get_drvdata(dev);
- int ret;
- u32 lsb = 200000; /* in uW */
- s64 __val = 0;
- switch (attr) {
- case hwmon_power_input:
- ret = ltc2947_val_read(st, LTC2947_REG_POWER, LTC2947_PAGE0,
- 3, &__val);
- lsb = 50000;
- break;
- case hwmon_power_input_highest:
- ret = ltc2947_val_read(st, LTC2947_REG_POWER_MAX, LTC2947_PAGE0,
- 2, &__val);
- break;
- case hwmon_power_input_lowest:
- ret = ltc2947_val_read(st, LTC2947_REG_POWER_MIN, LTC2947_PAGE0,
- 2, &__val);
- break;
- case hwmon_power_max_alarm:
- return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
- LTC2947_MAX_POWER_MASK, val);
- case hwmon_power_min_alarm:
- return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
- LTC2947_MIN_POWER_MASK, val);
- case hwmon_power_max:
- ret = ltc2947_val_read(st, LTC2947_REG_POWER_THRE_H,
- LTC2947_PAGE1, 2, &__val);
- break;
- case hwmon_power_min:
- ret = ltc2947_val_read(st, LTC2947_REG_POWER_THRE_L,
- LTC2947_PAGE1, 2, &__val);
- break;
- default:
- return -ENOTSUPP;
- }
- if (ret)
- return ret;
- *val = __val * lsb;
- return 0;
- }
- static int ltc2947_read_curr(struct device *dev, const u32 attr, long *val)
- {
- struct ltc2947_data *st = dev_get_drvdata(dev);
- int ret;
- u8 lsb = 12; /* in mA */
- s64 __val = 0;
- switch (attr) {
- case hwmon_curr_input:
- ret = ltc2947_val_read(st, LTC2947_REG_CURRENT,
- LTC2947_PAGE0, 3, &__val);
- lsb = 3;
- break;
- case hwmon_curr_highest:
- ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_MAX,
- LTC2947_PAGE0, 2, &__val);
- break;
- case hwmon_curr_lowest:
- ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_MIN,
- LTC2947_PAGE0, 2, &__val);
- break;
- case hwmon_curr_max_alarm:
- return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
- LTC2947_MAX_CURRENT_MASK, val);
- case hwmon_curr_min_alarm:
- return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
- LTC2947_MIN_CURRENT_MASK, val);
- case hwmon_curr_max:
- ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_THRE_H,
- LTC2947_PAGE1, 2, &__val);
- break;
- case hwmon_curr_min:
- ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_THRE_L,
- LTC2947_PAGE1, 2, &__val);
- break;
- default:
- return -ENOTSUPP;
- }
- if (ret)
- return ret;
- *val = __val * lsb;
- return 0;
- }
- static int ltc2947_read_in(struct device *dev, const u32 attr, long *val,
- const int channel)
- {
- struct ltc2947_data *st = dev_get_drvdata(dev);
- int ret;
- u8 lsb = 2; /* in mV */
- s64 __val = 0;
- if (channel < 0 || channel > LTC2947_VOLTAGE_DVCC_CHAN) {
- dev_err(st->dev, "Invalid chan%d for voltage", channel);
- return -EINVAL;
- }
- switch (attr) {
- case hwmon_in_input:
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
- ret = ltc2947_val_read(st, LTC2947_REG_DVCC,
- LTC2947_PAGE0, 2, &__val);
- lsb = 145;
- } else {
- ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE,
- LTC2947_PAGE0, 2, &__val);
- }
- break;
- case hwmon_in_highest:
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
- ret = ltc2947_val_read(st, LTC2947_REG_DVCC_MAX,
- LTC2947_PAGE0, 2, &__val);
- lsb = 145;
- } else {
- ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_MAX,
- LTC2947_PAGE0, 2, &__val);
- }
- break;
- case hwmon_in_lowest:
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
- ret = ltc2947_val_read(st, LTC2947_REG_DVCC_MIN,
- LTC2947_PAGE0, 2, &__val);
- lsb = 145;
- } else {
- ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_MIN,
- LTC2947_PAGE0, 2, &__val);
- }
- break;
- case hwmon_in_max_alarm:
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
- return ltc2947_alarm_read(st, LTC2947_REG_STATVDVCC,
- LTC2947_MAX_VOLTAGE_MASK,
- val);
- return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
- LTC2947_MAX_VOLTAGE_MASK, val);
- case hwmon_in_min_alarm:
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
- return ltc2947_alarm_read(st, LTC2947_REG_STATVDVCC,
- LTC2947_MIN_VOLTAGE_MASK,
- val);
- return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
- LTC2947_MIN_VOLTAGE_MASK, val);
- case hwmon_in_max:
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
- ret = ltc2947_val_read(st, LTC2947_REG_DVCC_THRE_H,
- LTC2947_PAGE1, 2, &__val);
- lsb = 145;
- } else {
- ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_THRE_H,
- LTC2947_PAGE1, 2, &__val);
- }
- break;
- case hwmon_in_min:
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
- ret = ltc2947_val_read(st, LTC2947_REG_DVCC_THRE_L,
- LTC2947_PAGE1, 2, &__val);
- lsb = 145;
- } else {
- ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_THRE_L,
- LTC2947_PAGE1, 2, &__val);
- }
- break;
- default:
- return -ENOTSUPP;
- }
- if (ret)
- return ret;
- *val = __val * lsb;
- return 0;
- }
- static int ltc2947_read(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long *val)
- {
- switch (type) {
- case hwmon_in:
- return ltc2947_read_in(dev, attr, val, channel);
- case hwmon_curr:
- return ltc2947_read_curr(dev, attr, val);
- case hwmon_power:
- return ltc2947_read_power(dev, attr, val);
- case hwmon_temp:
- return ltc2947_read_temp(dev, attr, val, channel);
- default:
- return -ENOTSUPP;
- }
- }
- static int ltc2947_write_temp(struct device *dev, const u32 attr,
- long val, const int channel)
- {
- struct ltc2947_data *st = dev_get_drvdata(dev);
- if (channel < 0 || channel > LTC2947_TEMP_FAN_CHAN) {
- dev_err(st->dev, "Invalid chan%d for temperature", channel);
- return -EINVAL;
- }
- switch (attr) {
- case hwmon_temp_reset_history:
- if (val != 1)
- return -EINVAL;
- return ltc2947_reset_history(st, LTC2947_REG_TEMP_MAX,
- LTC2947_REG_TEMP_MIN);
- case hwmon_temp_max:
- val = clamp_val(val, TEMP_MIN, TEMP_MAX);
- if (channel == LTC2947_TEMP_FAN_CHAN) {
- if (!st->gpio_out)
- return -ENOTSUPP;
- return ltc2947_val_write(st,
- LTC2947_REG_TEMP_FAN_THRE_H,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val - 550, 204));
- }
- return ltc2947_val_write(st, LTC2947_REG_TEMP_THRE_H,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val - 550, 204));
- case hwmon_temp_min:
- val = clamp_val(val, TEMP_MIN, TEMP_MAX);
- if (channel == LTC2947_TEMP_FAN_CHAN) {
- if (!st->gpio_out)
- return -ENOTSUPP;
- return ltc2947_val_write(st,
- LTC2947_REG_TEMP_FAN_THRE_L,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val - 550, 204));
- }
- return ltc2947_val_write(st, LTC2947_REG_TEMP_THRE_L,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val - 550, 204));
- default:
- return -ENOTSUPP;
- }
- }
- static int ltc2947_write_power(struct device *dev, const u32 attr,
- long val)
- {
- struct ltc2947_data *st = dev_get_drvdata(dev);
- switch (attr) {
- case hwmon_power_reset_history:
- if (val != 1)
- return -EINVAL;
- return ltc2947_reset_history(st, LTC2947_REG_POWER_MAX,
- LTC2947_REG_POWER_MIN);
- case hwmon_power_max:
- val = clamp_val(val, POWER_MIN, POWER_MAX);
- return ltc2947_val_write(st, LTC2947_REG_POWER_THRE_H,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val, 200000));
- case hwmon_power_min:
- val = clamp_val(val, POWER_MIN, POWER_MAX);
- return ltc2947_val_write(st, LTC2947_REG_POWER_THRE_L,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val, 200000));
- default:
- return -ENOTSUPP;
- }
- }
- static int ltc2947_write_curr(struct device *dev, const u32 attr,
- long val)
- {
- struct ltc2947_data *st = dev_get_drvdata(dev);
- switch (attr) {
- case hwmon_curr_reset_history:
- if (val != 1)
- return -EINVAL;
- return ltc2947_reset_history(st, LTC2947_REG_CURRENT_MAX,
- LTC2947_REG_CURRENT_MIN);
- case hwmon_curr_max:
- val = clamp_val(val, CURRENT_MIN, CURRENT_MAX);
- return ltc2947_val_write(st, LTC2947_REG_CURRENT_THRE_H,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val, 12));
- case hwmon_curr_min:
- val = clamp_val(val, CURRENT_MIN, CURRENT_MAX);
- return ltc2947_val_write(st, LTC2947_REG_CURRENT_THRE_L,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val, 12));
- default:
- return -ENOTSUPP;
- }
- }
- static int ltc2947_write_in(struct device *dev, const u32 attr, long val,
- const int channel)
- {
- struct ltc2947_data *st = dev_get_drvdata(dev);
- if (channel > LTC2947_VOLTAGE_DVCC_CHAN) {
- dev_err(st->dev, "Invalid chan%d for voltage", channel);
- return -EINVAL;
- }
- switch (attr) {
- case hwmon_in_reset_history:
- if (val != 1)
- return -EINVAL;
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
- return ltc2947_reset_history(st, LTC2947_REG_DVCC_MAX,
- LTC2947_REG_DVCC_MIN);
- return ltc2947_reset_history(st, LTC2947_REG_VOLTAGE_MAX,
- LTC2947_REG_VOLTAGE_MIN);
- case hwmon_in_max:
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
- val = clamp_val(val, VDVCC_MIN, VDVCC_MAX);
- return ltc2947_val_write(st, LTC2947_REG_DVCC_THRE_H,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val, 145));
- }
- val = clamp_val(val, VOLTAGE_MIN, VOLTAGE_MAX);
- return ltc2947_val_write(st, LTC2947_REG_VOLTAGE_THRE_H,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val, 2));
- case hwmon_in_min:
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
- val = clamp_val(val, VDVCC_MIN, VDVCC_MAX);
- return ltc2947_val_write(st, LTC2947_REG_DVCC_THRE_L,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val, 145));
- }
- val = clamp_val(val, VOLTAGE_MIN, VOLTAGE_MAX);
- return ltc2947_val_write(st, LTC2947_REG_VOLTAGE_THRE_L,
- LTC2947_PAGE1, 2,
- DIV_ROUND_CLOSEST(val, 2));
- default:
- return -ENOTSUPP;
- }
- }
- static int ltc2947_write(struct device *dev,
- enum hwmon_sensor_types type,
- u32 attr, int channel, long val)
- {
- switch (type) {
- case hwmon_in:
- return ltc2947_write_in(dev, attr, val, channel);
- case hwmon_curr:
- return ltc2947_write_curr(dev, attr, val);
- case hwmon_power:
- return ltc2947_write_power(dev, attr, val);
- case hwmon_temp:
- return ltc2947_write_temp(dev, attr, val, channel);
- default:
- return -ENOTSUPP;
- }
- }
- static int ltc2947_read_labels(struct device *dev,
- enum hwmon_sensor_types type,
- u32 attr, int channel, const char **str)
- {
- switch (type) {
- case hwmon_in:
- if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
- *str = "DVCC";
- else
- *str = "VP-VM";
- return 0;
- case hwmon_curr:
- *str = "IP-IM";
- return 0;
- case hwmon_temp:
- if (channel == LTC2947_TEMP_FAN_CHAN)
- *str = "TEMPFAN";
- else
- *str = "Ambient";
- return 0;
- case hwmon_power:
- *str = "Power";
- return 0;
- default:
- return -ENOTSUPP;
- }
- }
- static int ltc2947_in_is_visible(const u32 attr)
- {
- switch (attr) {
- case hwmon_in_input:
- case hwmon_in_highest:
- case hwmon_in_lowest:
- case hwmon_in_max_alarm:
- case hwmon_in_min_alarm:
- case hwmon_in_label:
- return 0444;
- case hwmon_in_reset_history:
- return 0200;
- case hwmon_in_max:
- case hwmon_in_min:
- return 0644;
- default:
- return 0;
- }
- }
- static int ltc2947_curr_is_visible(const u32 attr)
- {
- switch (attr) {
- case hwmon_curr_input:
- case hwmon_curr_highest:
- case hwmon_curr_lowest:
- case hwmon_curr_max_alarm:
- case hwmon_curr_min_alarm:
- case hwmon_curr_label:
- return 0444;
- case hwmon_curr_reset_history:
- return 0200;
- case hwmon_curr_max:
- case hwmon_curr_min:
- return 0644;
- default:
- return 0;
- }
- }
- static int ltc2947_power_is_visible(const u32 attr)
- {
- switch (attr) {
- case hwmon_power_input:
- case hwmon_power_input_highest:
- case hwmon_power_input_lowest:
- case hwmon_power_label:
- case hwmon_power_max_alarm:
- case hwmon_power_min_alarm:
- return 0444;
- case hwmon_power_reset_history:
- return 0200;
- case hwmon_power_max:
- case hwmon_power_min:
- return 0644;
- default:
- return 0;
- }
- }
- static int ltc2947_temp_is_visible(const u32 attr)
- {
- switch (attr) {
- case hwmon_temp_input:
- case hwmon_temp_highest:
- case hwmon_temp_lowest:
- case hwmon_temp_max_alarm:
- case hwmon_temp_min_alarm:
- case hwmon_temp_label:
- return 0444;
- case hwmon_temp_reset_history:
- return 0200;
- case hwmon_temp_max:
- case hwmon_temp_min:
- return 0644;
- default:
- return 0;
- }
- }
- static umode_t ltc2947_is_visible(const void *data,
- enum hwmon_sensor_types type,
- u32 attr, int channel)
- {
- switch (type) {
- case hwmon_in:
- return ltc2947_in_is_visible(attr);
- case hwmon_curr:
- return ltc2947_curr_is_visible(attr);
- case hwmon_power:
- return ltc2947_power_is_visible(attr);
- case hwmon_temp:
- return ltc2947_temp_is_visible(attr);
- default:
- return 0;
- }
- }
- static const struct hwmon_channel_info *ltc2947_info[] = {
- HWMON_CHANNEL_INFO(in,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |
- HWMON_I_MAX | HWMON_I_MIN | HWMON_I_RESET_HISTORY |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM |
- HWMON_I_LABEL,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |
- HWMON_I_MAX | HWMON_I_MIN | HWMON_I_RESET_HISTORY |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM |
- HWMON_I_LABEL),
- HWMON_CHANNEL_INFO(curr,
- HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST |
- HWMON_C_MAX | HWMON_C_MIN | HWMON_C_RESET_HISTORY |
- HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM |
- HWMON_C_LABEL),
- HWMON_CHANNEL_INFO(power,
- HWMON_P_INPUT | HWMON_P_INPUT_LOWEST |
- HWMON_P_INPUT_HIGHEST | HWMON_P_MAX | HWMON_P_MIN |
- HWMON_P_RESET_HISTORY | HWMON_P_MAX_ALARM |
- HWMON_P_MIN_ALARM | HWMON_P_LABEL),
- HWMON_CHANNEL_INFO(temp,
- HWMON_T_INPUT | HWMON_T_LOWEST | HWMON_T_HIGHEST |
- HWMON_T_MAX | HWMON_T_MIN | HWMON_T_RESET_HISTORY |
- HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
- HWMON_T_LABEL,
- HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | HWMON_T_MAX |
- HWMON_T_MIN | HWMON_T_LABEL),
- NULL
- };
- static const struct hwmon_ops ltc2947_hwmon_ops = {
- .is_visible = ltc2947_is_visible,
- .read = ltc2947_read,
- .write = ltc2947_write,
- .read_string = ltc2947_read_labels,
- };
- static const struct hwmon_chip_info ltc2947_chip_info = {
- .ops = <c2947_hwmon_ops,
- .info = ltc2947_info,
- };
- /* energy attributes are 6bytes wide so we need u64 */
- static SENSOR_DEVICE_ATTR(energy1_input, 0444, ltc2947_show_value, NULL,
- LTC2947_REG_ENERGY1);
- static SENSOR_DEVICE_ATTR(energy2_input, 0444, ltc2947_show_value, NULL,
- LTC2947_REG_ENERGY2);
- static struct attribute *ltc2947_attrs[] = {
- &sensor_dev_attr_energy1_input.dev_attr.attr,
- &sensor_dev_attr_energy2_input.dev_attr.attr,
- NULL,
- };
- ATTRIBUTE_GROUPS(ltc2947);
- static int ltc2947_setup(struct ltc2947_data *st)
- {
- int ret;
- struct clk *extclk;
- u32 dummy, deadband, pol;
- u32 accum[2];
- /* clear status register by reading it */
- ret = regmap_read(st->map, LTC2947_REG_STATUS, &dummy);
- if (ret)
- return ret;
- /*
- * Set max/min for power here since the default values x scale
- * would overflow on 32bit arch
- */
- ret = ltc2947_val_write(st, LTC2947_REG_POWER_THRE_H, LTC2947_PAGE1, 2,
- POWER_MAX / 200000);
- if (ret)
- return ret;
- ret = ltc2947_val_write(st, LTC2947_REG_POWER_THRE_L, LTC2947_PAGE1, 2,
- POWER_MIN / 200000);
- if (ret)
- return ret;
- /* check external clock presence */
- extclk = devm_clk_get_optional_enabled(st->dev, NULL);
- if (IS_ERR(extclk))
- return dev_err_probe(st->dev, PTR_ERR(extclk),
- "Failed to get external clock\n");
- if (extclk) {
- unsigned long rate_hz;
- u8 pre = 0, div, tbctl;
- u64 aux;
- /* let's calculate and set the right valus in TBCTL */
- rate_hz = clk_get_rate(extclk);
- if (rate_hz < LTC2947_CLK_MIN || rate_hz > LTC2947_CLK_MAX) {
- dev_err(st->dev, "Invalid rate:%lu for external clock",
- rate_hz);
- return -EINVAL;
- }
- /* as in table 1 of the datasheet */
- if (rate_hz >= LTC2947_CLK_MIN && rate_hz <= 1000000)
- pre = 0;
- else if (rate_hz > 1000000 && rate_hz <= 2000000)
- pre = 1;
- else if (rate_hz > 2000000 && rate_hz <= 4000000)
- pre = 2;
- else if (rate_hz > 4000000 && rate_hz <= 8000000)
- pre = 3;
- else if (rate_hz > 8000000 && rate_hz <= 16000000)
- pre = 4;
- else if (rate_hz > 16000000 && rate_hz <= LTC2947_CLK_MAX)
- pre = 5;
- /*
- * Div is given by:
- * floor(fref / (2^PRE * 32768))
- */
- div = rate_hz / ((1 << pre) * 32768);
- tbctl = LTC2947_PRE(pre) | LTC2947_DIV(div);
- ret = regmap_write(st->map, LTC2947_REG_TBCTL, tbctl);
- if (ret)
- return ret;
- /*
- * The energy lsb is given by (in W*s):
- * 06416 * (1/fref) * 2^PRE * (DIV + 1)
- * The value is multiplied by 10E9
- */
- aux = (div + 1) * ((1 << pre) * 641600000ULL);
- st->lsb_energy = DIV_ROUND_CLOSEST_ULL(aux, rate_hz);
- } else {
- /* 19.89E-6 * 10E9 */
- st->lsb_energy = 19890;
- }
- ret = of_property_read_u32_array(st->dev->of_node,
- "adi,accumulator-ctl-pol", accum,
- ARRAY_SIZE(accum));
- if (!ret) {
- u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) |
- LTC2947_ACCUM_POL_2(accum[1]);
- ret = regmap_write(st->map, LTC2947_REG_ACCUM_POL, accum_reg);
- if (ret)
- return ret;
- }
- ret = of_property_read_u32(st->dev->of_node,
- "adi,accumulation-deadband-microamp",
- &deadband);
- if (!ret) {
- /* the LSB is the same as the current, so 3mA */
- ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND,
- deadband / (1000 * 3));
- if (ret)
- return ret;
- }
- /* check gpio cfg */
- ret = of_property_read_u32(st->dev->of_node, "adi,gpio-out-pol", &pol);
- if (!ret) {
- /* setup GPIO as output */
- u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) |
- LTC2947_GPIO_FAN_POL(pol);
- st->gpio_out = true;
- ret = regmap_write(st->map, LTC2947_REG_GPIOSTATCTL, gpio_ctl);
- if (ret)
- return ret;
- }
- ret = of_property_read_u32_array(st->dev->of_node, "adi,gpio-in-accum",
- accum, ARRAY_SIZE(accum));
- if (!ret) {
- /*
- * Setup the accum options. The gpioctl is already defined as
- * input by default.
- */
- u32 accum_val = LTC2947_ACCUM_POL_1(accum[0]) |
- LTC2947_ACCUM_POL_2(accum[1]);
- if (st->gpio_out) {
- dev_err(st->dev,
- "Cannot have input gpio config if already configured as output");
- return -EINVAL;
- }
- ret = regmap_write(st->map, LTC2947_REG_GPIO_ACCUM, accum_val);
- if (ret)
- return ret;
- }
- /* set continuos mode */
- return regmap_update_bits(st->map, LTC2947_REG_CTRL,
- LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1));
- }
- int ltc2947_core_probe(struct regmap *map, const char *name)
- {
- struct ltc2947_data *st;
- struct device *dev = regmap_get_device(map);
- struct device *hwmon;
- int ret;
- st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
- if (!st)
- return -ENOMEM;
- st->map = map;
- st->dev = dev;
- dev_set_drvdata(dev, st);
- mutex_init(&st->lock);
- ret = ltc2947_setup(st);
- if (ret)
- return ret;
- hwmon = devm_hwmon_device_register_with_info(dev, name, st,
- <c2947_chip_info,
- ltc2947_groups);
- return PTR_ERR_OR_ZERO(hwmon);
- }
- EXPORT_SYMBOL_GPL(ltc2947_core_probe);
- static int ltc2947_resume(struct device *dev)
- {
- struct ltc2947_data *st = dev_get_drvdata(dev);
- u32 ctrl = 0;
- int ret;
- /* dummy read to wake the device */
- ret = regmap_read(st->map, LTC2947_REG_CTRL, &ctrl);
- if (ret)
- return ret;
- /*
- * Wait for the device. It takes 100ms to wake up so, 10ms extra
- * should be enough.
- */
- msleep(110);
- ret = regmap_read(st->map, LTC2947_REG_CTRL, &ctrl);
- if (ret)
- return ret;
- /* ctrl should be 0 */
- if (ctrl != 0) {
- dev_err(st->dev, "Device failed to wake up, ctl:%02X\n", ctrl);
- return -ETIMEDOUT;
- }
- /* set continuous mode */
- return regmap_update_bits(st->map, LTC2947_REG_CTRL,
- LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1));
- }
- static int ltc2947_suspend(struct device *dev)
- {
- struct ltc2947_data *st = dev_get_drvdata(dev);
- return regmap_update_bits(st->map, LTC2947_REG_CTRL,
- LTC2947_SHUTDOWN_MASK, 1);
- }
- EXPORT_SIMPLE_DEV_PM_OPS(ltc2947_pm_ops, ltc2947_suspend, ltc2947_resume);
- const struct of_device_id ltc2947_of_match[] = {
- { .compatible = "adi,ltc2947" },
- {}
- };
- EXPORT_SYMBOL_GPL(ltc2947_of_match);
- MODULE_DEVICE_TABLE(of, ltc2947_of_match);
- MODULE_AUTHOR("Nuno Sa <[email protected]>");
- MODULE_DESCRIPTION("LTC2947 power and energy monitor core driver");
- MODULE_LICENSE("GPL");
|