* Re: [PATCH v12 00/10] Support ROHM BD71828 PMIC
From: Vaittinen, Matti @ 2020-01-20 13:41 UTC (permalink / raw)
To: mazziesaccount@gmail.com
Cc: corbet@lwn.net, linux-doc@vger.kernel.org,
phil.edworthy@renesas.com, dmurphy@ti.com,
linux-leds@vger.kernel.org, devicetree@vger.kernel.org,
linux-rtc@vger.kernel.org, linux-gpio@vger.kernel.org,
mchehab+samsung@kernel.org, alexandre.belloni@bootlin.com,
linux-kernel@vger.kernel.org, mturquette@baylibre.com,
lgirdwood@gmail.com, jacek.anaszewski@gmail.com,
wsa+renesas@sang-engineering.com, a.zummo@towertech.it,
linus.walleij@linaro.org, arnd@arndb.de, mark.rutland@arm.com,
robh+dt@kernel.org, noralf@tronnes.org,
gregkh@linuxfoundation.org, bgolaszewski@baylibre.com,
linux-clk@vger.kernel.org, lee.jones@linaro.org, pavel@ucw.cz,
broonie@kernel.org, sboyd@kernel.org
In-Reply-To: <ecc8ab43dfdb78c7bcab82311f608f6d4e12dc5c.camel@fi.rohmeurope.com>
On Mon, 2020-01-20 at 14:41 +0200, Matti Vaittinen wrote:
> Hello,
>
> Do you know those days when nothing, _nothing_ just works out as
> intended? Today is one of those.
>
> On Mon, 2020-01-20 at 11:12 +0200, Matti Vaittinen wrote:
> > Patch series introducing support for ROHM BD71828 PMIC
> >
> > ROHM BD71828 is a power management IC containing 7 bucks and 7
> > LDOs.
> > All
> > regulators can be controlled individually via I2C. Bucks 1,2,6 and
> > 7 can also be assigned to a "regulator group" controlled by run-
> > levels.
> > Eg. Run level specific voltages and enable/disable statuses for
> > each
> > of
> > these bucks can be set via register interface. The buck run-level
> > group
> > assignment (selection if buck is to be controlled individually or
> > via
> > run-levels) can be changed at run-time via I2C.
> >
> > This patch series brings only the basic support for controlling
> > regulators individually via I2C.
>
> //snip
>
> > Patch 11:
> > Allow control of GP(I)O pins on BD71828 via GPIO subsystem
> >
>
> I accidentally rebased to wrong commit and cropped the GPIO patch out
> of the series. Lee - can you take the GPIO part from v11 (patch 11/13
> there). It should apply cleanly and I have no changes to it. Or
> should
> I just resend the whole series (again)?
Maybe it is clearest if I just do resend... v13 is on it's way but I
dropped most of the recipients. Please let me know if some of you want
to get it.
>
> Br,
> Matti Vaittinen
^ permalink raw reply
* Re: [PATCH v12 00/10] Support ROHM BD71828 PMIC
From: Vaittinen, Matti @ 2020-01-20 12:41 UTC (permalink / raw)
To: mazziesaccount@gmail.com
Cc: corbet@lwn.net, linux-doc@vger.kernel.org,
phil.edworthy@renesas.com, dmurphy@ti.com,
linux-leds@vger.kernel.org, devicetree@vger.kernel.org,
linux-rtc@vger.kernel.org, linux-gpio@vger.kernel.org,
mchehab+samsung@kernel.org, alexandre.belloni@bootlin.com,
linux-kernel@vger.kernel.org, mturquette@baylibre.com,
lgirdwood@gmail.com, jacek.anaszewski@gmail.com,
wsa+renesas@sang-engineering.com, a.zummo@towertech.it,
linus.walleij@linaro.org, arnd@arndb.de, mark.rutland@arm.com,
robh+dt@kernel.org, noralf@tronnes.org,
gregkh@linuxfoundation.org, bgolaszewski@baylibre.com,
linux-clk@vger.kernel.org, lee.jones@linaro.org, pavel@ucw.cz,
broonie@kernel.org, sboyd@kernel.org
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
Hello,
Do you know those days when nothing, _nothing_ just works out as
intended? Today is one of those.
On Mon, 2020-01-20 at 11:12 +0200, Matti Vaittinen wrote:
> Patch series introducing support for ROHM BD71828 PMIC
>
> ROHM BD71828 is a power management IC containing 7 bucks and 7 LDOs.
> All
> regulators can be controlled individually via I2C. Bucks 1,2,6 and
> 7 can also be assigned to a "regulator group" controlled by run-
> levels.
> Eg. Run level specific voltages and enable/disable statuses for each
> of
> these bucks can be set via register interface. The buck run-level
> group
> assignment (selection if buck is to be controlled individually or via
> run-levels) can be changed at run-time via I2C.
>
> This patch series brings only the basic support for controlling
> regulators individually via I2C.
//snip
> Patch 11:
> Allow control of GP(I)O pins on BD71828 via GPIO subsystem
>
I accidentally rebased to wrong commit and cropped the GPIO patch out
of the series. Lee - can you take the GPIO part from v11 (patch 11/13
there). It should apply cleanly and I have no changes to it. Or should
I just resend the whole series (again)?
Br,
Matti Vaittinen
^ permalink raw reply
* Re: [PATCH v1 5/8] x86/quirks: Add a DMI quirk for Microsoft Surface 3
From: Andy Shevchenko @ 2020-01-20 12:21 UTC (permalink / raw)
To: Mark Brown
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin,
x86, Alessandro Zummo, Alexandre Belloni, linux-rtc,
Guilherme G . Piccoli, linux-kernel, Cezary Rojewski,
Pierre-Louis Bossart, Liam Girdwood, Jie Yang, alsa-devel
In-Reply-To: <20200120120933.GB6852@sirena.org.uk>
On Mon, Jan 20, 2020 at 12:09:33PM +0000, Mark Brown wrote:
> On Fri, Jan 17, 2020 at 07:56:23PM +0200, Andy Shevchenko wrote:
> > Add a DMI quirk for Microsoft Surface 3 which will be utilized by few drivers.
>
> I have patches 5 and 7 with no other context, what's going on with
> dependencies here?
Other patches has no relation to ASoC or vise versa.
I will Cc you for entire series in next version.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v1 5/8] x86/quirks: Add a DMI quirk for Microsoft Surface 3
From: Mark Brown @ 2020-01-20 12:09 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin,
x86, Alessandro Zummo, Alexandre Belloni, linux-rtc,
Guilherme G . Piccoli, linux-kernel, Cezary Rojewski,
Pierre-Louis Bossart, Liam Girdwood, Jie Yang, alsa-devel
In-Reply-To: <20200117175626.56358-5-andriy.shevchenko@linux.intel.com>
[-- Attachment #1: Type: text/plain, Size: 233 bytes --]
On Fri, Jan 17, 2020 at 07:56:23PM +0200, Andy Shevchenko wrote:
> Add a DMI quirk for Microsoft Surface 3 which will be utilized by few drivers.
I have patches 5 and 7 with no other context, what's going on with
dependencies here?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v1 7/8] ASoC: Intel: Switch DMI table match to a test of variable
From: Andy Shevchenko @ 2020-01-20 10:17 UTC (permalink / raw)
To: Pierre-Louis Bossart
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin,
x86, Alessandro Zummo, Alexandre Belloni, linux-rtc,
Guilherme G . Piccoli, linux-kernel, Cezary Rojewski,
Liam Girdwood, Jie Yang, Mark Brown, alsa-devel
In-Reply-To: <c92e0395-0a08-a400-eb48-0aa05e52cf30@linux.intel.com>
On Fri, Jan 17, 2020 at 01:10:55PM -0600, Pierre-Louis Bossart wrote:
> On 1/17/20 11:56 AM, Andy Shevchenko wrote:
> > Since we have a common x86 quirk that provides an exported variable,
> > use it instead of local DMI table match.
> >
> > Cc: Cezary Rojewski <cezary.rojewski@intel.com>
> > Cc: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
> > Cc: Liam Girdwood <liam.r.girdwood@linux.intel.com>
> > Cc: Jie Yang <yang.jie@linux.intel.com>
> > Cc: Mark Brown <broonie@kernel.org>
> > Cc: alsa-devel@alsa-project.org
> > Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>
> Thanks Andy.
>
> Acked-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Thank you. Though I think I'll re-do this a bit, i.e.
- convert the cht_quirk() to oneliner that is using ternary operator
- convert also codec driver to use variable instead of DMI match
>
> > ---
> > .../intel/common/soc-acpi-intel-cht-match.c | 28 ++-----------------
> > 1 file changed, 3 insertions(+), 25 deletions(-)
> >
> > diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
> > index d0fb43c2b9f6..833d2e130e6e 100644
> > --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c
> > +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
> > @@ -5,31 +5,11 @@
> > * Copyright (c) 2017, Intel Corporation.
> > */
> > -#include <linux/dmi.h>
> > +#include <linux/platform_data/x86/machine.h>
> > +
> > #include <sound/soc-acpi.h>
> > #include <sound/soc-acpi-intel-match.h>
> > -static unsigned long cht_machine_id;
> > -
> > -#define CHT_SURFACE_MACH 1
> > -
> > -static int cht_surface_quirk_cb(const struct dmi_system_id *id)
> > -{
> > - cht_machine_id = CHT_SURFACE_MACH;
> > - return 1;
> > -}
> > -
> > -static const struct dmi_system_id cht_table[] = {
> > - {
> > - .callback = cht_surface_quirk_cb,
> > - .matches = {
> > - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
> > - DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
> > - },
> > - },
> > - { }
> > -};
> > -
> > static struct snd_soc_acpi_mach cht_surface_mach = {
> > .id = "10EC5640",
> > .drv_name = "cht-bsw-rt5645",
> > @@ -43,9 +23,7 @@ static struct snd_soc_acpi_mach *cht_quirk(void *arg)
> > {
> > struct snd_soc_acpi_mach *mach = arg;
> > - dmi_check_system(cht_table);
> > -
> > - if (cht_machine_id == CHT_SURFACE_MACH)
> > + if (x86_microsoft_surface_3_machine)
> > return &cht_surface_mach;
> > else
> > return mach;
> >
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH v12 10/10] rtc: bd70528: add BD71828 support
From: Matti Vaittinen @ 2020-01-20 9:22 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
ROHM BD71828 PMIC RTC block is from many parts similar to one
on BD70528. Support BD71828 RTC using BD70528 RTC driver and
avoid re-inventing the wheel.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
drivers/rtc/Kconfig | 3 +-
drivers/rtc/rtc-bd70528.c | 220 +++++++++++++++++++++++++------
include/linux/mfd/rohm-bd70528.h | 13 +-
include/linux/mfd/rohm-bd71828.h | 4 +-
include/linux/mfd/rohm-shared.h | 21 +++
5 files changed, 206 insertions(+), 55 deletions(-)
create mode 100644 include/linux/mfd/rohm-shared.h
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index d77515d8382c..df7a3843069d 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -498,12 +498,13 @@ config RTC_DRV_M41T80_WDT
help
If you say Y here you will get support for the
watchdog timer in the ST M41T60 and M41T80 RTC chips series.
+
config RTC_DRV_BD70528
tristate "ROHM BD70528 PMIC RTC"
depends on MFD_ROHM_BD70528 && (BD70528_WATCHDOG || !BD70528_WATCHDOG)
help
If you say Y here you will get support for the RTC
- on ROHM BD70528 Power Management IC.
+ block on ROHM BD70528 and BD71828 Power Management IC.
This driver can also be built as a module. If so, the module
will be called rtc-bd70528.
diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c
index 627037aa66a8..bbbb1f07c91f 100644
--- a/drivers/rtc/rtc-bd70528.c
+++ b/drivers/rtc/rtc-bd70528.c
@@ -6,6 +6,7 @@
#include <linux/bcd.h>
#include <linux/mfd/rohm-bd70528.h>
+#include <linux/mfd/rohm-bd71828.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -15,7 +16,7 @@
/*
* We read regs RTC_SEC => RTC_YEAR
* this struct is ordered according to chip registers.
- * Keep it u8 only to avoid padding issues.
+ * Keep it u8 only (or packed) to avoid padding issues.
*/
struct bd70528_rtc_day {
u8 sec;
@@ -36,6 +37,13 @@ struct bd70528_rtc_wake {
u8 ctrl;
} __packed;
+struct bd71828_rtc_alm {
+ struct bd70528_rtc_data alm0;
+ struct bd70528_rtc_data alm1;
+ u8 alm_mask;
+ u8 alm1_mask;
+} __packed;
+
struct bd70528_rtc_alm {
struct bd70528_rtc_data data;
u8 alm_mask;
@@ -43,8 +51,10 @@ struct bd70528_rtc_alm {
} __packed;
struct bd70528_rtc {
- struct rohm_regmap_dev *mfd;
+ struct rohm_regmap_dev *parent;
struct device *dev;
+ u8 reg_time_start;
+ bool has_rtc_timers;
};
static int bd70528_set_wake(struct rohm_regmap_dev *bd70528,
@@ -123,14 +133,14 @@ static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state,
{
int ret;
- ret = bd70528_wdt_set(r->mfd, new_state & BD70528_WDT_STATE_BIT,
+ ret = bd70528_wdt_set(r->parent, new_state & BD70528_WDT_STATE_BIT,
old_state);
if (ret) {
dev_err(r->dev,
"Failed to disable WDG for RTC setting (%d)\n", ret);
return ret;
}
- ret = bd70528_set_elapsed_tmr(r->mfd,
+ ret = bd70528_set_elapsed_tmr(r->parent,
new_state & BD70528_ELAPSED_STATE_BIT,
old_state);
if (ret) {
@@ -138,7 +148,7 @@ static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state,
"Failed to disable 'elapsed timer' for RTC setting\n");
return ret;
}
- ret = bd70528_set_wake(r->mfd, new_state & BD70528_WAKE_STATE_BIT,
+ ret = bd70528_set_wake(r->parent, new_state & BD70528_WAKE_STATE_BIT,
old_state);
if (ret) {
dev_err(r->dev,
@@ -152,12 +162,18 @@ static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state,
static int bd70528_re_enable_rtc_based_timers(struct bd70528_rtc *r,
int old_state)
{
+ if (!r->has_rtc_timers)
+ return 0;
+
return bd70528_set_rtc_based_timers(r, old_state, NULL);
}
static int bd70528_disable_rtc_based_timers(struct bd70528_rtc *r,
int *old_state)
{
+ if (!r->has_rtc_timers)
+ return 0;
+
return bd70528_set_rtc_based_timers(r, 0, old_state);
}
@@ -213,22 +229,52 @@ static inline void rtc2tm(struct bd70528_rtc_data *r, struct rtc_time *t)
t->tm_wday = bcd2bin(r->week & BD70528_MASK_RTC_WEEK);
}
+static int bd71828_set_alarm(struct device *dev, struct rtc_wkalrm *a)
+{
+ int ret;
+ struct bd71828_rtc_alm alm;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ struct rohm_regmap_dev *parent = r->parent;
+
+ ret = regmap_bulk_read(parent->regmap, BD71828_REG_RTC_ALM_START,
+ &alm, sizeof(alm));
+ if (ret) {
+ dev_err(dev, "Failed to read alarm regs\n");
+ return ret;
+ }
+
+ tm2rtc(&a->time, &alm.alm0);
+
+ if (!a->enabled)
+ alm.alm_mask &= ~BD70528_MASK_ALM_EN;
+ else
+ alm.alm_mask |= BD70528_MASK_ALM_EN;
+
+ ret = regmap_bulk_write(parent->regmap, BD71828_REG_RTC_ALM_START,
+ &alm, sizeof(alm));
+ if (ret)
+ dev_err(dev, "Failed to set alarm time\n");
+
+ return ret;
+
+}
+
static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
{
struct bd70528_rtc_wake wake;
struct bd70528_rtc_alm alm;
int ret;
struct bd70528_rtc *r = dev_get_drvdata(dev);
- struct rohm_regmap_dev *bd70528 = r->mfd;
+ struct rohm_regmap_dev *parent = r->parent;
- ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_WAKE_START,
+ ret = regmap_bulk_read(parent->regmap, BD70528_REG_RTC_WAKE_START,
&wake, sizeof(wake));
if (ret) {
dev_err(dev, "Failed to read wake regs\n");
return ret;
}
- ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_ALM_START,
+ ret = regmap_bulk_read(parent->regmap, BD70528_REG_RTC_ALM_START,
&alm, sizeof(alm));
if (ret) {
dev_err(dev, "Failed to read alarm regs\n");
@@ -246,14 +292,14 @@ static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
wake.ctrl &= ~BD70528_MASK_WAKE_EN;
}
- ret = regmap_bulk_write(bd70528->regmap,
+ ret = regmap_bulk_write(parent->regmap,
BD70528_REG_RTC_WAKE_START, &wake,
sizeof(wake));
if (ret) {
dev_err(dev, "Failed to set wake time\n");
return ret;
}
- ret = regmap_bulk_write(bd70528->regmap, BD70528_REG_RTC_ALM_START,
+ ret = regmap_bulk_write(parent->regmap, BD70528_REG_RTC_ALM_START,
&alm, sizeof(alm));
if (ret)
dev_err(dev, "Failed to set alarm time\n");
@@ -261,14 +307,38 @@ static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
return ret;
}
+static int bd71828_read_alarm(struct device *dev, struct rtc_wkalrm *a)
+{
+ int ret;
+ struct bd71828_rtc_alm alm;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ struct rohm_regmap_dev *parent = r->parent;
+
+ ret = regmap_bulk_read(parent->regmap, BD71828_REG_RTC_ALM_START,
+ &alm, sizeof(alm));
+ if (ret) {
+ dev_err(dev, "Failed to read alarm regs\n");
+ return ret;
+ }
+
+ rtc2tm(&alm.alm0, &a->time);
+ a->time.tm_mday = -1;
+ a->time.tm_mon = -1;
+ a->time.tm_year = -1;
+ a->enabled = !!(alm.alm_mask & BD70528_MASK_ALM_EN);
+ a->pending = 0;
+
+ return 0;
+}
+
static int bd70528_read_alarm(struct device *dev, struct rtc_wkalrm *a)
{
struct bd70528_rtc_alm alm;
int ret;
struct bd70528_rtc *r = dev_get_drvdata(dev);
- struct rohm_regmap_dev *bd70528 = r->mfd;
+ struct rohm_regmap_dev *parent = r->parent;
- ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_ALM_START,
+ ret = regmap_bulk_read(parent->regmap, BD70528_REG_RTC_ALM_START,
&alm, sizeof(alm));
if (ret) {
dev_err(dev, "Failed to read alarm regs\n");
@@ -290,14 +360,14 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
int ret, tmpret, old_states;
struct bd70528_rtc_data rtc_data;
struct bd70528_rtc *r = dev_get_drvdata(dev);
- struct rohm_regmap_dev *bd70528 = r->mfd;
+ struct rohm_regmap_dev *parent = r->parent;
ret = bd70528_disable_rtc_based_timers(r, &old_states);
if (ret)
return ret;
- tmpret = regmap_bulk_read(bd70528->regmap,
- BD70528_REG_RTC_START, &rtc_data,
+ tmpret = regmap_bulk_read(parent->regmap,
+ r->reg_time_start, &rtc_data,
sizeof(rtc_data));
if (tmpret) {
dev_err(dev, "Failed to read RTC time registers\n");
@@ -305,8 +375,8 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
}
tm2rtc(t, &rtc_data);
- tmpret = regmap_bulk_write(bd70528->regmap,
- BD70528_REG_RTC_START, &rtc_data,
+ tmpret = regmap_bulk_write(parent->regmap,
+ r->reg_time_start, &rtc_data,
sizeof(rtc_data));
if (tmpret) {
dev_err(dev, "Failed to set RTC time\n");
@@ -321,27 +391,32 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
return ret;
}
+static int bd71828_set_time(struct device *dev, struct rtc_time *t)
+{
+ return bd70528_set_time_locked(dev, t);
+}
+
static int bd70528_set_time(struct device *dev, struct rtc_time *t)
{
int ret;
struct bd70528_rtc *r = dev_get_drvdata(dev);
- bd70528_wdt_lock(r->mfd);
+ bd70528_wdt_lock(r->parent);
ret = bd70528_set_time_locked(dev, t);
- bd70528_wdt_unlock(r->mfd);
+ bd70528_wdt_unlock(r->parent);
return ret;
}
static int bd70528_get_time(struct device *dev, struct rtc_time *t)
{
struct bd70528_rtc *r = dev_get_drvdata(dev);
- struct rohm_regmap_dev *bd70528 = r->mfd;
+ struct rohm_regmap_dev *parent = r->parent;
struct bd70528_rtc_data rtc_data;
int ret;
/* read the RTC date and time registers all at once */
- ret = regmap_bulk_read(bd70528->regmap,
- BD70528_REG_RTC_START, &rtc_data,
+ ret = regmap_bulk_read(parent->regmap,
+ r->reg_time_start, &rtc_data,
sizeof(rtc_data));
if (ret) {
dev_err(dev, "Failed to read RTC time (err %d)\n", ret);
@@ -362,19 +437,36 @@ static int bd70528_alm_enable(struct device *dev, unsigned int enabled)
if (enabled)
enableval = 0;
- bd70528_wdt_lock(r->mfd);
- ret = bd70528_set_wake(r->mfd, enabled, NULL);
+ bd70528_wdt_lock(r->parent);
+ ret = bd70528_set_wake(r->parent, enabled, NULL);
if (ret) {
dev_err(dev, "Failed to change wake state\n");
goto out_unlock;
}
- ret = regmap_update_bits(r->mfd->regmap, BD70528_REG_RTC_ALM_MASK,
+ ret = regmap_update_bits(r->parent->regmap, BD70528_REG_RTC_ALM_MASK,
BD70528_MASK_ALM_EN, enableval);
if (ret)
dev_err(dev, "Failed to change alarm state\n");
out_unlock:
- bd70528_wdt_unlock(r->mfd);
+ bd70528_wdt_unlock(r->parent);
+ return ret;
+}
+
+static int bd71828_alm_enable(struct device *dev, unsigned int enabled)
+{
+ int ret;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ unsigned int enableval = BD70528_MASK_ALM_EN;
+
+ if (!enabled)
+ enableval = 0;
+
+ ret = regmap_update_bits(r->parent->regmap, BD71828_REG_RTC_ALM0_MASK,
+ BD70528_MASK_ALM_EN, enableval);
+ if (ret)
+ dev_err(dev, "Failed to change alarm state\n");
+
return ret;
}
@@ -386,6 +478,14 @@ static const struct rtc_class_ops bd70528_rtc_ops = {
.alarm_irq_enable = bd70528_alm_enable,
};
+static const struct rtc_class_ops bd71828_rtc_ops = {
+ .read_time = bd70528_get_time,
+ .set_time = bd71828_set_time,
+ .read_alarm = bd71828_read_alarm,
+ .set_alarm = bd71828_set_alarm,
+ .alarm_irq_enable = bd71828_alm_enable,
+};
+
static irqreturn_t alm_hndlr(int irq, void *data)
{
struct rtc_device *rtc = data;
@@ -397,14 +497,19 @@ static irqreturn_t alm_hndlr(int irq, void *data)
static int bd70528_probe(struct platform_device *pdev)
{
struct bd70528_rtc *bd_rtc;
- struct rohm_regmap_dev *mfd;
+ const struct rtc_class_ops *rtc_ops;
+ struct rohm_regmap_dev *parent;
+ const char *irq_name;
int ret;
struct rtc_device *rtc;
int irq;
unsigned int hr;
+ bool enable_main_irq = false;
+ u8 hour_reg;
+ enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data;
- mfd = dev_get_drvdata(pdev->dev.parent);
- if (!mfd) {
+ parent = dev_get_drvdata(pdev->dev.parent);
+ if (!parent) {
dev_err(&pdev->dev, "No MFD driver data\n");
return -EINVAL;
}
@@ -412,16 +517,39 @@ static int bd70528_probe(struct platform_device *pdev)
if (!bd_rtc)
return -ENOMEM;
- bd_rtc->mfd = mfd;
+ bd_rtc->parent = parent;
bd_rtc->dev = &pdev->dev;
- irq = platform_get_irq_byname(pdev, "bd70528-rtc-alm");
- if (irq < 0)
+ switch (chip) {
+ case ROHM_CHIP_TYPE_BD70528:
+ irq_name = "bd70528-rtc-alm";
+ bd_rtc->has_rtc_timers = true;
+ bd_rtc->reg_time_start = BD70528_REG_RTC_START;
+ hour_reg = BD70528_REG_RTC_HOUR;
+ enable_main_irq = true;
+ rtc_ops = &bd70528_rtc_ops;
+ break;
+ case ROHM_CHIP_TYPE_BD71828:
+ irq_name = "bd71828-rtc-alm-0";
+ bd_rtc->reg_time_start = BD71828_REG_RTC_START;
+ hour_reg = BD71828_REG_RTC_HOUR;
+ rtc_ops = &bd71828_rtc_ops;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unknown chip\n");
+ return -ENOENT;
+ }
+
+ irq = platform_get_irq_byname(pdev, irq_name);
+
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get irq\n");
return irq;
+ }
platform_set_drvdata(pdev, bd_rtc);
- ret = regmap_read(mfd->regmap, BD70528_REG_RTC_HOUR, &hr);
+ ret = regmap_read(parent->regmap, hour_reg, &hr);
if (ret) {
dev_err(&pdev->dev, "Failed to reag RTC clock\n");
@@ -431,10 +559,10 @@ static int bd70528_probe(struct platform_device *pdev)
if (!(hr & BD70528_MASK_RTC_HOUR_24H)) {
struct rtc_time t;
- ret = bd70528_get_time(&pdev->dev, &t);
+ ret = rtc_ops->read_time(&pdev->dev, &t);
if (!ret)
- ret = bd70528_set_time(&pdev->dev, &t);
+ ret = rtc_ops->set_time(&pdev->dev, &t);
if (ret) {
dev_err(&pdev->dev,
@@ -454,7 +582,7 @@ static int bd70528_probe(struct platform_device *pdev)
rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
rtc->range_max = RTC_TIMESTAMP_END_2099;
- rtc->ops = &bd70528_rtc_ops;
+ rtc->ops = rtc_ops;
/* Request alarm IRQ prior to registerig the RTC */
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, &alm_hndlr,
@@ -468,27 +596,37 @@ static int bd70528_probe(struct platform_device *pdev)
* leave them enabled as irq-controller should disable irqs
* from sub-registers when IRQ is disabled or freed.
*/
- ret = regmap_update_bits(mfd->regmap,
+ if (enable_main_irq) {
+ ret = regmap_update_bits(parent->regmap,
BD70528_REG_INT_MAIN_MASK,
BD70528_INT_RTC_MASK, 0);
- if (ret) {
- dev_err(&pdev->dev, "Failed to enable RTC interrupts\n");
- return ret;
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to enable RTC interrupts\n");
+ return ret;
+ }
}
return rtc_register_device(rtc);
}
+static const struct platform_device_id bd718x7_rtc_id[] = {
+ { "bd70528-rtc", ROHM_CHIP_TYPE_BD70528 },
+ { "bd71828-rtc", ROHM_CHIP_TYPE_BD71828 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id);
+
static struct platform_driver bd70528_rtc = {
.driver = {
.name = "bd70528-rtc"
},
.probe = bd70528_probe,
+ .id_table = bd718x7_rtc_id,
};
module_platform_driver(bd70528_rtc);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
-MODULE_DESCRIPTION("BD70528 RTC driver");
+MODULE_DESCRIPTION("ROHM BD70528 and BD71828 PMIC RTC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bd70528-rtc");
diff --git a/include/linux/mfd/rohm-bd70528.h b/include/linux/mfd/rohm-bd70528.h
index edae6f7afd8e..a57af878fd0c 100644
--- a/include/linux/mfd/rohm-bd70528.h
+++ b/include/linux/mfd/rohm-bd70528.h
@@ -7,6 +7,7 @@
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/mfd/rohm-generic.h>
+#include <linux/mfd/rohm-shared.h>
#include <linux/regmap.h>
enum {
@@ -307,17 +308,6 @@ enum {
/* RTC masks to mask out reserved bits */
-#define BD70528_MASK_RTC_SEC 0x7f
-#define BD70528_MASK_RTC_MINUTE 0x7f
-#define BD70528_MASK_RTC_HOUR_24H 0x80
-#define BD70528_MASK_RTC_HOUR_PM 0x20
-#define BD70528_MASK_RTC_HOUR 0x3f
-#define BD70528_MASK_RTC_DAY 0x3f
-#define BD70528_MASK_RTC_WEEK 0x07
-#define BD70528_MASK_RTC_MONTH 0x1f
-#define BD70528_MASK_RTC_YEAR 0xff
-#define BD70528_MASK_RTC_COUNT_L 0x7f
-
#define BD70528_MASK_ELAPSED_TIMER_EN 0x1
/* Mask second, min and hour fields
* HW would support ALM irq for over 24h
@@ -326,7 +316,6 @@ enum {
* wake-up we limit ALM to 24H and only
* unmask sec, min and hour
*/
-#define BD70528_MASK_ALM_EN 0x7
#define BD70528_MASK_WAKE_EN 0x1
/* WDT masks */
diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h
index d013e03f742d..017a4c01cb31 100644
--- a/include/linux/mfd/rohm-bd71828.h
+++ b/include/linux/mfd/rohm-bd71828.h
@@ -5,6 +5,7 @@
#define __LINUX_MFD_BD71828_H__
#include <linux/mfd/rohm-generic.h>
+#include <linux/mfd/rohm-shared.h>
/* Regulator IDs */
enum {
@@ -160,6 +161,7 @@ enum {
#define BD71828_REG_RTC_YEAR 0x52
#define BD71828_REG_RTC_ALM0_SEC 0x53
+#define BD71828_REG_RTC_ALM_START BD71828_REG_RTC_ALM0_SEC
#define BD71828_REG_RTC_ALM0_MINUTE 0x54
#define BD71828_REG_RTC_ALM0_HOUR 0x55
#define BD71828_REG_RTC_ALM0_WEEK 0x56
@@ -178,6 +180,7 @@ enum {
#define BD71828_REG_RTC_ALM1_MASK 0x62
#define BD71828_REG_RTC_ALM2 0x63
+#define BD71828_REG_RTC_START BD71828_REG_RTC_SEC
/* Charger/Battey */
#define BD71828_REG_CHG_STATE 0x65
@@ -204,7 +207,6 @@ enum {
#define BD71828_REG_INT_MASK_TEMP 0xdd
#define BD71828_REG_INT_MASK_RTC 0xde
-
#define BD71828_REG_INT_MAIN 0xdf
#define BD71828_REG_INT_BUCK 0xe0
#define BD71828_REG_INT_DCIN1 0xe1
diff --git a/include/linux/mfd/rohm-shared.h b/include/linux/mfd/rohm-shared.h
new file mode 100644
index 000000000000..53dd7f638bfd
--- /dev/null
+++ b/include/linux/mfd/rohm-shared.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2020 ROHM Semiconductors */
+
+
+#ifndef __LINUX_MFD_ROHM_SHARED_H__
+#define __LINUX_MFD_ROHM_SHARED_H__
+
+/* RTC definitions shared between BD70528 and BD71828 */
+
+#define BD70528_MASK_RTC_SEC 0x7f
+#define BD70528_MASK_RTC_MINUTE 0x7f
+#define BD70528_MASK_RTC_HOUR_24H 0x80
+#define BD70528_MASK_RTC_HOUR_PM 0x20
+#define BD70528_MASK_RTC_HOUR 0x3f
+#define BD70528_MASK_RTC_DAY 0x3f
+#define BD70528_MASK_RTC_WEEK 0x07
+#define BD70528_MASK_RTC_MONTH 0x1f
+#define BD70528_MASK_RTC_YEAR 0xff
+#define BD70528_MASK_ALM_EN 0x7
+
+#endif /* __LINUX_MFD_ROHM_SHARED_H__ */
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v12 09/10] mfd: bd70528: Fix hour register mask
From: Matti Vaittinen @ 2020-01-20 9:21 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
When RTC is used in 24H mode (and it is by this driver) the maximum
hour value is 24 in BCD. This occupies bits [5:0] - which means
correct mask for HOUR register is 0x3f not 0x1f. Fix the mask
Fixes: 32a4a4ebf768 ("rtc: bd70528: Initial support for ROHM bd70528 RTC")
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
---
include/linux/mfd/rohm-bd70528.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/mfd/rohm-bd70528.h b/include/linux/mfd/rohm-bd70528.h
index 2ad2320d0a96..edae6f7afd8e 100644
--- a/include/linux/mfd/rohm-bd70528.h
+++ b/include/linux/mfd/rohm-bd70528.h
@@ -311,7 +311,7 @@ enum {
#define BD70528_MASK_RTC_MINUTE 0x7f
#define BD70528_MASK_RTC_HOUR_24H 0x80
#define BD70528_MASK_RTC_HOUR_PM 0x20
-#define BD70528_MASK_RTC_HOUR 0x1f
+#define BD70528_MASK_RTC_HOUR 0x3f
#define BD70528_MASK_RTC_DAY 0x3f
#define BD70528_MASK_RTC_WEEK 0x07
#define BD70528_MASK_RTC_MONTH 0x1f
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v12 08/10] regulator: bd718x7: Split driver to common and bd718x7 specific parts
From: Matti Vaittinen @ 2020-01-20 9:21 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
Few ROHM PMICs allow setting the voltage states for different system states
like RUN, IDLE, SUSPEND and LPSR. States are then changed via SoC specific
mechanisms. bd718x7 driver implemented device-tree parsing functions for
these state specific voltages. The parsing functions can be re-used by
other ROHM chip drivers like bd71828. Split the generic functions from
bd718x7-regulator.c to rohm-regulator.c and export them for other modules
to use.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Acked-by: Mark Brown <broonie@kernel.org>
---
drivers/regulator/Kconfig | 4 +
drivers/regulator/Makefile | 1 +
drivers/regulator/bd718x7-regulator.c | 183 ++++++++------------------
drivers/regulator/rohm-regulator.c | 95 +++++++++++++
include/linux/mfd/rohm-generic.h | 66 ++++++++++
5 files changed, 221 insertions(+), 128 deletions(-)
create mode 100644 drivers/regulator/rohm-regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 74eb5af7295f..a4897ae52f14 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -197,6 +197,7 @@ config REGULATOR_BD70528
config REGULATOR_BD718XX
tristate "ROHM BD71837 Power Regulator"
depends on MFD_ROHM_BD718XX
+ select REGULATOR_ROHM
help
This driver supports voltage regulators on ROHM BD71837 PMIC.
This will enable support for the software controllable buck
@@ -790,6 +791,9 @@ config REGULATOR_RN5T618
Say y here to support the regulators found on Ricoh RN5T567,
RN5T618 or RC5T619 PMIC.
+config REGULATOR_ROHM
+ tristate
+
config REGULATOR_RT5033
tristate "Richtek RT5033 Regulators"
depends on MFD_RT5033
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 2210ba56f9bd..6bcab72c1fc7 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o
obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o
obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o
+obj-$(CONFIG_REGULATOR_ROHM) += rohm-regulator.o
obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o
obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c
index 6beaf867d9cb..55decb58c777 100644
--- a/drivers/regulator/bd718x7-regulator.c
+++ b/drivers/regulator/bd718x7-regulator.c
@@ -318,6 +318,7 @@ struct reg_init {
};
struct bd718xx_regulator_data {
struct regulator_desc desc;
+ const struct rohm_dvs_config dvs;
const struct reg_init init;
const struct reg_init *additional_inits;
int additional_init_amnt;
@@ -349,133 +350,15 @@ static const struct reg_init bd71837_ldo6_inits[] = {
},
};
-#define NUM_DVS_BUCKS 4
-
-struct of_dvs_setting {
- const char *prop;
- unsigned int reg;
-};
-
-static int set_dvs_levels(const struct of_dvs_setting *dvs,
- struct device_node *np,
- const struct regulator_desc *desc,
- struct regmap *regmap)
-{
- int ret, i;
- unsigned int uv;
-
- ret = of_property_read_u32(np, dvs->prop, &uv);
- if (ret) {
- if (ret != -EINVAL)
- return ret;
- return 0;
- }
-
- for (i = 0; i < desc->n_voltages; i++) {
- ret = regulator_desc_list_voltage_linear_range(desc, i);
- if (ret < 0)
- continue;
- if (ret == uv) {
- i <<= ffs(desc->vsel_mask) - 1;
- ret = regmap_update_bits(regmap, dvs->reg,
- DVS_BUCK_RUN_MASK, i);
- break;
- }
- }
- return ret;
-}
-
-static int buck4_set_hw_dvs_levels(struct device_node *np,
+static int buck_set_hw_dvs_levels(struct device_node *np,
const struct regulator_desc *desc,
struct regulator_config *cfg)
{
- int ret, i;
- const struct of_dvs_setting dvs[] = {
- {
- .prop = "rohm,dvs-run-voltage",
- .reg = BD71837_REG_BUCK4_VOLT_RUN,
- },
- };
+ struct bd718xx_regulator_data *data;
- for (i = 0; i < ARRAY_SIZE(dvs); i++) {
- ret = set_dvs_levels(&dvs[i], np, desc, cfg->regmap);
- if (ret)
- break;
- }
- return ret;
-}
-static int buck3_set_hw_dvs_levels(struct device_node *np,
- const struct regulator_desc *desc,
- struct regulator_config *cfg)
-{
- int ret, i;
- const struct of_dvs_setting dvs[] = {
- {
- .prop = "rohm,dvs-run-voltage",
- .reg = BD71837_REG_BUCK3_VOLT_RUN,
- },
- };
+ data = container_of(desc, struct bd718xx_regulator_data, desc);
- for (i = 0; i < ARRAY_SIZE(dvs); i++) {
- ret = set_dvs_levels(&dvs[i], np, desc, cfg->regmap);
- if (ret)
- break;
- }
- return ret;
-}
-
-static int buck2_set_hw_dvs_levels(struct device_node *np,
- const struct regulator_desc *desc,
- struct regulator_config *cfg)
-{
- int ret, i;
- const struct of_dvs_setting dvs[] = {
- {
- .prop = "rohm,dvs-run-voltage",
- .reg = BD718XX_REG_BUCK2_VOLT_RUN,
- },
- {
- .prop = "rohm,dvs-idle-voltage",
- .reg = BD718XX_REG_BUCK2_VOLT_IDLE,
- },
- };
-
-
-
- for (i = 0; i < ARRAY_SIZE(dvs); i++) {
- ret = set_dvs_levels(&dvs[i], np, desc, cfg->regmap);
- if (ret)
- break;
- }
- return ret;
-}
-
-static int buck1_set_hw_dvs_levels(struct device_node *np,
- const struct regulator_desc *desc,
- struct regulator_config *cfg)
-{
- int ret, i;
- const struct of_dvs_setting dvs[] = {
- {
- .prop = "rohm,dvs-run-voltage",
- .reg = BD718XX_REG_BUCK1_VOLT_RUN,
- },
- {
- .prop = "rohm,dvs-idle-voltage",
- .reg = BD718XX_REG_BUCK1_VOLT_IDLE,
- },
- {
- .prop = "rohm,dvs-suspend-voltage",
- .reg = BD718XX_REG_BUCK1_VOLT_SUSP,
- },
- };
-
- for (i = 0; i < ARRAY_SIZE(dvs); i++) {
- ret = set_dvs_levels(&dvs[i], np, desc, cfg->regmap);
- if (ret)
- break;
- }
- return ret;
+ return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
}
static const struct bd718xx_regulator_data bd71847_regulators[] = {
@@ -496,7 +379,17 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = {
.enable_reg = BD718XX_REG_BUCK1_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.owner = THIS_MODULE,
- .of_parse_cb = buck1_set_hw_dvs_levels,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD718XX_REG_BUCK1_VOLT_RUN,
+ .run_mask = DVS_BUCK_RUN_MASK,
+ .idle_reg = BD718XX_REG_BUCK1_VOLT_IDLE,
+ .idle_mask = DVS_BUCK_RUN_MASK,
+ .suspend_reg = BD718XX_REG_BUCK1_VOLT_SUSP,
+ .suspend_mask = DVS_BUCK_RUN_MASK,
},
.init = {
.reg = BD718XX_REG_BUCK1_CTRL,
@@ -520,7 +413,14 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = {
.enable_reg = BD718XX_REG_BUCK2_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.owner = THIS_MODULE,
- .of_parse_cb = buck2_set_hw_dvs_levels,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE,
+ .run_reg = BD718XX_REG_BUCK2_VOLT_RUN,
+ .run_mask = DVS_BUCK_RUN_MASK,
+ .idle_reg = BD718XX_REG_BUCK2_VOLT_IDLE,
+ .idle_mask = DVS_BUCK_RUN_MASK,
},
.init = {
.reg = BD718XX_REG_BUCK2_CTRL,
@@ -792,7 +692,17 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
.enable_reg = BD718XX_REG_BUCK1_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.owner = THIS_MODULE,
- .of_parse_cb = buck1_set_hw_dvs_levels,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD718XX_REG_BUCK1_VOLT_RUN,
+ .run_mask = DVS_BUCK_RUN_MASK,
+ .idle_reg = BD718XX_REG_BUCK1_VOLT_IDLE,
+ .idle_mask = DVS_BUCK_RUN_MASK,
+ .suspend_reg = BD718XX_REG_BUCK1_VOLT_SUSP,
+ .suspend_mask = DVS_BUCK_RUN_MASK,
},
.init = {
.reg = BD718XX_REG_BUCK1_CTRL,
@@ -816,7 +726,14 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
.enable_reg = BD718XX_REG_BUCK2_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.owner = THIS_MODULE,
- .of_parse_cb = buck2_set_hw_dvs_levels,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE,
+ .run_reg = BD718XX_REG_BUCK2_VOLT_RUN,
+ .run_mask = DVS_BUCK_RUN_MASK,
+ .idle_reg = BD718XX_REG_BUCK2_VOLT_IDLE,
+ .idle_mask = DVS_BUCK_RUN_MASK,
},
.init = {
.reg = BD718XX_REG_BUCK2_CTRL,
@@ -840,7 +757,12 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
.enable_reg = BD71837_REG_BUCK3_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.owner = THIS_MODULE,
- .of_parse_cb = buck3_set_hw_dvs_levels,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN,
+ .run_reg = BD71837_REG_BUCK3_VOLT_RUN,
+ .run_mask = DVS_BUCK_RUN_MASK,
},
.init = {
.reg = BD71837_REG_BUCK3_CTRL,
@@ -864,7 +786,12 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
.enable_reg = BD71837_REG_BUCK4_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.owner = THIS_MODULE,
- .of_parse_cb = buck4_set_hw_dvs_levels,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN,
+ .run_reg = BD71837_REG_BUCK4_VOLT_RUN,
+ .run_mask = DVS_BUCK_RUN_MASK,
},
.init = {
.reg = BD71837_REG_BUCK4_CTRL,
diff --git a/drivers/regulator/rohm-regulator.c b/drivers/regulator/rohm-regulator.c
new file mode 100644
index 000000000000..399002383b28
--- /dev/null
+++ b/drivers/regulator/rohm-regulator.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2020 ROHM Semiconductors
+
+#include <linux/errno.h>
+#include <linux/mfd/rohm-generic.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+
+static int set_dvs_level(const struct regulator_desc *desc,
+ struct device_node *np, struct regmap *regmap,
+ char *prop, unsigned int reg, unsigned int mask,
+ unsigned int omask, unsigned int oreg)
+{
+ int ret, i;
+ uint32_t uv;
+
+ ret = of_property_read_u32(np, prop, &uv);
+ if (ret) {
+ if (ret != -EINVAL)
+ return ret;
+ return 0;
+ }
+
+ if (uv == 0) {
+ if (omask)
+ return regmap_update_bits(regmap, oreg, omask, 0);
+ }
+ for (i = 0; i < desc->n_voltages; i++) {
+ ret = regulator_desc_list_voltage_linear_range(desc, i);
+ if (ret < 0)
+ continue;
+ if (ret == uv) {
+ i <<= ffs(desc->vsel_mask) - 1;
+ ret = regmap_update_bits(regmap, reg, mask, i);
+ if (omask && !ret)
+ ret = regmap_update_bits(regmap, oreg, omask,
+ omask);
+ break;
+ }
+ }
+ return ret;
+}
+
+int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
+ struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regmap *regmap)
+{
+ int i, ret = 0;
+ char *prop;
+ unsigned int reg, mask, omask, oreg = desc->enable_reg;
+
+ for (i = 0; i < ROHM_DVS_LEVEL_MAX && !ret; i++) {
+ if (dvs->level_map & (1 << i)) {
+ switch (i + 1) {
+ case ROHM_DVS_LEVEL_RUN:
+ prop = "rohm,dvs-run-voltage";
+ reg = dvs->run_reg;
+ mask = dvs->run_mask;
+ omask = dvs->run_on_mask;
+ break;
+ case ROHM_DVS_LEVEL_IDLE:
+ prop = "rohm,dvs-idle-voltage";
+ reg = dvs->idle_reg;
+ mask = dvs->idle_mask;
+ omask = dvs->idle_on_mask;
+ break;
+ case ROHM_DVS_LEVEL_SUSPEND:
+ prop = "rohm,dvs-suspend-voltage";
+ reg = dvs->suspend_reg;
+ mask = dvs->suspend_mask;
+ omask = dvs->suspend_on_mask;
+ break;
+ case ROHM_DVS_LEVEL_LPSR:
+ prop = "rohm,dvs-lpsr-voltage";
+ reg = dvs->lpsr_reg;
+ mask = dvs->lpsr_mask;
+ omask = dvs->lpsr_on_mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = set_dvs_level(desc, np, regmap, prop, reg, mask,
+ omask, oreg);
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL(rohm_regulator_set_dvs_levels);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers");
diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h
index ff3dd7578fd3..4283b5b33e04 100644
--- a/include/linux/mfd/rohm-generic.h
+++ b/include/linux/mfd/rohm-generic.h
@@ -4,6 +4,9 @@
#ifndef __LINUX_MFD_ROHM_H__
#define __LINUX_MFD_ROHM_H__
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+
enum rohm_chip_type {
ROHM_CHIP_TYPE_BD71837 = 0,
ROHM_CHIP_TYPE_BD71847,
@@ -17,4 +20,67 @@ struct rohm_regmap_dev {
struct regmap *regmap;
};
+enum {
+ ROHM_DVS_LEVEL_UNKNOWN,
+ ROHM_DVS_LEVEL_RUN,
+ ROHM_DVS_LEVEL_IDLE,
+ ROHM_DVS_LEVEL_SUSPEND,
+ ROHM_DVS_LEVEL_LPSR,
+ ROHM_DVS_LEVEL_MAX = ROHM_DVS_LEVEL_LPSR,
+};
+
+/**
+ * struct rohm_dvs_config - dynamic voltage scaling register descriptions
+ *
+ * @level_map: bitmap representing supported run-levels for this
+ * regulator
+ * @run_reg: register address for regulator config at 'run' state
+ * @run_mask: value mask for regulator voltages at 'run' state
+ * @run_on_mask: enable mask for regulator at 'run' state
+ * @idle_reg: register address for regulator config at 'idle' state
+ * @idle_mask: value mask for regulator voltages at 'idle' state
+ * @idle_on_mask: enable mask for regulator at 'idle' state
+ * @suspend_reg: register address for regulator config at 'suspend' state
+ * @suspend_mask: value mask for regulator voltages at 'suspend' state
+ * @suspend_on_mask: enable mask for regulator at 'suspend' state
+ * @lpsr_reg: register address for regulator config at 'lpsr' state
+ * @lpsr_mask: value mask for regulator voltages at 'lpsr' state
+ * @lpsr_on_mask: enable mask for regulator at 'lpsr' state
+ *
+ * Description of ROHM PMICs voltage configuration registers for different
+ * system states. This is used to correctly configure the PMIC at startup
+ * based on values read from DT.
+ */
+struct rohm_dvs_config {
+ uint64_t level_map;
+ unsigned int run_reg;
+ unsigned int run_mask;
+ unsigned int run_on_mask;
+ unsigned int idle_reg;
+ unsigned int idle_mask;
+ unsigned int idle_on_mask;
+ unsigned int suspend_reg;
+ unsigned int suspend_mask;
+ unsigned int suspend_on_mask;
+ unsigned int lpsr_reg;
+ unsigned int lpsr_mask;
+ unsigned int lpsr_on_mask;
+};
+
+#if IS_ENABLED(CONFIG_REGULATOR_ROHM)
+int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
+ struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regmap *regmap);
+
+#else
+static inline int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
+ struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regmap *regmap)
+{
+ return 0;
+}
+#endif
+
#endif
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v12 07/10] clk: bd718x7: Support ROHM BD71828 clk block
From: Matti Vaittinen @ 2020-01-20 9:19 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
BD71828GW is a single-chip power management IC for battery-powered portable
devices. Add support for controlling BD71828 clk using bd718x7 driver.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Stephen Boyd <sboyd@kernel.org>
---
drivers/clk/Kconfig | 6 ++---
drivers/clk/clk-bd718x7.c | 38 +++++++++++++++++++++++---------
include/linux/mfd/rohm-bd70528.h | 6 -----
include/linux/mfd/rohm-bd71828.h | 4 ----
include/linux/mfd/rohm-bd718x7.h | 6 -----
5 files changed, 31 insertions(+), 29 deletions(-)
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 45653a0e6ecd..ac5981ce2477 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -305,10 +305,10 @@ config COMMON_CLK_MMP2
Support for Marvell MMP2 and MMP3 SoC clocks
config COMMON_CLK_BD718XX
- tristate "Clock driver for ROHM BD718x7 PMIC"
- depends on MFD_ROHM_BD718XX || MFD_ROHM_BD70528
+ tristate "Clock driver for 32K clk gates on ROHM PMICs"
+ depends on MFD_ROHM_BD718XX || MFD_ROHM_BD70528 || MFD_ROHM_BD71828
help
- This driver supports ROHM BD71837, ROHM BD71847 and
+ This driver supports ROHM BD71837, ROHM BD71847, ROHM BD71828 and
ROHM BD70528 PMICs clock gates.
config COMMON_CLK_FIXED_MMIO
diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c
index 33699ee1bdf3..b52e8d6f660c 100644
--- a/drivers/clk/clk-bd718x7.c
+++ b/drivers/clk/clk-bd718x7.c
@@ -7,12 +7,25 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/mfd/rohm-bd718x7.h>
-#include <linux/mfd/rohm-bd70528.h>
+#include <linux/mfd/rohm-generic.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/regmap.h>
+/* clk control registers */
+/* BD70528 */
+#define BD70528_REG_OUT32K 0x2c
+/* BD71828 */
+#define BD71828_REG_OUT32K 0x4B
+/* BD71837 and BD71847 */
+#define BD718XX_REG_OUT32K 0x2E
+
+/*
+ * BD71837, BD71847, BD70528 and BD71828 all use bit [0] to clk output control
+ */
+#define CLK_OUT_EN_MASK BIT(0)
+
+
struct bd718xx_clk {
struct clk_hw hw;
u8 reg;
@@ -21,10 +34,8 @@ struct bd718xx_clk {
struct rohm_regmap_dev *mfd;
};
-static int bd71837_clk_set(struct clk_hw *hw, int status)
+static int bd71837_clk_set(struct bd718xx_clk *c, unsigned int status)
{
- struct bd718xx_clk *c = container_of(hw, struct bd718xx_clk, hw);
-
return regmap_update_bits(c->mfd->regmap, c->reg, c->mask, status);
}
@@ -33,14 +44,16 @@ static void bd71837_clk_disable(struct clk_hw *hw)
int rv;
struct bd718xx_clk *c = container_of(hw, struct bd718xx_clk, hw);
- rv = bd71837_clk_set(hw, 0);
+ rv = bd71837_clk_set(c, 0);
if (rv)
dev_dbg(&c->pdev->dev, "Failed to disable 32K clk (%d)\n", rv);
}
static int bd71837_clk_enable(struct clk_hw *hw)
{
- return bd71837_clk_set(hw, 1);
+ struct bd718xx_clk *c = container_of(hw, struct bd718xx_clk, hw);
+
+ return bd71837_clk_set(c, 0xffffffff);
}
static int bd71837_clk_is_enabled(struct clk_hw *hw)
@@ -92,11 +105,15 @@ static int bd71837_clk_probe(struct platform_device *pdev)
case ROHM_CHIP_TYPE_BD71837:
case ROHM_CHIP_TYPE_BD71847:
c->reg = BD718XX_REG_OUT32K;
- c->mask = BD718XX_OUT32K_EN;
+ c->mask = CLK_OUT_EN_MASK;
+ break;
+ case ROHM_CHIP_TYPE_BD71828:
+ c->reg = BD71828_REG_OUT32K;
+ c->mask = CLK_OUT_EN_MASK;
break;
case ROHM_CHIP_TYPE_BD70528:
- c->reg = BD70528_REG_CLK_OUT;
- c->mask = BD70528_CLK_OUT_EN_MASK;
+ c->reg = BD70528_REG_OUT32K;
+ c->mask = CLK_OUT_EN_MASK;
break;
default:
dev_err(&pdev->dev, "Unknown clk chip\n");
@@ -126,6 +143,7 @@ static const struct platform_device_id bd718x7_clk_id[] = {
{ "bd71837-clk", ROHM_CHIP_TYPE_BD71837 },
{ "bd71847-clk", ROHM_CHIP_TYPE_BD71847 },
{ "bd70528-clk", ROHM_CHIP_TYPE_BD70528 },
+ { "bd71828-clk", ROHM_CHIP_TYPE_BD71828 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd718x7_clk_id);
diff --git a/include/linux/mfd/rohm-bd70528.h b/include/linux/mfd/rohm-bd70528.h
index 1013e60c5b25..2ad2320d0a96 100644
--- a/include/linux/mfd/rohm-bd70528.h
+++ b/include/linux/mfd/rohm-bd70528.h
@@ -89,10 +89,6 @@ struct bd70528_data {
#define BD70528_REG_GPIO3_OUT 0x52
#define BD70528_REG_GPIO4_OUT 0x54
-/* clk control */
-
-#define BD70528_REG_CLK_OUT 0x2c
-
/* RTC */
#define BD70528_REG_RTC_COUNT_H 0x2d
@@ -309,8 +305,6 @@ enum {
#define BD70528_GPIO_IN_STATE_BASE 1
-#define BD70528_CLK_OUT_EN_MASK 0x1
-
/* RTC masks to mask out reserved bits */
#define BD70528_MASK_RTC_SEC 0x7f
diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h
index eb0557eb5314..d013e03f742d 100644
--- a/include/linux/mfd/rohm-bd71828.h
+++ b/include/linux/mfd/rohm-bd71828.h
@@ -183,9 +183,6 @@ enum {
#define BD71828_REG_CHG_STATE 0x65
#define BD71828_REG_CHG_FULL 0xd2
-/* CLK */
-#define BD71828_REG_OUT32K 0x4B
-
/* LEDs */
#define BD71828_REG_LED_CTRL 0x4A
#define BD71828_MASK_LED_AMBER 0x80
@@ -417,7 +414,6 @@ enum {
#define BD71828_INT_RTC1_MASK 0x2
#define BD71828_INT_RTC2_MASK 0x4
-#define BD71828_OUT32K_EN 0x1
#define BD71828_OUT_TYPE_MASK 0x2
#define BD71828_OUT_TYPE_OPEN_DRAIN 0x0
#define BD71828_OUT_TYPE_CMOS 0x2
diff --git a/include/linux/mfd/rohm-bd718x7.h b/include/linux/mfd/rohm-bd718x7.h
index 7f2dbde402a1..bee2474a8f9f 100644
--- a/include/linux/mfd/rohm-bd718x7.h
+++ b/include/linux/mfd/rohm-bd718x7.h
@@ -191,12 +191,6 @@ enum {
#define IRQ_ON_REQ 0x02
#define IRQ_STBY_REQ 0x01
-/* BD718XX_REG_OUT32K bits */
-#define BD718XX_OUT32K_EN 0x01
-
-/* BD7183XX gated clock rate */
-#define BD718XX_CLK_RATE 32768
-
/* ROHM BD718XX irqs */
enum {
BD718XX_INT_STBY_REQ,
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v12 06/10] mfd: bd71828: Add power-key support
From: Matti Vaittinen @ 2020-01-20 9:19 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
Use gpio_keys to send power input-event to user-space when power
button (short) press is detected.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
---
Changes since v11:
Removed input: tag from subject
drivers/mfd/rohm-bd71828.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
index 0ec386c9f26b..210261d026f2 100644
--- a/drivers/mfd/rohm-bd71828.c
+++ b/drivers/mfd/rohm-bd71828.c
@@ -4,7 +4,9 @@
//
// ROHM BD71828 PMIC driver
+#include <linux/gpio_keys.h>
#include <linux/i2c.h>
+#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/irq.h>
@@ -15,6 +17,18 @@
#include <linux/regmap.h>
#include <linux/types.h>
+static struct gpio_keys_button button = {
+ .code = KEY_POWER,
+ .gpio = -1,
+ .type = EV_KEY,
+};
+
+static struct gpio_keys_platform_data bd71828_powerkey_data = {
+ .buttons = &button,
+ .nbuttons = 1,
+ .name = "bd71828-pwrkey",
+};
+
static const struct resource rtc_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC0, "bd71828-rtc-alm-0"),
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC1, "bd71828-rtc-alm-1"),
@@ -35,6 +49,10 @@ static struct mfd_cell bd71828_mfd_cells[] = {
.name = "bd71828-rtc",
.resources = rtc_irqs,
.num_resources = ARRAY_SIZE(rtc_irqs),
+ }, {
+ .name = "gpio-keys",
+ .platform_data = &bd71828_powerkey_data,
+ .pdata_size = sizeof(bd71828_powerkey_data),
},
};
@@ -288,6 +306,14 @@ static int bd71828_i2c_probe(struct i2c_client *i2c)
dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n",
bd71828_irq_chip.num_irqs);
+ ret = regmap_irq_get_virq(irq_data, BD71828_INT_SHORTPUSH);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to get the power-key IRQ\n");
+ return ret;
+ }
+
+ button.irq = ret;
+
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
bd71828_mfd_cells,
ARRAY_SIZE(bd71828_mfd_cells), NULL, 0,
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v12 05/10] mfd: bd71828: Support ROHM BD71828 PMIC - core
From: Matti Vaittinen @ 2020-01-20 9:18 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
BD71828GW is a single-chip power management IC for battery-powered portable
devices. The IC integrates 7 buck converters, 7 LDOs, and a 1500 mA
single-cell linear charger. Also included is a Coulomb counter, a real-time
clock (RTC), 3 GPO/regulator control pins, HALL input and a 32.768 kHz
clock gate.
Add MFD core driver providing interrupt controller facilities and i2c
access to sub device drivers.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
---
drivers/mfd/Kconfig | 15 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/rohm-bd71828.c | 318 +++++++++++++++++++++++
include/linux/mfd/rohm-bd71828.h | 425 +++++++++++++++++++++++++++++++
include/linux/mfd/rohm-generic.h | 1 +
5 files changed, 760 insertions(+)
create mode 100644 drivers/mfd/rohm-bd71828.c
create mode 100644 include/linux/mfd/rohm-bd71828.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 420900852166..c3c9432ef51c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1906,6 +1906,21 @@ config MFD_ROHM_BD70528
10 bits SAR ADC for battery temperature monitor and 1S battery
charger.
+config MFD_ROHM_BD71828
+ tristate "ROHM BD71828 Power Management IC"
+ depends on I2C=y
+ depends on OF
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ select MFD_CORE
+ help
+ Select this option to get support for the ROHM BD71828 Power
+ Management IC. BD71828GW is a single-chip power management IC for
+ battery-powered portable devices. The IC integrates 7 buck
+ converters, 7 LDOs, and a 1500 mA single-cell linear charger.
+ Also included is a Coulomb counter, a real-time clock (RTC), and
+ a 32.768 kHz clock gate.
+
config MFD_STM32_LPTIMER
tristate "Support for STM32 Low-Power Timer"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index aed99f08739f..61b3093af39d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -252,6 +252,7 @@ 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_BD70528) += rohm-bd70528.o
+obj-$(CONFIG_MFD_ROHM_BD71828) += rohm-bd71828.o
obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
obj-$(CONFIG_MFD_STMFX) += stmfx.o
diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
new file mode 100644
index 000000000000..0ec386c9f26b
--- /dev/null
+++ b/drivers/mfd/rohm-bd71828.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (C) 2019 ROHM Semiconductors
+//
+// ROHM BD71828 PMIC driver
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rohm-bd71828.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+static const struct resource rtc_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC0, "bd71828-rtc-alm-0"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC1, "bd71828-rtc-alm-1"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC2, "bd71828-rtc-alm-2"),
+};
+
+static struct mfd_cell bd71828_mfd_cells[] = {
+ { .name = "bd71828-pmic", },
+ { .name = "bd71828-gpio", },
+ { .name = "bd71828-led", .of_compatible = "rohm,bd71828-leds" },
+ /*
+ * We use BD71837 driver to drive the clock block. Only differences to
+ * BD70528 clock gate are the register address and mask.
+ */
+ { .name = "bd71828-clk", },
+ { .name = "bd71827-power", },
+ {
+ .name = "bd71828-rtc",
+ .resources = rtc_irqs,
+ .num_resources = ARRAY_SIZE(rtc_irqs),
+ },
+};
+
+static const struct regmap_range volatile_ranges[] = {
+ {
+ .range_min = BD71828_REG_PS_CTRL_1,
+ .range_max = BD71828_REG_PS_CTRL_1,
+ }, {
+ .range_min = BD71828_REG_PS_CTRL_3,
+ .range_max = BD71828_REG_PS_CTRL_3,
+ }, {
+ .range_min = BD71828_REG_RTC_SEC,
+ .range_max = BD71828_REG_RTC_YEAR,
+ }, {
+ /*
+ * For now make all charger registers volatile because many
+ * needs to be and because the charger block is not that
+ * performance critical.
+ */
+ .range_min = BD71828_REG_CHG_STATE,
+ .range_max = BD71828_REG_CHG_FULL,
+ }, {
+ .range_min = BD71828_REG_INT_MAIN,
+ .range_max = BD71828_REG_IO_STAT,
+ },
+};
+
+static const struct regmap_access_table volatile_regs = {
+ .yes_ranges = &volatile_ranges[0],
+ .n_yes_ranges = ARRAY_SIZE(volatile_ranges),
+};
+
+static struct regmap_config bd71828_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &volatile_regs,
+ .max_register = BD71828_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/*
+ * Mapping of main IRQ register bits to sub-IRQ register offsets so that we can
+ * access corect sub-IRQ registers based on bits that are set in main IRQ
+ * register.
+ */
+
+static unsigned int bit0_offsets[] = {11}; /* RTC IRQ */
+static unsigned int bit1_offsets[] = {10}; /* TEMP IRQ */
+static unsigned int bit2_offsets[] = {6, 7, 8, 9}; /* BAT MON IRQ */
+static unsigned int bit3_offsets[] = {5}; /* BAT IRQ */
+static unsigned int bit4_offsets[] = {4}; /* CHG IRQ */
+static unsigned int bit5_offsets[] = {3}; /* VSYS IRQ */
+static unsigned int bit6_offsets[] = {1, 2}; /* DCIN IRQ */
+static unsigned int bit7_offsets[] = {0}; /* BUCK IRQ */
+
+static struct regmap_irq_sub_irq_map bd71828_sub_irq_offsets[] = {
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit3_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit4_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit5_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit6_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
+};
+
+static struct regmap_irq bd71828_irqs[] = {
+ REGMAP_IRQ_REG(BD71828_INT_BUCK1_OCP, 0, BD71828_INT_BUCK1_OCP_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BUCK2_OCP, 0, BD71828_INT_BUCK2_OCP_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BUCK3_OCP, 0, BD71828_INT_BUCK3_OCP_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BUCK4_OCP, 0, BD71828_INT_BUCK4_OCP_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BUCK5_OCP, 0, BD71828_INT_BUCK5_OCP_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BUCK6_OCP, 0, BD71828_INT_BUCK6_OCP_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BUCK7_OCP, 0, BD71828_INT_BUCK7_OCP_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_PGFAULT, 0, BD71828_INT_PGFAULT_MASK),
+ /* DCIN1 interrupts */
+ REGMAP_IRQ_REG(BD71828_INT_DCIN_DET, 1, BD71828_INT_DCIN_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_DCIN_RMV, 1, BD71828_INT_DCIN_RMV_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CLPS_OUT, 1, BD71828_INT_CLPS_OUT_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CLPS_IN, 1, BD71828_INT_CLPS_IN_MASK),
+ /* DCIN2 interrupts */
+ REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_RES, 2,
+ BD71828_INT_DCIN_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_DET, 2,
+ BD71828_INT_DCIN_MON_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_LONGPUSH, 2, BD71828_INT_LONGPUSH_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_MIDPUSH, 2, BD71828_INT_MIDPUSH_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_SHORTPUSH, 2, BD71828_INT_SHORTPUSH_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_PUSH, 2, BD71828_INT_PUSH_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_WDOG, 2, BD71828_INT_WDOG_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_SWRESET, 2, BD71828_INT_SWRESET_MASK),
+ /* Vsys */
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_RES, 3,
+ BD71828_INT_VSYS_UV_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_DET, 3,
+ BD71828_INT_VSYS_UV_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_RES, 3,
+ BD71828_INT_VSYS_LOW_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_DET, 3,
+ BD71828_INT_VSYS_LOW_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_IN, 3,
+ BD71828_INT_VSYS_HALL_IN_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_TOGGLE, 3,
+ BD71828_INT_VSYS_HALL_TOGGLE_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_RES, 3,
+ BD71828_INT_VSYS_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_DET, 3,
+ BD71828_INT_VSYS_MON_DET_MASK),
+ /* Charger */
+ REGMAP_IRQ_REG(BD71828_INT_CHG_DCIN_ILIM, 4,
+ BD71828_INT_CHG_DCIN_ILIM_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_TOPOFF_TO_DONE, 4,
+ BD71828_INT_CHG_TOPOFF_TO_DONE_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TEMP, 4,
+ BD71828_INT_CHG_WDG_TEMP_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TIME, 4,
+ BD71828_INT_CHG_WDG_TIME_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_RES, 4,
+ BD71828_INT_CHG_RECHARGE_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_DET, 4,
+ BD71828_INT_CHG_RECHARGE_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_RANGED_TEMP_TRANSITION, 4,
+ BD71828_INT_CHG_RANGED_TEMP_TRANSITION_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_CHG_STATE_TRANSITION, 4,
+ BD71828_INT_CHG_STATE_TRANSITION_MASK),
+ /* Battery */
+ REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_NORMAL, 5,
+ BD71828_INT_BAT_TEMP_NORMAL_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_ERANGE, 5,
+ BD71828_INT_BAT_TEMP_ERANGE_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_WARN, 5,
+ BD71828_INT_BAT_TEMP_WARN_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_REMOVED, 5,
+ BD71828_INT_BAT_REMOVED_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_DETECTED, 5,
+ BD71828_INT_BAT_DETECTED_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_THERM_REMOVED, 5,
+ BD71828_INT_THERM_REMOVED_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_THERM_DETECTED, 5,
+ BD71828_INT_THERM_DETECTED_MASK),
+ /* Battery Mon 1 */
+ REGMAP_IRQ_REG(BD71828_INT_BAT_DEAD, 6, BD71828_INT_BAT_DEAD_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_RES, 6,
+ BD71828_INT_BAT_SHORTC_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_DET, 6,
+ BD71828_INT_BAT_SHORTC_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_RES, 6,
+ BD71828_INT_BAT_LOW_VOLT_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_DET, 6,
+ BD71828_INT_BAT_LOW_VOLT_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_RES, 6,
+ BD71828_INT_BAT_OVER_VOLT_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_DET, 6,
+ BD71828_INT_BAT_OVER_VOLT_DET_MASK),
+ /* Battery Mon 2 */
+ REGMAP_IRQ_REG(BD71828_INT_BAT_MON_RES, 7,
+ BD71828_INT_BAT_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_MON_DET, 7,
+ BD71828_INT_BAT_MON_DET_MASK),
+ /* Battery Mon 3 (Coulomb counter) */
+ REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON1, 8,
+ BD71828_INT_BAT_CC_MON1_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON2, 8,
+ BD71828_INT_BAT_CC_MON2_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON3, 8,
+ BD71828_INT_BAT_CC_MON3_MASK),
+ /* Battery Mon 4 */
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_RES, 9,
+ BD71828_INT_BAT_OVER_CURR_1_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_DET, 9,
+ BD71828_INT_BAT_OVER_CURR_1_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_RES, 9,
+ BD71828_INT_BAT_OVER_CURR_2_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_DET, 9,
+ BD71828_INT_BAT_OVER_CURR_2_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_RES, 9,
+ BD71828_INT_BAT_OVER_CURR_3_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_DET, 9,
+ BD71828_INT_BAT_OVER_CURR_3_DET_MASK),
+ /* Temperature */
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_RES, 10,
+ BD71828_INT_TEMP_BAT_LOW_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_DET, 10,
+ BD71828_INT_TEMP_BAT_LOW_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_RES, 10,
+ BD71828_INT_TEMP_BAT_HI_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_DET, 10,
+ BD71828_INT_TEMP_BAT_HI_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_125_RES, 10,
+ BD71828_INT_TEMP_CHIP_OVER_125_RES_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_125_DET, 10,
+ BD71828_INT_TEMP_CHIP_OVER_125_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_VF_DET, 10,
+ BD71828_INT_TEMP_CHIP_OVER_VF_DET_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_VF_RES, 10,
+ BD71828_INT_TEMP_CHIP_OVER_VF_RES_MASK),
+ /* RTC Alarm */
+ REGMAP_IRQ_REG(BD71828_INT_RTC0, 11, BD71828_INT_RTC0_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_RTC1, 11, BD71828_INT_RTC1_MASK),
+ REGMAP_IRQ_REG(BD71828_INT_RTC2, 11, BD71828_INT_RTC2_MASK),
+};
+
+static struct regmap_irq_chip bd71828_irq_chip = {
+ .name = "bd71828_irq",
+ .main_status = BD71828_REG_INT_MAIN,
+ .irqs = &bd71828_irqs[0],
+ .num_irqs = ARRAY_SIZE(bd71828_irqs),
+ .status_base = BD71828_REG_INT_BUCK,
+ .mask_base = BD71828_REG_INT_MASK_BUCK,
+ .ack_base = BD71828_REG_INT_BUCK,
+ .mask_invert = true,
+ .init_ack_masked = true,
+ .num_regs = 12,
+ .num_main_regs = 1,
+ .sub_reg_offsets = &bd71828_sub_irq_offsets[0],
+ .num_main_status_bits = 8,
+ .irq_reg_stride = 1,
+};
+
+static int bd71828_i2c_probe(struct i2c_client *i2c)
+{
+ struct rohm_regmap_dev *chip;
+ struct regmap_irq_chip_data *irq_data;
+ int ret;
+
+ if (!i2c->irq) {
+ dev_err(&i2c->dev, "No IRQ configured\n");
+ return -EINVAL;
+ }
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ dev_set_drvdata(&i2c->dev, chip);
+
+ chip->regmap = devm_regmap_init_i2c(i2c, &bd71828_regmap);
+ if (IS_ERR(chip->regmap)) {
+ dev_err(&i2c->dev, "Failed to initialize Regmap\n");
+ return PTR_ERR(chip->regmap);
+ }
+
+ ret = devm_regmap_add_irq_chip(&i2c->dev, chip->regmap,
+ i2c->irq, IRQF_ONESHOT, 0,
+ &bd71828_irq_chip, &irq_data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to add IRQ chip\n");
+ return ret;
+ }
+
+ dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n",
+ bd71828_irq_chip.num_irqs);
+
+ ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
+ bd71828_mfd_cells,
+ ARRAY_SIZE(bd71828_mfd_cells), NULL, 0,
+ regmap_irq_get_domain(irq_data));
+ if (ret)
+ dev_err(&i2c->dev, "Failed to create subdevices\n");
+
+ return ret;
+}
+
+static const struct of_device_id bd71828_of_match[] = {
+ { .compatible = "rohm,bd71828", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bd71828_of_match);
+
+static struct i2c_driver bd71828_drv = {
+ .driver = {
+ .name = "rohm-bd71828",
+ .of_match_table = bd71828_of_match,
+ },
+ .probe_new = &bd71828_i2c_probe,
+};
+module_i2c_driver(bd71828_drv);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("ROHM BD71828 Power Management IC driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h
new file mode 100644
index 000000000000..eb0557eb5314
--- /dev/null
+++ b/include/linux/mfd/rohm-bd71828.h
@@ -0,0 +1,425 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2019 ROHM Semiconductors */
+
+#ifndef __LINUX_MFD_BD71828_H__
+#define __LINUX_MFD_BD71828_H__
+
+#include <linux/mfd/rohm-generic.h>
+
+/* Regulator IDs */
+enum {
+ BD71828_BUCK1,
+ BD71828_BUCK2,
+ BD71828_BUCK3,
+ BD71828_BUCK4,
+ BD71828_BUCK5,
+ BD71828_BUCK6,
+ BD71828_BUCK7,
+ BD71828_LDO1,
+ BD71828_LDO2,
+ BD71828_LDO3,
+ BD71828_LDO4,
+ BD71828_LDO5,
+ BD71828_LDO6,
+ BD71828_LDO_SNVS,
+ BD71828_REGULATOR_AMOUNT,
+};
+
+#define BD71828_BUCK1267_VOLTS 0xEF
+#define BD71828_BUCK3_VOLTS 0x10
+#define BD71828_BUCK4_VOLTS 0x20
+#define BD71828_BUCK5_VOLTS 0x10
+#define BD71828_LDO_VOLTS 0x32
+/* LDO6 is fixed 1.8V voltage */
+#define BD71828_LDO_6_VOLTAGE 1800000
+
+/* Registers and masks*/
+
+/* MODE control */
+#define BD71828_REG_PS_CTRL_1 0x04
+#define BD71828_REG_PS_CTRL_2 0x05
+#define BD71828_REG_PS_CTRL_3 0x06
+
+//#define BD71828_REG_SWRESET 0x06
+#define BD71828_MASK_RUN_LVL_CTRL 0x30
+
+/* Regulator control masks */
+
+#define BD71828_MASK_RAMP_DELAY 0x6
+
+#define BD71828_MASK_RUN_EN 0x08
+#define BD71828_MASK_SUSP_EN 0x04
+#define BD71828_MASK_IDLE_EN 0x02
+#define BD71828_MASK_LPSR_EN 0x01
+
+#define BD71828_MASK_RUN0_EN 0x01
+#define BD71828_MASK_RUN1_EN 0x02
+#define BD71828_MASK_RUN2_EN 0x04
+#define BD71828_MASK_RUN3_EN 0x08
+
+#define BD71828_MASK_DVS_BUCK1_CTRL 0x10
+#define BD71828_DVS_BUCK1_CTRL_I2C 0
+#define BD71828_DVS_BUCK1_USE_RUNLVL 0x10
+
+#define BD71828_MASK_DVS_BUCK2_CTRL 0x20
+#define BD71828_DVS_BUCK2_CTRL_I2C 0
+#define BD71828_DVS_BUCK2_USE_RUNLVL 0x20
+
+#define BD71828_MASK_DVS_BUCK6_CTRL 0x40
+#define BD71828_DVS_BUCK6_CTRL_I2C 0
+#define BD71828_DVS_BUCK6_USE_RUNLVL 0x40
+
+#define BD71828_MASK_DVS_BUCK7_CTRL 0x80
+#define BD71828_DVS_BUCK7_CTRL_I2C 0
+#define BD71828_DVS_BUCK7_USE_RUNLVL 0x80
+
+#define BD71828_MASK_BUCK1267_VOLT 0xff
+#define BD71828_MASK_BUCK3_VOLT 0x1f
+#define BD71828_MASK_BUCK4_VOLT 0x3f
+#define BD71828_MASK_BUCK5_VOLT 0x1f
+#define BD71828_MASK_LDO_VOLT 0x3f
+
+/* Regulator control regs */
+#define BD71828_REG_BUCK1_EN 0x08
+#define BD71828_REG_BUCK1_CTRL 0x09
+#define BD71828_REG_BUCK1_MODE 0x0a
+#define BD71828_REG_BUCK1_IDLE_VOLT 0x0b
+#define BD71828_REG_BUCK1_SUSP_VOLT 0x0c
+#define BD71828_REG_BUCK1_VOLT 0x0d
+
+#define BD71828_REG_BUCK2_EN 0x12
+#define BD71828_REG_BUCK2_CTRL 0x13
+#define BD71828_REG_BUCK2_MODE 0x14
+#define BD71828_REG_BUCK2_IDLE_VOLT 0x15
+#define BD71828_REG_BUCK2_SUSP_VOLT 0x16
+#define BD71828_REG_BUCK2_VOLT 0x17
+
+#define BD71828_REG_BUCK3_EN 0x1c
+#define BD71828_REG_BUCK3_MODE 0x1d
+#define BD71828_REG_BUCK3_VOLT 0x1e
+
+#define BD71828_REG_BUCK4_EN 0x1f
+#define BD71828_REG_BUCK4_MODE 0x20
+#define BD71828_REG_BUCK4_VOLT 0x21
+
+#define BD71828_REG_BUCK5_EN 0x22
+#define BD71828_REG_BUCK5_MODE 0x23
+#define BD71828_REG_BUCK5_VOLT 0x24
+
+#define BD71828_REG_BUCK6_EN 0x25
+#define BD71828_REG_BUCK6_CTRL 0x26
+#define BD71828_REG_BUCK6_MODE 0x27
+#define BD71828_REG_BUCK6_IDLE_VOLT 0x28
+#define BD71828_REG_BUCK6_SUSP_VOLT 0x29
+#define BD71828_REG_BUCK6_VOLT 0x2a
+
+#define BD71828_REG_BUCK7_EN 0x2f
+#define BD71828_REG_BUCK7_CTRL 0x30
+#define BD71828_REG_BUCK7_MODE 0x31
+#define BD71828_REG_BUCK7_IDLE_VOLT 0x32
+#define BD71828_REG_BUCK7_SUSP_VOLT 0x33
+#define BD71828_REG_BUCK7_VOLT 0x34
+
+#define BD71828_REG_LDO1_EN 0x39
+#define BD71828_REG_LDO1_VOLT 0x3a
+#define BD71828_REG_LDO2_EN 0x3b
+#define BD71828_REG_LDO2_VOLT 0x3c
+#define BD71828_REG_LDO3_EN 0x3d
+#define BD71828_REG_LDO3_VOLT 0x3e
+#define BD71828_REG_LDO4_EN 0x3f
+#define BD71828_REG_LDO4_VOLT 0x40
+#define BD71828_REG_LDO5_EN 0x41
+#define BD71828_REG_LDO5_VOLT 0x43
+#define BD71828_REG_LDO5_VOLT_OPT 0x42
+#define BD71828_REG_LDO6_EN 0x44
+//#define BD71828_REG_LDO6_VOLT 0x4
+#define BD71828_REG_LDO7_EN 0x45
+#define BD71828_REG_LDO7_VOLT 0x46
+
+/* GPIO */
+
+#define BD71828_GPIO_DRIVE_MASK 0x2
+#define BD71828_GPIO_OPEN_DRAIN 0x0
+#define BD71828_GPIO_PUSH_PULL 0x2
+#define BD71828_GPIO_OUT_HI 0x1
+#define BD71828_GPIO_OUT_LO 0x0
+#define BD71828_GPIO_OUT_MASK 0x1
+
+#define BD71828_REG_GPIO_CTRL1 0x47
+#define BD71828_REG_GPIO_CTRL2 0x48
+#define BD71828_REG_GPIO_CTRL3 0x49
+#define BD71828_REG_IO_STAT 0xed
+
+/* RTC */
+#define BD71828_REG_RTC_SEC 0x4c
+#define BD71828_REG_RTC_MINUTE 0x4d
+#define BD71828_REG_RTC_HOUR 0x4e
+#define BD71828_REG_RTC_WEEK 0x4f
+#define BD71828_REG_RTC_DAY 0x50
+#define BD71828_REG_RTC_MONTH 0x51
+#define BD71828_REG_RTC_YEAR 0x52
+
+#define BD71828_REG_RTC_ALM0_SEC 0x53
+#define BD71828_REG_RTC_ALM0_MINUTE 0x54
+#define BD71828_REG_RTC_ALM0_HOUR 0x55
+#define BD71828_REG_RTC_ALM0_WEEK 0x56
+#define BD71828_REG_RTC_ALM0_DAY 0x57
+#define BD71828_REG_RTC_ALM0_MONTH 0x58
+#define BD71828_REG_RTC_ALM0_YEAR 0x59
+#define BD71828_REG_RTC_ALM0_MASK 0x61
+
+#define BD71828_REG_RTC_ALM1_SEC 0x5a
+#define BD71828_REG_RTC_ALM1_MINUTE 0x5b
+#define BD71828_REG_RTC_ALM1_HOUR 0x5c
+#define BD71828_REG_RTC_ALM1_WEEK 0x5d
+#define BD71828_REG_RTC_ALM1_DAY 0x5e
+#define BD71828_REG_RTC_ALM1_MONTH 0x5f
+#define BD71828_REG_RTC_ALM1_YEAR 0x60
+#define BD71828_REG_RTC_ALM1_MASK 0x62
+
+#define BD71828_REG_RTC_ALM2 0x63
+
+/* Charger/Battey */
+#define BD71828_REG_CHG_STATE 0x65
+#define BD71828_REG_CHG_FULL 0xd2
+
+/* CLK */
+#define BD71828_REG_OUT32K 0x4B
+
+/* LEDs */
+#define BD71828_REG_LED_CTRL 0x4A
+#define BD71828_MASK_LED_AMBER 0x80
+#define BD71828_MASK_LED_GREEN 0x40
+#define BD71828_LED_ON 0xff
+#define BD71828_LED_OFF 0x0
+
+/* IRQ registers */
+#define BD71828_REG_INT_MASK_BUCK 0xd3
+#define BD71828_REG_INT_MASK_DCIN1 0xd4
+#define BD71828_REG_INT_MASK_DCIN2 0xd5
+#define BD71828_REG_INT_MASK_VSYS 0xd6
+#define BD71828_REG_INT_MASK_CHG 0xd7
+#define BD71828_REG_INT_MASK_BAT 0xd8
+#define BD71828_REG_INT_MASK_BAT_MON1 0xd9
+#define BD71828_REG_INT_MASK_BAT_MON2 0xda
+#define BD71828_REG_INT_MASK_BAT_MON3 0xdb
+#define BD71828_REG_INT_MASK_BAT_MON4 0xdc
+#define BD71828_REG_INT_MASK_TEMP 0xdd
+#define BD71828_REG_INT_MASK_RTC 0xde
+
+
+#define BD71828_REG_INT_MAIN 0xdf
+#define BD71828_REG_INT_BUCK 0xe0
+#define BD71828_REG_INT_DCIN1 0xe1
+#define BD71828_REG_INT_DCIN2 0xe2
+#define BD71828_REG_INT_VSYS 0xe3
+#define BD71828_REG_INT_CHG 0xe4
+#define BD71828_REG_INT_BAT 0xe5
+#define BD71828_REG_INT_BAT_MON1 0xe6
+#define BD71828_REG_INT_BAT_MON2 0xe7
+#define BD71828_REG_INT_BAT_MON3 0xe8
+#define BD71828_REG_INT_BAT_MON4 0xe9
+#define BD71828_REG_INT_TEMP 0xea
+#define BD71828_REG_INT_RTC 0xeb
+#define BD71828_REG_INT_UPDATE 0xec
+
+#define BD71828_MAX_REGISTER BD71828_REG_IO_STAT
+
+/* Masks for main IRQ register bits */
+enum {
+ BD71828_INT_BUCK,
+#define BD71828_INT_BUCK_MASK BIT(BD71828_INT_BUCK)
+ BD71828_INT_DCIN,
+#define BD71828_INT_DCIN_MASK BIT(BD71828_INT_DCIN)
+ BD71828_INT_VSYS,
+#define BD71828_INT_VSYS_MASK BIT(BD71828_INT_VSYS)
+ BD71828_INT_CHG,
+#define BD71828_INT_CHG_MASK BIT(BD71828_INT_CHG)
+ BD71828_INT_BAT,
+#define BD71828_INT_BAT_MASK BIT(BD71828_INT_BAT)
+ BD71828_INT_BAT_MON,
+#define BD71828_INT_BAT_MON_MASK BIT(BD71828_INT_BAT_MON)
+ BD71828_INT_TEMP,
+#define BD71828_INT_TEMP_MASK BIT(BD71828_INT_TEMP)
+ BD71828_INT_RTC,
+#define BD71828_INT_RTC_MASK BIT(BD71828_INT_RTC)
+};
+
+/* Interrupts */
+enum {
+ /* BUCK reg interrupts */
+ BD71828_INT_BUCK1_OCP,
+ BD71828_INT_BUCK2_OCP,
+ BD71828_INT_BUCK3_OCP,
+ BD71828_INT_BUCK4_OCP,
+ BD71828_INT_BUCK5_OCP,
+ BD71828_INT_BUCK6_OCP,
+ BD71828_INT_BUCK7_OCP,
+ BD71828_INT_PGFAULT,
+ /* DCIN1 interrupts */
+ BD71828_INT_DCIN_DET,
+ BD71828_INT_DCIN_RMV,
+ BD71828_INT_CLPS_OUT,
+ BD71828_INT_CLPS_IN,
+ /* DCIN2 interrupts */
+ BD71828_INT_DCIN_MON_RES,
+ BD71828_INT_DCIN_MON_DET,
+ BD71828_INT_LONGPUSH,
+ BD71828_INT_MIDPUSH,
+ BD71828_INT_SHORTPUSH,
+ BD71828_INT_PUSH,
+ BD71828_INT_WDOG,
+ BD71828_INT_SWRESET,
+ /* Vsys */
+ BD71828_INT_VSYS_UV_RES,
+ BD71828_INT_VSYS_UV_DET,
+ BD71828_INT_VSYS_LOW_RES,
+ BD71828_INT_VSYS_LOW_DET,
+ BD71828_INT_VSYS_HALL_IN,
+ BD71828_INT_VSYS_HALL_TOGGLE,
+ BD71828_INT_VSYS_MON_RES,
+ BD71828_INT_VSYS_MON_DET,
+ /* Charger */
+ BD71828_INT_CHG_DCIN_ILIM,
+ BD71828_INT_CHG_TOPOFF_TO_DONE,
+ BD71828_INT_CHG_WDG_TEMP,
+ BD71828_INT_CHG_WDG_TIME,
+ BD71828_INT_CHG_RECHARGE_RES,
+ BD71828_INT_CHG_RECHARGE_DET,
+ BD71828_INT_CHG_RANGED_TEMP_TRANSITION,
+ BD71828_INT_CHG_STATE_TRANSITION,
+ /* Battery */
+ BD71828_INT_BAT_TEMP_NORMAL,
+ BD71828_INT_BAT_TEMP_ERANGE,
+ BD71828_INT_BAT_TEMP_WARN,
+ BD71828_INT_BAT_REMOVED,
+ BD71828_INT_BAT_DETECTED,
+ BD71828_INT_THERM_REMOVED,
+ BD71828_INT_THERM_DETECTED,
+ /* Battery Mon 1 */
+ BD71828_INT_BAT_DEAD,
+ BD71828_INT_BAT_SHORTC_RES,
+ BD71828_INT_BAT_SHORTC_DET,
+ BD71828_INT_BAT_LOW_VOLT_RES,
+ BD71828_INT_BAT_LOW_VOLT_DET,
+ BD71828_INT_BAT_OVER_VOLT_RES,
+ BD71828_INT_BAT_OVER_VOLT_DET,
+ /* Battery Mon 2 */
+ BD71828_INT_BAT_MON_RES,
+ BD71828_INT_BAT_MON_DET,
+ /* Battery Mon 3 (Coulomb counter) */
+ BD71828_INT_BAT_CC_MON1,
+ BD71828_INT_BAT_CC_MON2,
+ BD71828_INT_BAT_CC_MON3,
+ /* Battery Mon 4 */
+ BD71828_INT_BAT_OVER_CURR_1_RES,
+ BD71828_INT_BAT_OVER_CURR_1_DET,
+ BD71828_INT_BAT_OVER_CURR_2_RES,
+ BD71828_INT_BAT_OVER_CURR_2_DET,
+ BD71828_INT_BAT_OVER_CURR_3_RES,
+ BD71828_INT_BAT_OVER_CURR_3_DET,
+ /* Temperature */
+ BD71828_INT_TEMP_BAT_LOW_RES,
+ BD71828_INT_TEMP_BAT_LOW_DET,
+ BD71828_INT_TEMP_BAT_HI_RES,
+ BD71828_INT_TEMP_BAT_HI_DET,
+ BD71828_INT_TEMP_CHIP_OVER_125_RES,
+ BD71828_INT_TEMP_CHIP_OVER_125_DET,
+ BD71828_INT_TEMP_CHIP_OVER_VF_DET,
+ BD71828_INT_TEMP_CHIP_OVER_VF_RES,
+ /* RTC Alarm */
+ BD71828_INT_RTC0,
+ BD71828_INT_RTC1,
+ BD71828_INT_RTC2,
+};
+
+#define BD71828_INT_BUCK1_OCP_MASK 0x1
+#define BD71828_INT_BUCK2_OCP_MASK 0x2
+#define BD71828_INT_BUCK3_OCP_MASK 0x4
+#define BD71828_INT_BUCK4_OCP_MASK 0x8
+#define BD71828_INT_BUCK5_OCP_MASK 0x10
+#define BD71828_INT_BUCK6_OCP_MASK 0x20
+#define BD71828_INT_BUCK7_OCP_MASK 0x40
+#define BD71828_INT_PGFAULT_MASK 0x80
+
+#define BD71828_INT_DCIN_DET_MASK 0x1
+#define BD71828_INT_DCIN_RMV_MASK 0x2
+#define BD71828_INT_CLPS_OUT_MASK 0x4
+#define BD71828_INT_CLPS_IN_MASK 0x8
+ /* DCIN2 interrupts */
+#define BD71828_INT_DCIN_MON_RES_MASK 0x1
+#define BD71828_INT_DCIN_MON_DET_MASK 0x2
+#define BD71828_INT_LONGPUSH_MASK 0x4
+#define BD71828_INT_MIDPUSH_MASK 0x8
+#define BD71828_INT_SHORTPUSH_MASK 0x10
+#define BD71828_INT_PUSH_MASK 0x20
+#define BD71828_INT_WDOG_MASK 0x40
+#define BD71828_INT_SWRESET_MASK 0x80
+ /* Vsys */
+#define BD71828_INT_VSYS_UV_RES_MASK 0x1
+#define BD71828_INT_VSYS_UV_DET_MASK 0x2
+#define BD71828_INT_VSYS_LOW_RES_MASK 0x4
+#define BD71828_INT_VSYS_LOW_DET_MASK 0x8
+#define BD71828_INT_VSYS_HALL_IN_MASK 0x10
+#define BD71828_INT_VSYS_HALL_TOGGLE_MASK 0x20
+#define BD71828_INT_VSYS_MON_RES_MASK 0x40
+#define BD71828_INT_VSYS_MON_DET_MASK 0x80
+ /* Charger */
+#define BD71828_INT_CHG_DCIN_ILIM_MASK 0x1
+#define BD71828_INT_CHG_TOPOFF_TO_DONE_MASK 0x2
+#define BD71828_INT_CHG_WDG_TEMP_MASK 0x4
+#define BD71828_INT_CHG_WDG_TIME_MASK 0x8
+#define BD71828_INT_CHG_RECHARGE_RES_MASK 0x10
+#define BD71828_INT_CHG_RECHARGE_DET_MASK 0x20
+#define BD71828_INT_CHG_RANGED_TEMP_TRANSITION_MASK 0x40
+#define BD71828_INT_CHG_STATE_TRANSITION_MASK 0x80
+ /* Battery */
+#define BD71828_INT_BAT_TEMP_NORMAL_MASK 0x1
+#define BD71828_INT_BAT_TEMP_ERANGE_MASK 0x2
+#define BD71828_INT_BAT_TEMP_WARN_MASK 0x4
+#define BD71828_INT_BAT_REMOVED_MASK 0x10
+#define BD71828_INT_BAT_DETECTED_MASK 0x20
+#define BD71828_INT_THERM_REMOVED_MASK 0x40
+#define BD71828_INT_THERM_DETECTED_MASK 0x80
+ /* Battery Mon 1 */
+#define BD71828_INT_BAT_DEAD_MASK 0x2
+#define BD71828_INT_BAT_SHORTC_RES_MASK 0x4
+#define BD71828_INT_BAT_SHORTC_DET_MASK 0x8
+#define BD71828_INT_BAT_LOW_VOLT_RES_MASK 0x10
+#define BD71828_INT_BAT_LOW_VOLT_DET_MASK 0x20
+#define BD71828_INT_BAT_OVER_VOLT_RES_MASK 0x40
+#define BD71828_INT_BAT_OVER_VOLT_DET_MASK 0x80
+ /* Battery Mon 2 */
+#define BD71828_INT_BAT_MON_RES_MASK 0x1
+#define BD71828_INT_BAT_MON_DET_MASK 0x2
+ /* Battery Mon 3 (Coulomb counter) */
+#define BD71828_INT_BAT_CC_MON1_MASK 0x1
+#define BD71828_INT_BAT_CC_MON2_MASK 0x2
+#define BD71828_INT_BAT_CC_MON3_MASK 0x4
+ /* Battery Mon 4 */
+#define BD71828_INT_BAT_OVER_CURR_1_RES_MASK 0x1
+#define BD71828_INT_BAT_OVER_CURR_1_DET_MASK 0x2
+#define BD71828_INT_BAT_OVER_CURR_2_RES_MASK 0x4
+#define BD71828_INT_BAT_OVER_CURR_2_DET_MASK 0x8
+#define BD71828_INT_BAT_OVER_CURR_3_RES_MASK 0x10
+#define BD71828_INT_BAT_OVER_CURR_3_DET_MASK 0x20
+ /* Temperature */
+#define BD71828_INT_TEMP_BAT_LOW_RES_MASK 0x1
+#define BD71828_INT_TEMP_BAT_LOW_DET_MASK 0x2
+#define BD71828_INT_TEMP_BAT_HI_RES_MASK 0x4
+#define BD71828_INT_TEMP_BAT_HI_DET_MASK 0x8
+#define BD71828_INT_TEMP_CHIP_OVER_125_RES_MASK 0x10
+#define BD71828_INT_TEMP_CHIP_OVER_125_DET_MASK 0x20
+#define BD71828_INT_TEMP_CHIP_OVER_VF_RES_MASK 0x40
+#define BD71828_INT_TEMP_CHIP_OVER_VF_DET_MASK 0x80
+ /* RTC Alarm */
+#define BD71828_INT_RTC0_MASK 0x1
+#define BD71828_INT_RTC1_MASK 0x2
+#define BD71828_INT_RTC2_MASK 0x4
+
+#define BD71828_OUT32K_EN 0x1
+#define BD71828_OUT_TYPE_MASK 0x2
+#define BD71828_OUT_TYPE_OPEN_DRAIN 0x0
+#define BD71828_OUT_TYPE_CMOS 0x2
+
+#endif /* __LINUX_MFD_BD71828_H__ */
diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h
index 922f88008232..ff3dd7578fd3 100644
--- a/include/linux/mfd/rohm-generic.h
+++ b/include/linux/mfd/rohm-generic.h
@@ -8,6 +8,7 @@ enum rohm_chip_type {
ROHM_CHIP_TYPE_BD71837 = 0,
ROHM_CHIP_TYPE_BD71847,
ROHM_CHIP_TYPE_BD70528,
+ ROHM_CHIP_TYPE_BD71828,
ROHM_CHIP_TYPE_AMOUNT
};
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v12 04/10] mfd: bd718x7: Add compatible for BD71850
From: Matti Vaittinen @ 2020-01-20 9:18 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
ROHM BD71850 PMIC is almost identical to BD71847. Main difference is some
initial voltage values for regulators. The BD71850 can be handled by
BD71847 driver but adding own compatible makes it clearer for one who
creates the DT for board containing this PMIC and allows SW to be
differentiating PMICs if needed.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
---
drivers/mfd/rohm-bd718x7.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
index bb86ec829079..c32c1b6c98fa 100644
--- a/drivers/mfd/rohm-bd718x7.c
+++ b/drivers/mfd/rohm-bd718x7.c
@@ -213,6 +213,10 @@ static const struct of_device_id bd718xx_of_match[] = {
.compatible = "rohm,bd71847",
.data = (void *)ROHM_CHIP_TYPE_BD71847,
},
+ {
+ .compatible = "rohm,bd71850",
+ .data = (void *)ROHM_CHIP_TYPE_BD71847,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, bd718xx_of_match);
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v12 03/10] mfd: rohm PMICs - use platform_device_id to match MFD sub-devices
From: Matti Vaittinen @ 2020-01-20 9:17 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount, Stephen Boyd, Mark Brown
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
Thanks to Stephen Boyd I today learned we can use platform_device_id
to do device and module matching for MFD sub-devices!
Do device matching using the platform_device_id instead of using
explicit module_aliases to load modules and custom parent-data field
to do module loading and sub-device matching.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Stephen Boyd <sboyd@kernel.org>
---
Mark and Stephen - Could you please ack this or tell me what to change?
drivers/clk/clk-bd718x7.c | 12 ++++++++-
drivers/mfd/rohm-bd70528.c | 3 +--
drivers/mfd/rohm-bd718x7.c | 39 ++++++++++++++++++++++-----
drivers/regulator/bd718x7-regulator.c | 17 +++++++++---
include/linux/mfd/rohm-generic.h | 3 +--
5 files changed, 58 insertions(+), 16 deletions(-)
diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c
index 00926c587390..33699ee1bdf3 100644
--- a/drivers/clk/clk-bd718x7.c
+++ b/drivers/clk/clk-bd718x7.c
@@ -74,6 +74,7 @@ static int bd71837_clk_probe(struct platform_device *pdev)
.name = "bd718xx-32k-out",
.ops = &bd71837_clk_ops,
};
+ enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data;
c = devm_kzalloc(&pdev->dev, sizeof(*c), GFP_KERNEL);
if (!c)
@@ -87,7 +88,7 @@ static int bd71837_clk_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "No parent clk found\n");
return -EINVAL;
}
- switch (mfd->chip_type) {
+ switch (chip) {
case ROHM_CHIP_TYPE_BD71837:
case ROHM_CHIP_TYPE_BD71847:
c->reg = BD718XX_REG_OUT32K;
@@ -121,11 +122,20 @@ static int bd71837_clk_probe(struct platform_device *pdev)
return rval;
}
+static const struct platform_device_id bd718x7_clk_id[] = {
+ { "bd71837-clk", ROHM_CHIP_TYPE_BD71837 },
+ { "bd71847-clk", ROHM_CHIP_TYPE_BD71847 },
+ { "bd70528-clk", ROHM_CHIP_TYPE_BD70528 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bd718x7_clk_id);
+
static struct platform_driver bd71837_clk = {
.driver = {
.name = "bd718xx-clk",
},
.probe = bd71837_clk_probe,
+ .id_table = bd718x7_clk_id,
};
module_platform_driver(bd71837_clk);
diff --git a/drivers/mfd/rohm-bd70528.c b/drivers/mfd/rohm-bd70528.c
index ef6786fd3b00..5c44d3b77b3e 100644
--- a/drivers/mfd/rohm-bd70528.c
+++ b/drivers/mfd/rohm-bd70528.c
@@ -48,7 +48,7 @@ static struct mfd_cell bd70528_mfd_cells[] = {
* We use BD71837 driver to drive the clock block. Only differences to
* BD70528 clock gate are the register address and mask.
*/
- { .name = "bd718xx-clk", },
+ { .name = "bd70528-clk", },
{ .name = "bd70528-wdt", },
{
.name = "bd70528-power",
@@ -236,7 +236,6 @@ static int bd70528_i2c_probe(struct i2c_client *i2c,
dev_set_drvdata(&i2c->dev, &bd70528->chip);
- bd70528->chip.chip_type = ROHM_CHIP_TYPE_BD70528;
bd70528->chip.regmap = devm_regmap_init_i2c(i2c, &bd70528_regmap);
if (IS_ERR(bd70528->chip.regmap)) {
dev_err(&i2c->dev, "Failed to initialize Regmap\n");
diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
index 85e7f5133365..bb86ec829079 100644
--- a/drivers/mfd/rohm-bd718x7.c
+++ b/drivers/mfd/rohm-bd718x7.c
@@ -30,14 +30,24 @@ static struct gpio_keys_platform_data bd718xx_powerkey_data = {
.name = "bd718xx-pwrkey",
};
-static struct mfd_cell bd718xx_mfd_cells[] = {
+static struct mfd_cell bd71837_mfd_cells[] = {
{
.name = "gpio-keys",
.platform_data = &bd718xx_powerkey_data,
.pdata_size = sizeof(bd718xx_powerkey_data),
},
- { .name = "bd718xx-clk", },
- { .name = "bd718xx-pmic", },
+ { .name = "bd71837-clk", },
+ { .name = "bd71837-pmic", },
+};
+
+static struct mfd_cell bd71847_mfd_cells[] = {
+ {
+ .name = "gpio-keys",
+ .platform_data = &bd718xx_powerkey_data,
+ .pdata_size = sizeof(bd718xx_powerkey_data),
+ },
+ { .name = "bd71847-clk", },
+ { .name = "bd71847-pmic", },
};
static const struct regmap_irq bd718xx_irqs[] = {
@@ -124,6 +134,9 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
{
struct bd718xx *bd718xx;
int ret;
+ unsigned int chip_type;
+ struct mfd_cell *mfd;
+ int cells;
if (!i2c->irq) {
dev_err(&i2c->dev, "No IRQ configured\n");
@@ -136,8 +149,21 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
bd718xx->chip_irq = i2c->irq;
- bd718xx->chip.chip_type = (unsigned int)(uintptr_t)
- of_device_get_match_data(&i2c->dev);
+ chip_type = (unsigned int)(uintptr_t)
+ of_device_get_match_data(&i2c->dev);
+ switch (chip_type) {
+ case ROHM_CHIP_TYPE_BD71837:
+ mfd = bd71837_mfd_cells;
+ cells = ARRAY_SIZE(bd71837_mfd_cells);
+ break;
+ case ROHM_CHIP_TYPE_BD71847:
+ mfd = bd71847_mfd_cells;
+ cells = ARRAY_SIZE(bd71847_mfd_cells);
+ break;
+ default:
+ dev_err(&i2c->dev, "Unknown device type");
+ return -EINVAL;
+ }
bd718xx->chip.dev = &i2c->dev;
dev_set_drvdata(&i2c->dev, bd718xx);
@@ -170,8 +196,7 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
button.irq = ret;
ret = devm_mfd_add_devices(bd718xx->chip.dev, PLATFORM_DEVID_AUTO,
- bd718xx_mfd_cells,
- ARRAY_SIZE(bd718xx_mfd_cells), NULL, 0,
+ mfd, cells, NULL, 0,
regmap_irq_get_domain(bd718xx->irq_data));
if (ret)
dev_err(&i2c->dev, "Failed to create subdevices\n");
diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c
index 13a43eee2e46..6beaf867d9cb 100644
--- a/drivers/regulator/bd718x7-regulator.c
+++ b/drivers/regulator/bd718x7-regulator.c
@@ -1164,6 +1164,7 @@ static int bd718xx_probe(struct platform_device *pdev)
int i, j, err;
bool use_snvs;
+ enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data;
mfd = dev_get_drvdata(pdev->dev.parent);
if (!mfd) {
@@ -1172,8 +1173,8 @@ static int bd718xx_probe(struct platform_device *pdev)
goto err;
}
- if (mfd->chip.chip_type >= ROHM_CHIP_TYPE_AMOUNT ||
- !pmic_regulators[mfd->chip.chip_type].r_datas) {
+ if (chip >= ROHM_CHIP_TYPE_AMOUNT || chip < 0 ||
+ !pmic_regulators[chip].r_datas) {
dev_err(&pdev->dev, "Unsupported chip type\n");
err = -EINVAL;
goto err;
@@ -1215,13 +1216,13 @@ static int bd718xx_probe(struct platform_device *pdev)
}
}
- for (i = 0; i < pmic_regulators[mfd->chip.chip_type].r_amount; i++) {
+ for (i = 0; i < pmic_regulators[chip].r_amount; i++) {
const struct regulator_desc *desc;
struct regulator_dev *rdev;
const struct bd718xx_regulator_data *r;
- r = &pmic_regulators[mfd->chip.chip_type].r_datas[i];
+ r = &pmic_regulators[chip].r_datas[i];
desc = &r->desc;
config.dev = pdev->dev.parent;
@@ -1281,11 +1282,19 @@ static int bd718xx_probe(struct platform_device *pdev)
return err;
}
+static const struct platform_device_id bd718x7_pmic_id[] = {
+ { "bd71837-pmic", ROHM_CHIP_TYPE_BD71837 },
+ { "bd71847-pmic", ROHM_CHIP_TYPE_BD71847 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bd718x7_pmic_id);
+
static struct platform_driver bd718xx_regulator = {
.driver = {
.name = "bd718xx-pmic",
},
.probe = bd718xx_probe,
+ .id_table = bd718x7_pmic_id,
};
module_platform_driver(bd718xx_regulator);
diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h
index bff15ac26f2c..922f88008232 100644
--- a/include/linux/mfd/rohm-generic.h
+++ b/include/linux/mfd/rohm-generic.h
@@ -4,7 +4,7 @@
#ifndef __LINUX_MFD_ROHM_H__
#define __LINUX_MFD_ROHM_H__
-enum {
+enum rohm_chip_type {
ROHM_CHIP_TYPE_BD71837 = 0,
ROHM_CHIP_TYPE_BD71847,
ROHM_CHIP_TYPE_BD70528,
@@ -12,7 +12,6 @@ enum {
};
struct rohm_regmap_dev {
- unsigned int chip_type;
struct device *dev;
struct regmap *regmap;
};
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v12 02/10] dt-bindings: mfd: Document ROHM BD71828 bindings
From: Matti Vaittinen @ 2020-01-20 9:13 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
ROHM BD71828 Power management IC integrates 7 buck converters, 7 LDOs,
a real-time clock (RTC), 3 GPO/regulator control pins, HALL input
and a 32.768 kHz clock gate.
Document the dt bindings drivers are using.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../bindings/mfd/rohm,bd71828-pmic.yaml | 193 ++++++++++++++++++
1 file changed, 193 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml
diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml
new file mode 100644
index 000000000000..4fbb9e734284
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml
@@ -0,0 +1,193 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/rohm,bd71828-pmic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ROHM BD71828 Power Management Integrated Circuit bindings
+
+maintainers:
+ - Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
+
+description: |
+ BD71828GW is a single-chip power management IC for battery-powered portable
+ devices. The IC integrates 7 buck converters, 7 LDOs, and a 1500 mA
+ single-cell linear charger. Also included is a Coulomb counter, a real-time
+ clock (RTC), and a 32.768 kHz clock gate.
+
+properties:
+ compatible:
+ const: rohm,bd71828
+
+ reg:
+ description:
+ I2C slave address.
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+ description: |
+ The first cell is the pin number and the second cell is used to specify
+ flags. See ../gpio/gpio.txt for more information.
+
+ clocks:
+ maxItems: 1
+
+ "#clock-cells":
+ const: 0
+
+ rohm,charger-sense-resistor-ohms:
+ minimum: 10000000
+ maximum: 50000000
+ description: |
+ BD71827 and BD71828 have SAR ADC for measuring charging currents.
+ External sense resistor (RSENSE in data sheet) should be used. If some
+ other but 30MOhm resistor is used the resistance value should be given
+ here in Ohms.
+
+ regulators:
+ $ref: ../regulator/rohm,bd71828-regulator.yaml
+ description:
+ List of child nodes that specify the regulators.
+
+ leds:
+ $ref: ../leds/rohm,bd71828-leds.yaml
+
+ gpio-reserved-ranges:
+ description: |
+ Usage of BD71828 GPIO pins can be changed via OTP. This property can be
+ used to mark the pins which should not be configured for GPIO. Please see
+ the ../gpio/gpio.txt for more information.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - "#clock-cells"
+ - regulators
+ - gpio-controller
+ - "#gpio-cells"
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/leds/common.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pmic: pmic@4b {
+ compatible = "rohm,bd71828";
+ reg = <0x4b>;
+
+ interrupt-parent = <&gpio1>;
+ interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
+
+ clocks = <&osc 0>;
+ #clock-cells = <0>;
+ clock-output-names = "bd71828-32k-out";
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-reserved-ranges = <0 1>, <2 1>;
+
+ rohm,charger-sense-resistor-ohms = <10000000>;
+
+ regulators {
+ buck1: BUCK1 {
+ regulator-name = "buck1";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-ramp-delay = <2500>;
+ };
+ buck2: BUCK2 {
+ regulator-name = "buck2";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-ramp-delay = <2500>;
+ };
+ buck3: BUCK3 {
+ regulator-name = "buck3";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <2000000>;
+ };
+ buck4: BUCK4 {
+ regulator-name = "buck4";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1800000>;
+ };
+ buck5: BUCK5 {
+ regulator-name = "buck5";
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ buck6: BUCK6 {
+ regulator-name = "buck6";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-ramp-delay = <2500>;
+ };
+ buck7: BUCK7 {
+ regulator-name = "buck7";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-ramp-delay = <2500>;
+ };
+ ldo1: LDO1 {
+ regulator-name = "ldo1";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ ldo2: LDO2 {
+ regulator-name = "ldo2";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ ldo3: LDO3 {
+ regulator-name = "ldo3";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ ldo4: LDO4 {
+ regulator-name = "ldo4";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ ldo5: LDO5 {
+ regulator-name = "ldo5";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ ldo6: LDO6 {
+ regulator-name = "ldo6";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+ ldo7_reg: LDO7 {
+ regulator-name = "ldo7";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+
+ leds {
+ compatible = "rohm,bd71828-leds";
+
+ led-1 {
+ rohm,led-compatible = "bd71828-grnled";
+ function = LED_FUNCTION_INDICATOR;
+ color = <LED_COLOR_ID_GREEN>;
+ };
+ led-2 {
+ rohm,led-compatible = "bd71828-ambled";
+ function = LED_FUNCTION_CHARGING;
+ color = <LED_COLOR_ID_AMBER>;
+ };
+ };
+ };
+ };
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v12 01/10] dt-bindings: leds: ROHM BD71282 PMIC LED driver
From: Matti Vaittinen @ 2020-01-20 9:12 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579511114.git.matti.vaittinen@fi.rohmeurope.com>
Document ROHM BD71828 PMIC LED driver device tree bindings.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
.../bindings/leds/rohm,bd71828-leds.yaml | 52 +++++++++++++++++++
1 file changed, 52 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
diff --git a/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml b/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
new file mode 100644
index 000000000000..b50f4bcc98f1
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/rohm,bd71828-leds.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ROHM BD71828 Power Management Integrated Circuit LED driver
+
+maintainers:
+ - Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
+
+description: |
+ This module is part of the ROHM BD71828 MFD device. For more details
+ see Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml.
+
+ The LED controller is represented as a sub-node of the PMIC node on the device
+ tree.
+
+ The device has two LED outputs referred as GRNLED and AMBLED in data-sheet.
+
+select: false
+
+properties:
+ compatible:
+ const: rohm,bd71828-leds
+
+patternProperties:
+ "^led-[1-2]$":
+ type: object
+ description:
+ Properties for a single LED.
+ properties:
+ #allOf:
+ #- $ref: "common.yaml#"
+ rohm,led-compatible:
+ description: LED identification string
+ allOf:
+ - $ref: "/schemas/types.yaml#/definitions/string"
+ - enum:
+ - bd71828-ambled
+ - bd71828-grnled
+ function:
+ description:
+ Purpose of LED as defined in dt-bindings/leds/common.h
+ $ref: "/schemas/types.yaml#/definitions/string"
+ color:
+ description:
+ LED colour as defined in dt-bindings/leds/common.h
+ $ref: "/schemas/types.yaml#/definitions/uint32"
+
+required:
+ - compatible
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v12 00/10] Support ROHM BD71828 PMIC
From: Matti Vaittinen @ 2020-01-20 9:12 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
Patch series introducing support for ROHM BD71828 PMIC
ROHM BD71828 is a power management IC containing 7 bucks and 7 LDOs. All
regulators can be controlled individually via I2C. Bucks 1,2,6 and
7 can also be assigned to a "regulator group" controlled by run-levels.
Eg. Run level specific voltages and enable/disable statuses for each of
these bucks can be set via register interface. The buck run-level group
assignment (selection if buck is to be controlled individually or via
run-levels) can be changed at run-time via I2C.
This patch series brings only the basic support for controlling
regulators individually via I2C.
In addition to the bucks and LDOs there are:
- The usual clk gate
- 4 IO pins (mostly usable as GPO or tied to specific purpose)
- power button support
- RTC
- two LEDs
- battery charger
- HALL sensor input
This patch series adds support to regulators, clk, RTC and GPIOs.
Power-supply driver for charger or LED driver is not included in this series.
Changelog v12:
- removed input: tag from MFD gpio-keys patch
- dropped LED patches for now.
Changelog v11:
- converted a macro to enum member as was requested by Lee.
Changelog v10:
- Split RTC patch to a BD70528 fix (which hopefully goes to 5.4) and to
BD71828 support
Changelog v9: (Changes suggested by Lee)
- Added kerneldoc to struct rohm_dvs_config
- cleaned few comments
- updated copyright date
- renamed variable 'mfd' to 'parent'.
Changelog v8:
LEDs:
- Fixed bunch of typos.
- Corrected the commit message which errorneously contained the
'leds-compatible' which I dropped in favour of match_property.
- use max_brightness instead of LED_FULL if it is given when
handling the default_state, "on".
- clean fwnode_owned at the end of unsuccessful registration or
at the end of deregistration.
- fix the accidental linuxdoc comment.
- rename led_find_fwnode to led_get_fwnode as it increases refcount.
Changelog v7:
- Rebased on top of v5.5-rc2
- Dropped already applied patches
- basic regulator support (in regulator tree now)
- regulator dt-bindings (in regulator tree now)
- gpio devm function documentation (in GPIO tree now)
- Dropped new devm-variant of gpio_array getting for MFD sub-devices who
have consumer information in parent DT node as gpio consumer was
dropped from the series
- removed extra line-breaks from MFD driver and Makefile
- fixed RTC patch subject line (added missing colon)
- included a patch which adds compatible for ROHM BD71850 PMIC
Changelog v6:
Rebased on top of v5.5-rc1
LED core:
- Do new fw-node look-up only if the new match data is given. That
way behaviour for existing drivers is not changed
- Handle generic LED properties by core only if explisitly requested
in init-data. That way behaviour for existing drivers is not changed
until they are verified to work.
BD71828 LEDs:
- Fix module loading by adding "dummy" of_device_id table.
DT bindings:
All:
- Remove regulator run-level properties as run-level support was
dropped for now.
- Change SPDX to dual lisence
LED:
- added select: false
- replace oneOf + const by enum
Regulator:
- remove forgotten comments
- comment indenting
MFD:
- remove unnecessary descriptions
Regulators:
- Dropped patch 12 with run-level controls
- Dropped unnecessary ramp_delay_supported() - ram_delay ops were
already only filled for DVS bucks.
GPIO:
- rename internal function.
RTC:
- Added missing blank line
Changelog v5:
Only LED patch (patch 15) changed, rest as in v4.
LED:
- Fixed issues reported by Dan Carpenter and kbuild-bot static
analysis.
Changelog v4 (first non RFC):
General:
- Changed subdevice loading and chip version identification to use
platform ID.
- License identifiers changed to GPL-2.0-only
MFD:
- Styling fixes mostly
DT-Bindings:
- a few more checks as suggested by Rob Herring.
- Order of DT patches changed.
- me as maintainer
- standard units to new properties (microvolts, ohms)
- runlevel values in an array
LED:
- BD71828 driver added (back)
- Added DT support
- Added LED DT node lookup in led framework when init_data is given
with DT node match information.
- Added common property parsing for default-state and
default-trigger.
Regulators:
- dropped sysfs interfaces
- fixed module unload/reload by binding gpio consumer information to
regulator device not to MFD.
GPIO:
- Added devm_gpiod_get_parent_array
- added few missing devm functions to documentation
Changelog v3:
DT-Bindings:
- yamlify
- add LED binding doc
CLK:
- Move clk register definitions from MFD headers to clk driver
GPIO:
- Add generic direction define and use it.
LED:
- Drop LED driver from the series (for now).
Changelog v2: Mainly RTC and GPIO fixes suggested by Alexandre and Bartosz
General:
-Patch ordering changed to provide dt binding documents right after the
MFD core.
DT-Bindings for regulators (Patch 3)
-Fix typo in PMIC model number
RTC (patch 11)
-Reverted renaming in order to reduce patch size.
-Reworded commit message
BD71828 regulator (patch 7)
-Add MODULE_ALIAS
GPIO (patch 12)
-Remove file-name from comment
-prefix IN and OUT defines with chip type
-improved documentation for the INPUT only pin.
-removed empty left-over function
-removed unnecessary #ifdef CONFIG_OF_GPIO
-removed unnecessary error print
-Add MODULE_ALIAS
Patch 1:
dt-bindings for LEDs on BD71828 PMIC
Patch 2:
dt-bindings for BD71828 PMIC
Patch 3:
Convert rohm PMICs with common sub-devices to use platform_
device_id to match MFD sub-devices
Patch 4:
Add compatible for BD71850
Patch 5:
BD71828 MFD core.
Patch 6:
Power button support using GPIO keys.
Patch 7:
CLK gate support using existing clk-bd718x7
Patch 8:
Split existing bd718x7 regulator driver to generic ROHM dt
parsing portion (used by more than one ROHM drivers) and
bd718x8 specific parts
Patch 9:
Fix BD70528 RTC HOUR mask
Patch 10:
Support BD71828 RTC block using BD70528 RTC driver
Patch 11:
Allow control of GP(I)O pins on BD71828 via GPIO subsystem
---
Matti Vaittinen (10):
dt-bindings: leds: ROHM BD71282 PMIC LED driver
dt-bindings: mfd: Document ROHM BD71828 bindings
mfd: rohm PMICs - use platform_device_id to match MFD sub-devices
mfd: bd718x7: Add compatible for BD71850
mfd: bd71828: Support ROHM BD71828 PMIC - core
mfd: bd71828: Add power-key support
clk: bd718x7: Support ROHM BD71828 clk block
regulator: bd718x7: Split driver to common and bd718x7 specific parts
mfd: bd70528: Fix hour register mask
rtc: bd70528: add BD71828 support
.../bindings/leds/rohm,bd71828-leds.yaml | 52 +++
.../bindings/mfd/rohm,bd71828-pmic.yaml | 193 ++++++++
drivers/clk/Kconfig | 6 +-
drivers/clk/clk-bd718x7.c | 50 ++-
drivers/mfd/Kconfig | 15 +
drivers/mfd/Makefile | 1 +
drivers/mfd/rohm-bd70528.c | 3 +-
drivers/mfd/rohm-bd71828.c | 344 ++++++++++++++
drivers/mfd/rohm-bd718x7.c | 43 +-
drivers/regulator/Kconfig | 4 +
drivers/regulator/Makefile | 1 +
drivers/regulator/bd718x7-regulator.c | 200 +++------
drivers/regulator/rohm-regulator.c | 95 ++++
drivers/rtc/Kconfig | 3 +-
drivers/rtc/rtc-bd70528.c | 220 +++++++--
include/linux/mfd/rohm-bd70528.h | 19 +-
include/linux/mfd/rohm-bd71828.h | 423 ++++++++++++++++++
include/linux/mfd/rohm-bd718x7.h | 6 -
include/linux/mfd/rohm-generic.h | 70 ++-
include/linux/mfd/rohm-shared.h | 21 +
20 files changed, 1546 insertions(+), 223 deletions(-)
create mode 100644 Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
create mode 100644 Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml
create mode 100644 drivers/mfd/rohm-bd71828.c
create mode 100644 drivers/regulator/rohm-regulator.c
create mode 100644 include/linux/mfd/rohm-bd71828.h
create mode 100644 include/linux/mfd/rohm-shared.h
base-commit: b3a987b0264d ("Linux 5.5-rc6")
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply
* Re: [PATCH v11 06/13] mfd: input: bd71828: Add power-key support
From: Vaittinen, Matti @ 2020-01-20 8:59 UTC (permalink / raw)
To: lee.jones@linaro.org
Cc: corbet@lwn.net, linux-doc@vger.kernel.org,
phil.edworthy@renesas.com, dmurphy@ti.com,
linux-leds@vger.kernel.org, devicetree@vger.kernel.org,
linux-rtc@vger.kernel.org, linux-gpio@vger.kernel.org,
mchehab+samsung@kernel.org, alexandre.belloni@bootlin.com,
linux-kernel@vger.kernel.org, mturquette@baylibre.com,
lgirdwood@gmail.com, jacek.anaszewski@gmail.com,
mazziesaccount@gmail.com, a.zummo@towertech.it,
linus.walleij@linaro.org, arnd@arndb.de, mark.rutland@arm.com,
robh+dt@kernel.org, noralf@tronnes.org,
gregkh@linuxfoundation.org, bgolaszewski@baylibre.com,
linux-clk@vger.kernel.org, pavel@ucw.cz, sboyd@kernel.org,
broonie@kernel.org, wsa+renesas@sang-engineering.com
In-Reply-To: <20200120085510.GB15507@dell>
Hello Lee,
On Mon, 2020-01-20 at 08:55 +0000, Lee Jones wrote:
> On Mon, 20 Jan 2020, Matti Vaittinen wrote:
>
> > Use gpio_keys to send power input-event to user-space when power
> > button (short) press is detected.
> >
> > Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> > Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
> > ---
> > No changes since v10
> >
> > drivers/mfd/rohm-bd71828.c | 26 ++++++++++++++++++++++++++
> > 1 file changed, 26 insertions(+)
>
> Drop the "input:" tag from the subject line please.
>
I'll do. And I'll also drop the LED patches (12 and 13) as I noticed
the patch 12 has some issues (besides LEDs - other than bindings -
still lack ack from Pavel).
I'll send v12 after a while.
Br,
Matti Vaittinen
^ permalink raw reply
* Re: [PATCH v10 12/13] leds: Add common LED binding parsing support to LED class/core
From: Vaittinen, Matti @ 2020-01-20 8:56 UTC (permalink / raw)
To: mazziesaccount@gmail.com
Cc: linux-leds@vger.kernel.org, dmurphy@ti.com,
linux-rtc@vger.kernel.org, linux-gpio@vger.kernel.org,
alexandre.belloni@bootlin.com, linux-kernel@vger.kernel.org,
mturquette@baylibre.com, lgirdwood@gmail.com,
devicetree@vger.kernel.org, jacek.anaszewski@gmail.com,
a.zummo@towertech.it, linus.walleij@linaro.org,
mark.rutland@arm.com, robh+dt@kernel.org,
bgolaszewski@baylibre.com, linux-clk@vger.kernel.org,
sboyd@kernel.org, lee.jones@linaro.org, broonie@kernel.org,
pavel@ucw.cz
In-Reply-To: <718ff08187974eff683a014b73afc4f234a8c960.1579249511.git.matti.vaittinen@fi.rohmeurope.com>
On Fri, 2020-01-17 at 11:43 +0200, Matti Vaittinen wrote:
> Quick grep for 'for_each' or 'linux,default-trigger' or
> 'default-state' under drivers/leds can tell quite a lot. It seems
> multiple LED controller drivers implement the very similar looping
> through the child nodes in order to locate the LED nodes and read
> and support the common LED dt bindings. Implementing this same
> stuff for all LED controllers gets old pretty fast.
>
> This commit adds support for locating the LED node (based on known
> node names - or given property name-value pair) and handling of
> few common LED properties.
>
> linux,default-trigger,
> default-state (with the exception of keep),
>
> (in addition to already handled
> function-enumerator,
> function,
> color
> and label).
>
> The handling of linux,default-trigger and default-state are omitted
> if init_data->parse_fwnode is not set to true. Thus the existing
> LED drivers are not impacted until they set this field to true.
>
> Regarding the node look-up:
>
> 1. If no init_data is given, then no node-lookup is done and cdev
> name is used as such. (as was earlier)
>
> 2. If neither match_property nor of_match is given then passed
> starting
> point is used as such. (as was earlier - it might help in
> few cases to use dev_fwnode() here but that would change existing
> logic and possibly cause some existing setup to break).
>
> 3. If init_data is given but no starting point for node lookup - then
> (parent) device's own DT node is used as starting point for look-
> up.
>
> 4. The match_property has priority over of_match and is first used
> for
> look-up. If a node with matching property is found then the found
> node
> is used.
>
> 5. If no match_property is given or no mathicng property was found
> then
> of_macth is used. The node which name matches of_match is searched
> for
> and used if found.
>
> This ensures that existing drivers which do not populate of_match or
> match_property in init_data wont be impacted bu node look-up.
>
> Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> ---
> no changes since v9
>
> drivers/leds/led-class.c | 115 +++++++++++++++--
> drivers/leds/led-core.c | 258 ++++++++++++++++++++++++++++++++-----
> --
> include/linux/leds.h | 94 ++++++++++++--
> 3 files changed, 401 insertions(+), 66 deletions(-)
>
> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index 438774315e6c..170ab227eaa9 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -235,6 +235,32 @@ static int led_classdev_next_name(const char
> *init_name, char *name,
> return i;
> }
>
> +static void led_add_props(struct led_classdev *ld, struct
> led_properties *props)
> +{
> + if (props->default_trigger)
> + ld->default_trigger = props->default_trigger;
> + /*
> + * According to binding docs the LED is by-default turned OFF
> unless
> + * default_state is used to indicate it should be ON or that
> state
> + * should be kept as is
> + */
> + if (props->default_state) {
> + ld->brightness = LED_OFF;
> + if (!strcmp(props->default_state, "on")) {
> + if (!ld->max_brightness)
> + ld->brightness = LED_FULL;
> + else
> + ld->brightness = ld->max_brightness;
This makes no sense as long as we unconditionally call the
led_update_brightness at the end of led_classdev_register_ext. I
misread the led_update_brightness - and thought it would update the
brightness value from led classdev to hardware. But this is actually
the opposite - it reads value from HW and sets it to classdev. So
updating value in classdev won't help as the led_update_brightness will
overwrite it.
So for proper common DT parsing we need a flag to update the new
default value from DT to HW - if the default is given. And spill out a
warning if driver does not implement callback to set brightness.
Anyways, I need to rework this patch to some extent (and I'll also try
to see how existing drivers could be updated).
So let's drop the patch 12 and 13 from this series - I try to rework
them for next kernel release then. :)
> + /*
> + * We probably should not call the brightness_get prior calling
> + * the of_parse_cb if one is provided.
> + * Add a flag to advertice that state should be queried and
> kept as-is.
> + */
> + } else if (!strcmp(props->default_state, "keep"))
> + props->brightness_keep = true;
> + }
> +}
> +
> /**
> * led_classdev_register_ext - register a new object of led_classdev
> class
> * with init data.
> @@ -251,22 +277,69 @@ int led_classdev_register_ext(struct device
> *parent,
> char final_name[LED_MAX_NAME_SIZE];
> const char *proposed_name = composed_name;
> int ret;
> -
> + struct led_properties props = {0};
> + struct fwnode_handle *fw;
> +
> + /*
> + * We don't try getting the name based on DT node if init-data
> is not
> + * given. We could see if we find LED properties from the
> device's node
> + * but that might change LED names for old users of
> + * led_classdev_register who have been providing the LED name
> in
> + * cdev->name. So we seek fwnode for names only if init_data is
> given
> + */
> if (init_data) {
> + led_cdev->init_data = init_data;
> if (init_data->devname_mandatory && !init_data-
> >devicename) {
> dev_err(parent, "Mandatory device name is
> missing");
> return -EINVAL;
> }
> - ret = led_compose_name(parent, init_data,
> composed_name);
> +
> + fw = led_get_fwnode(parent, init_data);
> + if (IS_ERR(fw))
> + return PTR_ERR(fw);
> +
> + if (fw) {
> + /*
> + * We did increase refcount for fwnode. Let's
> set a flag
> + * so we can decrease it during deregistration
> + */
> + led_cdev->fwnode_owned = true;
> +
> + ret = led_parse_fwnode_props(parent, fw,
> &props);
> + if (ret)
> + goto err_out;
> +
> + if (init_data->of_parse_cb)
> + ret = init_data->of_parse_cb(led_cdev,
> fw,
> + &props);
> + if (ret < 0)
> + goto err_out;
> +
> + /*
> + * Handle the common LED properties only for
> drivers
> + * that explicitly request it. This allows us
> to test
> + * and convert drivers to utilize common LED
> parsing one
> + * by one.
> + */
> + if (init_data->parse_fwnode)
> + led_add_props(led_cdev, &props);
> +
> + } else {
> + led_cdev->fwnode_owned = false;
> + }
> + ret = led_compose_name(parent, init_data, &props,
> + composed_name);
> if (ret < 0)
> - return ret;
> + goto err_out;
> } else {
> proposed_name = led_cdev->name;
> + led_cdev->fwnode_owned = false;
> + fw = NULL;
> }
>
> ret = led_classdev_next_name(proposed_name, final_name,
> sizeof(final_name));
> if (ret < 0)
> - return ret;
> + goto err_out;
>
> mutex_init(&led_cdev->led_access);
> mutex_lock(&led_cdev->led_access);
> @@ -274,22 +347,28 @@ int led_classdev_register_ext(struct device
> *parent,
> led_cdev, led_cdev->groups, "%s",
> final_name);
> if (IS_ERR(led_cdev->dev)) {
> mutex_unlock(&led_cdev->led_access);
> - return PTR_ERR(led_cdev->dev);
> + ret = PTR_ERR(led_cdev->dev);
> + goto err_out;
> }
> - if (init_data && init_data->fwnode)
> - led_cdev->dev->fwnode = init_data->fwnode;
> + if (fw)
> + led_cdev->dev->fwnode = fw;
>
> if (ret)
> dev_warn(parent, "Led %s renamed to %s due to name
> collision",
> led_cdev->name, dev_name(led_cdev-
> >dev));
>
> + if (props.brightness_keep)
> + if (led_cdev->brightness_get)
> + led_cdev->brightness =
> + led_cdev->brightness_get(led_cdev);
> +
> if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
> ret = led_add_brightness_hw_changed(led_cdev);
> if (ret) {
> device_unregister(led_cdev->dev);
> led_cdev->dev = NULL;
> mutex_unlock(&led_cdev->led_access);
> - return ret;
> + goto err_out;
> }
> }
>
> @@ -322,6 +401,17 @@ int led_classdev_register_ext(struct device
> *parent,
> led_cdev->name);
>
> return 0;
> +err_out:
> + if (led_cdev->fwnode_owned) {
> + fwnode_handle_put(fw);
> + /*
> + * This might not be needed as 'fwnode_owned' is always
> + * initialized in led_classdev_register_ext
> + */
> + led_cdev->fwnode_owned = false;
> + }
> +
> + return ret;
> }
> EXPORT_SYMBOL_GPL(led_classdev_register_ext);
>
> @@ -355,6 +445,15 @@ void led_classdev_unregister(struct led_classdev
> *led_cdev)
> if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
> led_remove_brightness_hw_changed(led_cdev);
>
> + if (led_cdev->fwnode_owned) {
> + fwnode_handle_put(led_cdev->dev->fwnode);
> + /*
> + * This might not be needed as 'fwnode_owned' is always
> + * initialized in led_classdev_register_ext
> + */
> + led_cdev->fwnode_owned = false;
> + }
> +
> device_unregister(led_cdev->dev);
>
> down_write(&leds_list_lock);
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index f1f718dbe0f8..3cd1784494f2 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -365,70 +365,226 @@ void led_sysfs_enable(struct led_classdev
> *led_cdev)
> }
> EXPORT_SYMBOL_GPL(led_sysfs_enable);
>
> -static void led_parse_fwnode_props(struct device *dev,
> - struct fwnode_handle *fwnode,
> - struct led_properties *props)
> +static int fw_is_match(struct fwnode_handle *fw,
> + struct led_fw_match_property *mp, void *val)
> {
> - int ret;
> + void *cmp = mp->raw_val;
> + int ret = -EINVAL;
> +
> + if (mp->raw_val) {
> + ret = fwnode_property_read_u8_array(fw, mp->name, val,
> + mp->size);
> + } else if (mp->intval) {
> + cmp = mp->intval;
> + switch (mp->size) {
> + case 1:
> + ret = fwnode_property_read_u8_array(fw, mp-
> >name, val,
> + mp->size);
> + break;
> + case 2:
> + ret = fwnode_property_read_u16_array(fw, mp-
> >name, val,
> + mp->size);
> + break;
> + case 4:
> + ret = fwnode_property_read_u32_array(fw, mp-
> >name, val,
> + mp->size);
> + break;
> + case 8:
> + ret = fwnode_property_read_u64_array(fw, mp-
> >name, val,
> + mp->size);
> + break;
> + default:
> + return -EINVAL;
> + }
> + }
> + if (!ret && cmp)
> + if (!memcmp(val, cmp, mp->size))
> + return 1;
> +
> + return 0;
> +}
> +/**
> + * led_get_fwnode - find fwnode for led
> + * @parent LED controller device
> + * @init_data led init data with match information
> + *
> + * Scans the firmware nodes and returns node matching the given
> init_data.
> + * NOTE: Function increases refcount for found node. Caller must
> decrease
> + * refcount using fwnode_handle_put when finished with node.
> + */
> +struct fwnode_handle *led_get_fwnode(struct device *parent,
> + struct led_init_data *init_data)
> +{
> + struct fwnode_handle *fw;
> +
> + /*
> + * This should never be called without init data. We could
> always return
> + * dev_fwnode() - but then we should pump-up the refcount
> + */
> + if (!init_data)
> + return NULL;
> +
> + /*
> + * For now we do only do node look-up for drivers which
> populate
> + * the new match properties. We could and perhaps should do
> + * fw = dev_fwnode(parent); if given fwnode is NULL. But in
> order not to
> + * break the existing setups we keep the old behaviour and just
> directly
> + * use the given init_data->fwnode no matter if it is NULL or
> not.
> + */
> +
> + if ((!init_data->match_property.name ||
> + !init_data->match_property.size) && !init_data->of_match)
> + return fwnode_handle_get(init_data->fwnode);
> +
> + /* match information was given - do node look-up */
> +
> + if (!init_data->fwnode)
> + fw = dev_fwnode(parent);
> + else
> + fw = init_data->fwnode;
> +
> + if (!fw)
> + return NULL;
> +
> + /*
> + * Simple things are pretty. I think simplest is to use DT
> node-name
> + * for matching the node with LED - same way regulators use the
> node
> + * name to match with desc.
> + *
> + * This may not work with existing LED DT entries if the node
> name has
> + * been freely pickable. In order to this to work the binding
> doc
> + * for LED driver should define usable node names.
> + *
> + * If this is not working we can define specific match property
> which
> + * value we scan and use for matching for LEDs connected to the
> + * controller.
> + */
> + if (init_data->match_property.name && init_data-
> >match_property.size) {
> + u8 *val;
> + int ret;
> + struct fwnode_handle *child;
> + struct led_fw_match_property *mp;
> +
> + mp = &init_data->match_property;
> +
> + val = kzalloc(mp->size, GFP_KERNEL);
> + if (!val)
> + return ERR_PTR(-ENOMEM);
> +
> + fwnode_for_each_child_node(fw, child) {
> + ret = fw_is_match(child, mp, val);
> + if (ret > 0) {
> + kfree(val);
> + return child;
> + }
> + if (ret < 0) {
> + dev_err(parent,
> + "invalid fw match. Use
> raw_val?\n");
> + fwnode_handle_put(child);
> + break;
> + }
> + }
> + kfree(val);
> + }
> + if (init_data->of_match)
> + fw = fwnode_get_named_child_node(fw, init_data-
> >of_match);
> +
> + return fw;
> +}
> +EXPORT_SYMBOL(led_get_fwnode);
> +
> +int led_parse_fwnode_props(struct device *dev, struct fwnode_handle
> *fwnode,
> + struct led_properties *props)
> +{
> + int ret = 0;
>
> if (!fwnode)
> - return;
> + return 0;
>
> if (fwnode_property_present(fwnode, "label")) {
> ret = fwnode_property_read_string(fwnode, "label",
> &props->label);
> if (ret)
> dev_err(dev, "Error parsing 'label' property
> (%d)\n", ret);
> - return;
> + return ret;
> }
>
> + /*
> + * Please note, logic changed - if invalid property is found we
> bail
> + * early out without parsing the rest of the properties.
> Originally
> + * this was the case only for 'label' property. I don't know
> the
> + * rationale behind original logic allowing invalid properties
> to be
> + * given. If there is a reason then we should reconsider this.
> + * Intuitively it feels correct to just yell and quit if we hit
> value we
> + * don't understand - but intuition may be wrong at times :)
> + */
> if (fwnode_property_present(fwnode, "color")) {
> ret = fwnode_property_read_u32(fwnode, "color", &props-
> >color);
> - if (ret)
> + if (ret) {
> dev_err(dev, "Error parsing 'color' property
> (%d)\n", ret);
> - else if (props->color >= LED_COLOR_ID_MAX)
> + return ret;
> + } else if (props->color >= LED_COLOR_ID_MAX) {
> dev_err(dev, "LED color identifier out of
> range\n");
> - else
> - props->color_present = true;
> + return ret;
> + }
> + props->color_present = true;
> }
>
> + if (fwnode_property_present(fwnode, "function")) {
> + ret = fwnode_property_read_string(fwnode, "function",
> + &props->function);
> + if (ret) {
> + dev_err(dev,
> + "Error parsing 'function' property
> (%d)\n",
> + ret);
> + return ret;
> + }
> + }
>
> - if (!fwnode_property_present(fwnode, "function"))
> - return;
> -
> - ret = fwnode_property_read_string(fwnode, "function", &props-
> >function);
> - if (ret) {
> - dev_err(dev,
> - "Error parsing 'function' property (%d)\n",
> - ret);
> + if (fwnode_property_present(fwnode, "function-enumerator")) {
> + ret = fwnode_property_read_u32(fwnode, "function-
> enumerator",
> + &props->func_enum);
> + if (ret) {
> + dev_err(dev,
> + "Error parsing 'function-enumerator'
> property (%d)\n",
> + ret);
> + return ret;
> + }
> + props->func_enum_present = true;
> }
>
> - if (!fwnode_property_present(fwnode, "function-enumerator"))
> - return;
> + if (fwnode_property_present(fwnode, "default-state")) {
> + ret = fwnode_property_read_string(fwnode, "default-
> state",
> + &props-
> >default_state);
> + if (ret) {
> + dev_err(dev,
> + "Error parsing 'default-state' property
> (%d)\n",
> + ret);
> + return ret;
> + }
> + }
>
> - ret = fwnode_property_read_u32(fwnode, "function-enumerator",
> - &props->func_enum);
> - if (ret) {
> - dev_err(dev,
> - "Error parsing 'function-enumerator' property
> (%d)\n",
> - ret);
> - } else {
> - props->func_enum_present = true;
> + if (fwnode_property_present(fwnode, "linux,default-trigger")) {
> + ret = fwnode_property_read_string(fwnode,
> + "linux,default-
> trigger",
> + &props-
> >default_trigger);
> + if (ret)
> + dev_err(dev,
> + "Error parsing 'linux,default-trigger'
> property (%d)\n",
> + ret);
> }
> + return ret;
> }
> +EXPORT_SYMBOL_GPL(led_parse_fwnode_props);
>
> int led_compose_name(struct device *dev, struct led_init_data
> *init_data,
> - char *led_classdev_name)
> + struct led_properties *props, char
> *led_classdev_name)
> {
> - struct led_properties props = {};
> - struct fwnode_handle *fwnode = init_data->fwnode;
> const char *devicename = init_data->devicename;
>
> if (!led_classdev_name)
> return -EINVAL;
>
> - led_parse_fwnode_props(dev, fwnode, &props);
> -
> - if (props.label) {
> + if (props->label) {
> /*
> * If init_data.devicename is NULL, then it indicates
> that
> * DT label should be used as-is for LED class device
> name.
> @@ -436,23 +592,23 @@ int led_compose_name(struct device *dev, struct
> led_init_data *init_data,
> * the final LED class device name.
> */
> if (!devicename) {
> - strscpy(led_classdev_name, props.label,
> + strscpy(led_classdev_name, props->label,
> LED_MAX_NAME_SIZE);
> } else {
> snprintf(led_classdev_name, LED_MAX_NAME_SIZE,
> "%s:%s",
> - devicename, props.label);
> + devicename, props->label);
> }
> - } else if (props.function || props.color_present) {
> + } else if (props->function || props->color_present) {
> char tmp_buf[LED_MAX_NAME_SIZE];
>
> - if (props.func_enum_present) {
> + if (props->func_enum_present) {
> snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-
> %d",
> - props.color_present ?
> led_colors[props.color] : "",
> - props.function ?: "",
> props.func_enum);
> + props->color_present ?
> led_colors[props->color] : "",
> + props->function ?: "", props-
> >func_enum);
> } else {
> snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
> - props.color_present ?
> led_colors[props.color] : "",
> - props.function ?: "");
> + props->color_present ?
> led_colors[props->color] : "",
> + props->function ?: "");
> }
> if (init_data->devname_mandatory) {
> snprintf(led_classdev_name, LED_MAX_NAME_SIZE,
> "%s:%s",
> @@ -468,11 +624,19 @@ int led_compose_name(struct device *dev, struct
> led_init_data *init_data,
> }
> snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
> devicename, init_data->default_label);
> - } else if (is_of_node(fwnode)) {
> - strscpy(led_classdev_name, to_of_node(fwnode)->name,
> - LED_MAX_NAME_SIZE);
> - } else
> - return -EINVAL;
> + } else {
> + struct fwnode_handle *fwnode = led_get_fwnode(dev,
> init_data);
> + int ret = -EINVAL;
> +
> + if (is_of_node(fwnode)) {
> + ret = 0;
> + strscpy(led_classdev_name, to_of_node(fwnode)-
> >name,
> + LED_MAX_NAME_SIZE);
> + }
> + fwnode_handle_put(fwnode);
> +
> + return ret;
> + }
>
> return 0;
> }
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index 242258f7d837..af8ca4291d7d 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -13,6 +13,7 @@
> #include <linux/kernfs.h>
> #include <linux/list.h>
> #include <linux/mutex.h>
> +#include <linux/property.h>
> #include <linux/rwsem.h>
> #include <linux/spinlock.h>
> #include <linux/timer.h>
> @@ -20,6 +21,7 @@
>
> struct device;
> struct led_pattern;
> +struct led_classdev;
> /*
> * LED Core
> */
> @@ -31,8 +33,47 @@ enum led_brightness {
> LED_FULL = 255,
> };
>
> +struct led_properties {
> + u32 color;
> + bool color_present;
> + const char *function;
> + u32 func_enum;
> + bool func_enum_present;
> + const char *label;
> + const char *default_trigger;
> + const char *default_state;
> + bool brightness_keep;
> +};
> +
> +struct led_fw_match_property {
> + const char *name; /* Name of the property to match */
> + void *raw_val; /* Raw property value as present in
> fwnode */
> + void *intval; /* Property value if 8,16,32 or 64bit
> integer */
> + size_t size; /* Size of value in bytes */
> +};
> +
> struct led_init_data {
> - /* device fwnode handle */
> + /*
> + * If DT binding dictates the node name the driver can fill
> of_match
> + * corresponding to node name describing this LED. If fwnode is
> given
> + * the match is searched from it's child nodes. If not, the
> match is
> + * searched from device's own child nodes.
> + */
> + const char *of_match;
> + /*
> + * If fwnode contains property with known value the driver can
> specify
> + * correct propertty-value pair here to do the matching. This
> has higher
> + * priority than of_match. If fwnode is given the match is
> searched
> + * from it's child nodes. If not match is searched from
> device's
> + * own child nodes.
> + */
> + struct led_fw_match_property match_property;
> + /*
> + * device fwnode handle. If of_match or led_compatible are not
> given
> + * this is used for LED as given. If of_match or led_compatible
> are
> + * given then this is used as a parent node whose child nodes
> are
> + * scanned for given match.
> + */
> struct fwnode_handle *fwnode;
> /*
> * default <color:function> tuple, for backward compatibility
> @@ -53,9 +94,19 @@ struct led_init_data {
> * set it to true
> */
> bool devname_mandatory;
> + /*
> + * Callback which a LED driver can register if it has own non-
> standard
> + * DT properties. Core calls this with the located DT node
> during
> + * class_device registration
> + */
> + int (*of_parse_cb)(struct led_classdev *ld, struct
> fwnode_handle *fw,
> + struct led_properties *props);
> + /* LED core should parse and handle the common firmware
> properties */
> + bool parse_fwnode;
> };
>
> struct led_classdev {
> + struct led_init_data *init_data;
> const char *name;
> enum led_brightness brightness;
> enum led_brightness max_brightness;
> @@ -148,6 +199,7 @@ struct led_classdev {
>
> /* Ensures consistent access to the LED Flash Class device */
> struct mutex led_access;
> + bool fwnode_owned;
> };
>
> /**
> @@ -299,6 +351,7 @@ void led_sysfs_enable(struct led_classdev
> *led_cdev);
> * led_compose_name - compose LED class device name
> * @dev: LED controller device object
> * @init_data: the LED class device initialization data
> + * @props: LED properties as parsed from fwnode.
> * @led_classdev_name: composed LED class device name
> *
> * Create LED class device name basing on the provided init_data
> argument.
> @@ -308,7 +361,8 @@ void led_sysfs_enable(struct led_classdev
> *led_cdev);
> * Returns: 0 on success or negative error value on failure
> */
> int led_compose_name(struct device *dev, struct led_init_data
> *init_data,
> - char *led_classdev_name);
> + struct led_properties *props,
> + char *led_classdev_name);
>
> /**
> * led_sysfs_is_disabled - check if LED sysfs interface is disabled
> @@ -321,6 +375,33 @@ static inline bool led_sysfs_is_disabled(struct
> led_classdev *led_cdev)
> return led_cdev->flags & LED_SYSFS_DISABLE;
> }
>
> +/**
> + * led_get_fwnode - find fwnode matching given LED init data
> + * @parent: LED controller device this LED is driven by
> + * @init_data: the LED class device initialization data
> + *
> + * Find the fw node matching given LED init data.
> + * NOTE: Function increases refcount for found node. Caller must
> decrease
> + * refcount using fwnode_handle_put when finished with node.
> + *
> + * Returns: node handle or NULL if matching fw node was not found
> + */
> +struct fwnode_handle *led_get_fwnode(struct device *parent,
> + struct led_init_data *init_data);
> +
> +/**
> + * led_parse_fwnode_props - parse LED common properties from fwnode
> + * @dev: pointer to LED device.
> + * @fwnode: LED node containing the properties
> + * @props: structure where found property data is stored.
> + *
> + * Parse the common LED properties from fwnode.
> + *
> + * Returns: 0 on success or negative error value on failure
> + */
> +int led_parse_fwnode_props(struct device *dev, struct fwnode_handle
> *fwnode,
> + struct led_properties *props);
> +
> /*
> * LED Triggers
> */
> @@ -478,15 +559,6 @@ struct led_platform_data {
> struct led_info *leds;
> };
>
> -struct led_properties {
> - u32 color;
> - bool color_present;
> - const char *function;
> - u32 func_enum;
> - bool func_enum_present;
> - const char *label;
> -};
> -
> struct gpio_desc;
> typedef int (*gpio_blink_set_t)(struct gpio_desc *desc, int state,
> unsigned long *delay_on,
> --
> 2.21.0
>
>
^ permalink raw reply
* Re: [PATCH v11 06/13] mfd: input: bd71828: Add power-key support
From: Lee Jones @ 2020-01-20 8:55 UTC (permalink / raw)
To: Matti Vaittinen
Cc: mazziesaccount, Jacek Anaszewski, Pavel Machek, Dan Murphy,
Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <aa965067acfbdaa862f5f5de8b66e2c161f276a0.1579501711.git.matti.vaittinen@fi.rohmeurope.com>
On Mon, 20 Jan 2020, Matti Vaittinen wrote:
> Use gpio_keys to send power input-event to user-space when power
> button (short) press is detected.
>
> Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
> ---
> No changes since v10
>
> drivers/mfd/rohm-bd71828.c | 26 ++++++++++++++++++++++++++
> 1 file changed, 26 insertions(+)
Drop the "input:" tag from the subject line please.
--
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH v11 03/13] mfd: rohm PMICs - use platform_device_id to match MFD sub-devices
From: Lee Jones @ 2020-01-20 8:54 UTC (permalink / raw)
To: Matti Vaittinen
Cc: mazziesaccount, Jacek Anaszewski, Pavel Machek, Dan Murphy,
Rob Herring, Mark Rutland, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <13994480cab6d5d6376c8f5228572e55ca06e479.1579501711.git.matti.vaittinen@fi.rohmeurope.com>
On Mon, 20 Jan 2020, Matti Vaittinen wrote:
> Thanks to Stephen Boyd I today learned we can use platform_device_id
> to do device and module matching for MFD sub-devices!
>
> Do device matching using the platform_device_id instead of using
> explicit module_aliases to load modules and custom parent-data field
> to do module loading and sub-device matching.
>
> Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
> ---
> No changes since v10
>
> drivers/clk/clk-bd718x7.c | 12 ++++++++-
> drivers/mfd/rohm-bd70528.c | 3 +--
> drivers/mfd/rohm-bd718x7.c | 39 ++++++++++++++++++++++-----
> drivers/regulator/bd718x7-regulator.c | 17 +++++++++---
> include/linux/mfd/rohm-generic.h | 3 +--
> 5 files changed, 58 insertions(+), 16 deletions(-)
Still needs Clk and Regulator Acks.
--
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH v10 11/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs
From: Lee Jones @ 2020-01-20 8:02 UTC (permalink / raw)
To: Vaittinen, Matti
Cc: linux-leds@vger.kernel.org, dmurphy@ti.com,
linux-rtc@vger.kernel.org, linux-gpio@vger.kernel.org,
alexandre.belloni@bootlin.com, linux-kernel@vger.kernel.org,
mturquette@baylibre.com, mazziesaccount@gmail.com,
devicetree@vger.kernel.org, jacek.anaszewski@gmail.com,
a.zummo@towertech.it, linus.walleij@linaro.org,
lgirdwood@gmail.com, mark.rutland@arm.com, robh+dt@kernel.org,
bgolaszewski@baylibre.com, linux-clk@vger.kernel.org,
sboyd@kernel.org, pavel@ucw.cz, broonie@kernel.org
In-Reply-To: <9f405dfc4c7e56e32f4eb2f9cb6e87c05aea1ac9.camel@fi.rohmeurope.com>
On Mon, 20 Jan 2020, Vaittinen, Matti wrote:
> Hello,
>
> On Fri, 2020-01-17 at 10:21 +0000, Lee Jones wrote:
> > On Fri, 17 Jan 2020, Matti Vaittinen wrote:
> >
> > > ROHM BD71828 PMIC contains 4 pins which can be configured by OTP
> > > to be used for general purposes. First 3 can be used as outputs
> > > and 4.th pin can be used as input. Allow them to be controlled
> > > via GPIO framework.
> > >
> > > The driver assumes all of the pins are configured as GPIOs and
> > > trusts that the reserved pins in other OTP configurations are
> > > excluded from control using "gpio-reserved-ranges" device tree
> > > property (or left untouched by GPIO users).
> > >
> > > Typical use for 4.th pin (input) is to use it as HALL sensor
> > > input so that this pin state is toggled when HALL sensor detects
> > > LID position change (from close to open or open to close). PMIC
> > > HW implements some extra logic which allows PMIC to power-up the
> > > system when this pin is toggled. Please see the data sheet for
> > > details of GPIO options which can be selected by OTP settings.
> > >
> > > Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> > > Reviewed-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> > > Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> >
> > Linus, Is that an Ack?
> I have always thought that reviewed-by implies that reviewer is Ok with
> the patch (imples Ack). Maybe I have mistaken?
I would rather not assume.
--
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH v10 08/13] regulator: bd718x7: Split driver to common and bd718x7 specific parts
From: Vaittinen, Matti @ 2020-01-20 7:06 UTC (permalink / raw)
To: lee.jones@linaro.org
Cc: dmurphy@ti.com, linux-leds@vger.kernel.org,
linux-rtc@vger.kernel.org, linux-gpio@vger.kernel.org,
linux-kernel@vger.kernel.org, alexandre.belloni@bootlin.com,
mazziesaccount@gmail.com, mturquette@baylibre.com,
lgirdwood@gmail.com, devicetree@vger.kernel.org,
linus.walleij@linaro.org, a.zummo@towertech.it,
mark.rutland@arm.com, robh+dt@kernel.org,
bgolaszewski@baylibre.com, linux-clk@vger.kernel.org,
sboyd@kernel.org, pavel@ucw.cz, jacek.anaszewski@gmail.com,
broonie@kernel.org
In-Reply-To: <20200117134026.GM15507@dell>
Hello,
On Fri, 2020-01-17 at 13:40 +0000, Lee Jones wrote:
> On Fri, 17 Jan 2020, Vaittinen, Matti wrote:
> > > >
> > > > +enum {
> > > > + ROHM_DVS_LEVEL_UNKNOWN,
> > > > + ROHM_DVS_LEVEL_RUN,
> > > > + ROHM_DVS_LEVEL_IDLE,
> > > > + ROHM_DVS_LEVEL_SUSPEND,
> > > > + ROHM_DVS_LEVEL_LPSR,
> > > > +#define ROHM_DVS_LEVEL_MAX ROHM_DVS_LEVEL_LPSR
> > >
> > > Haven't seen this before. Is it legit?
> > >
> >
> > I don't know why it wouldn't be :) I kind of grew used to that when
> > I
> > still did some networking stuff.
>
> Networking it not a good example.
>
> It's full of odd little quirks to the standard styling.
That was quite a strong wording... Some people might disagree :)
Anyways, as far as I know the preprocessor does not care about where
the preprocessor directives are placed. It just goes through the file
sequentially and macro definitions take effect at the place you write
them. And actual compiler does not see the directive - just code which
has been replaced. So from C point of view I see no problem here. From
coding conventions or guidelines point of view - well, that's more of
your territory ;)
> > What about:
> > > ROHM_DVS_LEVEL_MAX = ROHM_DVS_LEVEL_LPSR
> >
> > Anyways, I don't see why define wouldn't be Ok here - but sure it
> > can
> > be changed if you insist ;) Just let me know if you can accept the
> > define or not :)
>
> Let's go for not in this instance. :D
Ok. I sent v11 where this has been changed as you suggested :)
Br,
--Matti
^ permalink raw reply
* Re: [PATCH v10 11/13] gpio: bd71828: Initial support for ROHM BD71828 PMIC GPIOs
From: Vaittinen, Matti @ 2020-01-20 6:48 UTC (permalink / raw)
To: lee.jones@linaro.org
Cc: linux-leds@vger.kernel.org, dmurphy@ti.com,
linux-rtc@vger.kernel.org, linux-gpio@vger.kernel.org,
alexandre.belloni@bootlin.com, linux-kernel@vger.kernel.org,
mturquette@baylibre.com, mazziesaccount@gmail.com,
devicetree@vger.kernel.org, jacek.anaszewski@gmail.com,
a.zummo@towertech.it, linus.walleij@linaro.org,
lgirdwood@gmail.com, mark.rutland@arm.com, robh+dt@kernel.org,
bgolaszewski@baylibre.com, linux-clk@vger.kernel.org,
sboyd@kernel.org, pavel@ucw.cz, broonie@kernel.org
In-Reply-To: <20200117102127.GD15507@dell>
Hello,
On Fri, 2020-01-17 at 10:21 +0000, Lee Jones wrote:
> On Fri, 17 Jan 2020, Matti Vaittinen wrote:
>
> > ROHM BD71828 PMIC contains 4 pins which can be configured by OTP
> > to be used for general purposes. First 3 can be used as outputs
> > and 4.th pin can be used as input. Allow them to be controlled
> > via GPIO framework.
> >
> > The driver assumes all of the pins are configured as GPIOs and
> > trusts that the reserved pins in other OTP configurations are
> > excluded from control using "gpio-reserved-ranges" device tree
> > property (or left untouched by GPIO users).
> >
> > Typical use for 4.th pin (input) is to use it as HALL sensor
> > input so that this pin state is toggled when HALL sensor detects
> > LID position change (from close to open or open to close). PMIC
> > HW implements some extra logic which allows PMIC to power-up the
> > system when this pin is toggled. Please see the data sheet for
> > details of GPIO options which can be selected by OTP settings.
> >
> > Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
> > Reviewed-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> > Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
>
> Linus, Is that an Ack?
I have always thought that reviewed-by implies that reviewer is Ok with
the patch (imples Ack). Maybe I have mistaken?
Br,
--Matti
^ permalink raw reply
* [PATCH v11 13/13] led: bd71828: Support LED outputs on ROHM BD71828 PMIC
From: Matti Vaittinen @ 2020-01-20 6:42 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579501711.git.matti.vaittinen@fi.rohmeurope.com>
ROHM BD71828 power management IC has two LED outputs for charge status
and button pressing indications. The LED outputs can also be forced
by SW so add driver allowing to use these LEDs for other indications
as well.
Leds are controlled by SW using 'Force ON' bits. Please note the
constrains mentioned in data-sheet:
1. If one LED is forced ON - then also the other LED is forced.
=> You can't use SW control to force ON one LED and allow HW
to control the other.
2. You can't force both LEDs OFF. If the FORCE bit for both LED's is
zero, then LEDs are controlled by HW and indicate button/charger
states as explained in data-sheet.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
No changes since v10
drivers/leds/Kconfig | 10 +++
drivers/leds/Makefile | 1 +
drivers/leds/leds-bd71828.c | 118 ++++++++++++++++++++++++++++++++++++
3 files changed, 129 insertions(+)
create mode 100644 drivers/leds/leds-bd71828.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 4b68520ac251..db408e03e296 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -545,6 +545,16 @@ config LEDS_BD2802
This option enables support for BD2802GU RGB LED driver chips
accessed via the I2C bus.
+config LEDS_BD71828
+ tristate "LED driver for LEDS on ROHM BD71828 PMIC"
+ depends on LEDS_CLASS
+ depends on MFD_ROHM_BD71828
+ help
+ This option enables support for LED outputs located on ROHM
+ BD71828 power management IC. ROHM BD71828 has two led output pins
+ which can be left to indicate HW states or controlled by SW. Say
+ yes here if you want to enable SW control for these LEDs.
+
config LEDS_INTEL_SS4200
tristate "LED driver for Intel NAS SS4200 series"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 2da39e896ce8..a5164acfaf51 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o
obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o
obj-$(CONFIG_LEDS_BCM6358) += leds-bcm6358.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
+obj-$(CONFIG_LEDS_BD71828) += leds-bd71828.o
obj-$(CONFIG_LEDS_CPCAP) += leds-cpcap.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
diff --git a/drivers/leds/leds-bd71828.c b/drivers/leds/leds-bd71828.c
new file mode 100644
index 000000000000..a02c2ac4a70b
--- /dev/null
+++ b/drivers/leds/leds-bd71828.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2019 ROHM Semiconductors
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/mfd/rohm-bd71828.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define BD71828_LED_TO_DATA(l) ((l)->id == ID_GREEN_LED ? \
+ container_of((l), struct bd71828_leds, green) : \
+ container_of((l), struct bd71828_leds, amber))
+
+/* Names for led identification - these match the data sheet names */
+enum {
+ ID_GREEN_LED,
+ ID_AMBER_LED,
+ ID_NMBR_OF,
+};
+
+struct bd71828_led {
+ struct led_init_data init_data;
+ int id;
+ struct led_classdev l;
+ u8 force_mask;
+};
+
+struct bd71828_leds {
+ struct rohm_regmap_dev *bd71828;
+ struct bd71828_led green;
+ struct bd71828_led amber;
+};
+
+static int bd71828_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct bd71828_led *l = container_of(led_cdev, struct bd71828_led, l);
+ struct bd71828_leds *data;
+ unsigned int val = BD71828_LED_OFF;
+
+ data = BD71828_LED_TO_DATA(l);
+ if (value != LED_OFF)
+ val = BD71828_LED_ON;
+
+ return regmap_update_bits(data->bd71828->regmap, BD71828_REG_LED_CTRL,
+ l->force_mask, val);
+}
+
+static int bd71828_led_probe(struct platform_device *pdev)
+{
+ struct rohm_regmap_dev *bd71828;
+ struct bd71828_leds *l;
+ struct bd71828_led *g, *a;
+ int ret;
+
+ bd71828 = dev_get_drvdata(pdev->dev.parent);
+ l = devm_kzalloc(&pdev->dev, sizeof(*l), GFP_KERNEL);
+ if (!l)
+ return -ENOMEM;
+ l->bd71828 = bd71828;
+ a = &l->amber;
+ g = &l->green;
+
+ /* Fill in details for 'AMBLED' */
+ a->init_data.match_property.name = "rohm,led-compatible";
+ a->init_data.match_property.raw_val = "bd71828-ambled";
+ a->init_data.match_property.size = strlen("bd71828-ambled");
+ a->id = ID_AMBER_LED;
+ a->force_mask = BD71828_MASK_LED_AMBER;
+
+ /* Fill in details for 'GRNLED' */
+ g->init_data.match_property.name = "rohm,led-compatible";
+ g->init_data.match_property.raw_val = "bd71828-grnled";
+ g->init_data.match_property.size = strlen("bd71828-grnled");
+ g->id = ID_GREEN_LED;
+ g->force_mask = BD71828_MASK_LED_GREEN;
+
+ a->l.brightness_set_blocking = bd71828_led_brightness_set;
+ g->l.brightness_set_blocking = bd71828_led_brightness_set;
+
+ ret = devm_led_classdev_register_ext(&pdev->dev, &g->l, &g->init_data);
+ if (ret)
+ return ret;
+
+ return devm_led_classdev_register_ext(&pdev->dev, &a->l, &a->init_data);
+}
+
+/*
+ * Device is instantiated through parent MFD device and device matching is done
+ * through platform_device_id.
+ *
+ * However, the *module* matching will be done trough DT aliases. This requires
+ * of_device_id table - but no .of_match_table as *device* matching is still
+ * done through platform_device_id.
+ */
+static const struct of_device_id bd71828_dt_match[] __used = {
+ { .compatible = "rohm,bd71828-leds" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bd71828_dt_match);
+
+static struct platform_driver bd71828_led_driver = {
+ .driver = {
+ .name = "bd71828-led",
+ },
+ .probe = bd71828_led_probe,
+};
+
+module_platform_driver(bd71828_led_driver);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("ROHM BD71828 LED driver");
+MODULE_LICENSE("GPL");
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
* [PATCH v11 12/13] leds: Add common LED binding parsing support to LED class/core
From: Matti Vaittinen @ 2020-01-20 6:42 UTC (permalink / raw)
To: matti.vaittinen, mazziesaccount
Cc: Jacek Anaszewski, Pavel Machek, Dan Murphy, Rob Herring,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown,
Jonathan Corbet, Michael Turquette, Stephen Boyd, Linus Walleij,
Bartosz Golaszewski, Alessandro Zummo, Alexandre Belloni,
Greg Kroah-Hartman, Arnd Bergmann, Mauro Carvalho Chehab,
Wolfram Sang, Phil Edworthy, Noralf Trønnes, linux-leds,
devicetree, linux-kernel, linux-doc, linux-clk, linux-gpio,
linux-rtc
In-Reply-To: <cover.1579501711.git.matti.vaittinen@fi.rohmeurope.com>
Quick grep for 'for_each' or 'linux,default-trigger' or
'default-state' under drivers/leds can tell quite a lot. It seems
multiple LED controller drivers implement the very similar looping
through the child nodes in order to locate the LED nodes and read
and support the common LED dt bindings. Implementing this same
stuff for all LED controllers gets old pretty fast.
This commit adds support for locating the LED node (based on known
node names - or given property name-value pair) and handling of
few common LED properties.
linux,default-trigger,
default-state (with the exception of keep),
(in addition to already handled
function-enumerator,
function,
color
and label).
The handling of linux,default-trigger and default-state are omitted
if init_data->parse_fwnode is not set to true. Thus the existing
LED drivers are not impacted until they set this field to true.
Regarding the node look-up:
1. If no init_data is given, then no node-lookup is done and cdev
name is used as such. (as was earlier)
2. If neither match_property nor of_match is given then passed starting
point is used as such. (as was earlier - it might help in
few cases to use dev_fwnode() here but that would change existing
logic and possibly cause some existing setup to break).
3. If init_data is given but no starting point for node lookup - then
(parent) device's own DT node is used as starting point for look-up.
4. The match_property has priority over of_match and is first used for
look-up. If a node with matching property is found then the found node
is used.
5. If no match_property is given or no mathicng property was found then
of_macth is used. The node which name matches of_match is searched for
and used if found.
This ensures that existing drivers which do not populate of_match or
match_property in init_data wont be impacted bu node look-up.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
No changes since v10
drivers/leds/led-class.c | 115 +++++++++++++++--
drivers/leds/led-core.c | 258 ++++++++++++++++++++++++++++++++-------
include/linux/leds.h | 94 ++++++++++++--
3 files changed, 401 insertions(+), 66 deletions(-)
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 438774315e6c..170ab227eaa9 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -235,6 +235,32 @@ static int led_classdev_next_name(const char *init_name, char *name,
return i;
}
+static void led_add_props(struct led_classdev *ld, struct led_properties *props)
+{
+ if (props->default_trigger)
+ ld->default_trigger = props->default_trigger;
+ /*
+ * According to binding docs the LED is by-default turned OFF unless
+ * default_state is used to indicate it should be ON or that state
+ * should be kept as is
+ */
+ if (props->default_state) {
+ ld->brightness = LED_OFF;
+ if (!strcmp(props->default_state, "on")) {
+ if (!ld->max_brightness)
+ ld->brightness = LED_FULL;
+ else
+ ld->brightness = ld->max_brightness;
+ /*
+ * We probably should not call the brightness_get prior calling
+ * the of_parse_cb if one is provided.
+ * Add a flag to advertice that state should be queried and kept as-is.
+ */
+ } else if (!strcmp(props->default_state, "keep"))
+ props->brightness_keep = true;
+ }
+}
+
/**
* led_classdev_register_ext - register a new object of led_classdev class
* with init data.
@@ -251,22 +277,69 @@ int led_classdev_register_ext(struct device *parent,
char final_name[LED_MAX_NAME_SIZE];
const char *proposed_name = composed_name;
int ret;
-
+ struct led_properties props = {0};
+ struct fwnode_handle *fw;
+
+ /*
+ * We don't try getting the name based on DT node if init-data is not
+ * given. We could see if we find LED properties from the device's node
+ * but that might change LED names for old users of
+ * led_classdev_register who have been providing the LED name in
+ * cdev->name. So we seek fwnode for names only if init_data is given
+ */
if (init_data) {
+ led_cdev->init_data = init_data;
if (init_data->devname_mandatory && !init_data->devicename) {
dev_err(parent, "Mandatory device name is missing");
return -EINVAL;
}
- ret = led_compose_name(parent, init_data, composed_name);
+
+ fw = led_get_fwnode(parent, init_data);
+ if (IS_ERR(fw))
+ return PTR_ERR(fw);
+
+ if (fw) {
+ /*
+ * We did increase refcount for fwnode. Let's set a flag
+ * so we can decrease it during deregistration
+ */
+ led_cdev->fwnode_owned = true;
+
+ ret = led_parse_fwnode_props(parent, fw, &props);
+ if (ret)
+ goto err_out;
+
+ if (init_data->of_parse_cb)
+ ret = init_data->of_parse_cb(led_cdev, fw,
+ &props);
+ if (ret < 0)
+ goto err_out;
+
+ /*
+ * Handle the common LED properties only for drivers
+ * that explicitly request it. This allows us to test
+ * and convert drivers to utilize common LED parsing one
+ * by one.
+ */
+ if (init_data->parse_fwnode)
+ led_add_props(led_cdev, &props);
+
+ } else {
+ led_cdev->fwnode_owned = false;
+ }
+ ret = led_compose_name(parent, init_data, &props,
+ composed_name);
if (ret < 0)
- return ret;
+ goto err_out;
} else {
proposed_name = led_cdev->name;
+ led_cdev->fwnode_owned = false;
+ fw = NULL;
}
ret = led_classdev_next_name(proposed_name, final_name, sizeof(final_name));
if (ret < 0)
- return ret;
+ goto err_out;
mutex_init(&led_cdev->led_access);
mutex_lock(&led_cdev->led_access);
@@ -274,22 +347,28 @@ int led_classdev_register_ext(struct device *parent,
led_cdev, led_cdev->groups, "%s", final_name);
if (IS_ERR(led_cdev->dev)) {
mutex_unlock(&led_cdev->led_access);
- return PTR_ERR(led_cdev->dev);
+ ret = PTR_ERR(led_cdev->dev);
+ goto err_out;
}
- if (init_data && init_data->fwnode)
- led_cdev->dev->fwnode = init_data->fwnode;
+ if (fw)
+ led_cdev->dev->fwnode = fw;
if (ret)
dev_warn(parent, "Led %s renamed to %s due to name collision",
led_cdev->name, dev_name(led_cdev->dev));
+ if (props.brightness_keep)
+ if (led_cdev->brightness_get)
+ led_cdev->brightness =
+ led_cdev->brightness_get(led_cdev);
+
if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
ret = led_add_brightness_hw_changed(led_cdev);
if (ret) {
device_unregister(led_cdev->dev);
led_cdev->dev = NULL;
mutex_unlock(&led_cdev->led_access);
- return ret;
+ goto err_out;
}
}
@@ -322,6 +401,17 @@ int led_classdev_register_ext(struct device *parent,
led_cdev->name);
return 0;
+err_out:
+ if (led_cdev->fwnode_owned) {
+ fwnode_handle_put(fw);
+ /*
+ * This might not be needed as 'fwnode_owned' is always
+ * initialized in led_classdev_register_ext
+ */
+ led_cdev->fwnode_owned = false;
+ }
+
+ return ret;
}
EXPORT_SYMBOL_GPL(led_classdev_register_ext);
@@ -355,6 +445,15 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
led_remove_brightness_hw_changed(led_cdev);
+ if (led_cdev->fwnode_owned) {
+ fwnode_handle_put(led_cdev->dev->fwnode);
+ /*
+ * This might not be needed as 'fwnode_owned' is always
+ * initialized in led_classdev_register_ext
+ */
+ led_cdev->fwnode_owned = false;
+ }
+
device_unregister(led_cdev->dev);
down_write(&leds_list_lock);
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index f1f718dbe0f8..3cd1784494f2 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -365,70 +365,226 @@ void led_sysfs_enable(struct led_classdev *led_cdev)
}
EXPORT_SYMBOL_GPL(led_sysfs_enable);
-static void led_parse_fwnode_props(struct device *dev,
- struct fwnode_handle *fwnode,
- struct led_properties *props)
+static int fw_is_match(struct fwnode_handle *fw,
+ struct led_fw_match_property *mp, void *val)
{
- int ret;
+ void *cmp = mp->raw_val;
+ int ret = -EINVAL;
+
+ if (mp->raw_val) {
+ ret = fwnode_property_read_u8_array(fw, mp->name, val,
+ mp->size);
+ } else if (mp->intval) {
+ cmp = mp->intval;
+ switch (mp->size) {
+ case 1:
+ ret = fwnode_property_read_u8_array(fw, mp->name, val,
+ mp->size);
+ break;
+ case 2:
+ ret = fwnode_property_read_u16_array(fw, mp->name, val,
+ mp->size);
+ break;
+ case 4:
+ ret = fwnode_property_read_u32_array(fw, mp->name, val,
+ mp->size);
+ break;
+ case 8:
+ ret = fwnode_property_read_u64_array(fw, mp->name, val,
+ mp->size);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ if (!ret && cmp)
+ if (!memcmp(val, cmp, mp->size))
+ return 1;
+
+ return 0;
+}
+/**
+ * led_get_fwnode - find fwnode for led
+ * @parent LED controller device
+ * @init_data led init data with match information
+ *
+ * Scans the firmware nodes and returns node matching the given init_data.
+ * NOTE: Function increases refcount for found node. Caller must decrease
+ * refcount using fwnode_handle_put when finished with node.
+ */
+struct fwnode_handle *led_get_fwnode(struct device *parent,
+ struct led_init_data *init_data)
+{
+ struct fwnode_handle *fw;
+
+ /*
+ * This should never be called without init data. We could always return
+ * dev_fwnode() - but then we should pump-up the refcount
+ */
+ if (!init_data)
+ return NULL;
+
+ /*
+ * For now we do only do node look-up for drivers which populate
+ * the new match properties. We could and perhaps should do
+ * fw = dev_fwnode(parent); if given fwnode is NULL. But in order not to
+ * break the existing setups we keep the old behaviour and just directly
+ * use the given init_data->fwnode no matter if it is NULL or not.
+ */
+
+ if ((!init_data->match_property.name ||
+ !init_data->match_property.size) && !init_data->of_match)
+ return fwnode_handle_get(init_data->fwnode);
+
+ /* match information was given - do node look-up */
+
+ if (!init_data->fwnode)
+ fw = dev_fwnode(parent);
+ else
+ fw = init_data->fwnode;
+
+ if (!fw)
+ return NULL;
+
+ /*
+ * Simple things are pretty. I think simplest is to use DT node-name
+ * for matching the node with LED - same way regulators use the node
+ * name to match with desc.
+ *
+ * This may not work with existing LED DT entries if the node name has
+ * been freely pickable. In order to this to work the binding doc
+ * for LED driver should define usable node names.
+ *
+ * If this is not working we can define specific match property which
+ * value we scan and use for matching for LEDs connected to the
+ * controller.
+ */
+ if (init_data->match_property.name && init_data->match_property.size) {
+ u8 *val;
+ int ret;
+ struct fwnode_handle *child;
+ struct led_fw_match_property *mp;
+
+ mp = &init_data->match_property;
+
+ val = kzalloc(mp->size, GFP_KERNEL);
+ if (!val)
+ return ERR_PTR(-ENOMEM);
+
+ fwnode_for_each_child_node(fw, child) {
+ ret = fw_is_match(child, mp, val);
+ if (ret > 0) {
+ kfree(val);
+ return child;
+ }
+ if (ret < 0) {
+ dev_err(parent,
+ "invalid fw match. Use raw_val?\n");
+ fwnode_handle_put(child);
+ break;
+ }
+ }
+ kfree(val);
+ }
+ if (init_data->of_match)
+ fw = fwnode_get_named_child_node(fw, init_data->of_match);
+
+ return fw;
+}
+EXPORT_SYMBOL(led_get_fwnode);
+
+int led_parse_fwnode_props(struct device *dev, struct fwnode_handle *fwnode,
+ struct led_properties *props)
+{
+ int ret = 0;
if (!fwnode)
- return;
+ return 0;
if (fwnode_property_present(fwnode, "label")) {
ret = fwnode_property_read_string(fwnode, "label", &props->label);
if (ret)
dev_err(dev, "Error parsing 'label' property (%d)\n", ret);
- return;
+ return ret;
}
+ /*
+ * Please note, logic changed - if invalid property is found we bail
+ * early out without parsing the rest of the properties. Originally
+ * this was the case only for 'label' property. I don't know the
+ * rationale behind original logic allowing invalid properties to be
+ * given. If there is a reason then we should reconsider this.
+ * Intuitively it feels correct to just yell and quit if we hit value we
+ * don't understand - but intuition may be wrong at times :)
+ */
if (fwnode_property_present(fwnode, "color")) {
ret = fwnode_property_read_u32(fwnode, "color", &props->color);
- if (ret)
+ if (ret) {
dev_err(dev, "Error parsing 'color' property (%d)\n", ret);
- else if (props->color >= LED_COLOR_ID_MAX)
+ return ret;
+ } else if (props->color >= LED_COLOR_ID_MAX) {
dev_err(dev, "LED color identifier out of range\n");
- else
- props->color_present = true;
+ return ret;
+ }
+ props->color_present = true;
}
+ if (fwnode_property_present(fwnode, "function")) {
+ ret = fwnode_property_read_string(fwnode, "function",
+ &props->function);
+ if (ret) {
+ dev_err(dev,
+ "Error parsing 'function' property (%d)\n",
+ ret);
+ return ret;
+ }
+ }
- if (!fwnode_property_present(fwnode, "function"))
- return;
-
- ret = fwnode_property_read_string(fwnode, "function", &props->function);
- if (ret) {
- dev_err(dev,
- "Error parsing 'function' property (%d)\n",
- ret);
+ if (fwnode_property_present(fwnode, "function-enumerator")) {
+ ret = fwnode_property_read_u32(fwnode, "function-enumerator",
+ &props->func_enum);
+ if (ret) {
+ dev_err(dev,
+ "Error parsing 'function-enumerator' property (%d)\n",
+ ret);
+ return ret;
+ }
+ props->func_enum_present = true;
}
- if (!fwnode_property_present(fwnode, "function-enumerator"))
- return;
+ if (fwnode_property_present(fwnode, "default-state")) {
+ ret = fwnode_property_read_string(fwnode, "default-state",
+ &props->default_state);
+ if (ret) {
+ dev_err(dev,
+ "Error parsing 'default-state' property (%d)\n",
+ ret);
+ return ret;
+ }
+ }
- ret = fwnode_property_read_u32(fwnode, "function-enumerator",
- &props->func_enum);
- if (ret) {
- dev_err(dev,
- "Error parsing 'function-enumerator' property (%d)\n",
- ret);
- } else {
- props->func_enum_present = true;
+ if (fwnode_property_present(fwnode, "linux,default-trigger")) {
+ ret = fwnode_property_read_string(fwnode,
+ "linux,default-trigger",
+ &props->default_trigger);
+ if (ret)
+ dev_err(dev,
+ "Error parsing 'linux,default-trigger' property (%d)\n",
+ ret);
}
+ return ret;
}
+EXPORT_SYMBOL_GPL(led_parse_fwnode_props);
int led_compose_name(struct device *dev, struct led_init_data *init_data,
- char *led_classdev_name)
+ struct led_properties *props, char *led_classdev_name)
{
- struct led_properties props = {};
- struct fwnode_handle *fwnode = init_data->fwnode;
const char *devicename = init_data->devicename;
if (!led_classdev_name)
return -EINVAL;
- led_parse_fwnode_props(dev, fwnode, &props);
-
- if (props.label) {
+ if (props->label) {
/*
* If init_data.devicename is NULL, then it indicates that
* DT label should be used as-is for LED class device name.
@@ -436,23 +592,23 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data,
* the final LED class device name.
*/
if (!devicename) {
- strscpy(led_classdev_name, props.label,
+ strscpy(led_classdev_name, props->label,
LED_MAX_NAME_SIZE);
} else {
snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
- devicename, props.label);
+ devicename, props->label);
}
- } else if (props.function || props.color_present) {
+ } else if (props->function || props->color_present) {
char tmp_buf[LED_MAX_NAME_SIZE];
- if (props.func_enum_present) {
+ if (props->func_enum_present) {
snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-%d",
- props.color_present ? led_colors[props.color] : "",
- props.function ?: "", props.func_enum);
+ props->color_present ? led_colors[props->color] : "",
+ props->function ?: "", props->func_enum);
} else {
snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
- props.color_present ? led_colors[props.color] : "",
- props.function ?: "");
+ props->color_present ? led_colors[props->color] : "",
+ props->function ?: "");
}
if (init_data->devname_mandatory) {
snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
@@ -468,11 +624,19 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data,
}
snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
devicename, init_data->default_label);
- } else if (is_of_node(fwnode)) {
- strscpy(led_classdev_name, to_of_node(fwnode)->name,
- LED_MAX_NAME_SIZE);
- } else
- return -EINVAL;
+ } else {
+ struct fwnode_handle *fwnode = led_get_fwnode(dev, init_data);
+ int ret = -EINVAL;
+
+ if (is_of_node(fwnode)) {
+ ret = 0;
+ strscpy(led_classdev_name, to_of_node(fwnode)->name,
+ LED_MAX_NAME_SIZE);
+ }
+ fwnode_handle_put(fwnode);
+
+ return ret;
+ }
return 0;
}
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 242258f7d837..af8ca4291d7d 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -13,6 +13,7 @@
#include <linux/kernfs.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/property.h>
#include <linux/rwsem.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
@@ -20,6 +21,7 @@
struct device;
struct led_pattern;
+struct led_classdev;
/*
* LED Core
*/
@@ -31,8 +33,47 @@ enum led_brightness {
LED_FULL = 255,
};
+struct led_properties {
+ u32 color;
+ bool color_present;
+ const char *function;
+ u32 func_enum;
+ bool func_enum_present;
+ const char *label;
+ const char *default_trigger;
+ const char *default_state;
+ bool brightness_keep;
+};
+
+struct led_fw_match_property {
+ const char *name; /* Name of the property to match */
+ void *raw_val; /* Raw property value as present in fwnode */
+ void *intval; /* Property value if 8,16,32 or 64bit integer */
+ size_t size; /* Size of value in bytes */
+};
+
struct led_init_data {
- /* device fwnode handle */
+ /*
+ * If DT binding dictates the node name the driver can fill of_match
+ * corresponding to node name describing this LED. If fwnode is given
+ * the match is searched from it's child nodes. If not, the match is
+ * searched from device's own child nodes.
+ */
+ const char *of_match;
+ /*
+ * If fwnode contains property with known value the driver can specify
+ * correct propertty-value pair here to do the matching. This has higher
+ * priority than of_match. If fwnode is given the match is searched
+ * from it's child nodes. If not match is searched from device's
+ * own child nodes.
+ */
+ struct led_fw_match_property match_property;
+ /*
+ * device fwnode handle. If of_match or led_compatible are not given
+ * this is used for LED as given. If of_match or led_compatible are
+ * given then this is used as a parent node whose child nodes are
+ * scanned for given match.
+ */
struct fwnode_handle *fwnode;
/*
* default <color:function> tuple, for backward compatibility
@@ -53,9 +94,19 @@ struct led_init_data {
* set it to true
*/
bool devname_mandatory;
+ /*
+ * Callback which a LED driver can register if it has own non-standard
+ * DT properties. Core calls this with the located DT node during
+ * class_device registration
+ */
+ int (*of_parse_cb)(struct led_classdev *ld, struct fwnode_handle *fw,
+ struct led_properties *props);
+ /* LED core should parse and handle the common firmware properties */
+ bool parse_fwnode;
};
struct led_classdev {
+ struct led_init_data *init_data;
const char *name;
enum led_brightness brightness;
enum led_brightness max_brightness;
@@ -148,6 +199,7 @@ struct led_classdev {
/* Ensures consistent access to the LED Flash Class device */
struct mutex led_access;
+ bool fwnode_owned;
};
/**
@@ -299,6 +351,7 @@ void led_sysfs_enable(struct led_classdev *led_cdev);
* led_compose_name - compose LED class device name
* @dev: LED controller device object
* @init_data: the LED class device initialization data
+ * @props: LED properties as parsed from fwnode.
* @led_classdev_name: composed LED class device name
*
* Create LED class device name basing on the provided init_data argument.
@@ -308,7 +361,8 @@ void led_sysfs_enable(struct led_classdev *led_cdev);
* Returns: 0 on success or negative error value on failure
*/
int led_compose_name(struct device *dev, struct led_init_data *init_data,
- char *led_classdev_name);
+ struct led_properties *props,
+ char *led_classdev_name);
/**
* led_sysfs_is_disabled - check if LED sysfs interface is disabled
@@ -321,6 +375,33 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev)
return led_cdev->flags & LED_SYSFS_DISABLE;
}
+/**
+ * led_get_fwnode - find fwnode matching given LED init data
+ * @parent: LED controller device this LED is driven by
+ * @init_data: the LED class device initialization data
+ *
+ * Find the fw node matching given LED init data.
+ * NOTE: Function increases refcount for found node. Caller must decrease
+ * refcount using fwnode_handle_put when finished with node.
+ *
+ * Returns: node handle or NULL if matching fw node was not found
+ */
+struct fwnode_handle *led_get_fwnode(struct device *parent,
+ struct led_init_data *init_data);
+
+/**
+ * led_parse_fwnode_props - parse LED common properties from fwnode
+ * @dev: pointer to LED device.
+ * @fwnode: LED node containing the properties
+ * @props: structure where found property data is stored.
+ *
+ * Parse the common LED properties from fwnode.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int led_parse_fwnode_props(struct device *dev, struct fwnode_handle *fwnode,
+ struct led_properties *props);
+
/*
* LED Triggers
*/
@@ -478,15 +559,6 @@ struct led_platform_data {
struct led_info *leds;
};
-struct led_properties {
- u32 color;
- bool color_present;
- const char *function;
- u32 func_enum;
- bool func_enum_present;
- const char *label;
-};
-
struct gpio_desc;
typedef int (*gpio_blink_set_t)(struct gpio_desc *desc, int state,
unsigned long *delay_on,
--
2.21.0
--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox