Add driver for MAX42500#2659
Conversation
|
Is this still WIP? As it stands it's not really ready for reviewing... Please make sure to address CI complains and to re-organize your commits. Merge commits don't really make sense (rebase your dev branch) and please run |
a3dad5b to
a6008ba
Compare
32dc5ba to
5920e35
Compare
|
v2
|
kister-jimenez
left a comment
There was a problem hiding this comment.
First review attempt.
I think you need to do some adjustments in you formatting especially the tabs to make the code more readable. I have commented some of them, but I see a lot needs to be adjusted.
40e89e5 to
d468dbd
Compare
870d915 to
144fac7
Compare
|
v4
@nunojsa kindly review. |
nunojsa
left a comment
There was a problem hiding this comment.
Looks better to me... If you want we can still do another internal review. If not, take my latest inputs and send it upstream. Up to you :)
|
|
||
| #include <linux/err.h> | ||
| #include <linux/i2c.h> | ||
| #include <linux/kernel.h> |
There was a problem hiding this comment.
Typically the above header is not really needed and not encouraged because it includes a lot of things one usually do not need. It seems you're missing some headers but you're not realizing it because they are being included by kernel.h. Always prefer to explicitly include the needed headers...
| if (st->fpsr[MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE]) | ||
| gpiod_set_value(st->sleep_gpio, GPIOD_OUT_HIGH); | ||
| else | ||
| gpiod_set_value(st->sleep_gpio, GPIOD_OUT_LOW); |
There was a problem hiding this comment.
Why not just something like?
gpiod_set_value(st->sleep_gpio, !!st->fpsr[MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE])
| if (st->fpsr[MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE]) | ||
| gpiod_set_value(st->sleep_gpio, GPIOD_OUT_LOW); | ||
| else | ||
| gpiod_set_value(st->sleep_gpio, GPIOD_OUT_HIGH); |
| if (IS_ERR(st->sleep_gpio)) | ||
| return PTR_ERR(st->sleep_gpio); | ||
|
|
||
| devm_mutex_init(dev, &st->lock); |
| st->client = client; | ||
| st->regmap = regmap; | ||
|
|
||
| devm_mutex_init(dev, &st->lock); |
| { | ||
| struct device *parent_dev = pdev->dev.parent; | ||
| struct regmap *regmap = dev_get_drvdata(parent_dev); | ||
| struct i2c_client *client = to_i2c_client(parent_dev); |
There was a problem hiding this comment.
do you need the i2c_client?
| const u8 clk_div = MAX42500_WDOG_WD_CLOCK_DIVIDER; | ||
| const u8 win_mode = MAX42500_WDOG_WD_SIMPLE_MODE_ENABLE; | ||
| const u8 opn_win = MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL; | ||
| const u8 clo_win = MAX42500_WDOG_WD_CLOSE_WINDOW_INTERVAL; |
There was a problem hiding this comment.
I would drop the const qualifier. It does not give you much in here and the compiler should be more than capable of realizing it anyways without the qualifier
|
|
||
| ret = regmap_write(st->regmap, MAX42500_REG_WDKEY, reg_val); | ||
| if (ret) | ||
| return ret; |
There was a problem hiding this comment.
For simple mode, yes can update this. But for challenge-response mode, we need to read the current key first, apply the LFSR algorithm and write back the new key. Maybe we could leave this here or go with the change?
There was a problem hiding this comment.
Hmm, now that I look better into this, simple mode looks odd. We read the key and then write the same key back. Is this expected?
There was a problem hiding this comment.
For simple mode, any key value would do (whether same or not) as long as the WDKEY register is updated before the watchdog resets. In the datasheet it states:
The watchdog can operate in challenge/response mode (in which a specific key value must be written to WDKEY) or in simple mode (in which any write to WDKEY will update the watchdog).
There was a problem hiding this comment.
I see. Then I would say/ask. why not just writing 0 in the simple mode case? I mean, there's no real need for a register read right? And do the RMW only for the more complex case?
There was a problem hiding this comment.
yes, I see that would be simpler and optimal. So, code would now look like:
guard(mutex)(&st->lock);
switch (mode) {
case MAX42500_WD_MODE_CH_RESP:
ret = regmap_read(st->regmap, MAX42500_REG_WDKEY, &key);
if (ret)
return ret;
/* Linear-Feedback Shift Register (LFSR) Polynomial Equation */
lfsr = ((key >> 7) ^ (key >> 5) ^ (key >> 4) ^ (key >> 3)) & 1;
reg_val = (key << 1) | lfsr;
return regmap_write(st->regmap, MAX42500_REG_WDKEY, reg_val);
case MAX42500_WD_MODE_SIMPLE:
return regmap_write(st->regmap, MAX42500_REG_WDKEY, 0);
default:
return -EINVAL;
}576aa49 to
e305f27
Compare
|
@nunojsa could you take one final review before we upstream? |
|
@kister-jimenez is the dt-binding check error caused by a CI script error? |
Yes, our CI needs to be fixed for the bindings check |
| properties: | ||
| compatible: | ||
| enum: | ||
| - adi,max42500-mfd |
There was a problem hiding this comment.
Typically, you do not need the -mfd for the real device (meaning for the top device). Keep it only for -hwmon and -wdt
| gpiod_set_value(st->power_gpio, GPIOD_OUT_LOW); | ||
|
|
||
| gpiod_set_value(st->sleep_gpio, | ||
| st->fpsr[MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE]); |
There was a problem hiding this comment.
nit: I would do !!st->fpsr[MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE] just to be sure to pass 0 or 1 into gpiod_set_value()
|
|
||
| static int max42500_wdt_store_wd_rst2_cnt_enable_log(void *arg, u64 val) | ||
| { | ||
| return max42500_wdt_wdog_write(arg, (long)val, |
There was a problem hiding this comment.
this explicit cast should not be needed
|
|
||
| static int max42500_wdt_show_wd_rst2_cnt_enable_log(void *arg, u64 *val) | ||
| { | ||
| return max42500_wdt_wdog_read(arg, (long *)val, |
There was a problem hiding this comment.
not sure this is correct. This will go wrong in big endian 32bit archs... I would use a dedicated variable. This:
long __val;
int ret;
ret = max42500_wdt_wdog_read(arg, &__val, ...);
if (ret)
return ret;
*val = __val;
Same for the other places where this applies. Note this is mostly a problem fro getters and not setters since getters use pointers which this cast to potentially smaller types could be problematic.
|
|
||
| ret = regmap_write(st->regmap, MAX42500_REG_WDKEY, reg_val); | ||
| if (ret) | ||
| return ret; |
There was a problem hiding this comment.
Hmm, now that I look better into this, simple mode looks odd. We read the key and then write the same key back. Is this expected?
| return ret; | ||
|
|
||
| /* Update after successful register write */ | ||
| st->wdog[MAX42500_WDOG_WD_KEY] = FIELD_GET(GENMASK(7, 0), reg_val); |
There was a problem hiding this comment.
And I think I would also remove this line.
There was a problem hiding this comment.
yeah, no need to cache all values you write even more when you can read them from the device
| /* Update after successful register write */ | ||
| st->wdog[MAX42500_WDOG_WD_KEY] = FIELD_GET(GENMASK(7, 0), reg_val); | ||
|
|
||
| return 0; |
93c684e to
6560334
Compare
nunojsa
left a comment
There was a problem hiding this comment.
Feel free to send this upstream. As another advice, I would improve the commit messages for the bindings patch. Saying "adding bindings file" or something like that is only stating the obvious, You should add a short description of the device being added (similar to the driver patch)
| #address-cells = <1>; | ||
| #size-cells = <0>; | ||
|
|
||
| max42500@28 { |
There was a problem hiding this comment.
I do not think this will be accepted... Needs to be more generic but honestly i'm not sure what can we put in here. I know I was the one to mention this but maybe just send this upstream as you had it before. Meaning, "hwmon" since the primary function of the device is monitoring.
There was a problem hiding this comment.
ok will do. Previously I wrote "mfd" then "mfdev", but I thought it was too generic since all other mfd drivers are also too.
There was a problem hiding this comment.
Saw something similar in richtek and/or ricoh mfd dt-bindings and was not sure if same for max42500. But I guess not.
| max42500_subdev, ARRAY_SIZE(max42500_subdev), | ||
| NULL, 0, NULL); | ||
|
|
||
| return ret; |
There was a problem hiding this comment.
you can just return devm_mfd_add_devices(...)
| #include <linux/mutex.h> | ||
| #include <linux/mfd/max42500.h> | ||
| #include <linux/platform_device.h> | ||
| #include <linux/mod_devicetable.h> |
| static struct platform_driver max42500_hwmon_driver = { | ||
| .driver = { | ||
| .name = "max42500-hwmon", | ||
| .pm = &max42500_pm_ops, |
| return 0; | ||
| } | ||
|
|
||
| EXPORT_SIMPLE_DEV_PM_OPS(max42500_pm_ops, max42500_suspend, max42500_resume); |
There was a problem hiding this comment.
why exporting this? Do you need this function anywhere else? I guess not...
| int ret; | ||
|
|
||
| ret = max42500_wdt_wdog_read(watchdog_get_drvdata(wdd), &wdog_val, | ||
| MAX42500_WDOG_WD_STATUS); |
There was a problem hiding this comment.
alignment seems a bit off here
| guard(mutex)(&st->lock); | ||
|
|
||
| switch (mode) { | ||
| case MAX42500_WD_MODE_CH_RESP: |
There was a problem hiding this comment.
nit: the mutex is only needed in this case
There was a problem hiding this comment.
when the mutex is moved in the case, there is build error:
In file included from drivers/watchdog/max42500-wdt.c:8:
drivers/watchdog/max42500-wdt.c: In function ‘max42500_wdt_set_watchdog_key’:
./include/linux/cleanup.h:86:2: error: a label can only be part of a statement and a declaration is not a statement
86 | class_##_name##_t var __cleanup(class_##_name##_destructor) = \
| ^~~~~~
./include/linux/cleanup.h:131:2: note: in expansion of macro ‘CLASS’
131 | CLASS(_name, __UNIQUE_ID(guard))
| ^~~~~
drivers/watchdog/max42500-wdt.c:94:3: note: in expansion of macro ‘guard’
94 | guard(mutex)(&st->lock);
| ^~~~~
make[4]: *** [scripts/Makefile.build:243: drivers/watchdog/max42500-wdt.o] Error 1
make[3]: *** [scripts/Makefile.build:480: drivers/watchdog] Error 2
make[3]: *** Waiting for unfinished jobs....There was a problem hiding this comment.
You need the scoped version...
| reg_val = (key << 1) | lfsr; | ||
|
|
||
| return regmap_write(st->regmap, MAX42500_REG_WDKEY, reg_val); | ||
| case MAX42500_WD_MODE_SIMPLE: |
There was a problem hiding this comment.
nit: I would be tempted to put a comment stating that we just need to do a random write in this case (just to avoid potential questions) - not sure if the intent is already obvious.
There was a problem hiding this comment.
should I add the mutex in the case here too?
There was a problem hiding this comment.
no, regmap already has an internal lock unless you disable it in the config (which you're not doing)
The MAX42500 component structure of the nodes and properties of the serial interfaces and the I/O ports to access, control and configure the voltage monitors, and to program the power sequence timestamp recordings and the windowed watchdog timing. Signed-off-by: Kent Libetario <Kent.Libetario@analog.com>
The MAX42500 is a multiple function device for power system monitoring, power sequence timestamp recordings and windowed watchdog combined all- together in one SoC package. These features can be used independently or at the same time in a wide range of applications. Signed-off-by: Kent Libetario <Kent.Libetario@analog.com>
The MAX42500 supports standard power management, hwmon and watchdog ABI and sysfs attribute entries to expose the sensor data of the voltage monitors, power sequence timestamp recordings and windowed watchdog to the userspace. Signed-off-by: Kent Libetario <Kent.Libetario@analog.com>
The MAX42500 is a SoC power-system monitor with up to seven voltage monitor inputs that has programmable OV/UV thresholds and support DVS by I2C interface. Also, it has a programmable flexible power sequence recorder that stores the power-up and power-down timestamps separately. Signed-off-by: Kent Libetario <Kent.Libetario@analog.com>
5baf2de to
8772b88
Compare
The MAX42500 contains a programmable windowed watchdog and can be used either as simple mode windowed watchdog or challenge-and-response mode watchdog, which is accessible through the I2C interface, along with a configurable RESET output. Signed-off-by: Kent Libetario <Kent.Libetario@analog.com>
Add entry for the MAX42500 driver Signed-off-by: Kent Libetario <Kent.Libetario@analog.com>
|
Hi, I'm just checking in on the status of this pull request. Is there anything I can do to help move it forward? |
|
@kentlibe ping |
PR Description
PR Type
PR Checklist