-
Notifications
You must be signed in to change notification settings - Fork 904
add max31732 driver #2959
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
add max31732 driver #2959
Conversation
Add support for Analog Devices MAX31732, a five-channel temperature monitor with per-channel alarms, calibration helpers, optional IRQ handling, and MTP configuration. Signed-off-by: Sinan Divarci <sinan.divarci@analog.com>
Add documentation for the Analog Devices MAX31732 temperature sensor driver under Documentation/hwmon. Signed-off-by: Sinan Divarci <sinan.divarci@analog.com>
Add device tree bindings for the Analog Devices MAX31732 temperature sensor driver under Documentation/devicetree/bindings/hwmon/. Signed-off-by: Sinan Divarci <sinan.divarci@analog.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Sinan,
Just a first high level review. We first need to have this driver using modern hwmon API and have the userspace interface figured.
Also note that the expectation is for this to be first upstreamed (and accepted) before merging it in our tree
|
|
||
| * All temperatures reported via sysfs are in milli-degree Celsius. | ||
| * ``stop`` halts conversions but does not power-cycle the part; use ``soft_por`` | ||
| to restore all the registers to their default values. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this patch can typically be squashed with the one adding the driver
| static const struct attribute_group mtp_attr_group = { | ||
| .name = "mtp", | ||
| .attrs = mtp_attrs, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The above needs to be re-thought. The above is not the way to expose the hwmon interface. The below is what you need to be using (an example):
https://elixir.bootlin.com/linux/v6.17/source/drivers/hwmon/ltc4282.c#L1563
Then you need to look at the standard hwmon ABI:
https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
and try to map as much as you can. The reason is that you'll have to justify very carefully why you need some non standard interface to be exposed to userspace. Often we don't really need to expose everything.
| s32 ret; | ||
| u32 alarm_que; | ||
|
|
||
| if (fwnode_property_read_bool(dev_fwnode(dev), "adi,alarm1-interrupt-mode")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just as an hint for future versions of the patches you have device_property_read_bool(dev, ...). So no need for all of those dev_fwnode()
|
|
||
| ret = devm_device_add_group(dev, &mtp_attr_group); | ||
| if (ret) | ||
| return dev_err_probe(dev, ret, "failed to create mtp sysfs group\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not how you should be adding attr groups. Needs to be done through the hwmon API (when registering the device)
| if (reg_val == 0) | ||
| ret = regmap_set_bits(data->regmap, MAX31732_REG_CONF1, MAX31732_CONF1_STOP); | ||
| else | ||
| ret = regmap_clear_bits(data->regmap, MAX31732_REG_CONF1, MAX31732_CONF1_STOP); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for the above you can use regmap_update_bits()
| }; | ||
|
|
||
| MODULE_DEVICE_TABLE(i2c, max31732_ids); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
| { .compatible = "adi,max31732", }, | ||
| { }, | ||
| }; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
| .class = I2C_CLASS_HWMON, | ||
| .driver = { | ||
| .name = "max31732-driver", | ||
| .of_match_table = of_match_ptr(max31732_of_match), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drop of_match_ptr()
| .driver = { | ||
| .name = "max31732-driver", | ||
| .of_match_table = of_match_ptr(max31732_of_match), | ||
| .pm = &max31732_pm_ops, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use pm_sleep_ptr() together with DEFINE_SIMPLE_DEV_PM_OPS() and then no need of __maybe_unused
| MODULE_DESCRIPTION("MAX31732 driver"); | ||
| MODULE_LICENSE("GPL"); | ||
| MODULE_VERSION("1.0"); | ||
| MODULE_SOFTDEP("pre: regmap_i2c"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typically we dont really use the above two but I guess it does not harm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from man modprobe.d
The softdep command allows you to specify soft, or optional, module dependencies.
modulename can be used without these optional modules installed, but usually with some features missing.
but
static int max31732_probe(struct i2c_client *client)
{
// ...
data->regmap = devm_regmap_init_i2c(client, ®map_config);
if (IS_ERR(data->regmap))
return dev_err_probe(dev, PTR_ERR(data->regmap), "regmap init failed\n");
doesn't look optional to me.
| - compatible | ||
| - reg | ||
|
|
||
| additionalProperties: false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alarm properties are given as optional. But when I gave only compatible and reg in my dts and ignore the alarm part, it throws error.
[ 1.928192] max31732-driver 6-001c: error -EINVAL: cannot get ALARM1 IRQ
[ 1.935765] max31732-driver 6-001c: error -EINVAL: failed to setup ALARM1 irq
[ 1.944222] max31732-driver: probe of 6-001c failed with error -22
below is my dts,
&i2c6 {
status = "okay";
tempsnr@1c {
compatible = "adi,max31732";
reg = <0x1c>;
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @sdivarci,
A few comments from me.
I'm not very familiar with hwmon so feel safe to disregard any potentially misleading suggestion.
| reg: | ||
| description: I2C address of the device. | ||
| maxItems: 1 | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The guideline is to make dt docs as complete as possible. So, we can also describe the voltage supply here.
vcc-supply: true|
|
||
| required: | ||
| - compatible | ||
| - reg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I'm missing something, the device requires a power supply to operate, so
required:
- compatible
- reg
- vcc-supply|
|
||
| sensor@1c { | ||
| compatible = "adi,max31732"; | ||
| reg = <0x1c>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and it also needs to appear in the example. e.g.
vcc-supply = <&supply_3_3V>;
| #define MAX31732_TEMP_STEP_MC 1000 | ||
| #define MAX31732_TEMP_DIVISOR 16 | ||
| #define MAX31732_EXTENDED_RANGE_OFFSET 64000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpicking: these three are using spaces for indentation, while most of the other defines are using tabs. Standardize to using either spaces or tabs in all declarations (slight (personal) preference for tabs).
| #define MAX31732_REG_CUSTOM_IDEAL_R1 0x11 | ||
| #define MAX31732_REG_CUST_IDEAL_ENABLE 0x15 | ||
| #define MAX31732_REG_TEMP_OFFSET 0x16 | ||
| #define MAX31732_REG_OFFSET_ENABLE 0x17 | ||
| #define MAX31732_REG_FILTER_ENABLE 0x18 | ||
| #define MAX31732_REG_BETA_COMP_ENABLE 0x19 | ||
| #define MAX31732_REG_HIGHEST_TEMP_ENABLE 0x1A | ||
| #define MAX31732_REG_ALARM1_MASK 0x1B | ||
| #define MAX31732_REG_ALARM2_MASK 0x1C | ||
| #define MAX31732_REG_PRIM_HIGH_TEMP_R 0x1D | ||
| #define MAX31732_REG_PRIM_HIGH_TEMP_L 0x25 | ||
| #define MAX31732_REG_PRIM_LOW_TEMP 0x27 | ||
| #define MAX31732_REG_SECOND_HIGH_TEMP_R 0x29 | ||
| #define MAX31732_REG_SECOND_HIGH_TEMP_L 0x2D |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpicking: One additional tab would make these align nicely with #define MAX31732_CONF2_IGNORE_ISC2GND BIT(0) above.
| static const struct regmap_config regmap_config = { | ||
| .reg_bits = 8, | ||
| .val_bits = 8, | ||
| .cache_type = REGCACHE_RBTREE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do the device perform better with red-black tree cache compared to other cache types?
Regmap doc suggests new regmap clients to use other types of caches [1].
[1]: https://elixir.bootlin.com/linux/v6.17/source/include/linux/regmap.h#L62
Most other hwmon drivers that set cache type for regmap use REGCACHE_MAPLE.
| static bool max31732_volatile_reg(struct device *dev, u32 reg) | ||
| { | ||
| if ((reg >= MAX31732_REG_TEMP_R && reg <= MAX31732_REG_PRIM_LOW_STATUS) || | ||
| (reg >= MAX31732_MTP_REG_TEMP_R && reg <= MAX31732_MTP_REG_PRIM_LOW_STATUS)) | ||
| return true; | ||
|
|
||
| if (reg == MAX31732_REG_SECOND_HIGH_STATUS || reg == MAX31732_REG_SECOND_LOW_STATUS) | ||
| return true; | ||
|
|
||
| if (reg == MAX31732_REG_TEMP_FAULT) | ||
| return true; | ||
|
|
||
| if (reg >= MAX31732_REG_BETA_COMPEN_R1 && reg <= MAX31732_REG_BETA_COMPEN_R4) | ||
| return true; | ||
|
|
||
| if (reg == MAX31732_REG_HIGHEST_TEMP) | ||
| return true; | ||
|
|
||
| if (reg == MAX31732_REG_MTP_CONF2) | ||
| return true; | ||
|
|
||
| if (reg >= MAX31732_MTP_REG_USER_SW_REV) | ||
| return true; | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use regmap ranges to describe the volatile regs?
Something like
static const struct regmap_range max31732_regmap_volatile_ranges[] = {
regmap_reg_range(MAX31732_REG_TEMP_R, MAX31732_REG_PRIM_LOW_STATUS),
regmap_reg_range(MAX31732_MTP_REG_TEMP_R, MAX31732_MTP_REG_PRIM_LOW_STATUS),
regmap_reg_range(MAX31732_REG_SECOND_HIGH_STATUS,
MAX31732_REG_SECOND_HIGH_STATUS),
regmap_reg_range(MAX31732_REG_SECOND_LOW_STATUS,
MAX31732_REG_SECOND_LOW_STATUS),
...
};then the volatile function could be more concise
static bool max31732_volatile_reg(struct device *dev, u32 reg)
return regmap_reg_in_ranges(reg, max31732_regmap_volatile_ranges,
ARRAY_SIZE(max31732_regmap_volatile_ranges));
}| dev_set_drvdata(dev, data); | ||
|
|
||
| if (fwnode_property_read_bool(dev_fwnode(dev), "adi,extended-range")) | ||
| ret = regmap_set_bits(data->regmap, MAX31732_REG_CONF1, MAX31732_CONF1_EXTRANGE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The extended_range bit is only set here (at probe) and never changed after that. Then, there are a few places where it's read, (e.g. on max31732_read(), max31732_write(), max31732_highest_temperature_show(), ...). What about having a field for that in struct max31732_data so we can simplify code in those places where we currently do regmap_test_bits(data->regmap, MAX31732_REG_CONF1, MAX31732_CONF1_EXTRANGE);?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, to me, adi,extended-range doesn't seem very related to how hardware is setup. It's mostly a tweak in data offset. I worry dt maintainers might object to accepting that. Do hwmon offer any interface for changing data offset at runtime?
| ret = max31732_setup_irq(dev, data->regmap, "ALARM1", &data->irqs[0], | ||
| MAX31732_REG_ALARM1_MASK, alarm1_mask); | ||
| if (ret) | ||
| return dev_err_probe(dev, ret, "failed to setup ALARM1 irq\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check irq is available before trying to set it up?
if (data->irqs[0]) {
ret = max31732_setup_irq(dev, data->regmap, "ALARM1", &data->irqs[0],
MAX31732_REG_ALARM1_MASK, alarm1_mask);
...
}| if (ret) | ||
| return dev_err_probe(dev, ret, "failed to setup ALARM1 irq\n"); | ||
|
|
||
| ret = max31732_setup_irq(dev, data->regmap, "ALARM2", &data->irqs[1], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here.
These might solve the issue reported by @M-Kamalesh .
PR Description
Add MAX31732 hwmod driver and documentation.
PR Type
PR Checklist