diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-23 00:58:12 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-23 00:58:12 +0100 |
commit | b8e445b6895cfe76c5959a7135a3216703fe32d4 (patch) | |
tree | 285797138e87a67c25aa173de48a4cf3d18d6a61 | |
parent | cff229491af5df946781edfbeafd43e9cf66a3b4 (diff) | |
parent | e7c6a55606b5c46b449d76588968b4d8caae903f (diff) | |
download | linux-b8e445b6895cfe76c5959a7135a3216703fe32d4.tar.gz linux-b8e445b6895cfe76c5959a7135a3216703fe32d4.tar.bz2 linux-b8e445b6895cfe76c5959a7135a3216703fe32d4.zip |
Merge tag 'hwmon-for-v4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
- Add support for trace events to hwmon core
- Add support for NCT6797D, NCT6798D, MAX31725/6, LTM4686
- Support all AMD Family 15h Model 6xh and Model 7xh processors in
k10temp driver
- Convert ina3221 driver to _info API
- Fixes, cleanups, and improvements in various drivers
* tag 'hwmon-for-v4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (46 commits)
hwmon: (pmbus) Fix page count auto-detection.
hwmon: (pmbus) remove redundant 'default n' from Kconfig
hwmon: (core) Add trace events to _attr_show/store functions
hwmon: (ina3221) Use _info API to register hwmon device
hwmon: (npcm-750-pwm-fan) Change initial pwm target to 255
hwmon: (ina3221) Validate shunt resistor value from DT
hwmon: (tmp421) make const array 'names' static
hwmon: (core) Add hwmon_in_enable attribute
hwmon: (ina3221) mark PM functions as __maybe_unused
hwmon: (ina3221) Read channel input source info from DT
dt-bindings: hwmon: Add ina3221 documentation
hwmon: (ina3221) Add suspend and resume functions
hwmon: (ina3221) Fix INA3221_CONFIG_MODE macros
hwmon: (ina3221) Add INA3221_CONFIG to volatile_table
MAINTAINERS: Update PMBUS maintainer entry
hwmon: (pwm-fan) Set fan speed to 0 on suspend
hwmon: (pwm-fan) Silence error on probe deferral
hwmon: (scpi-hwmon) remove redundant continue
hwmon: (nct6775) Add support for NCT6798D
hwmon: (nct6775) Add support for NCT6797D
...
30 files changed, 878 insertions, 369 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/ina3221.txt b/Documentation/devicetree/bindings/hwmon/ina3221.txt new file mode 100644 index 000000000000..a7b25caa2b8e --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/ina3221.txt @@ -0,0 +1,44 @@ +Texas Instruments INA3221 Device Tree Bindings + +1) ina3221 node + Required properties: + - compatible: Must be "ti,ina3221" + - reg: I2C address + + Optional properties: + = The node contains optional child nodes for three channels = + = Each child node describes the information of input source = + + - #address-cells: Required only if a child node is present. Must be 1. + - #size-cells: Required only if a child node is present. Must be 0. + +2) child nodes + Required properties: + - reg: Must be 0, 1 or 2, corresponding to IN1, IN2 or IN3 port of INA3221 + + Optional properties: + - label: Name of the input source + - shunt-resistor-micro-ohms: Shunt resistor value in micro-Ohm + +Example: + +ina3221@40 { + compatible = "ti,ina3221"; + reg = <0x40>; + #address-cells = <1>; + #size-cells = <0>; + + input@0 { + reg = <0x0>; + status = "disabled"; + }; + input@1 { + reg = <0x1>; + shunt-resistor-micro-ohms = <5000>; + }; + input@2 { + reg = <0x2>; + label = "VDD_5V"; + shunt-resistor-micro-ohms = <5000>; + }; +}; diff --git a/Documentation/devicetree/bindings/hwmon/ltc2978.txt b/Documentation/devicetree/bindings/hwmon/ltc2978.txt index bf2a47bbdc58..b428a70a7cc0 100644 --- a/Documentation/devicetree/bindings/hwmon/ltc2978.txt +++ b/Documentation/devicetree/bindings/hwmon/ltc2978.txt @@ -15,6 +15,7 @@ Required properties: * "lltc,ltm2987" * "lltc,ltm4675" * "lltc,ltm4676" + * "lltc,ltm4686" - reg: I2C slave address Optional properties: @@ -30,6 +31,7 @@ Valid names of regulators depend on number of supplies supported per device: * ltc3880, ltc3882, ltc3886 : vout0 - vout1 * ltc3883 : vout0 * ltm4676 : vout0 - vout1 + * ltm4686 : vout0 - vout1 Example: ltc2978@5e { diff --git a/Documentation/hwmon/ina3221 b/Documentation/hwmon/ina3221 index 0ff74854cb2e..4b82cbfb551c 100644 --- a/Documentation/hwmon/ina3221 +++ b/Documentation/hwmon/ina3221 @@ -21,6 +21,8 @@ and power are calculated host-side from these. Sysfs entries ------------- +in[123]_label Voltage channel labels +in[123]_enable Voltage channel enable controls in[123]_input Bus voltage(mV) channels curr[123]_input Current(mA) measurement channels shunt[123]_resistor Shunt resistance(uOhm) channels diff --git a/Documentation/hwmon/lm75 b/Documentation/hwmon/lm75 index ac95edfcd907..2f1120f88c16 100644 --- a/Documentation/hwmon/lm75 +++ b/Documentation/hwmon/lm75 @@ -17,8 +17,8 @@ Supported chips: Addresses scanned: none Datasheet: Publicly available at the Maxim website http://www.maximintegrated.com/ - * Maxim MAX6625, MAX6626 - Prefixes: 'max6625', 'max6626' + * Maxim MAX6625, MAX6626, MAX31725, MAX31726 + Prefixes: 'max6625', 'max6626', 'max31725', 'max31726' Addresses scanned: none Datasheet: Publicly available at the Maxim website http://www.maxim-ic.com/ @@ -86,7 +86,7 @@ The LM75 is essentially an industry standard; there may be other LM75 clones not listed here, with or without various enhancements, that are supported. The clones are not detected by the driver, unless they reproduce the exact register tricks of the original LM75, and must -therefore be instantiated explicitly. Higher resolution up to 12-bit +therefore be instantiated explicitly. Higher resolution up to 16-bit is supported by this driver, other specific enhancements are not. The LM77 is not supported, contrary to what we pretended for a long time. diff --git a/Documentation/hwmon/ltc2978 b/Documentation/hwmon/ltc2978 index 9a49d3c90cd1..dfb2caa401d9 100644 --- a/Documentation/hwmon/ltc2978 +++ b/Documentation/hwmon/ltc2978 @@ -55,6 +55,10 @@ Supported chips: Prefix: 'ltm4676' Addresses scanned: - Datasheet: http://www.linear.com/product/ltm4676 + * Analog Devices LTM4686 + Prefix: 'ltm4686' + Addresses scanned: - + Datasheet: http://www.analog.com/ltm4686 Author: Guenter Roeck <linux@roeck-us.net> @@ -76,6 +80,7 @@ additional components on a single die. The chip is instantiated and reported as two separate chips on two different I2C bus addresses. LTM4675 is a dual 9A or single 18A μModule regulator LTM4676 is a dual 13A or single 26A uModule regulator. +LTM4686 is a dual 10A or single 20A uModule regulator. Usage Notes diff --git a/Documentation/hwmon/mc13783-adc b/Documentation/hwmon/mc13783-adc index d0e7b3fa9e75..05ccc9f159f1 100644 --- a/Documentation/hwmon/mc13783-adc +++ b/Documentation/hwmon/mc13783-adc @@ -2,12 +2,12 @@ Kernel driver mc13783-adc ========================= Supported chips: - * Freescale Atlas MC13783 + * Freescale MC13783 Prefix: 'mc13783' - Datasheet: http://www.freescale.com/files/rf_if/doc/data_sheet/MC13783.pdf?fsrch=1 - * Freescale Atlas MC13892 + Datasheet: https://www.nxp.com/docs/en/data-sheet/MC13783.pdf + * Freescale MC13892 Prefix: 'mc13892' - Datasheet: http://cache.freescale.com/files/analog/doc/data_sheet/MC13892.pdf?fsrch=1&sr=1 + Datasheet: https://www.nxp.com/docs/en/data-sheet/MC13892.pdf Authors: Sascha Hauer <s.hauer@pengutronix.de> diff --git a/MAINTAINERS b/MAINTAINERS index c393746b0209..c742c517f95c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6468,6 +6468,7 @@ F: Documentation/devicetree/bindings/hwmon/ F: Documentation/hwmon/ F: drivers/hwmon/ F: include/linux/hwmon*.h +F: include/trace/events/hwmon*.h HARDWARE RANDOM NUMBER GENERATOR CORE M: Matt Mackall <mpm@selenic.com> @@ -8860,13 +8861,6 @@ S: Maintained F: Documentation/hwmon/max16065 F: drivers/hwmon/max16065.c -MAX20751 HARDWARE MONITOR DRIVER -M: Guenter Roeck <linux@roeck-us.net> -L: linux-hwmon@vger.kernel.org -S: Maintained -F: Documentation/hwmon/max20751 -F: drivers/hwmon/max20751.c - MAX2175 SDR TUNER DRIVER M: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com> L: linux-media@vger.kernel.org @@ -11601,7 +11595,26 @@ W: http://hwmon.wiki.kernel.org/ W: http://www.roeck-us.net/linux/drivers/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git S: Maintained +F: Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt +F: Documentation/devicetree/bindings/hwmon/max31785.txt +F: Documentation/devicetree/bindings/hwmon/ltc2978.txt +F: Documentation/hwmon/adm1275 +F: Documentation/hwmon/ibm-cffps +F: Documentation/hwmon/ir35221 +F: Documentation/hwmon/lm25066 +F: Documentation/hwmon/ltc2978 +F: Documentation/hwmon/ltc3815 +F: Documentation/hwmon/max16064 +F: Documentation/hwmon/max20751 +F: Documentation/hwmon/max31785 +F: Documentation/hwmon/max34440 +F: Documentation/hwmon/max8688 F: Documentation/hwmon/pmbus +F: Documentation/hwmon/pmbus-core +F: Documentation/hwmon/tps40422 +F: Documentation/hwmon/ucd9000 +F: Documentation/hwmon/ucd9200 +F: Documentation/hwmon/zl6100 F: drivers/hwmon/pmbus/ F: include/linux/pmbus.h diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index 5e449eac788a..92de8139d398 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -852,7 +852,7 @@ static int aspeed_create_pwm_cooling(struct device *dev, dev_err(dev, "Property 'cooling-levels' cannot be read.\n"); return ret; } - snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%s%d", child->name, pwm_port); + snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%pOFn%d", child, pwm_port); cdev->tcdev = thermal_of_cooling_device_register(child, cdev->name, diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index a6636fe42189..a7cf00885c5d 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -1210,10 +1210,8 @@ static int atk_register_hwmon(struct atk_data *data) data->hwmon_dev = hwmon_device_register_with_groups(dev, "atk0110", data, data->attr_groups); - if (IS_ERR(data->hwmon_dev)) - return PTR_ERR(data->hwmon_dev); - return 0; + return PTR_ERR_OR_ZERO(data->hwmon_dev); } static int atk_probe_if(struct atk_data *data) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 33d51281272b..975c95169884 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -24,6 +24,9 @@ #include <linux/string.h> #include <linux/thermal.h> +#define CREATE_TRACE_POINTS +#include <trace/events/hwmon.h> + #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" @@ -171,6 +174,13 @@ static int hwmon_thermal_add_sensor(struct device *dev, } #endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */ +static int hwmon_attr_base(enum hwmon_sensor_types type) +{ + if (type == hwmon_in) + return 0; + return 1; +} + /* sysfs attribute management */ static ssize_t hwmon_attr_show(struct device *dev, @@ -185,6 +195,9 @@ static ssize_t hwmon_attr_show(struct device *dev, if (ret < 0) return ret; + trace_hwmon_attr_show(hattr->index + hwmon_attr_base(hattr->type), + hattr->name, val); + return sprintf(buf, "%ld\n", val); } @@ -193,6 +206,7 @@ static ssize_t hwmon_attr_show_string(struct device *dev, char *buf) { struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + enum hwmon_sensor_types type = hattr->type; const char *s; int ret; @@ -201,6 +215,9 @@ static ssize_t hwmon_attr_show_string(struct device *dev, if (ret < 0) return ret; + trace_hwmon_attr_show_string(hattr->index + hwmon_attr_base(type), + hattr->name, s); + return sprintf(buf, "%s\n", s); } @@ -221,14 +238,10 @@ static ssize_t hwmon_attr_store(struct device *dev, if (ret < 0) return ret; - return count; -} + trace_hwmon_attr_store(hattr->index + hwmon_attr_base(hattr->type), + hattr->name, val); -static int hwmon_attr_base(enum hwmon_sensor_types type) -{ - if (type == hwmon_in) - return 0; - return 1; + return count; } static bool is_string_attr(enum hwmon_sensor_types type, u32 attr) @@ -356,6 +369,7 @@ static const char * const hwmon_in_attr_templates[] = { [hwmon_in_max_alarm] = "in%d_max_alarm", [hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm", [hwmon_in_crit_alarm] = "in%d_crit_alarm", + [hwmon_in_enable] = "in%d_enable", }; static const char * const hwmon_curr_attr_templates[] = { diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 83472808c816..0ccca87f5271 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -458,9 +458,6 @@ static int populate_attr_groups(struct platform_device *pdev) for_each_child_of_node(opal, np) { const char *label; - if (np->name == NULL) - continue; - type = get_sensor_type(np); if (type == MAX_SENSOR_TYPE) continue; @@ -589,9 +586,6 @@ static int create_device_attrs(struct platform_device *pdev) const char *label; enum sensors type; - if (np->name == NULL) - continue; - type = get_sensor_type(np); if (type == MAX_SENSOR_TYPE) continue; @@ -603,8 +597,8 @@ static int create_device_attrs(struct platform_device *pdev) if (of_property_read_u32(np, "sensor-id", &sensor_id) && of_property_read_u32(np, "sensor-data", &sensor_id)) { dev_info(&pdev->dev, - "'sensor-id' missing in the node '%s'\n", - np->name); + "'sensor-id' missing in the node '%pOFn'\n", + np); continue; } diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 2f3f875c06ac..eed66e533ee2 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -65,13 +65,9 @@ static int iio_hwmon_probe(struct platform_device *pdev) int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1; enum iio_chan_type type; struct iio_channel *channels; - const char *name = "iio_hwmon"; struct device *hwmon_dev; char *sname; - if (dev->of_node && dev->of_node->name) - name = dev->of_node->name; - channels = devm_iio_channel_get_all(dev); if (IS_ERR(channels)) { if (PTR_ERR(channels) == -ENODEV) @@ -141,11 +137,15 @@ static int iio_hwmon_probe(struct platform_device *pdev) st->attr_group.attrs = st->attrs; st->groups[0] = &st->attr_group; - sname = devm_kstrdup(dev, name, GFP_KERNEL); - if (!sname) - return -ENOMEM; + if (dev->of_node) { + sname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); + if (!sname) + return -ENOMEM; + strreplace(sname, '-', '_'); + } else { + sname = "iio_hwmon"; + } - strreplace(sname, '-', '_'); hwmon_dev = devm_hwmon_device_register_with_groups(dev, sname, st, st->groups); return PTR_ERR_OR_ZERO(hwmon_dev); diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index e6b49500c52a..d61688f04594 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -38,9 +38,12 @@ #define INA3221_WARN3 0x0c #define INA3221_MASK_ENABLE 0x0f -#define INA3221_CONFIG_MODE_SHUNT BIT(1) -#define INA3221_CONFIG_MODE_BUS BIT(2) -#define INA3221_CONFIG_MODE_CONTINUOUS BIT(3) +#define INA3221_CONFIG_MODE_MASK GENMASK(2, 0) +#define INA3221_CONFIG_MODE_POWERDOWN 0 +#define INA3221_CONFIG_MODE_SHUNT BIT(0) +#define INA3221_CONFIG_MODE_BUS BIT(1) +#define INA3221_CONFIG_MODE_CONTINUOUS BIT(2) +#define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x)) #define INA3221_RSHUNT_DEFAULT 10000 @@ -74,30 +77,37 @@ enum ina3221_channels { INA3221_NUM_CHANNELS }; -static const unsigned int register_channel[] = { - [INA3221_SHUNT1] = INA3221_CHANNEL1, - [INA3221_SHUNT2] = INA3221_CHANNEL2, - [INA3221_SHUNT3] = INA3221_CHANNEL3, - [INA3221_CRIT1] = INA3221_CHANNEL1, - [INA3221_CRIT2] = INA3221_CHANNEL2, - [INA3221_CRIT3] = INA3221_CHANNEL3, - [INA3221_WARN1] = INA3221_CHANNEL1, - [INA3221_WARN2] = INA3221_CHANNEL2, - [INA3221_WARN3] = INA3221_CHANNEL3, +/** + * struct ina3221_input - channel input source specific information + * @label: label of channel input source + * @shunt_resistor: shunt resistor value of channel input source + * @disconnected: connection status of channel input source + */ +struct ina3221_input { + const char *label; + int shunt_resistor; + bool disconnected; }; /** * struct ina3221_data - device specific information * @regmap: Register map of the device * @fields: Register fields of the device - * @shunt_resistors: Array of resistor values per channel + * @inputs: Array of channel input source specific structures + * @reg_config: Register value of INA3221_CONFIG */ struct ina3221_data { struct regmap *regmap; struct regmap_field *fields[F_MAX_FIELDS]; - int shunt_resistors[INA3221_NUM_CHANNELS]; + struct ina3221_input inputs[INA3221_NUM_CHANNELS]; + u32 reg_config; }; +static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) +{ + return ina->reg_config & INA3221_CONFIG_CHx_EN(channel); +} + static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, int *val) { @@ -113,107 +123,284 @@ static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, return 0; } -static ssize_t ina3221_show_bus_voltage(struct device *dev, - struct device_attribute *attr, - char *buf) +static const u8 ina3221_in_reg[] = { + INA3221_BUS1, + INA3221_BUS2, + INA3221_BUS3, + INA3221_SHUNT1, + INA3221_SHUNT2, + INA3221_SHUNT3, +}; + +static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val) { - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + const bool is_shunt = channel > INA3221_CHANNEL3; struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int reg = sd_attr->index; - int val, voltage_mv, ret; - - ret = ina3221_read_value(ina, reg, &val); - if (ret) - return ret; + u8 reg = ina3221_in_reg[channel]; + int regval, ret; + + /* Translate shunt channel index to sensor channel index */ + channel %= INA3221_NUM_CHANNELS; + + switch (attr) { + case hwmon_in_input: + if (!ina3221_is_enabled(ina, channel)) + return -ENODATA; + + ret = ina3221_read_value(ina, reg, ®val); + if (ret) + return ret; + + /* + * Scale of shunt voltage (uV): LSB is 40uV + * Scale of bus voltage (mV): LSB is 8mV + */ + *val = regval * (is_shunt ? 40 : 8); + return 0; + case hwmon_in_enable: + *val = ina3221_is_enabled(ina, channel); + return 0; + default: + return -EOPNOTSUPP; + } +} - voltage_mv = val * 8; +static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = { + [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 }, + [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 }, + [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 }, + [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 }, + [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 }, +}; - return snprintf(buf, PAGE_SIZE, "%d\n", voltage_mv); +static int ina3221_read_curr(struct device *dev, u32 attr, + int channel, long *val) +{ + struct ina3221_data *ina = dev_get_drvdata(dev); + struct ina3221_input *input = &ina->inputs[channel]; + int resistance_uo = input->shunt_resistor; + u8 reg = ina3221_curr_reg[attr][channel]; + int regval, voltage_nv, ret; + + switch (attr) { + case hwmon_curr_input: + if (!ina3221_is_enabled(ina, channel)) + return -ENODATA; + /* fall through */ + case hwmon_curr_crit: + case hwmon_curr_max: + ret = ina3221_read_value(ina, reg, ®val); + if (ret) + return ret; + + /* Scale of shunt voltage: LSB is 40uV (40000nV) */ + voltage_nv = regval * 40000; + /* Return current in mA */ + *val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); + return 0; + case hwmon_curr_crit_alarm: + case hwmon_curr_max_alarm: + ret = regmap_field_read(ina->fields[reg], ®val); + if (ret) + return ret; + *val = regval; + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t ina3221_show_shunt_voltage(struct device *dev, - struct device_attribute *attr, - char *buf) +static int ina3221_write_curr(struct device *dev, u32 attr, + int channel, long val) { - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int reg = sd_attr->index; - int val, voltage_uv, ret; + struct ina3221_input *input = &ina->inputs[channel]; + int resistance_uo = input->shunt_resistor; + u8 reg = ina3221_curr_reg[attr][channel]; + int regval, current_ma, voltage_uv; - ret = ina3221_read_value(ina, reg, &val); - if (ret) - return ret; - voltage_uv = val * 40; + /* clamp current */ + current_ma = clamp_val(val, + INT_MIN / resistance_uo, + INT_MAX / resistance_uo); + + voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000); - return snprintf(buf, PAGE_SIZE, "%d\n", voltage_uv); + /* clamp voltage */ + voltage_uv = clamp_val(voltage_uv, -163800, 163800); + + /* 1 / 40uV(scale) << 3(register shift) = 5 */ + regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; + + return regmap_write(ina->regmap, reg, regval); } -static ssize_t ina3221_show_current(struct device *dev, - struct device_attribute *attr, char *buf) +static int ina3221_write_enable(struct device *dev, int channel, bool enable) { - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int reg = sd_attr->index; - unsigned int channel = register_channel[reg]; - int resistance_uo = ina->shunt_resistors[channel]; - int val, current_ma, voltage_nv, ret; + u16 config, mask = INA3221_CONFIG_CHx_EN(channel); + int ret; - ret = ina3221_read_value(ina, reg, &val); + config = enable ? mask : 0; + + /* Enable or disable the channel */ + ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config); if (ret) return ret; - voltage_nv = val * 40000; - current_ma = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); + /* Cache the latest config register value */ + ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); + if (ret) + return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", current_ma); + return 0; } -static ssize_t ina3221_set_current(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int ina3221_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_in: + /* 0-align channel ID */ + return ina3221_read_in(dev, attr, channel - 1, val); + case hwmon_curr: + return ina3221_read_curr(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int ina3221_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_in: + /* 0-align channel ID */ + return ina3221_write_enable(dev, channel - 1, val); + case hwmon_curr: + return ina3221_write_curr(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) { - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int reg = sd_attr->index; - unsigned int channel = register_channel[reg]; - int resistance_uo = ina->shunt_resistors[channel]; - int val, current_ma, voltage_uv, ret; + int index = channel - 1; - ret = kstrtoint(buf, 0, ¤t_ma); - if (ret) - return ret; + *str = ina->inputs[index].label; - /* clamp current */ - current_ma = clamp_val(current_ma, - INT_MIN / resistance_uo, - INT_MAX / resistance_uo); + return 0; +} - voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000); +static umode_t ina3221_is_visible(const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct ina3221_data *ina = drvdata; + const struct ina3221_input *input = NULL; + + switch (type) { + case hwmon_in: + /* Ignore in0_ */ + if (channel == 0) + return 0; + + switch (attr) { + case hwmon_in_label: + if (channel - 1 <= INA3221_CHANNEL3) + input = &ina->inputs[channel - 1]; + /* Hide label node if label is not provided */ + return (input && input->label) ? 0444 : 0; + case hwmon_in_input: + return 0444; + case hwmon_in_enable: + return 0644; + default: + return 0; + } + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_crit_alarm: + case hwmon_curr_max_alarm: + return 0444; + case hwmon_curr_crit: + case hwmon_curr_max: + return 0644; + default: + return 0; + } + default: + return 0; + } +} - /* clamp voltage */ - voltage_uv = clamp_val(voltage_uv, -163800, 163800); +static const u32 ina3221_in_config[] = { + /* 0: dummy, skipped in is_visible */ + HWMON_I_INPUT, + /* 1-3: input voltage Channels */ + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + /* 4-6: shunt voltage Channels */ + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + 0 +}; - /* 1 / 40uV(scale) << 3(register shift) = 5 */ - val = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; +static const struct hwmon_channel_info ina3221_in = { + .type = hwmon_in, + .config = ina3221_in_config, +}; - ret = regmap_write(ina->regmap, reg, val); - if (ret) - return ret; +#define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \ + HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \ + HWMON_C_MAX | HWMON_C_MAX_ALARM) - return count; -} +static const u32 ina3221_curr_config[] = { + INA3221_HWMON_CURR_CONFIG, + INA3221_HWMON_CURR_CONFIG, + INA3221_HWMON_CURR_CONFIG, + 0 +}; + +static const struct hwmon_channel_info ina3221_curr = { + .type = hwmon_curr, + .config = ina3221_curr_config, +}; + +static const struct hwmon_channel_info *ina3221_info[] = { + &ina3221_in, + &ina3221_curr, + NULL +}; + +static const struct hwmon_ops ina3221_hwmon_ops = { + .is_visible = ina3221_is_visible, + .read_string = ina3221_read_string, + .read = ina3221_read, + .write = ina3221_write, +}; +static const struct hwmon_chip_info ina3221_chip_info = { + .ops = &ina3221_hwmon_ops, + .info = ina3221_info, +}; + +/* Extra attribute groups */ static ssize_t ina3221_show_shunt(struct device *dev, struct device_attribute *attr, char *buf) { struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); struct ina3221_data *ina = dev_get_drvdata(dev); unsigned int channel = sd_attr->index; - unsigned int resistance_uo; - - resistance_uo = ina->shunt_resistors[channel]; + struct ina3221_input *input = &ina->inputs[channel]; < |