On 5/8/19 10:36 AM, Lee Jones wrote: > On Tue, 09 Apr 2019, Amelie Delaunay wrote: > >> STMicroelectronics Multi-Function eXpander (STMFX) is a slave controller >> using I2C for communication with the main MCU. Main features are: >> - 16 fast GPIOs individually configurable in input/output >> - 8 alternate GPIOs individually configurable in input/output when other >> STMFX functions are not used >> - Main MCU IDD measurement >> - Resistive touchscreen controller >> >> Signed-off-by: Amelie Delaunay <amelie.delaunay@xxxxxx> >> --- >> drivers/mfd/Kconfig | 13 ++ >> drivers/mfd/Makefile | 2 +- >> drivers/mfd/stmfx.c | 566 ++++++++++++++++++++++++++++++++++++++++++++++ >> include/linux/mfd/stmfx.h | 123 ++++++++++ >> 4 files changed, 703 insertions(+), 1 deletion(-) >> create mode 100644 drivers/mfd/stmfx.c >> create mode 100644 include/linux/mfd/stmfx.h >> >> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig >> index 3443f1a..9783e18 100644 >> --- a/drivers/mfd/Kconfig >> +++ b/drivers/mfd/Kconfig >> @@ -1907,6 +1907,19 @@ config MFD_STPMIC1 >> To compile this driver as a module, choose M here: the >> module will be called stpmic1. >> >> +config MFD_STMFX >> + tristate "Support for STMicroelectronics Multi-Function eXpander (STMFX)" >> + depends on I2C >> + depends on OF || COMPILE_TEST >> + select MFD_CORE >> + select REGMAP_I2C >> + help >> + Support for the STMicroelectronics Multi-Function eXpander. >> + >> + This driver provides common support for accessing the device, >> + additional drivers must be enabled in order to use the functionality >> + of the device. >> + >> menu "Multimedia Capabilities Port drivers" >> depends on ARCH_SA1100 >> >> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile >> index b4569ed7..614eea8 100644 >> --- a/drivers/mfd/Makefile >> +++ b/drivers/mfd/Makefile >> @@ -246,4 +246,4 @@ obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o >> obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o >> obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o >> obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o >> - >> +obj-$(CONFIG_MFD_STMFX) += stmfx.o >> diff --git a/drivers/mfd/stmfx.c b/drivers/mfd/stmfx.c >> new file mode 100644 >> index 0000000..59f0a03 >> --- /dev/null >> +++ b/drivers/mfd/stmfx.c >> @@ -0,0 +1,566 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * Driver for STMicroelectronics Multi-Function eXpander (STMFX) core >> + * >> + * Copyright (C) 2019 STMicroelectronics >> + * Author(s): Amelie Delaunay <amelie.delaunay@xxxxxx>. >> + */ >> +#include <linux/bitfield.h> >> +#include <linux/i2c.h> >> +#include <linux/interrupt.h> >> +#include <linux/irq.h> >> +#include <linux/mfd/core.h> >> +#include <linux/mfd/stmfx.h> >> +#include <linux/module.h> >> +#include <linux/regulator/consumer.h> > > [...] > >> +static int stmfx_chip_init(struct i2c_client *client) >> +{ >> + struct stmfx *stmfx = i2c_get_clientdata(client); >> + u32 id; >> + u8 version[2]; >> + int ret; >> + >> + stmfx->vdd = devm_regulator_get_optional(&client->dev, "vdd"); >> + if (IS_ERR(stmfx->vdd)) { >> + ret = PTR_ERR(stmfx->vdd); >> + if (ret != -ENODEV) { >> + if (ret != -EPROBE_DEFER) >> + dev_err(&client->dev, >> + "Can't get VDD regulator:%d\n", ret); >> + return ret; >> + } > > Any reason you've decided to stick with this 3-layer nested if instead > of going with my suggestion? > Sorry, I didn't see your suggestion. I'll go with it in v6. >> + } else { >> + ret = regulator_enable(stmfx->vdd); >> + if (ret) { >> + dev_err(&client->dev, "VDD enable failed: %d\n", ret); >> + return ret; >> + } >> + } > > [...] > >> +#ifdef CONFIG_PM_SLEEP >> +static int stmfx_backup_regs(struct stmfx *stmfx) >> +{ >> + int ret; >> + >> + ret = regmap_raw_read(stmfx->map, STMFX_REG_SYS_CTRL, >> + &stmfx->bkp_sysctrl, sizeof(stmfx->bkp_sysctrl)); >> + if (ret) >> + return ret; >> + >> + ret = regmap_raw_read(stmfx->map, STMFX_REG_IRQ_OUT_PIN, >> + &stmfx->bkp_irqoutpin, >> + sizeof(stmfx->bkp_irqoutpin)); >> + if (ret) >> + return ret; >> + >> + return 0; >> +} >> + >> +static int stmfx_restore_regs(struct stmfx *stmfx) >> +{ >> + int ret; >> + >> + ret = regmap_raw_write(stmfx->map, STMFX_REG_SYS_CTRL, >> + &stmfx->bkp_sysctrl, sizeof(stmfx->bkp_sysctrl)); >> + if (ret) >> + return ret; >> + >> + ret = regmap_raw_write(stmfx->map, STMFX_REG_IRQ_OUT_PIN, >> + &stmfx->bkp_irqoutpin, >> + sizeof(stmfx->bkp_irqoutpin)); >> + if (ret) >> + return ret; >> + >> + ret = regmap_raw_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, >> + &stmfx->irq_src, sizeof(stmfx->irq_src)); >> + if (ret) >> + return ret; >> + >> + return 0; >> +} >> + >> +static int stmfx_suspend(struct device *dev) >> +{ >> + struct stmfx *stmfx = dev_get_drvdata(dev); >> + int ret; >> + >> + ret = stmfx_backup_regs(stmfx); >> + if (ret) { >> + dev_err(stmfx->dev, "Registers backup failure\n"); >> + return ret; >> + } > > This doesn't need to be an extra function. You're just adding more > lines of code for no real gain in reusability/readability. > I used a separate function to have only one dev_err in case of backup/restore failure. But anyway, I'll drop backup/restore functions and put the code in suspend/resume. >> + if (!IS_ERR(stmfx->vdd)) { >> + ret = regulator_disable(stmfx->vdd); >> + if (ret) >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int stmfx_resume(struct device *dev) >> +{ >> + struct stmfx *stmfx = dev_get_drvdata(dev); >> + int ret; >> + >> + if (!IS_ERR(stmfx->vdd)) { >> + ret = regulator_enable(stmfx->vdd); >> + if (ret) { >> + dev_err(stmfx->dev, >> + "VDD enable failed: %d\n", ret); >> + return ret; >> + } >> + } >> + >> + ret = stmfx_restore_regs(stmfx); >> + if (ret) { >> + dev_err(stmfx->dev, "Registers restoration failure\n"); >> + return ret; >> + } > > This doesn't need to be an extra function. You're just adding more > lines of code for no real gain in reusability/readability. > >> + return 0; >> +} >> +#endif > > [...] >