-
Notifications
You must be signed in to change notification settings - Fork 871
Add driver for MAX42500 #2659
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 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
|
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.
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. |
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.
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 :)
drivers/mfd/max42500.c
Outdated
|
||
#include <linux/err.h> | ||
#include <linux/i2c.h> | ||
#include <linux/kernel.h> |
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 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...
drivers/hwmon/max42500-hwmon.c
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just something like?
gpiod_set_value(st->sleep_gpio, !!st->fpsr[MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE])
drivers/hwmon/max42500-hwmon.c
Outdated
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); |
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
drivers/hwmon/max42500-hwmon.c
Outdated
if (IS_ERR(st->sleep_gpio)) | ||
return PTR_ERR(st->sleep_gpio); | ||
|
||
devm_mutex_init(dev, &st->lock); |
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.
error handling
drivers/watchdog/max42500-wdt.c
Outdated
st->client = client; | ||
st->regmap = regmap; | ||
|
||
devm_mutex_init(dev, &st->lock); |
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.
error handling...
drivers/watchdog/max42500-wdt.c
Outdated
{ | ||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you need the i2c_client?
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.
will be removed
drivers/watchdog/max42500-wdt.c
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
drivers/watchdog/max42500-wdt.c
Outdated
|
||
ret = regmap_write(st->regmap, MAX42500_REG_WDKEY, reg_val); | ||
if (ret) | ||
return ret; |
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
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically, you do not need the -mfd for the real device (meaning for the top device). Keep it only for -hwmon and -wdt
drivers/hwmon/max42500-hwmon.c
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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()
drivers/watchdog/max42500-wdt.c
Outdated
|
||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this explicit cast should not be needed
drivers/watchdog/max42500-wdt.c
Outdated
|
||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
drivers/watchdog/max42500-wdt.c
Outdated
|
||
ret = regmap_write(st->regmap, MAX42500_REG_WDKEY, reg_val); | ||
if (ret) | ||
return ret; |
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.
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?
drivers/watchdog/max42500-wdt.c
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I think I would also remove this line.
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.
yeah, no need to cache all values you write even more when you can read them from the device
drivers/watchdog/max42500-wdt.c
Outdated
/* Update after successful register write */ | ||
st->wdog[MAX42500_WDOG_WD_KEY] = FIELD_GET(GENMASK(7, 0), reg_val); | ||
|
||
return 0; |
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 this as well
93c684e
to
6560334
Compare
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Saw something similar in richtek and/or ricoh mfd dt-bindings and was not sure if same for max42500. But I guess not.
drivers/mfd/max42500.c
Outdated
max42500_subdev, ARRAY_SIZE(max42500_subdev), | ||
NULL, 0, NULL); | ||
|
||
return ret; |
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.
you can just return devm_mfd_add_devices(...)
drivers/hwmon/max42500-hwmon.c
Outdated
#include <linux/mutex.h> | ||
#include <linux/mfd/max42500.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/mod_devicetable.h> |
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.
alphabetical order
drivers/hwmon/max42500-hwmon.c
Outdated
static struct platform_driver max42500_hwmon_driver = { | ||
.driver = { | ||
.name = "max42500-hwmon", | ||
.pm = &max42500_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()
drivers/hwmon/max42500-hwmon.c
Outdated
return 0; | ||
} | ||
|
||
EXPORT_SIMPLE_DEV_PM_OPS(max42500_pm_ops, max42500_suspend, max42500_resume); |
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.
why exporting this? Do you need this function anywhere else? I guess not...
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.
will remove this
drivers/watchdog/max42500-wdt.c
Outdated
int ret; | ||
|
||
ret = max42500_wdt_wdog_read(watchdog_get_drvdata(wdd), &wdog_val, | ||
MAX42500_WDOG_WD_STATUS); |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the mutex is only needed in this case
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should I add the mutex in the case here too?
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.
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>
PR Description
PR Type
PR Checklist