On Thu, Sep 24, 2020 at 10:26 PM Jonathan Neuschäfer <j.neuschaefer@xxxxxxx> wrote: > > The Netronix embedded controller is a microcontroller found in some > e-book readers designed by the ODM Netronix, Inc. It contains RTC, > battery monitoring, system power management, and PWM functionality. > > This driver implements register access and version detection. > > Third-party hardware documentation is available at: > > https://github.com/neuschaefer/linux/wiki/Netronix-MSP430-embedded-controller > > The EC supports interrupts, but the driver doesn't make use of them so > far. ... > +#include <asm/unaligned.h> This usually goes after linux/*.h (and actually not visible how it's being used, but see below first) > +#include <linux/delay.h> > +#include <linux/errno.h> > +#include <linux/i2c.h> > +#include <linux/mfd/core.h> > +#include <linux/mfd/ntxec.h> > +#include <linux/module.h> > +#include <linux/pm.h> > +#include <linux/reboot.h> > +#include <linux/regmap.h> > +#include <linux/types.h> ... > +static void ntxec_poweroff(void) > +{ > + int res; > + u8 buf[] = { > + NTXEC_REG_POWEROFF, > + (NTXEC_POWEROFF_VALUE >> 8) & 0xff, > + NTXEC_POWEROFF_VALUE & 0xff, '& 0xff' parts are redundant. *u8 implies that. Fix in all cases. Also I would rather see something like buf[0] = _POWEROFF; put_unaligned_be16(_VALUE, &buf[1]); to explicitly show the endianess of the register values. > + }; > + struct i2c_msg msgs[] = { > + { > + .addr = poweroff_restart_client->addr, > + .flags = 0, > + .len = sizeof(buf), > + .buf = buf It's slightly better to keep trailing commas in cases like this. > + } > + }; > + > + res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs)); > + if (res < 0) > + dev_alert(&poweroff_restart_client->dev, > + "Failed to power off (err = %d)\n", res); alert? This needs to be explained. > + /* > + * The time from the register write until the host CPU is powered off > + * has been observed to be about 2.5 to 3 seconds. Sleep long enough to > + * safely avoid returning from the poweroff handler. > + */ > + msleep(5000); > +} > + > +static int ntxec_restart(struct notifier_block *nb, > + unsigned long action, void *data) > +{ > + int res; > + /* > + * NOTE: The lower half of the reset value is not sent, because sending > + * it causes an error Why? Any root cause? Perhaps you need to send 0xffff ? > + */ > + u8 buf[] = { > + NTXEC_REG_RESET, > + (NTXEC_RESET_VALUE >> 8) & 0xff, Here you may still use put_unaligned_be16() but move the comment to be before len hardcoded to sizeof(buf) - 1. > + }; > + struct i2c_msg msgs[] = { > + { > + .addr = poweroff_restart_client->addr, > + .flags = 0, > + .len = sizeof(buf), > + .buf = buf > + } > + }; > + > + res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs)); > + if (res < 0) > + dev_alert(&poweroff_restart_client->dev, > + "Failed to restart (err = %d)\n", res); > + > + return NOTIFY_DONE; > +} ... > +static int ntxec_probe(struct i2c_client *client) > +{ > + struct ntxec *ec; > + unsigned int version; > + int res; > + > + ec = devm_kmalloc(&client->dev, sizeof(*ec), GFP_KERNEL); > + if (!ec) > + return -ENOMEM; > + > + ec->dev = &client->dev; > + > + ec->regmap = devm_regmap_init_i2c(client, ®map_config); > + if (IS_ERR(ec->regmap)) { > + dev_err(ec->dev, "Failed to set up regmap for device\n"); > + return res; > + } > + > + /* Determine the firmware version */ > + res = regmap_read(ec->regmap, NTXEC_REG_VERSION, &version); > + if (res < 0) { > + dev_err(ec->dev, "Failed to read firmware version number\n"); > + return res; > + } > + dev_info(ec->dev, > + "Netronix embedded controller version %04x detected.\n", > + version); This info level may confuse users if followed by an error path. > + /* Bail out if we encounter an unknown firmware version */ > + switch (version) { > + case 0xd726: /* found in Kobo Aura */ > + break; > + default: > + return -ENODEV; > + } > + > + if (of_device_is_system_power_controller(ec->dev->of_node)) { > + /* > + * Set the 'powerkeep' bit. This is necessary on some boards > + * in order to keep the system running. > + */ > + res = regmap_write(ec->regmap, NTXEC_REG_POWERKEEP, > + NTXEC_POWERKEEP_VALUE); > + if (res < 0) > + return res; > + WARN_ON(poweroff_restart_client); WARN_ON? All these alerts, WARNs, BUGs must be explained. Screaming to the user is not good if it wasn't justified. > + poweroff_restart_client = client; > + if (pm_power_off) > + dev_err(ec->dev, "pm_power_off already assigned\n"); > + else > + pm_power_off = ntxec_poweroff; > + > + res = register_restart_handler(&ntxec_restart_handler); > + if (res) > + dev_err(ec->dev, > + "Failed to register restart handler: %d\n", res); > + } > + > + i2c_set_clientdata(client, ec); > + > + res = devm_mfd_add_devices(ec->dev, PLATFORM_DEVID_NONE, ntxec_subdevices, > + ARRAY_SIZE(ntxec_subdevices), NULL, 0, NULL); > + if (res) > + dev_warn(ec->dev, "Failed to add subdevices: %d\n", res); 'warn' is inconsistent with 'return err'. Either do not return an error, or mark a message as an error one. And above with the restart handler has the same issue. > + return res; > +} > + > +static int ntxec_remove(struct i2c_client *client) > +{ > + if (client == poweroff_restart_client) { When it's not the case? > + poweroff_restart_client = NULL; > + pm_power_off = NULL; > + unregister_restart_handler(&ntxec_restart_handler); > + } > + > + return 0; > +} ... > +#include <linux/types.h> > + Missed struct device; struct regmap; here. > +struct ntxec { > + struct device *dev; > + struct regmap *regmap; > +}; > +/* > + * Some registers, such as the battery status register (0x41), are in > + * big-endian, but others only have eight significant bits, which are in the > + * first byte transmitted over I2C (the MSB of the big-endian value). > + * This convenience function converts an 8-bit value to 16-bit for use in the > + * second kind of register. > + */ > +static inline u16 ntxec_reg8(u8 value) > +{ > + return value << 8; > +} I'm wondering why __be16 is not used as returned type. -- With Best Regards, Andy Shevchenko