* Re: Re: [PATCH 6/7] regulator: AXP20x: Add support for regulators subsystem
From: Mark Brown @ 2014-03-11 19:29 UTC (permalink / raw)
To: Carlo Caione
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w
In-Reply-To: <20140311192411.GA4193-bi+AKbBUZKZeIdyRz4JgOMwOAu8XWILU@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 927 bytes --]
On Tue, Mar 11, 2014 at 08:24:11PM +0100, Carlo Caione wrote:
> On Mon, Mar 03, 2014 at 10:56:16AM +0900, Mark Brown wrote:
> > > + regulators = of_find_node_by_name(np, "regulators");
> > > + if (!regulators) {
> > > + dev_err(&pdev->dev, "regulators node not found\n");
> > > + return -EINVAL;
> > > + }
> > The driver should be able to start up with no configuration provided at
> > all except for the device being registered - the user won't be able to
> > change anything but they will be able to read the current state of the
> > hardware which can be useful for diagnostics.
> If the device is DT enabled and no configuration is provided for
> regulators, these are disabled according to:
> http://lxr.linux.no/linux+v3.13.5/drivers/regulator/core.c#L3797
> Am I missing something or do you have any pointer?
With the above code the driver will return an error and never get as far
as registering the regulator.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: Re: [PATCH 6/7] regulator: AXP20x: Add support for regulators subsystem
From: Carlo Caione @ 2014-03-11 21:06 UTC (permalink / raw)
To: Mark Brown
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w
In-Reply-To: <20140311192940.GA28112-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
On Tue, Mar 11, 2014 at 07:29:40PM +0000, Mark Brown wrote:
> On Tue, Mar 11, 2014 at 08:24:11PM +0100, Carlo Caione wrote:
> > On Mon, Mar 03, 2014 at 10:56:16AM +0900, Mark Brown wrote:
>
> > > > + regulators = of_find_node_by_name(np, "regulators");
> > > > + if (!regulators) {
> > > > + dev_err(&pdev->dev, "regulators node not found\n");
> > > > + return -EINVAL;
> > > > + }
>
> > > The driver should be able to start up with no configuration provided at
> > > all except for the device being registered - the user won't be able to
> > > change anything but they will be able to read the current state of the
> > > hardware which can be useful for diagnostics.
>
> > If the device is DT enabled and no configuration is provided for
> > regulators, these are disabled according to:
>
> > http://lxr.linux.no/linux+v3.13.5/drivers/regulator/core.c#L3797
>
> > Am I missing something or do you have any pointer?
>
> With the above code the driver will return an error and never get as far
> as registering the regulator.
I agree, but if I register the regulators without having the constrains
defined in the DT, when regulator_init_complete() is
called, the regulators are disabled powering off the SoC (at least for
DCDC2 and DCDC3).
Thanks,
--
Carlo Caione
^ permalink raw reply
* UOB-X1H: Message..
From: Cham Tao Soon @ 2014-03-11 22:38 UTC (permalink / raw)
I have a proposal for you.
^ permalink raw reply
* Re: Re: [PATCH 6/7] regulator: AXP20x: Add support for regulators subsystem
From: Mark Brown @ 2014-03-12 0:38 UTC (permalink / raw)
To: Carlo Caione
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w
In-Reply-To: <20140311210659.GA10961-bi+AKbBUZKZeIdyRz4JgOMwOAu8XWILU@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 603 bytes --]
On Tue, Mar 11, 2014 at 10:06:59PM +0100, Carlo Caione wrote:
> On Tue, Mar 11, 2014 at 07:29:40PM +0000, Mark Brown wrote:
> > With the above code the driver will return an error and never get as far
> > as registering the regulator.
> I agree, but if I register the regulators without having the constrains
> defined in the DT, when regulator_init_complete() is
> called, the regulators are disabled powering off the SoC (at least for
> DCDC2 and DCDC3).
Right, but the kernel will say what it's doing before it does so and
then the user can provide the appropriate setup in DT to force things
on.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: Qualcomm PMIC8XXX keypad never worked?
From: Bjorn Andersson @ 2014-03-12 5:18 UTC (permalink / raw)
To: Lee Jones, Stephen Boyd, Arnd Bergmann
Cc: Trilok Soni, Samuel Ortiz, Dmitry Torokhov,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, linux-input, Anirudh Ghayal
In-Reply-To: <20140311130609.GL21216@lee--X1>
On Tue, Mar 11, 2014 at 6:06 AM, Lee Jones <lee.jones@linaro.org> wrote:
[snip]
>
> ... but the MFD isn't broken, so I guess your solution is the most
> elegant. If it's not fixed soon, I guess we should think about ripping
> it out. It's obvoious that no-one cares, as it's been broken for
> nearly 3 years.
Stephen Boyd have a series out on these drivers; fixing this, turning
them DT and some other goodies.
The related part for this would be:
https://patchwork.kernel.org/patch/3765191/
Unfortunately lkml.org seems down, so I can't link to the series.
Regards,
Bjorn
^ permalink raw reply
* Re: [PATCH] Input: fix dependencies of OLPC AP-SP driver
From: Dmitry Torokhov @ 2014-03-12 6:11 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-input
In-Reply-To: <20140311194253.23daf5e3@endymion.delvare>
Hi Jean,
On Tue, Mar 11, 2014 at 07:42:53PM +0100, Jean Delvare wrote:
> I don't think the OLPC AP-SP driver is generally useful without OLPC
> support. So make it depend on OLPC, unless build testing is enabled.
>
> Signed-off-by: Jean Delvare <jdelvare@suse.de>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> ---
> drivers/input/serio/Kconfig | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> --- linux-3.14-rc6.orig/drivers/input/serio/Kconfig 2014-02-09 16:53:02.931633818 +0100
> +++ linux-3.14-rc6/drivers/input/serio/Kconfig 2014-03-11 18:52:42.280541110 +0100
> @@ -263,7 +263,7 @@ config SERIO_APBPS2
>
> config SERIO_OLPC_APSP
> tristate "OLPC AP-SP input support"
> - depends on OF
> + depends on OLPC || (OF && COMPILE_TEST)
Actually for compile coverage we do not actually need OF, I'll make it
depends on OLPC || COMPILE_TEST
instead.
> help
> Say Y here if you want support for the keyboard and touchpad included
> in the OLPC XO-1.75 and XO-4 laptops.
>
>
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH] Input: fix dependencies of OLPC AP-SP driver
From: Jean Delvare @ 2014-03-12 13:04 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input
In-Reply-To: <20140312061134.GA2263@core.coreip.homeip.net>
Hi Dmitry,
On Tue, 11 Mar 2014 23:11:34 -0700, Dmitry Torokhov wrote:
> On Tue, Mar 11, 2014 at 07:42:53PM +0100, Jean Delvare wrote:
> > I don't think the OLPC AP-SP driver is generally useful without OLPC
> > support. So make it depend on OLPC, unless build testing is enabled.
> >
> > Signed-off-by: Jean Delvare <jdelvare@suse.de>
> > Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> > ---
> > drivers/input/serio/Kconfig | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > --- linux-3.14-rc6.orig/drivers/input/serio/Kconfig 2014-02-09 16:53:02.931633818 +0100
> > +++ linux-3.14-rc6/drivers/input/serio/Kconfig 2014-03-11 18:52:42.280541110 +0100
> > @@ -263,7 +263,7 @@ config SERIO_APBPS2
> >
> > config SERIO_OLPC_APSP
> > tristate "OLPC AP-SP input support"
> > - depends on OF
> > + depends on OLPC || (OF && COMPILE_TEST)
>
> Actually for compile coverage we do not actually need OF, I'll make it
>
> depends on OLPC || COMPILE_TEST
>
> instead.
Ah, you're right. Thanks for spotting it, your solution is easier and
cleaner :)
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply
* [PATCH v2 0/3] mfd: max8997: add regmap support
From: Robert Baldyga @ 2014-03-12 13:37 UTC (permalink / raw)
To: sameo, lee.jones
Cc: myungjoo.ham, cw00.choi, dmitry.torokhov, cooloney, rpurdie,
dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo, paul.gortmaker,
linux-kernel, linux-input, linux-leds, rtc-linux, m.szyprowski,
k.kozlowski, Robert Baldyga
Hi,
This patchset modifies max8997 driver and associated function drivers to use
register maps instead of operating directly on i2c bus. This change allowed
to simplify irq handling, and to move some initializations to individual
function drivers. Hence now when some functions are not enabled, their i2c
clients, regmaps and irqs are not registered.
Best regards
Robert Baldyga
Samsung R&D Institute Poland
Changelog:
v2:
- rebase patches on Lee Jones' MFD tree
- add missing selects in Kconfig
- add missing deinitializations
- add interrupt disabling when suspend
- few minor changes and typo fixes
v1: https://lkml.org/lkml/2014/3/11/291
Robert Baldyga (3):
mfd: max8997: use regmap to access registers
mfd: max8997: handle IRQs using regmap
mfd: max8997: move regmap handling to function drivers
drivers/extcon/extcon-max8997.c | 176 +++++++++++++---
drivers/input/misc/max8997_haptic.c | 121 +++++++++--
drivers/leds/leds-max8997.c | 13 +-
drivers/mfd/Kconfig | 3 +-
drivers/mfd/Makefile | 2 +-
drivers/mfd/max8997-irq.c | 387 -----------------------------------
drivers/mfd/max8997.c | 237 +++++++--------------
drivers/power/max8997_charger.c | 33 +--
drivers/regulator/max8997.c | 87 ++++----
drivers/rtc/rtc-max8997.c | 102 ++++++---
include/linux/mfd/max8997-private.h | 88 ++++++--
11 files changed, 528 insertions(+), 721 deletions(-)
delete mode 100644 drivers/mfd/max8997-irq.c
--
1.7.9.5
^ permalink raw reply
* [PATCH v2 1/3] mfd: max8997: use regmap to access registers
From: Robert Baldyga @ 2014-03-12 13:37 UTC (permalink / raw)
To: sameo, lee.jones
Cc: myungjoo.ham, cw00.choi, dmitry.torokhov, cooloney, rpurdie,
dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo, paul.gortmaker,
linux-kernel, linux-input, linux-leds, rtc-linux, m.szyprowski,
k.kozlowski, Robert Baldyga
In-Reply-To: <1394631466-6429-1-git-send-email-r.baldyga@samsung.com>
This patch modifies max8997 driver and each associated function driver,
to use regmap instead of operating directly on i2c bus. It will allow to
simplify IRQ handling using regmap-irq.
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
drivers/extcon/extcon-max8997.c | 31 ++++----
drivers/input/misc/max8997_haptic.c | 34 +++++----
drivers/leds/leds-max8997.c | 13 ++--
drivers/mfd/Kconfig | 1 +
drivers/mfd/max8997-irq.c | 64 +++++++---------
drivers/mfd/max8997.c | 141 ++++++++++++++++-------------------
drivers/power/max8997_charger.c | 33 ++++----
drivers/regulator/max8997.c | 87 +++++++++++----------
drivers/rtc/rtc-max8997.c | 56 +++++++-------
include/linux/mfd/max8997-private.h | 17 ++---
10 files changed, 228 insertions(+), 249 deletions(-)
diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
index 6a00464..f258c08 100644
--- a/drivers/extcon/extcon-max8997.c
+++ b/drivers/extcon/extcon-max8997.c
@@ -27,6 +27,7 @@
#include <linux/mfd/max8997-private.h>
#include <linux/extcon.h>
#include <linux/irqdomain.h>
+#include <linux/regmap.h>
#define DEV_NAME "max8997-muic"
#define DELAY_MS_DEFAULT 20000 /* unit: millisecond */
@@ -116,7 +117,7 @@ enum max8997_muic_charger_type {
struct max8997_muic_info {
struct device *dev;
- struct i2c_client *muic;
+ struct max8997_dev *max8997;
struct extcon_dev *edev;
int prev_cable_type;
int prev_chg_type;
@@ -190,10 +191,10 @@ static int max8997_muic_set_debounce_time(struct max8997_muic_info *info,
case ADC_DEBOUNCE_TIME_10MS:
case ADC_DEBOUNCE_TIME_25MS:
case ADC_DEBOUNCE_TIME_38_62MS:
- ret = max8997_update_reg(info->muic,
+ ret = regmap_update_bits(info->max8997->regmap_muic,
MAX8997_MUIC_REG_CONTROL3,
- time << CONTROL3_ADCDBSET_SHIFT,
- CONTROL3_ADCDBSET_MASK);
+ CONTROL3_ADCDBSET_MASK,
+ time << CONTROL3_ADCDBSET_SHIFT);
if (ret) {
dev_err(info->dev, "failed to set ADC debounce time\n");
return ret;
@@ -228,8 +229,8 @@ static int max8997_muic_set_path(struct max8997_muic_info *info,
else
ctrl1 = CONTROL1_SW_OPEN;
- ret = max8997_update_reg(info->muic,
- MAX8997_MUIC_REG_CONTROL1, ctrl1, COMP_SW_MASK);
+ ret = regmap_update_bits(info->max8997->regmap_muic,
+ MAX8997_MUIC_REG_CONTROL1, COMP_SW_MASK, ctrl1);
if (ret < 0) {
dev_err(info->dev, "failed to update MUIC register\n");
return ret;
@@ -240,9 +241,9 @@ static int max8997_muic_set_path(struct max8997_muic_info *info,
else
ctrl2 |= CONTROL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */
- ret = max8997_update_reg(info->muic,
- MAX8997_MUIC_REG_CONTROL2, ctrl2,
- CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK);
+ ret = regmap_update_bits(info->max8997->regmap_muic,
+ MAX8997_MUIC_REG_CONTROL2,
+ CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK, ctrl2);
if (ret < 0) {
dev_err(info->dev, "failed to update MUIC register\n");
return ret;
@@ -543,8 +544,8 @@ static void max8997_muic_irq_work(struct work_struct *work)
if (info->irq == muic_irqs[i].virq)
irq_type = muic_irqs[i].irq;
- ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1,
- 2, info->status);
+ ret = regmap_bulk_read(info->max8997->regmap_muic,
+ MAX8997_MUIC_REG_STATUS1, info->status, 2);
if (ret) {
dev_err(info->dev, "failed to read muic register\n");
mutex_unlock(&info->mutex);
@@ -605,8 +606,8 @@ static int max8997_muic_detect_dev(struct max8997_muic_info *info)
mutex_lock(&info->mutex);
/* Read STATUSx register to detect accessory */
- ret = max8997_bulk_read(info->muic,
- MAX8997_MUIC_REG_STATUS1, 2, info->status);
+ ret = regmap_bulk_read(info->max8997->regmap_muic,
+ MAX8997_MUIC_REG_STATUS1, info->status, 2);
if (ret) {
dev_err(info->dev, "failed to read MUIC register\n");
mutex_unlock(&info->mutex);
@@ -667,7 +668,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
}
info->dev = &pdev->dev;
- info->muic = max8997->muic;
+ info->max8997 = max8997;
platform_set_drvdata(pdev, info);
mutex_init(&info->mutex);
@@ -721,7 +722,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
/* Initialize registers according to platform data */
for (i = 0; i < muic_pdata->num_init_data; i++) {
- max8997_write_reg(info->muic,
+ regmap_write(info->max8997->regmap_muic,
muic_pdata->init_data[i].addr,
muic_pdata->init_data[i].data);
}
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
index 1fea548..d3f7079 100644
--- a/drivers/input/misc/max8997_haptic.c
+++ b/drivers/input/misc/max8997_haptic.c
@@ -31,6 +31,7 @@
#include <linux/mfd/max8997-private.h>
#include <linux/mfd/max8997.h>
#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
/* Haptic configuration 2 register */
#define MAX8997_MOTOR_TYPE_SHIFT 7
@@ -45,7 +46,7 @@
struct max8997_haptic {
struct device *dev;
- struct i2c_client *client;
+ struct max8997_dev *max8997;
struct input_dev *input_dev;
struct regulator *regulator;
@@ -86,19 +87,19 @@ static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
}
switch (chip->internal_mode_pattern) {
case 0:
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index);
break;
case 1:
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index);
break;
case 2:
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index);
break;
case 3:
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index);
break;
default:
@@ -115,50 +116,51 @@ static void max8997_haptic_configure(struct max8997_haptic *chip)
value = chip->type << MAX8997_MOTOR_TYPE_SHIFT |
chip->enabled << MAX8997_ENABLE_SHIFT |
chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor;
- max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value);
+ regmap_write(chip->max8997->regmap_haptic,
+ MAX8997_HAPTIC_REG_CONF2, value);
if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) {
value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT |
chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT |
chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT |
chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT;
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_DRVCONF, value);
switch (chip->internal_mode_pattern) {
case 0:
value = chip->pattern_cycle << 4;
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_CYCLECONF1, value);
value = chip->pattern_signal_period;
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_SIGCONF1, value);
break;
case 1:
value = chip->pattern_cycle;
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_CYCLECONF1, value);
value = chip->pattern_signal_period;
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_SIGCONF2, value);
break;
case 2:
value = chip->pattern_cycle << 4;
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_CYCLECONF2, value);
value = chip->pattern_signal_period;
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_SIGCONF3, value);
break;
case 3:
value = chip->pattern_cycle;
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_CYCLECONF2, value);
value = chip->pattern_signal_period;
- max8997_write_reg(chip->client,
+ regmap_write(chip->max8997->regmap_haptic,
MAX8997_HAPTIC_REG_SIGCONF4, value);
break;
@@ -267,7 +269,7 @@ static int max8997_haptic_probe(struct platform_device *pdev)
INIT_WORK(&chip->work, max8997_haptic_play_effect_work);
mutex_init(&chip->mutex);
- chip->client = iodev->haptic;
+ chip->max8997 = iodev;
chip->dev = &pdev->dev;
chip->input_dev = input_dev;
chip->pwm_period = haptic_pdata->pwm_period;
diff --git a/drivers/leds/leds-max8997.c b/drivers/leds/leds-max8997.c
index f449a8b..e2da8f2 100644
--- a/drivers/leds/leds-max8997.c
+++ b/drivers/leds/leds-max8997.c
@@ -18,6 +18,7 @@
#include <linux/mfd/max8997.h>
#include <linux/mfd/max8997-private.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#define MAX8997_LED_FLASH_SHIFT 3
#define MAX8997_LED_FLASH_CUR_MASK 0xf8
@@ -53,7 +54,6 @@ static void max8997_led_set_mode(struct max8997_led *led,
enum max8997_led_mode mode)
{
int ret;
- struct i2c_client *client = led->iodev->i2c;
u8 mask = 0, val;
switch (mode) {
@@ -89,8 +89,8 @@ static void max8997_led_set_mode(struct max8997_led *led,
}
if (mask) {
- ret = max8997_update_reg(client, MAX8997_REG_LEN_CNTL, val,
- mask);
+ ret = regmap_update_bits(led->iodev->regmap,
+ MAX8997_REG_LEN_CNTL, mask, val);
if (ret)
dev_err(led->iodev->dev,
"failed to update register(%d)\n", ret);
@@ -102,7 +102,6 @@ static void max8997_led_set_mode(struct max8997_led *led,
static void max8997_led_enable(struct max8997_led *led, bool enable)
{
int ret;
- struct i2c_client *client = led->iodev->i2c;
u8 val = 0, mask = MAX8997_LED_BOOST_ENABLE_MASK;
if (led->enabled == enable)
@@ -110,7 +109,8 @@ static void max8997_led_enable(struct max8997_led *led, bool enable)
val = enable ? MAX8997_LED_BOOST_ENABLE_MASK : 0;
- ret = max8997_update_reg(client, MAX8997_REG_BOOST_CNTL, val, mask);
+ ret = regmap_update_bits(led->iodev->regmap,
+ MAX8997_REG_BOOST_CNTL, mask, val);
if (ret)
dev_err(led->iodev->dev,
"failed to update register(%d)\n", ret);
@@ -122,7 +122,6 @@ static void max8997_led_set_current(struct max8997_led *led,
enum led_brightness value)
{
int ret;
- struct i2c_client *client = led->iodev->i2c;
u8 val = 0, mask = 0, reg = 0;
switch (led->led_mode) {
@@ -143,7 +142,7 @@ static void max8997_led_set_current(struct max8997_led *led,
}
if (mask) {
- ret = max8997_update_reg(client, reg, val, mask);
+ ret = regmap_update_bits(led->iodev->regmap, reg, mask, val);
if (ret)
dev_err(led->iodev->dev,
"failed to update register(%d)\n", ret);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 456a7e7..2871a65 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -387,6 +387,7 @@ config MFD_MAX8997
bool "Maxim Semiconductor MAX8997/8966 PMIC Support"
depends on I2C=y
select MFD_CORE
+ select REGMAP_I2C
select IRQ_DOMAIN
help
Say yes here to add support for Maxim Semiconductor MAX8997/8966.
diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c
index 43fa614..0e7ff39 100644
--- a/drivers/mfd/max8997-irq.c
+++ b/drivers/mfd/max8997-irq.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/mfd/max8997.h>
#include <linux/mfd/max8997-private.h>
+#include <linux/regmap.h>
static const u8 max8997_mask_reg[] = {
[PMIC_INT1] = MAX8997_REG_INT1MSK,
@@ -41,25 +42,6 @@ static const u8 max8997_mask_reg[] = {
[FLASH_STATUS] = MAX8997_REG_INVALID,
};
-static struct i2c_client *get_i2c(struct max8997_dev *max8997,
- enum max8997_irq_source src)
-{
- switch (src) {
- case PMIC_INT1 ... PMIC_INT4:
- return max8997->i2c;
- case FUEL_GAUGE:
- return NULL;
- case MUIC_INT1 ... MUIC_INT3:
- return max8997->muic;
- case GPIO_LOW ... GPIO_HI:
- return max8997->i2c;
- case FLASH_STATUS:
- return max8997->i2c;
- default:
- return ERR_PTR(-EINVAL);
- }
-}
-
struct max8997_irq_data {
int mask;
enum max8997_irq_source group;
@@ -124,15 +106,20 @@ static void max8997_irq_sync_unlock(struct irq_data *data)
int i;
for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
+ struct regmap *map;
u8 mask_reg = max8997_mask_reg[i];
- struct i2c_client *i2c = get_i2c(max8997, i);
+
+ if (i >= MUIC_INT1 && i <= MUIC_INT3)
+ map = max8997->regmap_muic;
+ else
+ map = max8997->regmap;
if (mask_reg == MAX8997_REG_INVALID ||
- IS_ERR_OR_NULL(i2c))
+ IS_ERR_OR_NULL(map))
continue;
max8997->irq_masks_cache[i] = max8997->irq_masks_cur[i];
- max8997_write_reg(i2c, max8997_mask_reg[i],
+ regmap_write(map, max8997_mask_reg[i],
max8997->irq_masks_cur[i]);
}
@@ -181,11 +168,11 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
{
struct max8997_dev *max8997 = data;
u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {};
- u8 irq_src;
+ unsigned int irq_src;
int ret;
int i, cur_irq;
- ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src);
+ ret = regmap_read(max8997->regmap, MAX8997_REG_INTSRC, &irq_src);
if (ret < 0) {
dev_err(max8997->dev, "Failed to read interrupt source: %d\n",
ret);
@@ -194,8 +181,8 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
if (irq_src & MAX8997_IRQSRC_PMIC) {
/* PMIC INT1 ~ INT4 */
- max8997_bulk_read(max8997->i2c, MAX8997_REG_INT1, 4,
- &irq_reg[PMIC_INT1]);
+ regmap_bulk_read(max8997->regmap, MAX8997_REG_INT1,
+ &irq_reg[PMIC_INT1], 4);
}
if (irq_src & MAX8997_IRQSRC_FUELGAUGE) {
/*
@@ -215,8 +202,8 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
}
if (irq_src & MAX8997_IRQSRC_MUIC) {
/* MUIC INT1 ~ INT3 */
- max8997_bulk_read(max8997->muic, MAX8997_MUIC_REG_INT1, 3,
- &irq_reg[MUIC_INT1]);
+ regmap_bulk_read(max8997->regmap_muic, MAX8997_MUIC_REG_INT1,
+ &irq_reg[MUIC_INT1], 3);
}
if (irq_src & MAX8997_IRQSRC_GPIO) {
/* GPIO Interrupt */
@@ -225,8 +212,8 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
irq_reg[GPIO_LOW] = 0;
irq_reg[GPIO_HI] = 0;
- max8997_bulk_read(max8997->i2c, MAX8997_REG_GPIOCNTL1,
- MAX8997_NUM_GPIO, gpio_info);
+ regmap_bulk_read(max8997->regmap, MAX8997_REG_GPIOCNTL1,
+ gpio_info, MAX8997_NUM_GPIO);
for (i = 0; i < MAX8997_NUM_GPIO; i++) {
bool interrupt = false;
@@ -260,8 +247,10 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
}
if (irq_src & MAX8997_IRQSRC_FLASH) {
/* Flash Status Interrupt */
- ret = max8997_read_reg(max8997->i2c, MAX8997_REG_FLASHSTATUS,
- &irq_reg[FLASH_STATUS]);
+ unsigned int data;
+ ret = regmap_read(max8997->regmap,
+ MAX8997_REG_FLASHSTATUS, &data);
+ irq_reg[FLASH_STATUS] = data;
}
/* Apply masking */
@@ -312,7 +301,7 @@ int max8997_irq_init(struct max8997_dev *max8997)
struct irq_domain *domain;
int i;
int ret;
- u8 val;
+ unsigned int val;
if (!max8997->irq) {
dev_warn(max8997->dev, "No interrupt specified.\n");
@@ -323,22 +312,19 @@ int max8997_irq_init(struct max8997_dev *max8997)
/* Mask individual interrupt sources */
for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
- struct i2c_client *i2c;
-
max8997->irq_masks_cur[i] = 0xff;
max8997->irq_masks_cache[i] = 0xff;
- i2c = get_i2c(max8997, i);
- if (IS_ERR_OR_NULL(i2c))
+ if (IS_ERR_OR_NULL(max8997->regmap))
continue;
if (max8997_mask_reg[i] == MAX8997_REG_INVALID)
continue;
- max8997_write_reg(i2c, max8997_mask_reg[i], 0xff);
+ regmap_write(max8997->regmap, max8997_mask_reg[i], 0xff);
}
for (i = 0; i < MAX8997_NUM_GPIO; i++) {
- max8997->gpio_status[i] = (max8997_read_reg(max8997->i2c,
+ max8997->gpio_status[i] = (regmap_read(max8997->regmap,
MAX8997_REG_GPIOCNTL1 + i,
&val)
& MAX8997_GPIO_DATA_MASK) ?
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 8cf7a01..782d7c9 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -33,6 +33,7 @@
#include <linux/mfd/core.h>
#include <linux/mfd/max8997.h>
#include <linux/mfd/max8997-private.h>
+#include <linux/regmap.h>
#define I2C_ADDR_PMIC (0xCC >> 1)
#define I2C_ADDR_MUIC (0x4A >> 1)
@@ -57,81 +58,29 @@ static struct of_device_id max8997_pmic_dt_match[] = {
};
#endif
-int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
-{
- struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
- int ret;
-
- mutex_lock(&max8997->iolock);
- ret = i2c_smbus_read_byte_data(i2c, reg);
- mutex_unlock(&max8997->iolock);
- if (ret < 0)
- return ret;
-
- ret &= 0xff;
- *dest = ret;
- return 0;
-}
-EXPORT_SYMBOL_GPL(max8997_read_reg);
-
-int max8997_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
-{
- struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
- int ret;
-
- mutex_lock(&max8997->iolock);
- ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
- mutex_unlock(&max8997->iolock);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(max8997_bulk_read);
-
-int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
-{
- struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
- int ret;
-
- mutex_lock(&max8997->iolock);
- ret = i2c_smbus_write_byte_data(i2c, reg, value);
- mutex_unlock(&max8997->iolock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(max8997_write_reg);
-
-int max8997_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
-{
- struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
- int ret;
+static const struct regmap_config max8997_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX8997_REG_PMIC_END,
+};
- mutex_lock(&max8997->iolock);
- ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
- mutex_unlock(&max8997->iolock);
- if (ret < 0)
- return ret;
+static const struct regmap_config max8997_regmap_rtc_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX8997_RTC_REG_END,
+};
- return 0;
-}
-EXPORT_SYMBOL_GPL(max8997_bulk_write);
+static const struct regmap_config max8997_regmap_haptic_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX8997_HAPTIC_REG_END,
+};
-int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
-{
- struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
- int ret;
-
- mutex_lock(&max8997->iolock);
- ret = i2c_smbus_read_byte_data(i2c, reg);
- if (ret >= 0) {
- u8 old_val = ret & 0xff;
- u8 new_val = (val & mask) | (old_val & (~mask));
- ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
- }
- mutex_unlock(&max8997->iolock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(max8997_update_reg);
+static const struct regmap_config max8997_regmap_muic_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX8997_MUIC_REG_END,
+};
/*
* Only the common platform data elements for max8997 are parsed here from the
@@ -230,6 +179,41 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
}
i2c_set_clientdata(max8997->muic, max8997);
+ max8997->regmap = devm_regmap_init_i2c(i2c, &max8997_regmap_config);
+ if (IS_ERR(max8997->regmap)) {
+ ret = PTR_ERR(max8997->regmap);
+ dev_err(max8997->dev,
+ "failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ max8997->regmap_rtc = devm_regmap_init_i2c(max8997->rtc,
+ &max8997_regmap_rtc_config);
+ if (IS_ERR(max8997->regmap_rtc)) {
+ ret = PTR_ERR(max8997->regmap_rtc);
+ dev_err(max8997->dev,
+ "failed to allocate register map: %d\n", ret);
+ goto err_regmap;
+ }
+
+ max8997->regmap_haptic = devm_regmap_init_i2c(max8997->haptic,
+ &max8997_regmap_haptic_config);
+ if (IS_ERR(max8997->regmap_haptic)) {
+ ret = PTR_ERR(max8997->regmap_haptic);
+ dev_err(max8997->dev,
+ "failed to allocate register map: %d\n", ret);
+ goto err_regmap;
+ }
+
+ max8997->regmap_muic = devm_regmap_init_i2c(max8997->muic,
+ &max8997_regmap_muic_config);
+ if (IS_ERR(max8997->regmap_muic)) {
+ ret = PTR_ERR(max8997->regmap_muic);
+ dev_err(max8997->dev,
+ "failed to allocate register map: %d\n", ret);
+ goto err_regmap;
+ }
+
pm_runtime_set_active(max8997->dev);
max8997_irq_init(max8997);
@@ -254,6 +238,7 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
err_mfd:
mfd_remove_devices(max8997->dev);
+err_regmap:
i2c_unregister_device(max8997->muic);
err_i2c_muic:
i2c_unregister_device(max8997->haptic);
@@ -441,15 +426,15 @@ static int max8997_freeze(struct device *dev)
int i;
for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++)
- max8997_read_reg(i2c, max8997_dumpaddr_pmic[i],
+ regmap_read(max8997->regmap, max8997_dumpaddr_pmic[i],
&max8997->reg_dump[i]);
for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++)
- max8997_read_reg(i2c, max8997_dumpaddr_muic[i],
+ regmap_read(max8997->regmap_muic, max8997_dumpaddr_muic[i],
&max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++)
- max8997_read_reg(i2c, max8997_dumpaddr_haptic[i],
+ regmap_read(max8997->regmap_haptic, max8997_dumpaddr_haptic[i],
&max8997->reg_dump[i + MAX8997_REG_PMIC_END +
MAX8997_MUIC_REG_END]);
@@ -463,15 +448,15 @@ static int max8997_restore(struct device *dev)
int i;
for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++)
- max8997_write_reg(i2c, max8997_dumpaddr_pmic[i],
+ regmap_write(max8997->regmap, max8997_dumpaddr_pmic[i],
max8997->reg_dump[i]);
for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++)
- max8997_write_reg(i2c, max8997_dumpaddr_muic[i],
+ regmap_write(max8997->regmap_muic, max8997_dumpaddr_muic[i],
max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++)
- max8997_write_reg(i2c, max8997_dumpaddr_haptic[i],
+ regmap_write(max8997->regmap_haptic, max8997_dumpaddr_haptic[i],
max8997->reg_dump[i + MAX8997_REG_PMIC_END +
MAX8997_MUIC_REG_END]);
diff --git a/drivers/power/max8997_charger.c b/drivers/power/max8997_charger.c
index 4bdedfe..43d8481 100644
--- a/drivers/power/max8997_charger.c
+++ b/drivers/power/max8997_charger.c
@@ -26,6 +26,7 @@
#include <linux/power_supply.h>
#include <linux/mfd/max8997.h>
#include <linux/mfd/max8997-private.h>
+#include <linux/regmap.h>
struct charger_data {
struct device *dev;
@@ -46,14 +47,14 @@ static int max8997_battery_get_property(struct power_supply *psy,
{
struct charger_data *charger = container_of(psy,
struct charger_data, battery);
- struct i2c_client *i2c = charger->iodev->i2c;
int ret;
- u8 reg;
+ unsigned int reg;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = 0;
- ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, ®);
+ ret = regmap_read(charger->iodev->regmap,
+ MAX8997_REG_STATUS4, ®);
if (ret)
return ret;
if ((reg & (1 << 0)) == 0x1)
@@ -62,7 +63,8 @@ static int max8997_battery_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 0;
- ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, ®);
+ ret = regmap_read(charger->iodev->regmap,
+ MAX8997_REG_STATUS4, ®);
if (ret)
return ret;
if ((reg & (1 << 2)) == 0x0)
@@ -71,7 +73,8 @@ static int max8997_battery_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = 0;
- ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, ®);
+ ret = regmap_read(charger->iodev->regmap,
+ MAX8997_REG_STATUS4, ®);
if (ret)
return ret;
/* DCINOK */
@@ -103,8 +106,8 @@ static int max8997_battery_probe(struct platform_device *pdev)
if (val > 0xf)
val = 0xf;
- ret = max8997_update_reg(iodev->i2c,
- MAX8997_REG_MBCCTRL5, val, 0xf);
+ ret = regmap_update_bits(iodev->regmap,
+ MAX8997_REG_MBCCTRL5, 0xf, val);
if (ret < 0) {
dev_err(&pdev->dev, "Cannot use i2c bus.\n");
return ret;
@@ -113,20 +116,20 @@ static int max8997_battery_probe(struct platform_device *pdev)
switch (pdata->timeout) {
case 5:
- ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
- 0x2 << 4, 0x7 << 4);
+ ret = regmap_update_bits(iodev->regmap,
+ MAX8997_REG_MBCCTRL1, 0x7 << 4, 0x2 << 4);
break;
case 6:
- ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
- 0x3 << 4, 0x7 << 4);
+ ret = regmap_update_bits(iodev->regmap,
+ MAX8997_REG_MBCCTRL1, 0x7 << 4, 0x3 << 4);
break;
case 7:
- ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
- 0x4 << 4, 0x7 << 4);
+ ret = regmap_update_bits(iodev->regmap,
+ MAX8997_REG_MBCCTRL1, 0x7 << 4, 0x4 << 4);
break;
case 0:
- ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
- 0x7 << 4, 0x7 << 4);
+ ret = regmap_update_bits(iodev->regmap,
+ MAX8997_REG_MBCCTRL1, 0x7 << 4, 0x7 << 4);
break;
default:
dev_err(&pdev->dev, "incorrect timeout value (%d)\n",
diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c
index 2d618fc..4001ed7 100644
--- a/drivers/regulator/max8997.c
+++ b/drivers/regulator/max8997.c
@@ -33,6 +33,7 @@
#include <linux/mfd/max8997.h>
#include <linux/mfd/max8997-private.h>
#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
struct max8997_data {
struct device *dev;
@@ -51,7 +52,7 @@ struct max8997_data {
int buck125_gpioindex;
bool ignore_gpiodvs_side_effect;
- u8 saved_states[MAX8997_REG_MAX];
+ unsigned int saved_states[MAX8997_REG_MAX];
};
static const unsigned int safeoutvolt[] = {
@@ -258,15 +259,14 @@ static int max8997_get_enable_register(struct regulator_dev *rdev,
static int max8997_reg_is_enabled(struct regulator_dev *rdev)
{
struct max8997_data *max8997 = rdev_get_drvdata(rdev);
- struct i2c_client *i2c = max8997->iodev->i2c;
int ret, reg, mask, pattern;
- u8 val;
+ unsigned int val;
ret = max8997_get_enable_register(rdev, ®, &mask, &pattern);
if (ret)
return ret;
- ret = max8997_read_reg(i2c, reg, &val);
+ ret = regmap_read(max8997->iodev->regmap, reg, &val);
if (ret)
return ret;
@@ -276,27 +276,25 @@ static int max8997_reg_is_enabled(struct regulator_dev *rdev)
static int max8997_reg_enable(struct regulator_dev *rdev)
{
struct max8997_data *max8997 = rdev_get_drvdata(rdev);
- struct i2c_client *i2c = max8997->iodev->i2c;
int ret, reg, mask, pattern;
ret = max8997_get_enable_register(rdev, ®, &mask, &pattern);
if (ret)
return ret;
- return max8997_update_reg(i2c, reg, pattern, mask);
+ return regmap_update_bits(max8997->iodev->regmap, reg, mask, pattern);
}
static int max8997_reg_disable(struct regulator_dev *rdev)
{
struct max8997_data *max8997 = rdev_get_drvdata(rdev);
- struct i2c_client *i2c = max8997->iodev->i2c;
int ret, reg, mask, pattern;
ret = max8997_get_enable_register(rdev, ®, &mask, &pattern);
if (ret)
return ret;
- return max8997_update_reg(i2c, reg, ~pattern, mask);
+ return regmap_update_bits(max8997->iodev->regmap, reg, mask, ~pattern);
}
static int max8997_get_voltage_register(struct regulator_dev *rdev,
@@ -368,15 +366,14 @@ static int max8997_get_voltage_register(struct regulator_dev *rdev,
static int max8997_get_voltage_sel(struct regulator_dev *rdev)
{
struct max8997_data *max8997 = rdev_get_drvdata(rdev);
- struct i2c_client *i2c = max8997->iodev->i2c;
int reg, shift, mask, ret;
- u8 val;
+ unsigned int val;
ret = max8997_get_voltage_register(rdev, ®, &shift, &mask);
if (ret)
return ret;
- ret = max8997_read_reg(i2c, reg, &val);
+ ret = regmap_read(max8997->iodev->regmap, reg, &val);
if (ret)
return ret;
@@ -413,7 +410,6 @@ static int max8997_set_voltage_charger_cv(struct regulator_dev *rdev,
int min_uV, int max_uV, unsigned *selector)
{
struct max8997_data *max8997 = rdev_get_drvdata(rdev);
- struct i2c_client *i2c = max8997->iodev->i2c;
int rid = rdev_get_id(rdev);
int lb, ub;
int reg, shift = 0, mask, ret = 0;
@@ -455,7 +451,8 @@ static int max8997_set_voltage_charger_cv(struct regulator_dev *rdev,
*selector = val;
- ret = max8997_update_reg(i2c, reg, val << shift, mask);
+ ret = regmap_update_bits(max8997->iodev->regmap,
+ reg, mask, val << shift);
return ret;
}
@@ -468,7 +465,6 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev,
int min_uV, int max_uV, unsigned *selector)
{
struct max8997_data *max8997 = rdev_get_drvdata(rdev);
- struct i2c_client *i2c = max8997->iodev->i2c;
const struct voltage_map_desc *desc;
int rid = rdev_get_id(rdev);
int i, reg, shift, mask, ret;
@@ -500,7 +496,8 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev,
if (ret)
return ret;
- ret = max8997_update_reg(i2c, reg, i << shift, mask << shift);
+ ret = regmap_update_bits(max8997->iodev->regmap,
+ reg, mask << shift, i << shift);
*selector = i;
return ret;
@@ -710,7 +707,6 @@ static int max8997_set_voltage_safeout_sel(struct regulator_dev *rdev,
unsigned selector)
{
struct max8997_data *max8997 = rdev_get_drvdata(rdev);
- struct i2c_client *i2c = max8997->iodev->i2c;
int rid = rdev_get_id(rdev);
int reg, shift = 0, mask, ret;
@@ -721,13 +717,13 @@ static int max8997_set_voltage_safeout_sel(struct regulator_dev *rdev,
if (ret)
return ret;
- return max8997_update_reg(i2c, reg, selector << shift, mask << shift);
+ return regmap_update_bits(max8997->iodev->regmap,
+ reg, mask << shift, selector << shift);
}
static int max8997_reg_disable_suspend(struct regulator_dev *rdev)
{
struct max8997_data *max8997 = rdev_get_drvdata(rdev);
- struct i2c_client *i2c = max8997->iodev->i2c;
int ret, reg, mask, pattern;
int rid = rdev_get_id(rdev);
@@ -735,20 +731,22 @@ static int max8997_reg_disable_suspend(struct regulator_dev *rdev)
if (ret)
return ret;
- max8997_read_reg(i2c, reg, &max8997->saved_states[rid]);
+ regmap_read(max8997->iodev->regmap,
+ reg, &max8997->saved_states[rid]);
if (rid == MAX8997_LDO1 ||
rid == MAX8997_LDO10 ||
rid == MAX8997_LDO21) {
dev_dbg(&rdev->dev, "Conditional Power-Off for %s\n",
rdev->desc->name);
- return max8997_update_reg(i2c, reg, 0x40, mask);
+ return regmap_update_bits(max8997->iodev->regmap,
+ reg, mask, 0x40);
}
dev_dbg(&rdev->dev, "Full Power-Off for %s (%xh -> %xh)\n",
rdev->desc->name, max8997->saved_states[rid] & mask,
(~pattern) & mask);
- return max8997_update_reg(i2c, reg, ~pattern, mask);
+ return regmap_update_bits(max8997->iodev->regmap, reg, mask, ~pattern);
}
static struct regulator_ops max8997_ldo_ops = {
@@ -1032,7 +1030,6 @@ static int max8997_pmic_probe(struct platform_device *pdev)
struct regulator_config config = { };
struct regulator_dev **rdev;
struct max8997_data *max8997;
- struct i2c_client *i2c;
int i, ret, size, nr_dvs;
u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0;
@@ -1062,7 +1059,6 @@ static int max8997_pmic_probe(struct platform_device *pdev)
max8997->iodev = iodev;
max8997->num_regulators = pdata->num_regulators;
platform_set_drvdata(pdev, max8997);
- i2c = max8997->iodev->i2c;
max8997->buck125_gpioindex = pdata->buck125_default_idx;
max8997->buck1_gpiodvs = pdata->buck1_gpiodvs;
@@ -1112,25 +1108,25 @@ static int max8997_pmic_probe(struct platform_device *pdev)
/* For the safety, set max voltage before setting up */
for (i = 0; i < 8; i++) {
- max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i,
- max_buck1, 0x3f);
- max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i,
- max_buck2, 0x3f);
- max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i,
- max_buck5, 0x3f);
+ regmap_update_bits(max8997->iodev->regmap,
+ MAX8997_REG_BUCK1DVS1 + i, 0x3f, max_buck1);
+ regmap_update_bits(max8997->iodev->regmap,
+ MAX8997_REG_BUCK2DVS1 + i, 0x3f, max_buck2);
+ regmap_update_bits(max8997->iodev->regmap,
+ MAX8997_REG_BUCK5DVS1 + i, 0x3f, max_buck5);
}
/* Initialize all the DVS related BUCK registers */
for (i = 0; i < nr_dvs; i++) {
- max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i,
- max8997->buck1_vol[i],
- 0x3f);
- max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i,
- max8997->buck2_vol[i],
- 0x3f);
- max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i,
- max8997->buck5_vol[i],
- 0x3f);
+ regmap_update_bits(max8997->iodev->regmap,
+ MAX8997_REG_BUCK1DVS1 + i,
+ 0x3f, max8997->buck1_vol[i]);
+ regmap_update_bits(max8997->iodev->regmap,
+ MAX8997_REG_BUCK2DVS1 + i,
+ 0x3f, max8997->buck2_vol[i]);
+ regmap_update_bits(max8997->iodev->regmap,
+ MAX8997_REG_BUCK5DVS1 + i,
+ 0x3f, max8997->buck5_vol[i]);
}
/*
@@ -1174,16 +1170,17 @@ static int max8997_pmic_probe(struct platform_device *pdev)
}
/* DVS-GPIO disabled */
- max8997_update_reg(i2c, MAX8997_REG_BUCK1CTRL, (pdata->buck1_gpiodvs) ?
- (1 << 1) : (0 << 1), 1 << 1);
- max8997_update_reg(i2c, MAX8997_REG_BUCK2CTRL, (pdata->buck2_gpiodvs) ?
- (1 << 1) : (0 << 1), 1 << 1);
- max8997_update_reg(i2c, MAX8997_REG_BUCK5CTRL, (pdata->buck5_gpiodvs) ?
- (1 << 1) : (0 << 1), 1 << 1);
+ regmap_update_bits(max8997->iodev->regmap, MAX8997_REG_BUCK1CTRL,
+ 1 << 1, (pdata->buck1_gpiodvs) ? (1 << 1) : (0 << 1));
+ regmap_update_bits(max8997->iodev->regmap, MAX8997_REG_BUCK2CTRL,
+ 1 << 1, (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1));
+ regmap_update_bits(max8997->iodev->regmap, MAX8997_REG_BUCK5CTRL,
+ 1 << 1, (pdata->buck5_gpiodvs) ? (1 << 1) : (0 << 1));
/* Misc Settings */
max8997->ramp_delay = 10; /* set 10mV/us, which is the default */
- max8997_write_reg(i2c, MAX8997_REG_BUCKRAMP, (0xf << 4) | 0x9);
+ regmap_write(max8997->iodev->regmap,
+ MAX8997_REG_BUCKRAMP, (0xf << 4) | 0x9);
for (i = 0; i < pdata->num_regulators; i++) {
const struct voltage_map_desc *desc;
diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c
index 0777c01..b866f7d5 100644
--- a/drivers/rtc/rtc-max8997.c
+++ b/drivers/rtc/rtc-max8997.c
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/mfd/max8997-private.h>
#include <linux/irqdomain.h>
+#include <linux/regmap.h>
/* Module parameter for WTSR function control */
static int wtsr_en = 1;
@@ -68,7 +69,6 @@ enum {
struct max8997_rtc_info {
struct device *dev;
struct max8997_dev *max8997;
- struct i2c_client *rtc;
struct rtc_device *rtc_dev;
struct mutex lock;
int virq;
@@ -118,8 +118,8 @@ static inline int max8997_rtc_set_update_reg(struct max8997_rtc_info *info)
{
int ret;
- ret = max8997_write_reg(info->rtc, MAX8997_RTC_UPDATE1,
- RTC_UDR_MASK);
+ ret = regmap_write(info->max8997->regmap_rtc,
+ MAX8997_RTC_UPDATE1, RTC_UDR_MASK);
if (ret < 0)
dev_err(info->dev, "%s: fail to write update reg(%d)\n",
__func__, ret);
@@ -140,7 +140,8 @@ static int max8997_rtc_read_time(struct device *dev, struct rtc_time *tm)
int ret;
mutex_lock(&info->lock);
- ret = max8997_bulk_read(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data);
+ ret = regmap_bulk_read(info->max8997->regmap_rtc,
+ MAX8997_RTC_SEC, data, RTC_NR_TIME);
mutex_unlock(&info->lock);
if (ret < 0) {
@@ -166,7 +167,8 @@ static int max8997_rtc_set_time(struct device *dev, struct rtc_time *tm)
mutex_lock(&info->lock);
- ret = max8997_bulk_write(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data);
+ ret = regmap_bulk_write(info->max8997->regmap_rtc,
+ MAX8997_RTC_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
ret);
@@ -183,13 +185,13 @@ static int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct max8997_rtc_info *info = dev_get_drvdata(dev);
u8 data[RTC_NR_TIME];
- u8 val;
+ unsigned int val;
int i, ret;
mutex_lock(&info->lock);
- ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
- data);
+ ret = regmap_bulk_read(info->max8997->regmap_rtc,
+ MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
__func__, __LINE__, ret);
@@ -207,7 +209,8 @@ static int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
}
alrm->pending = 0;
- ret = max8997_read_reg(info->max8997->i2c, MAX8997_REG_STATUS1, &val);
+ ret = regmap_read(info->max8997->regmap_rtc,
+ MAX8997_REG_STATUS1, &val);
if (ret < 0) {
dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n",
__func__, __LINE__, ret);
@@ -230,8 +233,8 @@ static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info)
if (!mutex_is_locked(&info->lock))
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
- ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
- data);
+ ret = regmap_bulk_read(info->max8997->regmap_rtc,
+ MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
__func__, ret);
@@ -241,8 +244,8 @@ static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info)
for (i = 0; i < RTC_NR_TIME; i++)
data[i] &= ~ALARM_ENABLE_MASK;
- ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
- data);
+ ret = regmap_bulk_write(info->max8997->regmap_rtc,
+ MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
@@ -262,8 +265,8 @@ static int max8997_rtc_start_alarm(struct max8997_rtc_info *info)
if (!mutex_is_locked(&info->lock))
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
- ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
- data);
+ ret = regmap_bulk_read(info->max8997->regmap_rtc,
+ MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
__func__, ret);
@@ -281,8 +284,8 @@ static int max8997_rtc_start_alarm(struct max8997_rtc_info *info)
if (data[RTC_DATE] & 0x1f)
data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
- ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
- data);
+ ret = regmap_bulk_write(info->max8997->regmap_rtc,
+ MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
@@ -313,8 +316,8 @@ static int max8997_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
if (ret < 0)
goto out;
- ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
- data);
+ ret = regmap_bulk_write(info->max8997->regmap_rtc,
+ MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
@@ -385,7 +388,8 @@ static void max8997_rtc_enable_wtsr(struct max8997_rtc_info *info, bool enable)
dev_info(info->dev, "%s: %s WTSR\n", __func__,
enable ? "enable" : "disable");
- ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask);
+ ret = regmap_update_bits(info->max8997->regmap_rtc,
+ MAX8997_RTC_WTSR_SMPL, mask, val);
if (ret < 0) {
dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
__func__, ret);
@@ -398,7 +402,7 @@ static void max8997_rtc_enable_wtsr(struct max8997_rtc_info *info, bool enable)
static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable)
{
int ret;
- u8 val, mask;
+ unsigned int val, mask;
if (!smpl_en)
return;
@@ -413,7 +417,8 @@ static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable)
dev_info(info->dev, "%s: %s SMPL\n", __func__,
enable ? "enable" : "disable");
- ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask);
+ ret = regmap_update_bits(info->max8997->regmap_rtc,
+ MAX8997_RTC_WTSR_SMPL, mask, val);
if (ret < 0) {
dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
__func__, ret);
@@ -423,7 +428,8 @@ static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable)
max8997_rtc_set_update_reg(info);
val = 0;
- max8997_read_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, &val);
+ regmap_read(info->max8997->regmap_rtc,
+ MAX8997_RTC_WTSR_SMPL, &val);
pr_info("%s: WTSR_SMPL(0x%02x)\n", __func__, val);
}
@@ -438,7 +444,8 @@ static int max8997_rtc_init_reg(struct max8997_rtc_info *info)
info->rtc_24hr_mode = 1;
- ret = max8997_bulk_write(info->rtc, MAX8997_RTC_CTRLMASK, 2, data);
+ ret = regmap_bulk_write(info->max8997->regmap_rtc,
+ MAX8997_RTC_CTRLMASK, data, 2);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
__func__, ret);
@@ -463,7 +470,6 @@ static int max8997_rtc_probe(struct platform_device *pdev)
mutex_init(&info->lock);
info->dev = &pdev->dev;
info->max8997 = max8997;
- info->rtc = max8997->rtc;
platform_set_drvdata(pdev, info);
diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h
index 78c76cd..ea80ef8 100644
--- a/include/linux/mfd/max8997-private.h
+++ b/include/linux/mfd/max8997-private.h
@@ -309,6 +309,8 @@ enum max8997_rtc_reg {
MAX8997_RTC_ALARM2_MONTH = 0x22,
MAX8997_RTC_ALARM2_YEAR = 0x23,
MAX8997_RTC_ALARM2_DAY_OF_MONTH = 0x24,
+
+ MAX8997_RTC_REG_END = 0x25,
};
enum max8997_irq_source {
@@ -390,6 +392,11 @@ struct max8997_dev {
unsigned long type;
struct platform_device *battery; /* battery control (not fuel gauge) */
+ struct regmap *regmap;
+ struct regmap *regmap_rtc;
+ struct regmap *regmap_haptic;
+ struct regmap *regmap_muic;
+
int irq;
int ono;
struct irq_domain *irq_domain;
@@ -398,7 +405,7 @@ struct max8997_dev {
int irq_masks_cache[MAX8997_IRQ_GROUP_NR];
/* For hibernation */
- u8 reg_dump[MAX8997_REG_PMIC_END + MAX8997_MUIC_REG_END +
+ unsigned int reg_dump[MAX8997_REG_PMIC_END + MAX8997_MUIC_REG_END +
MAX8997_HAPTIC_REG_END];
bool gpio_status[MAX8997_NUM_GPIO];
@@ -413,14 +420,6 @@ extern int max8997_irq_init(struct max8997_dev *max8997);
extern void max8997_irq_exit(struct max8997_dev *max8997);
extern int max8997_irq_resume(struct max8997_dev *max8997);
-extern int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest);
-extern int max8997_bulk_read(struct i2c_client *i2c, u8 reg, int count,
- u8 *buf);
-extern int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value);
-extern int max8997_bulk_write(struct i2c_client *i2c, u8 reg, int count,
- u8 *buf);
-extern int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask);
-
#define MAX8997_GPIO_INT_BOTH (0x3 << 4)
#define MAX8997_GPIO_INT_RISE (0x2 << 4)
#define MAX8997_GPIO_INT_FALL (0x1 << 4)
--
1.7.9.5
^ permalink raw reply related
* [PATCH v2 2/3] mfd: max8997: handle IRQs using regmap
From: Robert Baldyga @ 2014-03-12 13:37 UTC (permalink / raw)
To: sameo, lee.jones
Cc: myungjoo.ham, cw00.choi, dmitry.torokhov, cooloney, rpurdie,
dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo, paul.gortmaker,
linux-kernel, linux-input, linux-leds, rtc-linux, m.szyprowski,
k.kozlowski, Robert Baldyga
In-Reply-To: <1394631466-6429-1-git-send-email-r.baldyga@samsung.com>
This patch modifies mfd driver to use regmap for handling interrupts.
It allows to simplify irq handling process. This modifications needed
to make small changes in function drivers, which use interrupts.
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
drivers/extcon/extcon-max8997.c | 35 ++--
drivers/mfd/Kconfig | 2 +-
drivers/mfd/Makefile | 2 +-
drivers/mfd/max8997-irq.c | 373 -----------------------------------
drivers/mfd/max8997.c | 113 ++++++++++-
drivers/rtc/rtc-max8997.c | 2 +-
include/linux/mfd/max8997-private.h | 65 +++++-
7 files changed, 183 insertions(+), 409 deletions(-)
delete mode 100644 drivers/mfd/max8997-irq.c
diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
index f258c08..15fc5c0 100644
--- a/drivers/extcon/extcon-max8997.c
+++ b/drivers/extcon/extcon-max8997.c
@@ -46,15 +46,15 @@ struct max8997_muic_irq {
};
static struct max8997_muic_irq muic_irqs[] = {
- { MAX8997_MUICIRQ_ADCError, "muic-ADCERROR" },
- { MAX8997_MUICIRQ_ADCLow, "muic-ADCLOW" },
- { MAX8997_MUICIRQ_ADC, "muic-ADC" },
- { MAX8997_MUICIRQ_VBVolt, "muic-VBVOLT" },
- { MAX8997_MUICIRQ_DBChg, "muic-DBCHG" },
- { MAX8997_MUICIRQ_DCDTmr, "muic-DCDTMR" },
- { MAX8997_MUICIRQ_ChgDetRun, "muic-CHGDETRUN" },
- { MAX8997_MUICIRQ_ChgTyp, "muic-CHGTYP" },
- { MAX8997_MUICIRQ_OVP, "muic-OVP" },
+ { MAX8997_MUICIRQ_ADCERROR, "MUIC-ADCERROR" },
+ { MAX8997_MUICIRQ_ADCLOW, "MUIC-ADCLOW" },
+ { MAX8997_MUICIRQ_ADC, "MUIC-ADC" },
+ { MAX8997_MUICIRQ_VBVOLT, "MUIC-VBVOLT" },
+ { MAX8997_MUICIRQ_DBCHG, "MUIC-DBCHG" },
+ { MAX8997_MUICIRQ_DCDTMR, "MUIC-DCDTMR" },
+ { MAX8997_MUICIRQ_CHGDETRUN, "MUIC-CHGDETRUN" },
+ { MAX8997_MUICIRQ_CHGTYP, "MUIC-CHGTYP" },
+ { MAX8997_MUICIRQ_OVP, "MUIC-OVP" },
};
/* Define supported cable type */
@@ -553,17 +553,17 @@ static void max8997_muic_irq_work(struct work_struct *work)
}
switch (irq_type) {
- case MAX8997_MUICIRQ_ADCError:
- case MAX8997_MUICIRQ_ADCLow:
+ case MAX8997_MUICIRQ_ADCERROR:
+ case MAX8997_MUICIRQ_ADCLOW:
case MAX8997_MUICIRQ_ADC:
/* Handle all of cable except for charger cable */
ret = max8997_muic_adc_handler(info);
break;
- case MAX8997_MUICIRQ_VBVolt:
- case MAX8997_MUICIRQ_DBChg:
- case MAX8997_MUICIRQ_DCDTmr:
- case MAX8997_MUICIRQ_ChgDetRun:
- case MAX8997_MUICIRQ_ChgTyp:
+ case MAX8997_MUICIRQ_VBVOLT:
+ case MAX8997_MUICIRQ_DBCHG:
+ case MAX8997_MUICIRQ_DCDTMR:
+ case MAX8997_MUICIRQ_CHGDETRUN:
+ case MAX8997_MUICIRQ_CHGTYP:
/* Handle charger cable */
ret = max8997_muic_chg_handler(info);
break;
@@ -679,7 +679,8 @@ static int max8997_muic_probe(struct platform_device *pdev)
struct max8997_muic_irq *muic_irq = &muic_irqs[i];
unsigned int virq = 0;
- virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);
+ virq = regmap_irq_get_virq(max8997->irq_data_muic,
+ muic_irq->irq);
if (!virq) {
ret = -EINVAL;
goto err_irq;
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2871a65..2273574 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -388,7 +388,7 @@ config MFD_MAX8997
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
- select IRQ_DOMAIN
+ select REGMAP_IRQ
help
Say yes here to add support for Maxim Semiconductor MAX8997/8966.
This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f5a7b2c..4cec8ad 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -119,7 +119,7 @@ obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
obj-$(CONFIG_MFD_MAX8907) += max8907.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
-obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
+obj-$(CONFIG_MFD_MAX8997) += max8997.o
obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
pcf50633-objs := pcf50633-core.o pcf50633-irq.o
diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c
deleted file mode 100644
index 0e7ff39..0000000
--- a/drivers/mfd/max8997-irq.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * max8997-irq.c - Interrupt controller support for MAX8997
- *
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * MyungJoo Ham <myungjoo.ham@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * This driver is based on max8998-irq.c
- */
-
-#include <linux/err.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/mfd/max8997.h>
-#include <linux/mfd/max8997-private.h>
-#include <linux/regmap.h>
-
-static const u8 max8997_mask_reg[] = {
- [PMIC_INT1] = MAX8997_REG_INT1MSK,
- [PMIC_INT2] = MAX8997_REG_INT2MSK,
- [PMIC_INT3] = MAX8997_REG_INT3MSK,
- [PMIC_INT4] = MAX8997_REG_INT4MSK,
- [FUEL_GAUGE] = MAX8997_REG_INVALID,
- [MUIC_INT1] = MAX8997_MUIC_REG_INTMASK1,
- [MUIC_INT2] = MAX8997_MUIC_REG_INTMASK2,
- [MUIC_INT3] = MAX8997_MUIC_REG_INTMASK3,
- [GPIO_LOW] = MAX8997_REG_INVALID,
- [GPIO_HI] = MAX8997_REG_INVALID,
- [FLASH_STATUS] = MAX8997_REG_INVALID,
-};
-
-struct max8997_irq_data {
- int mask;
- enum max8997_irq_source group;
-};
-
-#define DECLARE_IRQ(idx, _group, _mask) \
- [(idx)] = { .group = (_group), .mask = (_mask) }
-static const struct max8997_irq_data max8997_irqs[] = {
- DECLARE_IRQ(MAX8997_PMICIRQ_PWRONR, PMIC_INT1, 1 << 0),
- DECLARE_IRQ(MAX8997_PMICIRQ_PWRONF, PMIC_INT1, 1 << 1),
- DECLARE_IRQ(MAX8997_PMICIRQ_PWRON1SEC, PMIC_INT1, 1 << 3),
- DECLARE_IRQ(MAX8997_PMICIRQ_JIGONR, PMIC_INT1, 1 << 4),
- DECLARE_IRQ(MAX8997_PMICIRQ_JIGONF, PMIC_INT1, 1 << 5),
- DECLARE_IRQ(MAX8997_PMICIRQ_LOWBAT2, PMIC_INT1, 1 << 6),
- DECLARE_IRQ(MAX8997_PMICIRQ_LOWBAT1, PMIC_INT1, 1 << 7),
-
- DECLARE_IRQ(MAX8997_PMICIRQ_JIGR, PMIC_INT2, 1 << 0),
- DECLARE_IRQ(MAX8997_PMICIRQ_JIGF, PMIC_INT2, 1 << 1),
- DECLARE_IRQ(MAX8997_PMICIRQ_MR, PMIC_INT2, 1 << 2),
- DECLARE_IRQ(MAX8997_PMICIRQ_DVS1OK, PMIC_INT2, 1 << 3),
- DECLARE_IRQ(MAX8997_PMICIRQ_DVS2OK, PMIC_INT2, 1 << 4),
- DECLARE_IRQ(MAX8997_PMICIRQ_DVS3OK, PMIC_INT2, 1 << 5),
- DECLARE_IRQ(MAX8997_PMICIRQ_DVS4OK, PMIC_INT2, 1 << 6),
-
- DECLARE_IRQ(MAX8997_PMICIRQ_CHGINS, PMIC_INT3, 1 << 0),
- DECLARE_IRQ(MAX8997_PMICIRQ_CHGRM, PMIC_INT3, 1 << 1),
- DECLARE_IRQ(MAX8997_PMICIRQ_DCINOVP, PMIC_INT3, 1 << 2),
- DECLARE_IRQ(MAX8997_PMICIRQ_TOPOFFR, PMIC_INT3, 1 << 3),
- DECLARE_IRQ(MAX8997_PMICIRQ_CHGRSTF, PMIC_INT3, 1 << 5),
- DECLARE_IRQ(MAX8997_PMICIRQ_MBCHGTMEXPD, PMIC_INT3, 1 << 7),
-
- DECLARE_IRQ(MAX8997_PMICIRQ_RTC60S, PMIC_INT4, 1 << 0),
- DECLARE_IRQ(MAX8997_PMICIRQ_RTCA1, PMIC_INT4, 1 << 1),
- DECLARE_IRQ(MAX8997_PMICIRQ_RTCA2, PMIC_INT4, 1 << 2),
- DECLARE_IRQ(MAX8997_PMICIRQ_SMPL_INT, PMIC_INT4, 1 << 3),
- DECLARE_IRQ(MAX8997_PMICIRQ_RTC1S, PMIC_INT4, 1 << 4),
- DECLARE_IRQ(MAX8997_PMICIRQ_WTSR, PMIC_INT4, 1 << 5),
-
- DECLARE_IRQ(MAX8997_MUICIRQ_ADCError, MUIC_INT1, 1 << 2),
- DECLARE_IRQ(MAX8997_MUICIRQ_ADCLow, MUIC_INT1, 1 << 1),
- DECLARE_IRQ(MAX8997_MUICIRQ_ADC, MUIC_INT1, 1 << 0),
-
- DECLARE_IRQ(MAX8997_MUICIRQ_VBVolt, MUIC_INT2, 1 << 4),
- DECLARE_IRQ(MAX8997_MUICIRQ_DBChg, MUIC_INT2, 1 << 3),
- DECLARE_IRQ(MAX8997_MUICIRQ_DCDTmr, MUIC_INT2, 1 << 2),
- DECLARE_IRQ(MAX8997_MUICIRQ_ChgDetRun, MUIC_INT2, 1 << 1),
- DECLARE_IRQ(MAX8997_MUICIRQ_ChgTyp, MUIC_INT2, 1 << 0),
-
- DECLARE_IRQ(MAX8997_MUICIRQ_OVP, MUIC_INT3, 1 << 2),
-};
-
-static void max8997_irq_lock(struct irq_data *data)
-{
- struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
-
- mutex_lock(&max8997->irqlock);
-}
-
-static void max8997_irq_sync_unlock(struct irq_data *data)
-{
- struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
- int i;
-
- for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
- struct regmap *map;
- u8 mask_reg = max8997_mask_reg[i];
-
- if (i >= MUIC_INT1 && i <= MUIC_INT3)
- map = max8997->regmap_muic;
- else
- map = max8997->regmap;
-
- if (mask_reg == MAX8997_REG_INVALID ||
- IS_ERR_OR_NULL(map))
- continue;
- max8997->irq_masks_cache[i] = max8997->irq_masks_cur[i];
-
- regmap_write(map, max8997_mask_reg[i],
- max8997->irq_masks_cur[i]);
- }
-
- mutex_unlock(&max8997->irqlock);
-}
-
-static const inline struct max8997_irq_data *
-irq_to_max8997_irq(struct max8997_dev *max8997, int irq)
-{
- struct irq_data *data = irq_get_irq_data(irq);
- return &max8997_irqs[data->hwirq];
-}
-
-static void max8997_irq_mask(struct irq_data *data)
-{
- struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
- const struct max8997_irq_data *irq_data = irq_to_max8997_irq(max8997,
- data->irq);
-
- max8997->irq_masks_cur[irq_data->group] |= irq_data->mask;
-}
-
-static void max8997_irq_unmask(struct irq_data *data)
-{
- struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
- const struct max8997_irq_data *irq_data = irq_to_max8997_irq(max8997,
- data->irq);
-
- max8997->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
-}
-
-static struct irq_chip max8997_irq_chip = {
- .name = "max8997",
- .irq_bus_lock = max8997_irq_lock,
- .irq_bus_sync_unlock = max8997_irq_sync_unlock,
- .irq_mask = max8997_irq_mask,
- .irq_unmask = max8997_irq_unmask,
-};
-
-#define MAX8997_IRQSRC_PMIC (1 << 1)
-#define MAX8997_IRQSRC_FUELGAUGE (1 << 2)
-#define MAX8997_IRQSRC_MUIC (1 << 3)
-#define MAX8997_IRQSRC_GPIO (1 << 4)
-#define MAX8997_IRQSRC_FLASH (1 << 5)
-static irqreturn_t max8997_irq_thread(int irq, void *data)
-{
- struct max8997_dev *max8997 = data;
- u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {};
- unsigned int irq_src;
- int ret;
- int i, cur_irq;
-
- ret = regmap_read(max8997->regmap, MAX8997_REG_INTSRC, &irq_src);
- if (ret < 0) {
- dev_err(max8997->dev, "Failed to read interrupt source: %d\n",
- ret);
- return IRQ_NONE;
- }
-
- if (irq_src & MAX8997_IRQSRC_PMIC) {
- /* PMIC INT1 ~ INT4 */
- regmap_bulk_read(max8997->regmap, MAX8997_REG_INT1,
- &irq_reg[PMIC_INT1], 4);
- }
- if (irq_src & MAX8997_IRQSRC_FUELGAUGE) {
- /*
- * TODO: FUEL GAUGE
- *
- * This is to be supported by Max17042 driver. When
- * an interrupt incurs here, it should be relayed to a
- * Max17042 device that is connected (probably by
- * platform-data). However, we do not have interrupt
- * handling in Max17042 driver currently. The Max17042 IRQ
- * driver should be ready to be used as a stand-alone device and
- * a Max8997-dependent device. Because it is not ready in
- * Max17042-side and it is not too critical in operating
- * Max8997, we do not implement this in initial releases.
- */
- irq_reg[FUEL_GAUGE] = 0;
- }
- if (irq_src & MAX8997_IRQSRC_MUIC) {
- /* MUIC INT1 ~ INT3 */
- regmap_bulk_read(max8997->regmap_muic, MAX8997_MUIC_REG_INT1,
- &irq_reg[MUIC_INT1], 3);
- }
- if (irq_src & MAX8997_IRQSRC_GPIO) {
- /* GPIO Interrupt */
- u8 gpio_info[MAX8997_NUM_GPIO];
-
- irq_reg[GPIO_LOW] = 0;
- irq_reg[GPIO_HI] = 0;
-
- regmap_bulk_read(max8997->regmap, MAX8997_REG_GPIOCNTL1,
- gpio_info, MAX8997_NUM_GPIO);
- for (i = 0; i < MAX8997_NUM_GPIO; i++) {
- bool interrupt = false;
-
- switch (gpio_info[i] & MAX8997_GPIO_INT_MASK) {
- case MAX8997_GPIO_INT_BOTH:
- if (max8997->gpio_status[i] != gpio_info[i])
- interrupt = true;
- break;
- case MAX8997_GPIO_INT_RISE:
- if ((max8997->gpio_status[i] != gpio_info[i]) &&
- (gpio_info[i] & MAX8997_GPIO_DATA_MASK))
- interrupt = true;
- break;
- case MAX8997_GPIO_INT_FALL:
- if ((max8997->gpio_status[i] != gpio_info[i]) &&
- !(gpio_info[i] & MAX8997_GPIO_DATA_MASK))
- interrupt = true;
- break;
- default:
- break;
- }
-
- if (interrupt) {
- if (i < 8)
- irq_reg[GPIO_LOW] |= (1 << i);
- else
- irq_reg[GPIO_HI] |= (1 << (i - 8));
- }
-
- }
- }
- if (irq_src & MAX8997_IRQSRC_FLASH) {
- /* Flash Status Interrupt */
- unsigned int data;
- ret = regmap_read(max8997->regmap,
- MAX8997_REG_FLASHSTATUS, &data);
- irq_reg[FLASH_STATUS] = data;
- }
-
- /* Apply masking */
- for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++)
- irq_reg[i] &= ~max8997->irq_masks_cur[i];
-
- /* Report */
- for (i = 0; i < MAX8997_IRQ_NR; i++) {
- if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) {
- cur_irq = irq_find_mapping(max8997->irq_domain, i);
- if (cur_irq)
- handle_nested_irq(cur_irq);
- }
- }
-
- return IRQ_HANDLED;
-}
-
-int max8997_irq_resume(struct max8997_dev *max8997)
-{
- if (max8997->irq && max8997->irq_domain)
- max8997_irq_thread(0, max8997);
- return 0;
-}
-
-static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hw)
-{
- struct max8997_dev *max8997 = d->host_data;
-
- irq_set_chip_data(irq, max8997);
- irq_set_chip_and_handler(irq, &max8997_irq_chip, handle_edge_irq);
- irq_set_nested_thread(irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID);
-#else
- irq_set_noprobe(irq);
-#endif
- return 0;
-}
-
-static struct irq_domain_ops max8997_irq_domain_ops = {
- .map = max8997_irq_domain_map,
-};
-
-int max8997_irq_init(struct max8997_dev *max8997)
-{
- struct irq_domain *domain;
- int i;
- int ret;
- unsigned int val;
-
- if (!max8997->irq) {
- dev_warn(max8997->dev, "No interrupt specified.\n");
- return 0;
- }
-
- mutex_init(&max8997->irqlock);
-
- /* Mask individual interrupt sources */
- for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
- max8997->irq_masks_cur[i] = 0xff;
- max8997->irq_masks_cache[i] = 0xff;
-
- if (IS_ERR_OR_NULL(max8997->regmap))
- continue;
- if (max8997_mask_reg[i] == MAX8997_REG_INVALID)
- continue;
-
- regmap_write(max8997->regmap, max8997_mask_reg[i], 0xff);
- }
-
- for (i = 0; i < MAX8997_NUM_GPIO; i++) {
- max8997->gpio_status[i] = (regmap_read(max8997->regmap,
- MAX8997_REG_GPIOCNTL1 + i,
- &val)
- & MAX8997_GPIO_DATA_MASK) ?
- true : false;
- }
-
- domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR,
- &max8997_irq_domain_ops, max8997);
- if (!domain) {
- dev_err(max8997->dev, "could not create irq domain\n");
- return -ENODEV;
- }
- max8997->irq_domain = domain;
-
- ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "max8997-irq", max8997);
-
- if (ret) {
- dev_err(max8997->dev, "Failed to request IRQ %d: %d\n",
- max8997->irq, ret);
- return ret;
- }
-
- if (!max8997->ono)
- return 0;
-
- ret = request_threaded_irq(max8997->ono, NULL, max8997_irq_thread,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
- IRQF_ONESHOT, "max8997-ono", max8997);
-
- if (ret)
- dev_err(max8997->dev, "Failed to request ono-IRQ %d: %d\n",
- max8997->ono, ret);
-
- return 0;
-}
-
-void max8997_irq_exit(struct max8997_dev *max8997)
-{
- if (max8997->ono)
- free_irq(max8997->ono, max8997);
-
- if (max8997->irq)
- free_irq(max8997->irq, max8997);
-}
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 782d7c9..26a360d 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -64,6 +64,49 @@ static const struct regmap_config max8997_regmap_config = {
.max_register = MAX8997_REG_PMIC_END,
};
+static const struct regmap_irq max8997_irqs[] = {
+ /* PMIC_INT1 interrupts */
+ { .reg_offset = 0, .mask = PMIC_INT1_PWRONR_MASK, },
+ { .reg_offset = 0, .mask = PMIC_INT1_PWRONF_MASK, },
+ { .reg_offset = 0, .mask = PMIC_INT1_PWRON1SEC_MASK, },
+ { .reg_offset = 0, .mask = PMIC_INT1_JIGONR_MASK, },
+ { .reg_offset = 0, .mask = PMIC_INT1_JIGONF_MASK, },
+ { .reg_offset = 0, .mask = PMIC_INT1_LOWBAT2_MASK, },
+ { .reg_offset = 0, .mask = PMIC_INT1_LOWBAT1_MASK, },
+ /* PMIC_INT2 interrupts */
+ { .reg_offset = 1, .mask = PMIC_INT2_JIGR_MASK, },
+ { .reg_offset = 1, .mask = PMIC_INT2_JIGF_MASK, },
+ { .reg_offset = 1, .mask = PMIC_INT2_MR_MASK, },
+ { .reg_offset = 1, .mask = PMIC_INT2_DVS1OK_MASK, },
+ { .reg_offset = 1, .mask = PMIC_INT2_DVS2OK_MASK, },
+ { .reg_offset = 1, .mask = PMIC_INT2_DVS3OK_MASK, },
+ { .reg_offset = 1, .mask = PMIC_INT2_DVS4OK_MASK, },
+ /* PMIC_INT3 interrupts */
+ { .reg_offset = 2, .mask = PMIC_INT3_CHGINS_MASK, },
+ { .reg_offset = 2, .mask = PMIC_INT3_CHGRM_MASK, },
+ { .reg_offset = 2, .mask = PMIC_INT3_DCINOVP_MASK, },
+ { .reg_offset = 2, .mask = PMIC_INT3_TOPOFFR_MASK, },
+ { .reg_offset = 2, .mask = PMIC_INT3_CHGRSTF_MASK, },
+ { .reg_offset = 2, .mask = PMIC_INT3_MBCHGTMEXPD_MASK, },
+ /* PMIC_INT4 interrupts */
+ { .reg_offset = 3, .mask = PMIC_INT4_RTC60S_MASK, },
+ { .reg_offset = 3, .mask = PMIC_INT4_RTCA1_MASK, },
+ { .reg_offset = 3, .mask = PMIC_INT4_RTCA2_MASK, },
+ { .reg_offset = 3, .mask = PMIC_INT4_SMPL_INT_MASK, },
+ { .reg_offset = 3, .mask = PMIC_INT4_RTC1S_MASK, },
+ { .reg_offset = 3, .mask = PMIC_INT4_WTSR_MASK, },
+};
+
+static const struct regmap_irq_chip max8997_irq_chip = {
+ .name = "max8997",
+ .status_base = MAX8997_REG_INT1,
+ .mask_base = MAX8997_REG_INT1MSK,
+ .mask_invert = false,
+ .num_regs = 4,
+ .irqs = max8997_irqs,
+ .num_irqs = ARRAY_SIZE(max8997_irqs),
+};
+
static const struct regmap_config max8997_regmap_rtc_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -82,6 +125,31 @@ static const struct regmap_config max8997_regmap_muic_config = {
.max_register = MAX8997_MUIC_REG_END,
};
+static const struct regmap_irq max8997_irqs_muic[] = {
+ /* MUIC_INT1 interrupts */
+ { .reg_offset = 0, .mask = MUIC_INT1_ADC_MASK, },
+ { .reg_offset = 0, .mask = MUIC_INT1_ADCLOW_MASK, },
+ { .reg_offset = 0, .mask = MUIC_INT1_ADCERROR_MASK, },
+ /* MUIC_INT2 interrupts */
+ { .reg_offset = 1, .mask = MUIC_INT2_CHGTYP_MASK, },
+ { .reg_offset = 1, .mask = MUIC_INT2_CHGDETRUN_MASK, },
+ { .reg_offset = 1, .mask = MUIC_INT2_DCDTMR_MASK, },
+ { .reg_offset = 1, .mask = MUIC_INT2_DBCHG_MASK, },
+ { .reg_offset = 1, .mask = MUIC_INT2_VBVOLT_MASK, },
+ /* MUIC_INT3 interrupts */
+ { .reg_offset = 2, .mask = MUIC_INT3_OVP_MASK, },
+};
+
+static const struct regmap_irq_chip max8997_irq_chip_muic = {
+ .name = "max8997-muic",
+ .status_base = MAX8997_MUIC_REG_INT1,
+ .mask_base = MAX8997_MUIC_REG_INTMASK1,
+ .mask_invert = true,
+ .num_regs = 3,
+ .irqs = max8997_irqs_muic,
+ .num_irqs = ARRAY_SIZE(max8997_irqs_muic),
+};
+
/*
* Only the common platform data elements for max8997 are parsed here from the
* device tree. Other sub-modules of max8997 such as pmic, rtc and others have
@@ -214,9 +282,26 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
goto err_regmap;
}
- pm_runtime_set_active(max8997->dev);
+ ret = regmap_add_irq_chip(max8997->regmap, max8997->irq,
+ IRQF_ONESHOT | IRQF_SHARED |
+ IRQF_TRIGGER_FALLING, 0,
+ &max8997_irq_chip, &max8997->irq_data);
+ if (ret) {
+ dev_err(max8997->dev, "failed to add irq chip: %d\n", ret);
+ goto err_irq;
+ }
- max8997_irq_init(max8997);
+ ret = regmap_add_irq_chip(max8997->regmap_muic, max8997->irq,
+ IRQF_ONESHOT | IRQF_SHARED |
+ IRQF_TRIGGER_FALLING, 0,
+ &max8997_irq_chip_muic,
+ &max8997->irq_data_muic);
+ if (ret) {
+ dev_err(max8997->dev, "failed to add irq chip: %d\n", ret);
+ goto err_irq_muic;
+ }
+
+ pm_runtime_set_active(max8997->dev);
ret = mfd_add_devices(max8997->dev, -1, max8997_devs,
ARRAY_SIZE(max8997_devs),
@@ -238,6 +323,10 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
err_mfd:
mfd_remove_devices(max8997->dev);
+err_irq_muic:
+ regmap_del_irq_chip(max8997->irq, max8997->irq_data_muic);
+err_irq:
+ regmap_del_irq_chip(max8997->irq, max8997->irq_data);
err_regmap:
i2c_unregister_device(max8997->muic);
err_i2c_muic:
@@ -252,6 +341,10 @@ static int max8997_i2c_remove(struct i2c_client *i2c)
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
mfd_remove_devices(max8997->dev);
+
+ regmap_del_irq_chip(max8997->irq, max8997->irq_data_muic);
+ regmap_del_irq_chip(max8997->irq, max8997->irq_data);
+
i2c_unregister_device(max8997->muic);
i2c_unregister_device(max8997->haptic);
i2c_unregister_device(max8997->rtc);
@@ -468,8 +561,11 @@ static int max8997_suspend(struct device *dev)
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
- if (device_may_wakeup(dev))
- irq_set_irq_wake(max8997->irq, 1);
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(max8997->irq);
+ disable_irq(max8997->irq);
+ }
+
return 0;
}
@@ -478,9 +574,12 @@ static int max8997_resume(struct device *dev)
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
- if (device_may_wakeup(dev))
- irq_set_irq_wake(max8997->irq, 0);
- return max8997_irq_resume(max8997);
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(max8997->irq);
+ enable_irq(max8997->irq);
+ }
+
+ return 0;
}
static const struct dev_pm_ops max8997_pm = {
diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c
index b866f7d5..22769ea 100644
--- a/drivers/rtc/rtc-max8997.c
+++ b/drivers/rtc/rtc-max8997.c
@@ -494,7 +494,7 @@ static int max8997_rtc_probe(struct platform_device *pdev)
return ret;
}
- virq = irq_create_mapping(max8997->irq_domain, MAX8997_PMICIRQ_RTCA1);
+ virq = regmap_irq_get_virq(max8997->irq_data, MAX8997_PMICIRQ_RTCA1);
if (!virq) {
dev_err(&pdev->dev, "Failed to create mapping alarm IRQ\n");
ret = -ENXIO;
diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h
index ea80ef8..2817fa6 100644
--- a/include/linux/mfd/max8997-private.h
+++ b/include/linux/mfd/max8997-private.h
@@ -333,6 +333,48 @@ enum max8997_irq_source {
MAX8997_IRQ_GROUP_NR,
};
+#define PMIC_INT1_PWRONR_MASK (0x1 << 0)
+#define PMIC_INT1_PWRONF_MASK (0x1 << 1)
+#define PMIC_INT1_PWRON1SEC_MASK (0x1 << 3)
+#define PMIC_INT1_JIGONR_MASK (0x1 << 4)
+#define PMIC_INT1_JIGONF_MASK (0x1 << 5)
+#define PMIC_INT1_LOWBAT2_MASK (0x1 << 6)
+#define PMIC_INT1_LOWBAT1_MASK (0x1 << 7)
+
+#define PMIC_INT2_JIGR_MASK (0x1 << 0)
+#define PMIC_INT2_JIGF_MASK (0x1 << 1)
+#define PMIC_INT2_MR_MASK (0x1 << 2)
+#define PMIC_INT2_DVS1OK_MASK (0x1 << 3)
+#define PMIC_INT2_DVS2OK_MASK (0x1 << 4)
+#define PMIC_INT2_DVS3OK_MASK (0x1 << 5)
+#define PMIC_INT2_DVS4OK_MASK (0x1 << 6)
+
+#define PMIC_INT3_CHGINS_MASK (0x1 << 0)
+#define PMIC_INT3_CHGRM_MASK (0x1 << 1)
+#define PMIC_INT3_DCINOVP_MASK (0x1 << 2)
+#define PMIC_INT3_TOPOFFR_MASK (0x1 << 3)
+#define PMIC_INT3_CHGRSTF_MASK (0x1 << 5)
+#define PMIC_INT3_MBCHGTMEXPD_MASK (0x1 << 7)
+
+#define PMIC_INT4_RTC60S_MASK (0x1 << 0)
+#define PMIC_INT4_RTCA1_MASK (0x1 << 1)
+#define PMIC_INT4_RTCA2_MASK (0x1 << 2)
+#define PMIC_INT4_SMPL_INT_MASK (0x1 << 3)
+#define PMIC_INT4_RTC1S_MASK (0x1 << 4)
+#define PMIC_INT4_WTSR_MASK (0x1 << 5)
+
+#define MUIC_INT1_ADC_MASK (0x1 << 0)
+#define MUIC_INT1_ADCLOW_MASK (0x1 << 1)
+#define MUIC_INT1_ADCERROR_MASK (0x1 << 2)
+
+#define MUIC_INT2_CHGTYP_MASK (0x1 << 0)
+#define MUIC_INT2_CHGDETRUN_MASK (0x1 << 1)
+#define MUIC_INT2_DCDTMR_MASK (0x1 << 2)
+#define MUIC_INT2_DBCHG_MASK (0x1 << 3)
+#define MUIC_INT2_VBVOLT_MASK (0x1 << 4)
+
+#define MUIC_INT3_OVP_MASK (0x1 << 2)
+
enum max8997_irq {
MAX8997_PMICIRQ_PWRONR,
MAX8997_PMICIRQ_PWRONF,
@@ -364,19 +406,23 @@ enum max8997_irq {
MAX8997_PMICIRQ_RTC1S,
MAX8997_PMICIRQ_WTSR,
- MAX8997_MUICIRQ_ADCError,
- MAX8997_MUICIRQ_ADCLow,
+ MAX8997_PMICIRQ_NR,
+};
+
+enum max8997_irq_muic {
MAX8997_MUICIRQ_ADC,
+ MAX8997_MUICIRQ_ADCLOW,
+ MAX8997_MUICIRQ_ADCERROR,
- MAX8997_MUICIRQ_VBVolt,
- MAX8997_MUICIRQ_DBChg,
- MAX8997_MUICIRQ_DCDTmr,
- MAX8997_MUICIRQ_ChgDetRun,
- MAX8997_MUICIRQ_ChgTyp,
+ MAX8997_MUICIRQ_CHGTYP,
+ MAX8997_MUICIRQ_CHGDETRUN,
+ MAX8997_MUICIRQ_DCDTMR,
+ MAX8997_MUICIRQ_DBCHG,
+ MAX8997_MUICIRQ_VBVOLT,
MAX8997_MUICIRQ_OVP,
- MAX8997_IRQ_NR,
+ MAX8997_MUCIRQ_NR,
};
#define MAX8997_NUM_GPIO 12
@@ -397,9 +443,10 @@ struct max8997_dev {
struct regmap *regmap_haptic;
struct regmap *regmap_muic;
+ struct regmap_irq_chip_data *irq_data;
+ struct regmap_irq_chip_data *irq_data_muic;
int irq;
int ono;
- struct irq_domain *irq_domain;
struct mutex irqlock;
int irq_masks_cur[MAX8997_IRQ_GROUP_NR];
int irq_masks_cache[MAX8997_IRQ_GROUP_NR];
--
1.7.9.5
^ permalink raw reply related
* [PATCH v2 3/3] mfd: max8997: move regmap handling to function drivers
From: Robert Baldyga @ 2014-03-12 13:37 UTC (permalink / raw)
To: sameo, lee.jones
Cc: myungjoo.ham, cw00.choi, dmitry.torokhov, cooloney, rpurdie,
dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo, paul.gortmaker,
linux-kernel, linux-input, linux-leds, rtc-linux, m.szyprowski,
k.kozlowski, Robert Baldyga
In-Reply-To: <1394631466-6429-1-git-send-email-r.baldyga@samsung.com>
This patch moves code creating new i2c clients and regmaps to function
drivers which use them. It allows to avoid creating this instances when
individual function drivers are not enabled.
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
Conflicts:
drivers/mfd/max8997.c
---
drivers/extcon/extcon-max8997.c | 131 +++++++++++++++++++++++++--
drivers/input/misc/max8997_haptic.c | 117 +++++++++++++++++++-----
drivers/mfd/max8997.c | 167 -----------------------------------
drivers/rtc/rtc-max8997.c | 78 +++++++++++-----
include/linux/mfd/max8997-private.h | 14 +--
5 files changed, 283 insertions(+), 224 deletions(-)
diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
index 15fc5c0..42c9915 100644
--- a/drivers/extcon/extcon-max8997.c
+++ b/drivers/extcon/extcon-max8997.c
@@ -118,6 +118,9 @@ enum max8997_muic_charger_type {
struct max8997_muic_info {
struct device *dev;
struct max8997_dev *max8997;
+ struct i2c_client *i2c;
+ struct regmap *regmap;
+ struct regmap_irq_chip_data *irq_data;
struct extcon_dev *edev;
int prev_cable_type;
int prev_chg_type;
@@ -144,6 +147,39 @@ struct max8997_muic_info {
*/
int path_usb;
int path_uart;
+
+ unsigned int reg_dump[MAX8997_MUIC_REG_END];
+};
+
+static const struct regmap_config max8997_muic_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX8997_MUIC_REG_END,
+};
+
+static const struct regmap_irq max8997_irqs_muic[] = {
+ /* MUIC_INT1 interrupts */
+ { .reg_offset = 0, .mask = MUIC_INT1_ADC_MASK, },
+ { .reg_offset = 0, .mask = MUIC_INT1_ADCLOW_MASK, },
+ { .reg_offset = 0, .mask = MUIC_INT1_ADCERROR_MASK, },
+ /* MUIC_INT2 interrupts */
+ { .reg_offset = 1, .mask = MUIC_INT2_CHGTYP_MASK, },
+ { .reg_offset = 1, .mask = MUIC_INT2_CHGDETRUN_MASK, },
+ { .reg_offset = 1, .mask = MUIC_INT2_DCDTMR_MASK, },
+ { .reg_offset = 1, .mask = MUIC_INT2_DBCHG_MASK, },
+ { .reg_offset = 1, .mask = MUIC_INT2_VBVOLT_MASK, },
+ /* MUIC_INT3 interrupts */
+ { .reg_offset = 2, .mask = MUIC_INT3_OVP_MASK, },
+};
+
+static const struct regmap_irq_chip max8997_muic_irq_chip = {
+ .name = "max8997-muic",
+ .status_base = MAX8997_MUIC_REG_INT1,
+ .mask_base = MAX8997_MUIC_REG_INTMASK1,
+ .mask_invert = true,
+ .num_regs = 3,
+ .irqs = max8997_irqs_muic,
+ .num_irqs = ARRAY_SIZE(max8997_irqs_muic),
};
enum {
@@ -191,7 +227,7 @@ static int max8997_muic_set_debounce_time(struct max8997_muic_info *info,
case ADC_DEBOUNCE_TIME_10MS:
case ADC_DEBOUNCE_TIME_25MS:
case ADC_DEBOUNCE_TIME_38_62MS:
- ret = regmap_update_bits(info->max8997->regmap_muic,
+ ret = regmap_update_bits(info->regmap,
MAX8997_MUIC_REG_CONTROL3,
CONTROL3_ADCDBSET_MASK,
time << CONTROL3_ADCDBSET_SHIFT);
@@ -229,7 +265,7 @@ static int max8997_muic_set_path(struct max8997_muic_info *info,
else
ctrl1 = CONTROL1_SW_OPEN;
- ret = regmap_update_bits(info->max8997->regmap_muic,
+ ret = regmap_update_bits(info->regmap,
MAX8997_MUIC_REG_CONTROL1, COMP_SW_MASK, ctrl1);
if (ret < 0) {
dev_err(info->dev, "failed to update MUIC register\n");
@@ -241,7 +277,7 @@ static int max8997_muic_set_path(struct max8997_muic_info *info,
else
ctrl2 |= CONTROL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */
- ret = regmap_update_bits(info->max8997->regmap_muic,
+ ret = regmap_update_bits(info->regmap,
MAX8997_MUIC_REG_CONTROL2,
CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK, ctrl2);
if (ret < 0) {
@@ -544,7 +580,7 @@ static void max8997_muic_irq_work(struct work_struct *work)
if (info->irq == muic_irqs[i].virq)
irq_type = muic_irqs[i].irq;
- ret = regmap_bulk_read(info->max8997->regmap_muic,
+ ret = regmap_bulk_read(info->regmap,
MAX8997_MUIC_REG_STATUS1, info->status, 2);
if (ret) {
dev_err(info->dev, "failed to read muic register\n");
@@ -606,7 +642,7 @@ static int max8997_muic_detect_dev(struct max8997_muic_info *info)
mutex_lock(&info->mutex);
/* Read STATUSx register to detect accessory */
- ret = regmap_bulk_read(info->max8997->regmap_muic,
+ ret = regmap_bulk_read(info->regmap,
MAX8997_MUIC_REG_STATUS1, info->status, 2);
if (ret) {
dev_err(info->dev, "failed to read MUIC register\n");
@@ -670,6 +706,31 @@ static int max8997_muic_probe(struct platform_device *pdev)
info->dev = &pdev->dev;
info->max8997 = max8997;
+ info->i2c = i2c_new_dummy(max8997->i2c->adapter, MAX8997_I2C_ADDR_MUIC);
+ if (!info->i2c) {
+ dev_err(info->dev, "failed to allocate I2C device\n");
+ return -ENODEV;
+ }
+
+ info->regmap = devm_regmap_init_i2c(info->i2c,
+ &max8997_muic_regmap_config);
+ if (IS_ERR(info->regmap)) {
+ ret = PTR_ERR(info->regmap);
+ dev_err(info->dev,
+ "failed to allocate register map: %d\n", ret);
+ goto err_regmap;
+ }
+
+ ret = regmap_add_irq_chip(info->regmap, max8997->irq,
+ IRQF_ONESHOT | IRQF_SHARED |
+ IRQF_TRIGGER_FALLING, 0,
+ &max8997_muic_irq_chip,
+ &info->irq_data);
+ if (ret) {
+ dev_err(info->dev, "failed to add irq chip: %d\n", ret);
+ goto err_regmap_irq;
+ }
+
platform_set_drvdata(pdev, info);
mutex_init(&info->mutex);
@@ -679,8 +740,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
struct max8997_muic_irq *muic_irq = &muic_irqs[i];
unsigned int virq = 0;
- virq = regmap_irq_get_virq(max8997->irq_data_muic,
- muic_irq->irq);
+ virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);
if (!virq) {
ret = -EINVAL;
goto err_irq;
@@ -723,7 +783,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
/* Initialize registers according to platform data */
for (i = 0; i < muic_pdata->num_init_data; i++) {
- regmap_write(info->max8997->regmap_muic,
+ regmap_write(info->regmap,
muic_pdata->init_data[i].addr,
muic_pdata->init_data[i].data);
}
@@ -779,6 +839,10 @@ static int max8997_muic_probe(struct platform_device *pdev)
err_irq:
while (--i >= 0)
free_irq(muic_irqs[i].virq, info);
+err_regmap_irq:
+ regmap_del_irq_chip(info->max8997->irq, info->irq_data);
+err_regmap:
+ i2c_unregister_device(info->i2c);
return ret;
}
@@ -787,6 +851,8 @@ static int max8997_muic_remove(struct platform_device *pdev)
struct max8997_muic_info *info = platform_get_drvdata(pdev);
int i;
+ regmap_del_irq_chip(info->max8997->irq, info->irq_data);
+
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
free_irq(muic_irqs[i].virq, info);
cancel_work_sync(&info->irq_work);
@@ -796,10 +862,59 @@ static int max8997_muic_remove(struct platform_device *pdev)
return 0;
}
+static u8 max8997_muic_dumpaddr[] = {
+ MAX8997_MUIC_REG_INTMASK1,
+ MAX8997_MUIC_REG_INTMASK2,
+ MAX8997_MUIC_REG_INTMASK3,
+ MAX8997_MUIC_REG_CDETCTRL,
+ MAX8997_MUIC_REG_CONTROL1,
+ MAX8997_MUIC_REG_CONTROL2,
+ MAX8997_MUIC_REG_CONTROL3,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int max8997_muic_freeze(struct device *dev)
+{
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ struct max8997_muic_info *info = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max8997_muic_dumpaddr); i++)
+ regmap_read(info->regmap, max8997_muic_dumpaddr[i],
+ &info->reg_dump[i]);
+
+ return 0;
+}
+
+static int max8997_muic_restore(struct device *dev)
+{
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ struct max8997_muic_info *info = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max8997_muic_dumpaddr); i++)
+ regmap_write(info->regmap, max8997_muic_dumpaddr[i],
+ info->reg_dump[i]);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max8997_muic_pm = {
+#ifdef CONFIG_PM_SLEEP
+ .freeze = max8997_muic_freeze,
+ .restore = max8997_muic_restore,
+#endif
+
+};
+
static struct platform_driver max8997_muic_driver = {
.driver = {
.name = DEV_NAME,
.owner = THIS_MODULE,
+ .pm = &max8997_muic_pm,
},
.probe = max8997_muic_probe,
.remove = max8997_muic_remove,
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
index d3f7079..9952eb2 100644
--- a/drivers/input/misc/max8997_haptic.c
+++ b/drivers/input/misc/max8997_haptic.c
@@ -47,6 +47,8 @@
struct max8997_haptic {
struct device *dev;
struct max8997_dev *max8997;
+ struct i2c_client *i2c;
+ struct regmap *regmap;
struct input_dev *input_dev;
struct regulator *regulator;
@@ -66,6 +68,14 @@ struct max8997_haptic {
unsigned int internal_mode_pattern;
unsigned int pattern_cycle;
unsigned int pattern_signal_period;
+
+ unsigned int reg_dump[MAX8997_HAPTIC_REG_END];
+};
+
+static const struct regmap_config max8997_haptic_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX8997_HAPTIC_REG_END,
};
static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
@@ -87,19 +97,19 @@ static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
}
switch (chip->internal_mode_pattern) {
case 0:
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index);
break;
case 1:
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index);
break;
case 2:
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index);
break;
case 3:
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index);
break;
default:
@@ -116,51 +126,49 @@ static void max8997_haptic_configure(struct max8997_haptic *chip)
value = chip->type << MAX8997_MOTOR_TYPE_SHIFT |
chip->enabled << MAX8997_ENABLE_SHIFT |
chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor;
- regmap_write(chip->max8997->regmap_haptic,
- MAX8997_HAPTIC_REG_CONF2, value);
+ regmap_write(chip->regmap, MAX8997_HAPTIC_REG_CONF2, value);
if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) {
value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT |
chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT |
chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT |
chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT;
- regmap_write(chip->max8997->regmap_haptic,
- MAX8997_HAPTIC_REG_DRVCONF, value);
+ regmap_write(chip->regmap, MAX8997_HAPTIC_REG_DRVCONF, value);
switch (chip->internal_mode_pattern) {
case 0:
value = chip->pattern_cycle << 4;
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_CYCLECONF1, value);
value = chip->pattern_signal_period;
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_SIGCONF1, value);
break;
case 1:
value = chip->pattern_cycle;
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_CYCLECONF1, value);
value = chip->pattern_signal_period;
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_SIGCONF2, value);
break;
case 2:
value = chip->pattern_cycle << 4;
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_CYCLECONF2, value);
value = chip->pattern_signal_period;
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_SIGCONF3, value);
break;
case 3:
value = chip->pattern_cycle;
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_CYCLECONF2, value);
value = chip->pattern_signal_period;
- regmap_write(chip->max8997->regmap_haptic,
+ regmap_write(chip->regmap,
MAX8997_HAPTIC_REG_SIGCONF4, value);
break;
@@ -277,6 +285,21 @@ static int max8997_haptic_probe(struct platform_device *pdev)
chip->mode = haptic_pdata->mode;
chip->pwm_divisor = haptic_pdata->pwm_divisor;
+ chip->i2c = i2c_new_dummy(iodev->i2c->adapter, MAX8997_I2C_ADDR_HAPTIC);
+ if (!chip->i2c) {
+ dev_err(info->dev, "failed to allocate I2C device\n");
+ return -ENODEV;
+ }
+
+ chip->regmap = devm_regmap_init_i2c(chip->i2c,
+ &max8997_haptic_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ error = PTR_ERR(chip->regmap);
+ dev_err(chip->dev,
+ "failed to allocate register map: %d\n", error);
+ goto err_free_i2c;
+ }
+
switch (chip->mode) {
case MAX8997_INTERNAL_MODE:
chip->internal_mode_pattern =
@@ -294,7 +317,7 @@ static int max8997_haptic_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"unable to request PWM for haptic, error: %d\n",
error);
- goto err_free_mem;
+ goto err_free_i2c;
}
break;
@@ -302,7 +325,7 @@ static int max8997_haptic_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Invalid chip mode specified (%d)\n", chip->mode);
error = -EINVAL;
- goto err_free_mem;
+ goto err_free_i2c;
}
chip->regulator = regulator_get(&pdev->dev, "inmotor");
@@ -348,6 +371,8 @@ err_put_regulator:
err_free_pwm:
if (chip->mode == MAX8997_EXTERNAL_MODE)
pwm_free(chip->pwm);
+err_free_i2c:
+ i2c_unregister_device(chip->i2c);
err_free_mem:
input_free_device(input_dev);
kfree(chip);
@@ -370,6 +395,24 @@ static int max8997_haptic_remove(struct platform_device *pdev)
return 0;
}
+static u8 max8997_haptic_dumpaddr[] = {
+ MAX8997_HAPTIC_REG_CONF1,
+ MAX8997_HAPTIC_REG_CONF2,
+ MAX8997_HAPTIC_REG_DRVCONF,
+ MAX8997_HAPTIC_REG_CYCLECONF1,
+ MAX8997_HAPTIC_REG_CYCLECONF2,
+ MAX8997_HAPTIC_REG_SIGCONF1,
+ MAX8997_HAPTIC_REG_SIGCONF2,
+ MAX8997_HAPTIC_REG_SIGCONF3,
+ MAX8997_HAPTIC_REG_SIGCONF4,
+ MAX8997_HAPTIC_REG_SIGDC1,
+ MAX8997_HAPTIC_REG_SIGDC2,
+ MAX8997_HAPTIC_REG_SIGPWMDC1,
+ MAX8997_HAPTIC_REG_SIGPWMDC2,
+ MAX8997_HAPTIC_REG_SIGPWMDC3,
+ MAX8997_HAPTIC_REG_SIGPWMDC4,
+};
+
#ifdef CONFIG_PM_SLEEP
static int max8997_haptic_suspend(struct device *dev)
{
@@ -380,9 +423,43 @@ static int max8997_haptic_suspend(struct device *dev)
return 0;
}
+
+static int max8997_haptic_freeze(struct device *dev)
+{
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ struct max8997_haptic *chip = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max8997_haptic_dumpaddr); i++)
+ regmap_read(chip->regmap, max8997_haptic_dumpaddr[i],
+ &chip->reg_dump[i]);
+
+ return 0;
+}
+
+static int max8997_haptic_restore(struct device *dev)
+{
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ struct max8997_haptic *chip = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max8997_haptic_dumpaddr); i++)
+ regmap_write(chip->regmap, max8997_haptic_dumpaddr[i],
+ chip->reg_dump[i]);
+
+ return 0;
+}
#endif
-static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL);
+static const struct dev_pm_ops max8997_haptic_pm = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = max8997_haptic_suspend,
+ .freeze = max8997_haptic_freeze,
+ .restore = max8997_haptic_restore,
+#endif
+};
static const struct platform_device_id max8997_haptic_id[] = {
{ "max8997-haptic", 0 },
@@ -394,7 +471,7 @@ static struct platform_driver max8997_haptic_driver = {
.driver = {
.name = "max8997-haptic",
.owner = THIS_MODULE,
- .pm = &max8997_haptic_pm_ops,
+ .pm = &max8997_haptic_pm,
},
.probe = max8997_haptic_probe,
.remove = max8997_haptic_remove,
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 26a360d..11fd8a0 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -35,12 +35,6 @@
#include <linux/mfd/max8997-private.h>
#include <linux/regmap.h>
-#define I2C_ADDR_PMIC (0xCC >> 1)
-#define I2C_ADDR_MUIC (0x4A >> 1)
-#define I2C_ADDR_BATTERY (0x6C >> 1)
-#define I2C_ADDR_RTC (0x0C >> 1)
-#define I2C_ADDR_HAPTIC (0x90 >> 1)
-
static const struct mfd_cell max8997_devs[] = {
{ .name = "max8997-pmic", },
{ .name = "max8997-rtc", },
@@ -107,48 +101,6 @@ static const struct regmap_irq_chip max8997_irq_chip = {
.num_irqs = ARRAY_SIZE(max8997_irqs),
};
-static const struct regmap_config max8997_regmap_rtc_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = MAX8997_RTC_REG_END,
-};
-
-static const struct regmap_config max8997_regmap_haptic_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = MAX8997_HAPTIC_REG_END,
-};
-
-static const struct regmap_config max8997_regmap_muic_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = MAX8997_MUIC_REG_END,
-};
-
-static const struct regmap_irq max8997_irqs_muic[] = {
- /* MUIC_INT1 interrupts */
- { .reg_offset = 0, .mask = MUIC_INT1_ADC_MASK, },
- { .reg_offset = 0, .mask = MUIC_INT1_ADCLOW_MASK, },
- { .reg_offset = 0, .mask = MUIC_INT1_ADCERROR_MASK, },
- /* MUIC_INT2 interrupts */
- { .reg_offset = 1, .mask = MUIC_INT2_CHGTYP_MASK, },
- { .reg_offset = 1, .mask = MUIC_INT2_CHGDETRUN_MASK, },
- { .reg_offset = 1, .mask = MUIC_INT2_DCDTMR_MASK, },
- { .reg_offset = 1, .mask = MUIC_INT2_DBCHG_MASK, },
- { .reg_offset = 1, .mask = MUIC_INT2_VBVOLT_MASK, },
- /* MUIC_INT3 interrupts */
- { .reg_offset = 2, .mask = MUIC_INT3_OVP_MASK, },
-};
-
-static const struct regmap_irq_chip max8997_irq_chip_muic = {
- .name = "max8997-muic",
- .status_base = MAX8997_MUIC_REG_INT1,
- .mask_base = MAX8997_MUIC_REG_INTMASK1,
- .mask_invert = true,
- .num_regs = 3,
- .irqs = max8997_irqs_muic,
- .num_irqs = ARRAY_SIZE(max8997_irqs_muic),
-};
/*
* Only the common platform data elements for max8997 are parsed here from the
@@ -224,29 +176,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
mutex_init(&max8997->iolock);
- max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
- if (!max8997->rtc) {
- dev_err(max8997->dev, "Failed to allocate I2C device for RTC\n");
- return -ENODEV;
- }
- i2c_set_clientdata(max8997->rtc, max8997);
-
- max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
- if (!max8997->haptic) {
- dev_err(max8997->dev, "Failed to allocate I2C device for Haptic\n");
- ret = -ENODEV;
- goto err_i2c_haptic;
- }
- i2c_set_clientdata(max8997->haptic, max8997);
-
- max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
- if (!max8997->muic) {
- dev_err(max8997->dev, "Failed to allocate I2C device for MUIC\n");
- ret = -ENODEV;
- goto err_i2c_muic;
- }
- i2c_set_clientdata(max8997->muic, max8997);
-
max8997->regmap = devm_regmap_init_i2c(i2c, &max8997_regmap_config);
if (IS_ERR(max8997->regmap)) {
ret = PTR_ERR(max8997->regmap);
@@ -255,33 +184,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
return ret;
}
- max8997->regmap_rtc = devm_regmap_init_i2c(max8997->rtc,
- &max8997_regmap_rtc_config);
- if (IS_ERR(max8997->regmap_rtc)) {
- ret = PTR_ERR(max8997->regmap_rtc);
- dev_err(max8997->dev,
- "failed to allocate register map: %d\n", ret);
- goto err_regmap;
- }
-
- max8997->regmap_haptic = devm_regmap_init_i2c(max8997->haptic,
- &max8997_regmap_haptic_config);
- if (IS_ERR(max8997->regmap_haptic)) {
- ret = PTR_ERR(max8997->regmap_haptic);
- dev_err(max8997->dev,
- "failed to allocate register map: %d\n", ret);
- goto err_regmap;
- }
-
- max8997->regmap_muic = devm_regmap_init_i2c(max8997->muic,
- &max8997_regmap_muic_config);
- if (IS_ERR(max8997->regmap_muic)) {
- ret = PTR_ERR(max8997->regmap_muic);
- dev_err(max8997->dev,
- "failed to allocate register map: %d\n", ret);
- goto err_regmap;
- }
-
ret = regmap_add_irq_chip(max8997->regmap, max8997->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
@@ -291,16 +193,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
goto err_irq;
}
- ret = regmap_add_irq_chip(max8997->regmap_muic, max8997->irq,
- IRQF_ONESHOT | IRQF_SHARED |
- IRQF_TRIGGER_FALLING, 0,
- &max8997_irq_chip_muic,
- &max8997->irq_data_muic);
- if (ret) {
- dev_err(max8997->dev, "failed to add irq chip: %d\n", ret);
- goto err_irq_muic;
- }
-
pm_runtime_set_active(max8997->dev);
ret = mfd_add_devices(max8997->dev, -1, max8997_devs,
@@ -323,16 +215,8 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
err_mfd:
mfd_remove_devices(max8997->dev);
-err_irq_muic:
- regmap_del_irq_chip(max8997->irq, max8997->irq_data_muic);
err_irq:
regmap_del_irq_chip(max8997->irq, max8997->irq_data);
-err_regmap:
- i2c_unregister_device(max8997->muic);
-err_i2c_muic:
- i2c_unregister_device(max8997->haptic);
-err_i2c_haptic:
- i2c_unregister_device(max8997->rtc);
return ret;
}
@@ -342,13 +226,8 @@ static int max8997_i2c_remove(struct i2c_client *i2c)
mfd_remove_devices(max8997->dev);
- regmap_del_irq_chip(max8997->irq, max8997->irq_data_muic);
regmap_del_irq_chip(max8997->irq, max8997->irq_data);
- i2c_unregister_device(max8997->muic);
- i2c_unregister_device(max8997->haptic);
- i2c_unregister_device(max8997->rtc);
-
return 0;
}
@@ -484,34 +363,6 @@ static u8 max8997_dumpaddr_pmic[] = {
MAX8997_REG_DVSOKTIMER5,
};
-static u8 max8997_dumpaddr_muic[] = {
- MAX8997_MUIC_REG_INTMASK1,
- MAX8997_MUIC_REG_INTMASK2,
- MAX8997_MUIC_REG_INTMASK3,
- MAX8997_MUIC_REG_CDETCTRL,
- MAX8997_MUIC_REG_CONTROL1,
- MAX8997_MUIC_REG_CONTROL2,
- MAX8997_MUIC_REG_CONTROL3,
-};
-
-static u8 max8997_dumpaddr_haptic[] = {
- MAX8997_HAPTIC_REG_CONF1,
- MAX8997_HAPTIC_REG_CONF2,
- MAX8997_HAPTIC_REG_DRVCONF,
- MAX8997_HAPTIC_REG_CYCLECONF1,
- MAX8997_HAPTIC_REG_CYCLECONF2,
- MAX8997_HAPTIC_REG_SIGCONF1,
- MAX8997_HAPTIC_REG_SIGCONF2,
- MAX8997_HAPTIC_REG_SIGCONF3,
- MAX8997_HAPTIC_REG_SIGCONF4,
- MAX8997_HAPTIC_REG_SIGDC1,
- MAX8997_HAPTIC_REG_SIGDC2,
- MAX8997_HAPTIC_REG_SIGPWMDC1,
- MAX8997_HAPTIC_REG_SIGPWMDC2,
- MAX8997_HAPTIC_REG_SIGPWMDC3,
- MAX8997_HAPTIC_REG_SIGPWMDC4,
-};
-
static int max8997_freeze(struct device *dev)
{
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
@@ -522,15 +373,6 @@ static int max8997_freeze(struct device *dev)
regmap_read(max8997->regmap, max8997_dumpaddr_pmic[i],
&max8997->reg_dump[i]);
- for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++)
- regmap_read(max8997->regmap_muic, max8997_dumpaddr_muic[i],
- &max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
-
- for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++)
- regmap_read(max8997->regmap_haptic, max8997_dumpaddr_haptic[i],
- &max8997->reg_dump[i + MAX8997_REG_PMIC_END +
- MAX8997_MUIC_REG_END]);
-
return 0;
}
@@ -544,15 +386,6 @@ static int max8997_restore(struct device *dev)
regmap_write(max8997->regmap, max8997_dumpaddr_pmic[i],
max8997->reg_dump[i]);
- for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++)
- regmap_write(max8997->regmap_muic, max8997_dumpaddr_muic[i],
- max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
-
- for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++)
- regmap_write(max8997->regmap_haptic, max8997_dumpaddr_haptic[i],
- max8997->reg_dump[i + MAX8997_REG_PMIC_END +
- MAX8997_MUIC_REG_END]);
-
return 0;
}
diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c
index 22769ea..57fab040 100644
--- a/drivers/rtc/rtc-max8997.c
+++ b/drivers/rtc/rtc-max8997.c
@@ -69,12 +69,20 @@ enum {
struct max8997_rtc_info {
struct device *dev;
struct max8997_dev *max8997;
+ struct i2c_client *i2c;
+ struct regmap *regmap;
struct rtc_device *rtc_dev;
struct mutex lock;
int virq;
int rtc_24hr_mode;
};
+static const struct regmap_config max8997_rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX8997_RTC_REG_END,
+};
+
static void max8997_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
int rtc_24hr_mode)
{
@@ -118,8 +126,7 @@ static inline int max8997_rtc_set_update_reg(struct max8997_rtc_info *info)
{
int ret;
- ret = regmap_write(info->max8997->regmap_rtc,
- MAX8997_RTC_UPDATE1, RTC_UDR_MASK);
+ ret = regmap_write(info->regmap, MAX8997_RTC_UPDATE1, RTC_UDR_MASK);
if (ret < 0)
dev_err(info->dev, "%s: fail to write update reg(%d)\n",
__func__, ret);
@@ -140,7 +147,7 @@ static int max8997_rtc_read_time(struct device *dev, struct rtc_time *tm)
int ret;
mutex_lock(&info->lock);
- ret = regmap_bulk_read(info->max8997->regmap_rtc,
+ ret = regmap_bulk_read(info->regmap,
MAX8997_RTC_SEC, data, RTC_NR_TIME);
mutex_unlock(&info->lock);
@@ -167,7 +174,7 @@ static int max8997_rtc_set_time(struct device *dev, struct rtc_time *tm)
mutex_lock(&info->lock);
- ret = regmap_bulk_write(info->max8997->regmap_rtc,
+ ret = regmap_bulk_write(info->regmap,
MAX8997_RTC_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
@@ -190,7 +197,7 @@ static int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
mutex_lock(&info->lock);
- ret = regmap_bulk_read(info->max8997->regmap_rtc,
+ ret = regmap_bulk_read(info->regmap,
MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
@@ -209,8 +216,7 @@ static int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
}
alrm->pending = 0;
- ret = regmap_read(info->max8997->regmap_rtc,
- MAX8997_REG_STATUS1, &val);
+ ret = regmap_read(info->regmap, MAX8997_REG_STATUS1, &val);
if (ret < 0) {
dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n",
__func__, __LINE__, ret);
@@ -233,7 +239,7 @@ static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info)
if (!mutex_is_locked(&info->lock))
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
- ret = regmap_bulk_read(info->max8997->regmap_rtc,
+ ret = regmap_bulk_read(info->regmap,
MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
@@ -244,7 +250,7 @@ static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info)
for (i = 0; i < RTC_NR_TIME; i++)
data[i] &= ~ALARM_ENABLE_MASK;
- ret = regmap_bulk_write(info->max8997->regmap_rtc,
+ ret = regmap_bulk_write(info->regmap,
MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
@@ -265,7 +271,7 @@ static int max8997_rtc_start_alarm(struct max8997_rtc_info *info)
if (!mutex_is_locked(&info->lock))
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
- ret = regmap_bulk_read(info->max8997->regmap_rtc,
+ ret = regmap_bulk_read(info->regmap,
MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
@@ -284,7 +290,7 @@ static int max8997_rtc_start_alarm(struct max8997_rtc_info *info)
if (data[RTC_DATE] & 0x1f)
data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
- ret = regmap_bulk_write(info->max8997->regmap_rtc,
+ ret = regmap_bulk_write(info->regmap,
MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
@@ -316,7 +322,7 @@ static int max8997_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
if (ret < 0)
goto out;
- ret = regmap_bulk_write(info->max8997->regmap_rtc,
+ ret = regmap_bulk_write(info->regmap,
MAX8997_RTC_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
@@ -388,7 +394,7 @@ static void max8997_rtc_enable_wtsr(struct max8997_rtc_info *info, bool enable)
dev_info(info->dev, "%s: %s WTSR\n", __func__,
enable ? "enable" : "disable");
- ret = regmap_update_bits(info->max8997->regmap_rtc,
+ ret = regmap_update_bits(info->regmap,
MAX8997_RTC_WTSR_SMPL, mask, val);
if (ret < 0) {
dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
@@ -417,7 +423,7 @@ static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable)
dev_info(info->dev, "%s: %s SMPL\n", __func__,
enable ? "enable" : "disable");
- ret = regmap_update_bits(info->max8997->regmap_rtc,
+ ret = regmap_update_bits(info->regmap,
MAX8997_RTC_WTSR_SMPL, mask, val);
if (ret < 0) {
dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
@@ -428,8 +434,7 @@ static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable)
max8997_rtc_set_update_reg(info);
val = 0;
- regmap_read(info->max8997->regmap_rtc,
- MAX8997_RTC_WTSR_SMPL, &val);
+ regmap_read(info->regmap, MAX8997_RTC_WTSR_SMPL, &val);
pr_info("%s: WTSR_SMPL(0x%02x)\n", __func__, val);
}
@@ -444,7 +449,7 @@ static int max8997_rtc_init_reg(struct max8997_rtc_info *info)
info->rtc_24hr_mode = 1;
- ret = regmap_bulk_write(info->max8997->regmap_rtc,
+ ret = regmap_bulk_write(info->regmap,
MAX8997_RTC_CTRLMASK, data, 2);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
@@ -471,13 +476,28 @@ static int max8997_rtc_probe(struct platform_device *pdev)
info->dev = &pdev->dev;
info->max8997 = max8997;
+ info->i2c = i2c_new_dummy(max8997->i2c->adapter, MAX8997_I2C_ADDR_RTC);
+ if (!info->i2c) {
+ dev_err(info->dev, "failed to allocate I2C device\n");
+ return -ENODEV;
+ }
+
+ info->regmap = devm_regmap_init_i2c(info->i2c,
+ &max8997_rtc_regmap_config);
+ if (IS_ERR(info->regmap)) {
+ ret = PTR_ERR(info->regmap);
+ dev_err(info->dev,
+ "failed to allocate register map: %d\n", ret);
+ goto err_regmap;
+ }
+
platform_set_drvdata(pdev, info);
ret = max8997_rtc_init_reg(info);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
- return ret;
+ goto err_regmap;
}
max8997_rtc_enable_wtsr(info, true);
@@ -491,28 +511,41 @@ static int max8997_rtc_probe(struct platform_device *pdev)
if (IS_ERR(info->rtc_dev)) {
ret = PTR_ERR(info->rtc_dev);
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
- return ret;
+ goto err_regmap;
}
virq = regmap_irq_get_virq(max8997->irq_data, MAX8997_PMICIRQ_RTCA1);
if (!virq) {
dev_err(&pdev->dev, "Failed to create mapping alarm IRQ\n");
ret = -ENXIO;
- goto err_out;
+ goto err_regmap;
}
info->virq = virq;
ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
max8997_rtc_alarm_irq, 0,
"rtc-alarm0", info);
- if (ret < 0)
+ if (ret < 0) {
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
info->virq, ret);
+ goto err_regmap;
+ }
+
+ return ret;
-err_out:
+err_regmap:
+ i2c_unregister_device(info->i2c);
return ret;
}
+static int max8997_rtc_remove(struct platform_device *pdev)
+{
+ struct max8997_rtc_info *info = platform_get_drvdata(pdev);
+ i2c_unregister_device(info->i2c);
+
+ return 0;
+}
+
static void max8997_rtc_shutdown(struct platform_device *pdev)
{
struct max8997_rtc_info *info = platform_get_drvdata(pdev);
@@ -532,6 +565,7 @@ static struct platform_driver max8997_rtc_driver = {
.owner = THIS_MODULE,
},
.probe = max8997_rtc_probe,
+ .remove = max8997_rtc_remove,
.shutdown = max8997_rtc_shutdown,
.id_table = rtc_id,
};
diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h
index 2817fa6..c2458c3 100644
--- a/include/linux/mfd/max8997-private.h
+++ b/include/linux/mfd/max8997-private.h
@@ -26,6 +26,13 @@
#include <linux/export.h>
#include <linux/irqdomain.h>
+#define MAX8997_I2C_ADDR_PMIC (0xCC >> 1)
+#define MAX8997_I2C_ADDR_MUIC (0x4A >> 1)
+#define MAX8997_I2C_ADDR_BATTERY (0x6C >> 1)
+#define MAX8997_I2C_ADDR_RTC (0x0C >> 1)
+#define MAX8997_I2C_ADDR_HAPTIC (0x90 >> 1)
+
+
#define MAX8997_REG_INVALID (0xff)
enum max8997_pmic_reg {
@@ -430,21 +437,14 @@ struct max8997_dev {
struct device *dev;
struct max8997_platform_data *pdata;
struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */
- struct i2c_client *rtc; /* slave addr 0x0c */
- struct i2c_client *haptic; /* slave addr 0x90 */
- struct i2c_client *muic; /* slave addr 0x4a */
struct mutex iolock;
unsigned long type;
struct platform_device *battery; /* battery control (not fuel gauge) */
struct regmap *regmap;
- struct regmap *regmap_rtc;
- struct regmap *regmap_haptic;
- struct regmap *regmap_muic;
struct regmap_irq_chip_data *irq_data;
- struct regmap_irq_chip_data *irq_data_muic;
int irq;
int ono;
struct mutex irqlock;
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH v2 1/3] mfd: max8997: use regmap to access registers
From: Krzysztof Kozlowski @ 2014-03-12 14:24 UTC (permalink / raw)
To: Robert Baldyga
Cc: sameo, lee.jones, myungjoo.ham, cw00.choi, dmitry.torokhov,
cooloney, rpurdie, dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo,
paul.gortmaker, linux-kernel, linux-input, linux-leds, rtc-linux,
m.szyprowski
In-Reply-To: <1394631466-6429-2-git-send-email-r.baldyga@samsung.com>
On Wed, 2014-03-12 at 14:37 +0100, Robert Baldyga wrote:
> This patch modifies max8997 driver and each associated function driver,
> to use regmap instead of operating directly on i2c bus. It will allow to
> simplify IRQ handling using regmap-irq.
>
> Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
> ---
> drivers/extcon/extcon-max8997.c | 31 ++++----
> drivers/input/misc/max8997_haptic.c | 34 +++++----
> drivers/leds/leds-max8997.c | 13 ++--
> drivers/mfd/Kconfig | 1 +
> drivers/mfd/max8997-irq.c | 64 +++++++---------
> drivers/mfd/max8997.c | 141 ++++++++++++++++-------------------
> drivers/power/max8997_charger.c | 33 ++++----
> drivers/regulator/max8997.c | 87 +++++++++++----------
> drivers/rtc/rtc-max8997.c | 56 +++++++-------
> include/linux/mfd/max8997-private.h | 17 ++---
> 10 files changed, 228 insertions(+), 249 deletions(-)
Looks fine to me:
Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v2 2/3] mfd: max8997: handle IRQs using regmap
From: Krzysztof Kozlowski @ 2014-03-12 14:26 UTC (permalink / raw)
To: Robert Baldyga
Cc: sameo, lee.jones, myungjoo.ham, cw00.choi, dmitry.torokhov,
cooloney, rpurdie, dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo,
paul.gortmaker, linux-kernel, linux-input, linux-leds, rtc-linux,
m.szyprowski
In-Reply-To: <1394631466-6429-3-git-send-email-r.baldyga@samsung.com>
On Wed, 2014-03-12 at 14:37 +0100, Robert Baldyga wrote:
> This patch modifies mfd driver to use regmap for handling interrupts.
> It allows to simplify irq handling process. This modifications needed
> to make small changes in function drivers, which use interrupts.
>
> Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
(...)
> diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
> index 782d7c9..26a360d 100644
> --- a/drivers/mfd/max8997.c
> +++ b/drivers/mfd/max8997.c
> @@ -64,6 +64,49 @@ static const struct regmap_config max8997_regmap_config = {
> .max_register = MAX8997_REG_PMIC_END,
> };
>
> +static const struct regmap_irq max8997_irqs[] = {
> + /* PMIC_INT1 interrupts */
> + { .reg_offset = 0, .mask = PMIC_INT1_PWRONR_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_PWRONF_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_PWRON1SEC_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_JIGONR_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_JIGONF_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_LOWBAT2_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_LOWBAT1_MASK, },
> + /* PMIC_INT2 interrupts */
> + { .reg_offset = 1, .mask = PMIC_INT2_JIGR_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_JIGF_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_MR_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_DVS1OK_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_DVS2OK_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_DVS3OK_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_DVS4OK_MASK, },
> + /* PMIC_INT3 interrupts */
> + { .reg_offset = 2, .mask = PMIC_INT3_CHGINS_MASK, },
> + { .reg_offset = 2, .mask = PMIC_INT3_CHGRM_MASK, },
> + { .reg_offset = 2, .mask = PMIC_INT3_DCINOVP_MASK, },
> + { .reg_offset = 2, .mask = PMIC_INT3_TOPOFFR_MASK, },
> + { .reg_offset = 2, .mask = PMIC_INT3_CHGRSTF_MASK, },
> + { .reg_offset = 2, .mask = PMIC_INT3_MBCHGTMEXPD_MASK, },
> + /* PMIC_INT4 interrupts */
> + { .reg_offset = 3, .mask = PMIC_INT4_RTC60S_MASK, },
> + { .reg_offset = 3, .mask = PMIC_INT4_RTCA1_MASK, },
> + { .reg_offset = 3, .mask = PMIC_INT4_RTCA2_MASK, },
> + { .reg_offset = 3, .mask = PMIC_INT4_SMPL_INT_MASK, },
> + { .reg_offset = 3, .mask = PMIC_INT4_RTC1S_MASK, },
> + { .reg_offset = 3, .mask = PMIC_INT4_WTSR_MASK, },
> +};
> +
> +static const struct regmap_irq_chip max8997_irq_chip = {
> + .name = "max8997",
> + .status_base = MAX8997_REG_INT1,
> + .mask_base = MAX8997_REG_INT1MSK,
> + .mask_invert = false,
> + .num_regs = 4,
> + .irqs = max8997_irqs,
> + .num_irqs = ARRAY_SIZE(max8997_irqs),
> +};
> +
> static const struct regmap_config max8997_regmap_rtc_config = {
> .reg_bits = 8,
> .val_bits = 8,
> @@ -82,6 +125,31 @@ static const struct regmap_config max8997_regmap_muic_config = {
> .max_register = MAX8997_MUIC_REG_END,
> };
>
> +static const struct regmap_irq max8997_irqs_muic[] = {
> + /* MUIC_INT1 interrupts */
> + { .reg_offset = 0, .mask = MUIC_INT1_ADC_MASK, },
> + { .reg_offset = 0, .mask = MUIC_INT1_ADCLOW_MASK, },
> + { .reg_offset = 0, .mask = MUIC_INT1_ADCERROR_MASK, },
> + /* MUIC_INT2 interrupts */
> + { .reg_offset = 1, .mask = MUIC_INT2_CHGTYP_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_CHGDETRUN_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_DCDTMR_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_DBCHG_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_VBVOLT_MASK, },
> + /* MUIC_INT3 interrupts */
> + { .reg_offset = 2, .mask = MUIC_INT3_OVP_MASK, },
> +};
> +
> +static const struct regmap_irq_chip max8997_irq_chip_muic = {
> + .name = "max8997-muic",
> + .status_base = MAX8997_MUIC_REG_INT1,
> + .mask_base = MAX8997_MUIC_REG_INTMASK1,
> + .mask_invert = true,
> + .num_regs = 3,
> + .irqs = max8997_irqs_muic,
> + .num_irqs = ARRAY_SIZE(max8997_irqs_muic),
> +};
> +
> /*
> * Only the common platform data elements for max8997 are parsed here from the
> * device tree. Other sub-modules of max8997 such as pmic, rtc and others have
> @@ -214,9 +282,26 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
> goto err_regmap;
> }
>
> - pm_runtime_set_active(max8997->dev);
> + ret = regmap_add_irq_chip(max8997->regmap, max8997->irq,
> + IRQF_ONESHOT | IRQF_SHARED |
> + IRQF_TRIGGER_FALLING, 0,
> + &max8997_irq_chip, &max8997->irq_data);
> + if (ret) {
> + dev_err(max8997->dev, "failed to add irq chip: %d\n", ret);
> + goto err_irq;
> + }
In case of error here you shouldn't call regmap_del_irq_chip() so:
goto err_regmap
The regmap_add_irq_chip() may fail before or AFTER setting the value
under max8997->irq_data. However in both error cases the memory stored
there will be freed already by regmap_add_irq_chip().
>
> - max8997_irq_init(max8997);
> + ret = regmap_add_irq_chip(max8997->regmap_muic, max8997->irq,
> + IRQF_ONESHOT | IRQF_SHARED |
> + IRQF_TRIGGER_FALLING, 0,
> + &max8997_irq_chip_muic,
> + &max8997->irq_data_muic);
> + if (ret) {
> + dev_err(max8997->dev, "failed to add irq chip: %d\n", ret);
> + goto err_irq_muic;
Same as above - on error remove only one regmap_irq_chip
(max8997->irq_data), not both.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v2 3/3] mfd: max8997: move regmap handling to function drivers
From: Krzysztof Kozlowski @ 2014-03-12 15:11 UTC (permalink / raw)
To: Robert Baldyga
Cc: sameo, lee.jones, myungjoo.ham, cw00.choi, dmitry.torokhov,
cooloney, rpurdie, dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo,
paul.gortmaker, linux-kernel, linux-input, linux-leds, rtc-linux,
m.szyprowski
In-Reply-To: <1394631466-6429-4-git-send-email-r.baldyga@samsung.com>
On Wed, 2014-03-12 at 14:37 +0100, Robert Baldyga wrote:
> This patch moves code creating new i2c clients and regmaps to function
> drivers which use them. It allows to avoid creating this instances when
> individual function drivers are not enabled.
>
> Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
>
> Conflicts:
>
> drivers/mfd/max8997.c
You can strip out the conflict from commit message.
> ---
> drivers/extcon/extcon-max8997.c | 131 +++++++++++++++++++++++++--
> drivers/input/misc/max8997_haptic.c | 117 +++++++++++++++++++-----
> drivers/mfd/max8997.c | 167 -----------------------------------
> drivers/rtc/rtc-max8997.c | 78 +++++++++++-----
> include/linux/mfd/max8997-private.h | 14 +--
> 5 files changed, 283 insertions(+), 224 deletions(-)
>
> diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
> index 15fc5c0..42c9915 100644
> --- a/drivers/extcon/extcon-max8997.c
> +++ b/drivers/extcon/extcon-max8997.c
> @@ -118,6 +118,9 @@ enum max8997_muic_charger_type {
> struct max8997_muic_info {
> struct device *dev;
> struct max8997_dev *max8997;
> + struct i2c_client *i2c;
> + struct regmap *regmap;
> + struct regmap_irq_chip_data *irq_data;
> struct extcon_dev *edev;
> int prev_cable_type;
> int prev_chg_type;
> @@ -144,6 +147,39 @@ struct max8997_muic_info {
> */
> int path_usb;
> int path_uart;
> +
> + unsigned int reg_dump[MAX8997_MUIC_REG_END];
> +};
> +
> +static const struct regmap_config max8997_muic_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = MAX8997_MUIC_REG_END,
> +};
> +
> +static const struct regmap_irq max8997_irqs_muic[] = {
> + /* MUIC_INT1 interrupts */
> + { .reg_offset = 0, .mask = MUIC_INT1_ADC_MASK, },
> + { .reg_offset = 0, .mask = MUIC_INT1_ADCLOW_MASK, },
> + { .reg_offset = 0, .mask = MUIC_INT1_ADCERROR_MASK, },
> + /* MUIC_INT2 interrupts */
> + { .reg_offset = 1, .mask = MUIC_INT2_CHGTYP_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_CHGDETRUN_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_DCDTMR_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_DBCHG_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_VBVOLT_MASK, },
> + /* MUIC_INT3 interrupts */
> + { .reg_offset = 2, .mask = MUIC_INT3_OVP_MASK, },
> +};
> +
> +static const struct regmap_irq_chip max8997_muic_irq_chip = {
> + .name = "max8997-muic",
> + .status_base = MAX8997_MUIC_REG_INT1,
> + .mask_base = MAX8997_MUIC_REG_INTMASK1,
> + .mask_invert = true,
> + .num_regs = 3,
> + .irqs = max8997_irqs_muic,
> + .num_irqs = ARRAY_SIZE(max8997_irqs_muic),
> };
>
> enum {
> @@ -191,7 +227,7 @@ static int max8997_muic_set_debounce_time(struct max8997_muic_info *info,
> case ADC_DEBOUNCE_TIME_10MS:
> case ADC_DEBOUNCE_TIME_25MS:
> case ADC_DEBOUNCE_TIME_38_62MS:
> - ret = regmap_update_bits(info->max8997->regmap_muic,
> + ret = regmap_update_bits(info->regmap,
> MAX8997_MUIC_REG_CONTROL3,
> CONTROL3_ADCDBSET_MASK,
> time << CONTROL3_ADCDBSET_SHIFT);
> @@ -229,7 +265,7 @@ static int max8997_muic_set_path(struct max8997_muic_info *info,
> else
> ctrl1 = CONTROL1_SW_OPEN;
>
> - ret = regmap_update_bits(info->max8997->regmap_muic,
> + ret = regmap_update_bits(info->regmap,
> MAX8997_MUIC_REG_CONTROL1, COMP_SW_MASK, ctrl1);
> if (ret < 0) {
> dev_err(info->dev, "failed to update MUIC register\n");
> @@ -241,7 +277,7 @@ static int max8997_muic_set_path(struct max8997_muic_info *info,
> else
> ctrl2 |= CONTROL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */
>
> - ret = regmap_update_bits(info->max8997->regmap_muic,
> + ret = regmap_update_bits(info->regmap,
> MAX8997_MUIC_REG_CONTROL2,
> CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK, ctrl2);
> if (ret < 0) {
> @@ -544,7 +580,7 @@ static void max8997_muic_irq_work(struct work_struct *work)
> if (info->irq == muic_irqs[i].virq)
> irq_type = muic_irqs[i].irq;
>
> - ret = regmap_bulk_read(info->max8997->regmap_muic,
> + ret = regmap_bulk_read(info->regmap,
> MAX8997_MUIC_REG_STATUS1, info->status, 2);
> if (ret) {
> dev_err(info->dev, "failed to read muic register\n");
> @@ -606,7 +642,7 @@ static int max8997_muic_detect_dev(struct max8997_muic_info *info)
> mutex_lock(&info->mutex);
>
> /* Read STATUSx register to detect accessory */
> - ret = regmap_bulk_read(info->max8997->regmap_muic,
> + ret = regmap_bulk_read(info->regmap,
> MAX8997_MUIC_REG_STATUS1, info->status, 2);
> if (ret) {
> dev_err(info->dev, "failed to read MUIC register\n");
> @@ -670,6 +706,31 @@ static int max8997_muic_probe(struct platform_device *pdev)
> info->dev = &pdev->dev;
> info->max8997 = max8997;
>
> + info->i2c = i2c_new_dummy(max8997->i2c->adapter, MAX8997_I2C_ADDR_MUIC);
> + if (!info->i2c) {
> + dev_err(info->dev, "failed to allocate I2C device\n");
> + return -ENODEV;
> + }
> +
> + info->regmap = devm_regmap_init_i2c(info->i2c,
> + &max8997_muic_regmap_config);
> + if (IS_ERR(info->regmap)) {
> + ret = PTR_ERR(info->regmap);
> + dev_err(info->dev,
> + "failed to allocate register map: %d\n", ret);
> + goto err_regmap;
> + }
> +
> + ret = regmap_add_irq_chip(info->regmap, max8997->irq,
> + IRQF_ONESHOT | IRQF_SHARED |
> + IRQF_TRIGGER_FALLING, 0,
> + &max8997_muic_irq_chip,
> + &info->irq_data);
> + if (ret) {
> + dev_err(info->dev, "failed to add irq chip: %d\n", ret);
> + goto err_regmap_irq;
> + }
Same issue as in patch 2/3 - regmap_del_irq_chip() shouldn't be called
when add fails.
> +
> platform_set_drvdata(pdev, info);
> mutex_init(&info->mutex);
>
> @@ -679,8 +740,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
> struct max8997_muic_irq *muic_irq = &muic_irqs[i];
> unsigned int virq = 0;
>
> - virq = regmap_irq_get_virq(max8997->irq_data_muic,
> - muic_irq->irq);
> + virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);
> if (!virq) {
> ret = -EINVAL;
> goto err_irq;
> @@ -723,7 +783,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
>
> /* Initialize registers according to platform data */
> for (i = 0; i < muic_pdata->num_init_data; i++) {
> - regmap_write(info->max8997->regmap_muic,
> + regmap_write(info->regmap,
> muic_pdata->init_data[i].addr,
> muic_pdata->init_data[i].data);
> }
> @@ -779,6 +839,10 @@ static int max8997_muic_probe(struct platform_device *pdev)
> err_irq:
> while (--i >= 0)
> free_irq(muic_irqs[i].virq, info);
> +err_regmap_irq:
> + regmap_del_irq_chip(info->max8997->irq, info->irq_data);
> +err_regmap:
> + i2c_unregister_device(info->i2c);
> return ret;
> }
>
> @@ -787,6 +851,8 @@ static int max8997_muic_remove(struct platform_device *pdev)
> struct max8997_muic_info *info = platform_get_drvdata(pdev);
> int i;
>
> + regmap_del_irq_chip(info->max8997->irq, info->irq_data);
> +
> for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
> free_irq(muic_irqs[i].virq, info);
I think the order should be different: first free_irq() then
regmap_del_irq_chip(). At least this is logically expected as the
virtual irqs aren't useful without irq_chip.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v4 0/3] input: appletouch: fixes for jagged/uneven cursor movement
From: Clinton Sprain @ 2014-03-12 23:13 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov, Henrik Rydberg
In-Reply-To: <52D9DE7A.7040509@gmail.com>
input: appletouch: fixes for jagged/uneven cursor movement
The appletouch driver can make some touchpads feel insensitive, with movement that tends to jerk in steps. This is particularly evident when moving diagonally. There is also undesirable movement when additional fingers enter or leave the touchpad.
These patches address these issues by dialing back the fuzz setting, by implementing sensor data smoothing and by discarding movements that directly coincide with a change in the number of fingers detected on the touchpad.
Changes since v3:
1: No change
2: Fix loop counter in atp_calculate_abs
3: No change
Clinton Sprain (3):
input: appletouch: dial back fuzz setting
input: appletouch: implement sensor data smoothing
input: appletouch: fix jumps when additional fingers are detected
drivers/input/mouse/appletouch.c | 114 +++++++++++++++++++++++++++-----------
1 file changed, 83 insertions(+), 31 deletions(-)
--
1.7.9.5
^ permalink raw reply
* [PATCH v4 1/3] input: appletouch: dial back fuzz setting
From: Clinton Sprain @ 2014-03-12 23:14 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov, Henrik Rydberg
In-Reply-To: <5320EA03.2040206@gmail.com>
input: appletouch: dial back fuzz setting
Dials back the default fuzz setting for most devices using this driver, based on values from user feedback from forums and bug reports.
Signed-off-by: Clinton Sprain <clintonsprain@gmail.com>
---
drivers/input/mouse/appletouch.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
index 800ca7d..2745832 100644
--- a/drivers/input/mouse/appletouch.c
+++ b/drivers/input/mouse/appletouch.c
@@ -48,6 +48,7 @@ struct atp_info {
int yfact; /* Y multiplication factor */
int datalen; /* size of USB transfers */
void (*callback)(struct urb *); /* callback function */
+ int fuzz; /* fuzz touchpad generates */
};
static void atp_complete_geyser_1_2(struct urb *urb);
@@ -61,6 +62,7 @@ static const struct atp_info fountain_info = {
.yfact = 43,
.datalen = 81,
.callback = atp_complete_geyser_1_2,
+ .fuzz = 16,
};
static const struct atp_info geyser1_info = {
@@ -71,6 +73,7 @@ static const struct atp_info geyser1_info = {
.yfact = 43,
.datalen = 81,
.callback = atp_complete_geyser_1_2,
+ .fuzz = 16,
};
static const struct atp_info geyser2_info = {
@@ -81,6 +84,7 @@ static const struct atp_info geyser2_info = {
.yfact = 43,
.datalen = 64,
.callback = atp_complete_geyser_1_2,
+ .fuzz = 0,
};
static const struct atp_info geyser3_info = {
@@ -90,6 +94,7 @@ static const struct atp_info geyser3_info = {
.yfact = 64,
.datalen = 64,
.callback = atp_complete_geyser_3_4,
+ .fuzz = 0,
};
static const struct atp_info geyser4_info = {
@@ -99,6 +104,7 @@ static const struct atp_info geyser4_info = {
.yfact = 64,
.datalen = 64,
.callback = atp_complete_geyser_3_4,
+ .fuzz = 0,
};
#define ATP_DEVICE(prod, info) \
@@ -155,9 +161,6 @@ MODULE_DEVICE_TABLE(usb, atp_table);
#define ATP_XSENSORS 26
#define ATP_YSENSORS 16
-/* amount of fuzz this touchpad generates */
-#define ATP_FUZZ 16
-
/* maximum pressure this driver will report */
#define ATP_PRESSURE 300
@@ -455,7 +458,7 @@ static void atp_detect_size(struct atp *dev)
input_set_abs_params(dev->input, ABS_X, 0,
(dev->info->xsensors_17 - 1) *
dev->info->xfact - 1,
- ATP_FUZZ, 0);
+ dev->info->fuzz, 0);
break;
}
}
@@ -843,10 +846,10 @@ static int atp_probe(struct usb_interface *iface,
input_set_abs_params(input_dev, ABS_X, 0,
(dev->info->xsensors - 1) * dev->info->xfact - 1,
- ATP_FUZZ, 0);
+ dev->info->fuzz, 0);
input_set_abs_params(input_dev, ABS_Y, 0,
(dev->info->ysensors - 1) * dev->info->yfact - 1,
- ATP_FUZZ, 0);
+ dev->info->fuzz, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
set_bit(EV_KEY, input_dev->evbit);
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 2/3] input: appletouch: implement sensor data smoothing
From: Clinton Sprain @ 2014-03-12 23:16 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov, Henrik Rydberg
In-Reply-To: <5320EA03.2040206@gmail.com>
input: appletouch: implement sensor data smoothing
Use smoothed version of sensor array data to calculate movement and add weight
to prior values when calculating average. This gives more granular and more
predictable movement.
Signed-off-by: Clinton Sprain <clintonsprain@gmail.com>
---
drivers/input/mouse/appletouch.c | 72 ++++++++++++++++++++++++++++----------
1 file changed, 53 insertions(+), 19 deletions(-)
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
index 2745832..e00f126 100644
--- a/drivers/input/mouse/appletouch.c
+++ b/drivers/input/mouse/appletouch.c
@@ -332,7 +332,11 @@ static void atp_reinit(struct work_struct *work)
static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
int *z, int *fingers)
{
- int i;
+ int i, k;
+ int smooth[nb_sensors + 8];
+ int smooth_tmp[nb_sensors + 8];
+ int scale = 12;
+
/* values to calculate mean */
int pcum = 0, psum = 0;
int is_increasing = 0;
@@ -344,9 +348,6 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
if (is_increasing)
is_increasing = 0;
- continue;
- }
-
/*
* Makes the finger detection more versatile. For example,
* two fingers with no gap will be detected. Also, my
@@ -361,27 +362,60 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
*
* - Jason Parekh <jasonparekh@gmail.com>
*/
- if (i < 1 ||
+
+ } else if (i < 1 ||
(!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
(*fingers)++;
is_increasing = 1;
} else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) {
is_increasing = 0;
}
+ }
- /*
- * Subtracts threshold so a high sensor that just passes the
- * threshold won't skew the calculated absolute coordinate.
- * Fixes an issue where slowly moving the mouse would
- * occasionally jump a number of pixels (slowly moving the
- * finger makes this issue most apparent.)
- */
- pcum += (xy_sensors[i] - threshold) * i;
- psum += (xy_sensors[i] - threshold);
+ /*
+ * Use a smoothed version of sensor data for movement calculations, to
+ * combat noise without needing to rely so heavily on a threshold.
+ * This improves tracking.
+ *
+ * The smoothed array is bigger than the original so that the smoothing
+ * doesn't result in edge values being truncated.
+ */
+
+ for (i = 0; i < 4; i++)
+ smooth[i] = 0;
+ for (i = nb_sensors + 4; i < nb_sensors + 8; i++)
+ smooth[i] = 0;
+
+ /* Pull base values, scaled up to help avoid truncation errors. */
+
+ for (i = 0; i < nb_sensors; i++)
+ smooth[i + 4] = xy_sensors[i] << scale;
+
+ for (k = 0; k < 4; k++) {
+
+ /* Handle edge. */
+ smooth_tmp[0] = (smooth[0] + smooth[1]) >> 1;
+
+ /* Average values with neighbors. */
+ for (i = 1; i < nb_sensors + 7; i++)
+ smooth_tmp[i] = (smooth[i - 1] + smooth[i] * 2 + smooth[i + 1]) >> 2;
+
+ /* Handle other edge. */
+ smooth_tmp[nb_sensors + 7] = (smooth[nb_sensors + 7] + smooth[nb_sensors + 6]) >> 1;
+
+ for (i = 0; i < nb_sensors + 8; i++)
+ smooth[i] = smooth_tmp[i];
+ }
+
+ for (i = 0; i < nb_sensors + 8; i++) {
+ if ((smooth[i] >> scale) > 0) { /* Skip individual values if */
+ pcum += (smooth[i]) * i; /* they are small enough to */
+ psum += (smooth[i]); /* be truncated to 0 by our */
+ } /* scale; mostly just noise. */
}
if (psum > 0) {
- *z = psum;
+ *z = psum >> scale; /* Scale down pressure output. */
return pcum * fact / psum;
}
@@ -559,8 +593,8 @@ static void atp_complete_geyser_1_2(struct urb *urb)
if (x && y) {
if (dev->x_old != -1) {
- x = (dev->x_old * 3 + x) >> 2;
- y = (dev->y_old * 3 + y) >> 2;
+ x = (dev->x_old * 7 + x) >> 3;
+ y = (dev->y_old * 7 + y) >> 3;
dev->x_old = x;
dev->y_old = y;
@@ -671,8 +705,8 @@ static void atp_complete_geyser_3_4(struct urb *urb)
if (x && y) {
if (dev->x_old != -1) {
- x = (dev->x_old * 3 + x) >> 2;
- y = (dev->y_old * 3 + y) >> 2;
+ x = (dev->x_old * 7 + x) >> 3;
+ y = (dev->y_old * 7 + y) >> 3;
dev->x_old = x;
dev->y_old = y;
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 3/3] input: appletouch: fix jumps when additional fingers are detected
From: Clinton Sprain @ 2014-03-12 23:17 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov, Henrik Rydberg
In-Reply-To: <5320EA03.2040206@gmail.com>
input: appletouch: fix jumps when additional fingers are detected
Addresses issues related to when a second finger enters or leaves the field,
causing the cursor to jump or the page to scroll unexpectedly; now, we
discard any movement change that happens at the exact moment we detect a
change in the number of fingers touching the trackpad. This doesn't
completely resolve the issue but does greatly mitigate it.
Signed-off-by: Clinton Sprain <clintonsprain@gmail.com>
---
drivers/input/mouse/appletouch.c | 27 +++++++++++++++++++++------
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
index e00f126..5140293 100644
--- a/drivers/input/mouse/appletouch.c
+++ b/drivers/input/mouse/appletouch.c
@@ -206,6 +206,7 @@ struct atp {
bool valid; /* are the samples valid? */
bool size_detect_done;
bool overflow_warned;
+ int fingers_old; /* last reported finger count */
int x_old; /* last reported x/y, */
int y_old; /* used for smoothing */
signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS];
@@ -508,7 +509,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i, j;
- int key;
+ int key, fingers;
struct atp *dev = urb->context;
int status = atp_status_check(urb);
@@ -591,7 +592,9 @@ static void atp_complete_geyser_1_2(struct urb *urb)
dev->info->yfact, &y_z, &y_f);
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
- if (x && y) {
+ fingers = max(x_f, y_f);
+
+ if (x && y && (fingers == dev->fingers_old)) {
if (dev->x_old != -1) {
x = (dev->x_old * 7 + x) >> 3;
y = (dev->y_old * 7 + y) >> 3;
@@ -608,7 +611,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
input_report_abs(dev->input, ABS_Y, y);
input_report_abs(dev->input, ABS_PRESSURE,
min(ATP_PRESSURE, x_z + y_z));
- atp_report_fingers(dev->input, max(x_f, y_f));
+ atp_report_fingers(dev->input, fingers);
}
dev->x_old = x;
dev->y_old = y;
@@ -616,6 +619,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
} else if (!x && !y) {
dev->x_old = dev->y_old = -1;
+ dev->fingers_old = 0;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
@@ -624,6 +628,10 @@ static void atp_complete_geyser_1_2(struct urb *urb)
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
+ if (fingers != dev->fingers_old)
+ dev->x_old = dev->y_old = -1;
+ dev->fingers_old = fingers;
+
input_report_key(dev->input, BTN_LEFT, key);
input_sync(dev->input);
@@ -641,7 +649,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i, j;
- int key;
+ int key, fingers;
struct atp *dev = urb->context;
int status = atp_status_check(urb);
@@ -703,7 +711,9 @@ static void atp_complete_geyser_3_4(struct urb *urb)
dev->info->yfact, &y_z, &y_f);
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
- if (x && y) {
+ fingers = max(x_f, y_f);
+
+ if (x && y && (fingers == dev->fingers_old)) {
if (dev->x_old != -1) {
x = (dev->x_old * 7 + x) >> 3;
y = (dev->y_old * 7 + y) >> 3;
@@ -720,7 +730,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)
input_report_abs(dev->input, ABS_Y, y);
input_report_abs(dev->input, ABS_PRESSURE,
min(ATP_PRESSURE, x_z + y_z));
- atp_report_fingers(dev->input, max(x_f, y_f));
+ atp_report_fingers(dev->input, fingers);
}
dev->x_old = x;
dev->y_old = y;
@@ -728,6 +738,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)
} else if (!x && !y) {
dev->x_old = dev->y_old = -1;
+ dev->fingers_old = 0;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
@@ -736,6 +747,10 @@ static void atp_complete_geyser_3_4(struct urb *urb)
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
+ if (fingers != dev->fingers_old)
+ dev->x_old = dev->y_old = -1;
+ dev->fingers_old = fingers;
+
input_report_key(dev->input, BTN_LEFT, key);
input_sync(dev->input);
--
1.7.9.5
^ permalink raw reply related
* [PATCH v2 01/06] input synaptics-rmi4: Split F01 definitions into header file
From: Christopher Heiny @ 2014-03-13 1:53 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
Creates rmi_f01.h for use by the firmware update code.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/rmi_f01.c | 96 ++-----------------------------------
drivers/input/rmi4/rmi_f01.h | 110 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 114 insertions(+), 92 deletions(-)
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index ee5f4a1..41cb795 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -13,95 +13,7 @@
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "rmi_driver.h"
-
-#define RMI_PRODUCT_ID_LENGTH 10
-#define RMI_PRODUCT_INFO_LENGTH 2
-
-#define RMI_DATE_CODE_LENGTH 3
-
-#define PRODUCT_ID_OFFSET 0x10
-#define PRODUCT_INFO_OFFSET 0x1E
-
-
-/* Force a firmware reset of the sensor */
-#define RMI_F01_CMD_DEVICE_RESET 1
-
-/* Various F01_RMI_QueryX bits */
-
-#define RMI_F01_QRY1_CUSTOM_MAP (1 << 0)
-#define RMI_F01_QRY1_NON_COMPLIANT (1 << 1)
-#define RMI_F01_QRY1_HAS_LTS (1 << 2)
-#define RMI_F01_QRY1_HAS_SENSOR_ID (1 << 3)
-#define RMI_F01_QRY1_HAS_CHARGER_INP (1 << 4)
-#define RMI_F01_QRY1_HAS_ADJ_DOZE (1 << 5)
-#define RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF (1 << 6)
-#define RMI_F01_QRY1_HAS_PROPS_2 (1 << 7)
-
-#define RMI_F01_QRY5_YEAR_MASK 0x1f
-#define RMI_F01_QRY6_MONTH_MASK 0x0f
-#define RMI_F01_QRY7_DAY_MASK 0x1f
-
-#define RMI_F01_QRY2_PRODINFO_MASK 0x7f
-
-#define RMI_F01_BASIC_QUERY_LEN 21 /* From Query 00 through 20 */
-
-struct f01_basic_properties {
- u8 manufacturer_id;
- bool has_lts;
- bool has_adjustable_doze;
- bool has_adjustable_doze_holdoff;
- char dom[11]; /* YYYY/MM/DD + '\0' */
- u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
- u16 productinfo;
-};
-
-/* F01 device status bits */
-
-/* Most recent device status event */
-#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
-/* The device has lost its configuration for some reason. */
-#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
-
-/* Control register bits */
-
-/*
- * Sleep mode controls power management on the device and affects all
- * functions of the device.
- */
-#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03
-
-#define RMI_SLEEP_MODE_NORMAL 0x00
-#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01
-#define RMI_SLEEP_MODE_RESERVED0 0x02
-#define RMI_SLEEP_MODE_RESERVED1 0x03
-
-#define RMI_IS_VALID_SLEEPMODE(mode) \
- (mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1)
-
-/*
- * This bit disables whatever sleep mode may be selected by the sleep_mode
- * field and forces the device to run at full power without sleeping.
- */
-#define RMI_F01_CRTL0_NOSLEEP_BIT (1 << 2)
-
-/*
- * When this bit is set, the touch controller employs a noise-filtering
- * algorithm designed for use with a connected battery charger.
- */
-#define RMI_F01_CRTL0_CHARGER_BIT (1 << 5)
-
-/*
- * Sets the report rate for the device. The effect of this setting is
- * highly product dependent. Check the spec sheet for your particular
- * touch sensor.
- */
-#define RMI_F01_CRTL0_REPORTRATE_BIT (1 << 6)
-
-/*
- * Written by the host as an indicator that the device has been
- * successfully configured.
- */
-#define RMI_F01_CRTL0_CONFIGURED_BIT (1 << 7)
+#include "rmi_f01.h"
/**
* @ctrl0 - see the bit definitions above.
@@ -136,8 +48,7 @@ struct f01_data {
unsigned int num_of_irq_regs;
};
-static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
- u16 query_base_addr,
+int rmi_f01_read_properties(struct rmi_device *rmi_dev, u16 query_base_addr,
struct f01_basic_properties *props)
{
u8 basic_query[RMI_F01_BASIC_QUERY_LEN];
@@ -180,7 +91,8 @@ static int rmi_f01_probe(struct rmi_function *fn)
{
struct rmi_device *rmi_dev = fn->rmi_dev;
struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
- const struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
+ const struct rmi_device_platform_data *pdata =
+ rmi_get_platform_data(rmi_dev);
struct f01_data *f01;
int error;
u16 ctrl_base_addr = fn->fd.control_base_addr;
diff --git a/drivers/input/rmi4/rmi_f01.h b/drivers/input/rmi4/rmi_f01.h
new file mode 100644
index 0000000..9e5cc2b
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f01.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2014 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _RMI_F01_H
+#define _RMI_F01_H
+
+
+#define RMI_PRODUCT_ID_LENGTH 10
+#define RMI_PRODUCT_INFO_LENGTH 2
+
+#define RMI_DATE_CODE_LENGTH 3
+
+#define PRODUCT_ID_OFFSET 0x10
+#define PRODUCT_INFO_OFFSET 0x1E
+
+
+/* Force a firmware reset of the sensor */
+#define RMI_F01_CMD_DEVICE_RESET 1
+
+/* Various F01_RMI_QueryX bits */
+
+#define RMI_F01_QRY1_CUSTOM_MAP (1 << 0)
+#define RMI_F01_QRY1_NON_COMPLIANT (1 << 1)
+#define RMI_F01_QRY1_HAS_LTS (1 << 2)
+#define RMI_F01_QRY1_HAS_SENSOR_ID (1 << 3)
+#define RMI_F01_QRY1_HAS_CHARGER_INP (1 << 4)
+#define RMI_F01_QRY1_HAS_ADJ_DOZE (1 << 5)
+#define RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF (1 << 6)
+#define RMI_F01_QRY1_HAS_PROPS_2 (1 << 7)
+
+#define RMI_F01_QRY5_YEAR_MASK 0x1f
+#define RMI_F01_QRY6_MONTH_MASK 0x0f
+#define RMI_F01_QRY7_DAY_MASK 0x1f
+
+#define RMI_F01_QRY2_PRODINFO_MASK 0x7f
+
+#define RMI_F01_BASIC_QUERY_LEN 21 /* From Query 00 through 20 */
+
+struct f01_basic_properties {
+ u8 manufacturer_id;
+ bool has_lts;
+ bool has_adjustable_doze;
+ bool has_adjustable_doze_holdoff;
+ char dom[11]; /* YYYY/MM/DD + '\0' */
+ u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+ u16 productinfo;
+};
+
+/* F01 device status bits */
+
+/* Most recent device status event */
+#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
+/* The device has lost its configuration for some reason. */
+#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
+
+/* Control register bits */
+
+/*
+ * Sleep mode controls power management on the device and affects all
+ * functions of the device.
+ */
+#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03
+
+#define RMI_SLEEP_MODE_NORMAL 0x00
+#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01
+#define RMI_SLEEP_MODE_RESERVED0 0x02
+#define RMI_SLEEP_MODE_RESERVED1 0x03
+
+#define RMI_IS_VALID_SLEEPMODE(mode) \
+ (mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1)
+
+/*
+ * This bit disables whatever sleep mode may be selected by the sleep_mode
+ * field and forces the device to run at full power without sleeping.
+ */
+#define RMI_F01_CRTL0_NOSLEEP_BIT (1 << 2)
+
+/*
+ * When this bit is set, the touch controller employs a noise-filtering
+ * algorithm designed for use with a connected battery charger.
+ */
+#define RMI_F01_CRTL0_CHARGER_BIT (1 << 5)
+
+/*
+ * Sets the report rate for the device. The effect of this setting is
+ * highly product dependent. Check the spec sheet for your particular
+ * touch sensor.
+ */
+#define RMI_F01_CRTL0_REPORTRATE_BIT (1 << 6)
+
+/*
+ * Written by the host as an indicator that the device has been
+ * successfully configured.
+ */
+#define RMI_F01_CRTL0_CONFIGURED_BIT (1 << 7)
+
+/** Read the F01 query registers and populate the basic_properties structure.
+ * @rmi_dev - the device to be queries.
+ * @query_base_addr - address of the start of the query registers.
+ * @props - pointer to the structure to be filled in.
+ */
+int rmi_f01_read_properties(struct rmi_device *rmi_dev, u16 query_base_addr,
+ struct f01_basic_properties *props);
+
+#endif
^ permalink raw reply related
* [PATCH v2 04/06] input synaptics-rmi4: rmi_driver - Export some symbols and functions
From: Christopher Heiny @ 2014-03-13 1:53 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
In-Reply-To: <1394675637-23853-1-git-send-email-cheiny@synaptics.com>
Firmware update functionality will need to unload the existing
functions and rescan the PDT before starting the update process;
then reload the functions afterwards.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Signed-off-by: Vincent Huang <vincent.huang@tw.synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/rmi_driver.c | 165 ++++++++++++++++++++++------------------
drivers/input/rmi4/rmi_driver.h | 22 +++---
2 files changed, 101 insertions(+), 86 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 2172c80..70410e8 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -31,14 +31,20 @@
#include <uapi/linux/input.h>
#include "rmi_bus.h"
#include "rmi_driver.h"
+#include "rmi_f01.h"
#define HAS_NONSTANDARD_PDT_MASK 0x40
#define RMI4_MAX_PAGE 0xff
#define RMI4_PAGE_SIZE 0x100
#define RMI4_PAGE_MASK 0xFF00
-#define RMI_DEVICE_RESET_CMD 0x01
-#define DEFAULT_RESET_DELAY_MS 100
+#define RMI_PDT_ENTRY_SIZE 6
+#define RMI_PDT_FUNCTION_VERSION_MASK 0x60
+#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+#define PDT_END_SCAN_LOCATION 0x0005
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
#define DEFAULT_POLL_INTERVAL_MS 13
@@ -174,7 +180,7 @@ static int enable_sensor(struct rmi_device *rmi_dev)
return process_interrupt_requests(rmi_dev);
}
-static void rmi_free_function_list(struct rmi_device *rmi_dev)
+void rmi_free_function_list(struct rmi_device *rmi_dev)
{
struct rmi_function *fn, *tmp;
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
@@ -435,8 +441,8 @@ static int rmi_driver_reset_handler(struct rmi_device *rmi_dev)
return 0;
}
-int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
- u16 pdt_address)
+static int rmi_read_pdt_entry(struct rmi_device *rmi_dev,
+ struct pdt_entry *entry, u16 pdt_address)
{
u8 buf[RMI_PDT_ENTRY_SIZE];
int error;
@@ -459,7 +465,6 @@ int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
return 0;
}
-EXPORT_SYMBOL_GPL(rmi_read_pdt_entry);
static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt,
struct rmi_function_descriptor *fd)
@@ -473,9 +478,6 @@ static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt,
fd->function_version = pdt->function_version;
}
-#define RMI_SCAN_CONTINUE 0
-#define RMI_SCAN_DONE 1
-
static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
int page,
void *ctx,
@@ -508,10 +510,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
return data->f01_bootloader_mode ? RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
}
-static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
- int (*callback)(struct rmi_device *rmi_dev,
- void *ctx,
- const struct pdt_entry *entry))
+int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
+ int (*callback)(struct rmi_device *rmi_dev,
+ void *ctx, const struct pdt_entry *entry))
{
int page;
int retval = RMI_SCAN_DONE;
@@ -525,16 +526,13 @@ static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
return retval < 0 ? retval : 0;
}
-/* Indicates that flash programming is enabled (bootloader mode). */
-#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40))
-
/*
* Given the PDT entry for F01, read the device status register to determine
* if we're stuck in bootloader mode or not.
*
*/
-static int rmi_check_bootloader_mode(struct rmi_device *rmi_dev,
- const struct pdt_entry *pdt)
+int rmi_check_bootloader_mode(struct rmi_device *rmi_dev,
+ const struct pdt_entry *pdt)
{
int error;
u8 device_status;
@@ -575,7 +573,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev,
if (pdt->function_number == 0x01) {
u16 cmd_addr = pdt->page_start + pdt->command_base_addr;
- u8 cmd_buf = RMI_DEVICE_RESET_CMD;
+ u8 cmd_buf = RMI_F01_CMD_DEVICE_RESET;
const struct rmi_device_platform_data *pdata =
rmi_get_platform_data(rmi_dev);
@@ -586,7 +584,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev,
return error;
}
- mdelay(pdata->reset_delay_ms ?: DEFAULT_RESET_DELAY_MS);
+ mdelay(pdata->reset_delay_ms ?: RMI_F01_DEFAULT_RESET_DELAY_MS);
return RMI_SCAN_DONE;
}
@@ -728,15 +726,80 @@ static int rmi_driver_remove(struct device *dev)
return 0;
}
+int rmi_driver_detect_functions(struct rmi_device *rmi_dev)
+{
+ int error;
+ int irq_count;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ void *irq_memory;
+ size_t size;
+
+ /*
+ * We need to count the IRQs and allocate their storage before scanning
+ * the PDT and creating the function entries, because adding a new
+ * function can trigger events that result in the IRQ related storage
+ * being accessed.
+ */
+ dev_dbg(&rmi_dev->dev, "Counting IRQs.\n");
+ irq_count = 0;
+ error = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs);
+ if (error) {
+ error = -ENODEV;
+ dev_err(&rmi_dev->dev, "IRQ counting failed with code %d.\n",
+ error);
+ goto err_free_mem;
+ }
+ data->irq_count = irq_count;
+ data->num_of_irq_regs = (data->irq_count + 7) / 8;
+
+ size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long);
+ irq_memory = kzalloc(size * 4, GFP_KERNEL);
+ if (!irq_memory) {
+ dev_err(&rmi_dev->dev, "Failed to allocate memory for irq masks.\n");
+ goto err_free_mem;
+ }
+ kfree(data->irq_status); /* free the memory if it's allocated already */
+ data->irq_status = irq_memory + size * 0;
+ data->fn_irq_bits = irq_memory + size * 1;
+ data->current_irq_mask = irq_memory + size * 2;
+ data->new_irq_mask = irq_memory + size * 3;
+
+ irq_count = 0;
+ dev_dbg(&rmi_dev->dev, "Creating functions.");
+ error = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Function creation failed with code %d.\n",
+ error);
+ goto err_destroy_functions;
+ }
+ if (!data->f01_container) {
+ dev_err(&rmi_dev->dev, "missing F01 container!\n");
+ error = -EINVAL;
+ goto err_destroy_functions;
+ }
+ error = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->current_irq_mask, data->num_of_irq_regs);
+ if (error) {
+ dev_err(&rmi_dev->dev, "%s: Failed to read current IRQ mask.\n",
+ __func__);
+ goto err_destroy_functions;
+ }
+ return 0;
+
+err_destroy_functions:
+ rmi_free_function_list(rmi_dev);
+ kfree(irq_memory);
+err_free_mem:
+ return error;
+}
+
static int rmi_driver_probe(struct device *dev)
{
struct rmi_driver *rmi_driver;
struct rmi_driver_data *data;
const struct rmi_device_platform_data *pdata;
struct rmi_device *rmi_dev;
- size_t size;
- void *irq_memory;
- int irq_count;
int retval;
dev_dbg(dev, "%s: Starting probe.\n", __func__);
@@ -796,59 +859,11 @@ static int rmi_driver_probe(struct device *dev)
dev_warn(dev, "Could not read PDT properties from %#06x (code %d). Assuming 0x00.\n",
PDT_PROPERTIES_LOCATION, retval);
}
-
- /*
- * We need to count the IRQs and allocate their storage before scanning
- * the PDT and creating the function entries, because adding a new
- * function can trigger events that result in the IRQ related storage
- * being accessed.
- */
- dev_dbg(dev, "Counting IRQs.\n");
- irq_count = 0;
- retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs);
- if (retval < 0) {
- dev_err(dev, "IRQ counting for %s failed with code %d.\n",
- pdata->sensor_name, retval);
- goto err_free_mem;
- }
- data->irq_count = irq_count;
- data->num_of_irq_regs = (data->irq_count + 7) / 8;
-
mutex_init(&data->irq_mutex);
- size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long);
- irq_memory = kzalloc(size * 4, GFP_KERNEL);
- if (!irq_memory) {
- dev_err(dev, "Failed to allocate memory for irq masks.\n");
- goto err_free_mem;
- }
-
- data->irq_status = irq_memory + size * 0;
- data->fn_irq_bits = irq_memory + size * 1;
- data->current_irq_mask = irq_memory + size * 2;
- data->new_irq_mask = irq_memory + size * 3;
-
- irq_count = 0;
- dev_dbg(dev, "Creating functions.");
- retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function);
- if (retval < 0) {
- dev_err(dev, "Function creation failed with code %d.\n",
- retval);
- goto err_destroy_functions;
- }
-
- if (!data->f01_container) {
- dev_err(dev, "Missing F01 container!\n");
- retval = -EINVAL;
- goto err_destroy_functions;
- }
-
- retval = rmi_read_block(rmi_dev,
- data->f01_container->fd.control_base_addr + 1,
- data->current_irq_mask, data->num_of_irq_regs);
- if (retval < 0) {
- dev_err(dev, "%s: Failed to read current IRQ mask.\n",
- __func__);
+ retval = rmi_driver_detect_functions(rmi_dev);
+ if (retval) {
+ dev_err(dev, "Failed to detect functions!\n");
goto err_destroy_functions;
}
@@ -918,8 +933,6 @@ static int rmi_driver_probe(struct device *dev)
err_destroy_functions:
rmi_free_function_list(rmi_dev);
- kfree(irq_memory);
-err_free_mem:
if (data->gpio_held)
gpio_free(pdata->attn_gpio);
kfree(data);
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 34f7a7d..2291591 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -84,14 +84,6 @@ struct rmi_driver_data {
void *data;
};
-#define RMI_PDT_ENTRY_SIZE 6
-#define RMI_PDT_FUNCTION_VERSION_MASK 0x60
-#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07
-
-#define PDT_START_SCAN_LOCATION 0x00e9
-#define PDT_END_SCAN_LOCATION 0x0005
-#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
-
struct pdt_entry {
u16 page_start;
u8 query_base_addr;
@@ -103,8 +95,12 @@ struct pdt_entry {
u8 function_number;
};
-int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
- u16 pdt_address);
+#define RMI_SCAN_CONTINUE 0
+#define RMI_SCAN_DONE 1
+
+int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
+ int (*callback)(struct rmi_device *rmi_dev,
+ void *ctx, const struct pdt_entry *entry));
bool rmi_is_physical_driver(struct device_driver *);
int rmi_register_physical_driver(void);
@@ -113,4 +109,10 @@ void rmi_unregister_physical_driver(void);
int rmi_register_f01_handler(void);
void rmi_unregister_f01_handler(void);
+int rmi_check_bootloader_mode(struct rmi_device *rmi_dev,
+ const struct pdt_entry *pdt);
+void rmi_free_function_list(struct rmi_device *rmi_dev);
+int rmi_driver_detect_functions(struct rmi_device *rmi_dev);
+
+
#endif
^ permalink raw reply related
* [PATCH v2 03/06] input synaptics-rmi4: rmi_f01 - Fix a comment, add a diagnostic message.
From: Christopher Heiny @ 2014-03-13 1:53 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
In-Reply-To: <1394675637-23853-1-git-send-email-cheiny@synaptics.com>
In debugging certain touch sensor failures, it's useful to know
whether the device is stuck in bootloader, so print a message
to that effect.
Also, point to the actual location of the defs for the F01 CTRL0
bitfields.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/rmi_f01.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index 8504865..a078d7d 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -16,7 +16,7 @@
#include "rmi_f01.h"
/**
- * @ctrl0 - see the bit definitions above.
+ * @ctrl0 - see the bit definitions in rmi_f01.h.
* @doze_interval - controls the interval between checks for finger presence
* when the touch sensor is in doze mode, in units of 10ms.
* @wakeup_threshold - controls the capacitance threshold at which the touch
@@ -415,6 +415,13 @@ static int rmi_f01_probe(struct rmi_function *fn)
return error;
}
+ driver_data->f01_bootloader_mode =
+ RMI_F01_STATUS_BOOTLOADER(device_status);
+ if (driver_data->f01_bootloader_mode)
+ dev_warn(&rmi_dev->dev,
+ "WARNING: RMI4 device is in bootloader mode!\n");
+
+
if (RMI_F01_STATUS_UNCONFIGURED(device_status)) {
dev_err(&fn->dev,
"Device was reset during configuration process, status: %#02x!\n",
^ permalink raw reply related
* [PATCH v2 02/06] input synaptics-rmi4: Add some more F01 properties
From: Christopher Heiny @ 2014-03-13 1:53 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
In-Reply-To: <1394675637-23853-1-git-send-email-cheiny@synaptics.com>
The firmware update code will need access to the firmware
build ID and some other info. As long as we're taking the
scenic route to the firmware build ID, we remember a number
of other interesting settings for use by diagnostic modules.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/rmi_f01.c | 170 ++++++++++++++++++++++++++++++++++++++++---
drivers/input/rmi4/rmi_f01.h | 101 +++++++++++++++++++++++++
2 files changed, 262 insertions(+), 9 deletions(-)
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index 41cb795..8504865 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -48,11 +48,18 @@ struct f01_data {
unsigned int num_of_irq_regs;
};
+#define PACKAGE_ID_BYTES 4
+#define BUILD_ID_BYTES 3
+
int rmi_f01_read_properties(struct rmi_device *rmi_dev, u16 query_base_addr,
struct f01_basic_properties *props)
{
u8 basic_query[RMI_F01_BASIC_QUERY_LEN];
+ u8 info_buf[4];
int error;
+ int i;
+ u16 query_addr = query_base_addr;
+ u16 prod_info_addr;
error = rmi_read_block(rmi_dev, query_base_addr,
basic_query, sizeof(basic_query));
@@ -70,19 +77,164 @@ int rmi_f01_read_properties(struct rmi_device *rmi_dev, u16 query_base_addr,
basic_query[1] & RMI_F01_QRY1_HAS_ADJ_DOZE;
props->has_adjustable_doze_holdoff =
basic_query[1] & RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF;
+ props->has_query42 = basic_query[1] & RMI_F01_QRY1_HAS_PROPS_2;
+
+ props->productinfo =
+ ((basic_query[2] & RMI_F01_QRY2_PRODINFO_MASK) << 7) |
+ (basic_query[3] & RMI_F01_QRY2_PRODINFO_MASK);
snprintf(props->dom, sizeof(props->dom), "20%02d/%02d/%02d",
basic_query[5] & RMI_F01_QRY5_YEAR_MASK,
basic_query[6] & RMI_F01_QRY6_MONTH_MASK,
basic_query[7] & RMI_F01_QRY7_DAY_MASK);
- memcpy(props->product_id, &basic_query[11],
- RMI_PRODUCT_ID_LENGTH);
+ memcpy(props->product_id, &basic_query[11], RMI_PRODUCT_ID_LENGTH);
props->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+ query_addr += 11;
- props->productinfo =
- ((basic_query[2] & RMI_F01_QRY2_PRODINFO_MASK) << 7) |
- (basic_query[3] & RMI_F01_QRY2_PRODINFO_MASK);
+
+ error = rmi_read_block(rmi_dev, query_addr, props->product_id,
+ RMI_PRODUCT_ID_LENGTH);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read product ID.\n");
+ return error;
+ }
+ props->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+
+ /* We'll come back and use this later, depending on some other query
+ * bits.
+ */
+ prod_info_addr = query_addr + 6;
+
+ query_addr += RMI_PRODUCT_ID_LENGTH;
+ if (props->has_lts) {
+ error = rmi_read(rmi_dev, query_addr, info_buf);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read LTS info.\n");
+ return error;
+ }
+ props->slave_asic_rows = info_buf[0] &
+ RMI_F01_QRY21_SLAVE_ROWS_MASK;
+ props->slave_asic_columns = (info_buf[1] &
+ RMI_F01_QRY21_SLAVE_COLUMNS_MASK) >> 3;
+ query_addr++;
+ }
+
+ if (props->has_sensor_id) {
+ error = rmi_read(rmi_dev, query_addr, &props->sensor_id);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read sensor ID.\n");
+ return error;
+ }
+ query_addr++;
+ }
+
+ /* Maybe skip a block of undefined LTS registers. */
+ if (props->has_lts)
+ query_addr += RMI_F01_LTS_RESERVED_SIZE;
+
+ if (props->has_query42) {
+ error = rmi_read(rmi_dev, query_addr, info_buf);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read additional properties.\n");
+ return error;
+ }
+ props->has_ds4_queries = info_buf[0] &
+ RMI_F01_QRY42_DS4_QUERIES;
+ props->has_multi_physical = info_buf[0] &
+ RMI_F01_QRY42_MULTI_PHYS;
+ props->has_guest = info_buf[0] & RMI_F01_QRY42_GUEST;
+ props->has_swr = info_buf[0] & RMI_F01_QRY42_SWR;
+ props->has_nominal_report_rate = info_buf[0] &
+ RMI_F01_QRY42_NOMINAL_REPORT;
+ props->has_recalibration_interval = info_buf[0] &
+ RMI_F01_QRY42_RECAL_INTERVAL;
+ query_addr++;
+ }
+
+ if (props->has_ds4_queries) {
+ error = rmi_read(rmi_dev, query_addr, &props->ds4_query_length);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read DS4 query length size.\n");
+ return error;
+ }
+ query_addr++;
+ }
+
+ for (i = 1; i <= props->ds4_query_length; i++) {
+ u8 val;
+ error = rmi_read(rmi_dev, query_addr, &val);
+ query_addr++;
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read F01_RMI_QUERY43.%02d, code: %d.\n",
+ i, error);
+ continue;
+ }
+ switch (i) {
+ case 1:
+ props->has_package_id_query = val &
+ RMI_F01_QRY43_01_PACKAGE_ID;
+ props->has_build_id_query = val &
+ RMI_F01_QRY43_01_BUILD_ID;
+ props->has_reset_query = val & RMI_F01_QRY43_01_RESET;
+ props->has_maskrev_query = val &
+ RMI_F01_QRY43_01_PACKAGE_ID;
+ break;
+ case 2:
+ props->has_i2c_control = val & RMI_F01_QRY43_02_I2C_CTL;
+ props->has_spi_control = val & RMI_F01_QRY43_02_SPI_CTL;
+ props->has_attn_control = val &
+ RMI_F01_QRY43_02_ATTN_CTL;
+ props->has_win8_vendor_info = val &
+ RMI_F01_QRY43_02_WIN8;
+ props->has_timestamp = val & RMI_F01_QRY43_02_TIMESTAMP;
+ break;
+ case 3:
+ props->has_tool_id_query = val &
+ RMI_F01_QRY43_03_TOOL_ID;
+ props->has_fw_revision_query = val &
+ RMI_F01_QRY43_03_FW_REVISION;
+ break;
+ default:
+ dev_warn(&rmi_dev->dev, "No handling for F01_RMI_QUERY43.%02d.\n",
+ i);
+ }
+ }
+
+ /* If present, the ASIC package ID registers are overlaid on the
+ * product ID. Go back to the right address (saved previously) and
+ * read them.
+ */
+ if (props->has_package_id_query) {
+ error = rmi_read_block(rmi_dev, prod_info_addr, info_buf,
+ PACKAGE_ID_BYTES);
+ if (error < 0)
+ dev_warn(&rmi_dev->dev, "Failed to read package ID.\n");
+ else {
+ u16 *val = (u16 *)info_buf;
+ props->package_id = le16_to_cpu(*val);
+ val = (u16 *)(info_buf + 2);
+ props->package_rev = le16_to_cpu(*val);
+ }
+ }
+ prod_info_addr++;
+
+ /* The firmware build id (if present) is similarly overlaid on product
+ * ID registers. Go back again and read that data.
+ */
+ if (props->has_build_id_query) {
+ error = rmi_read_block(rmi_dev, prod_info_addr, info_buf,
+ BUILD_ID_BYTES);
+ if (error < 0)
+ dev_warn(&rmi_dev->dev, "Failed to read FW build ID.\n");
+ else {
+ u16 *val = (u16 *)info_buf;
+ props->build_id = le16_to_cpu(*val);
+ props->build_id += info_buf[2] * 65536;
+ dev_info(&rmi_dev->dev, "FW build ID: %#08x (%u).\n",
+ props->build_id, props->build_id);
+ }
+ }
return 0;
}
@@ -164,10 +316,10 @@ static int rmi_f01_probe(struct rmi_function *fn)
dev_err(&fn->dev, "Failed to read F01 properties.\n");
return error;
}
-
- dev_info(&fn->dev, "found RMI device, manufacturer: %s, product: %s\n",
- f01->properties.manufacturer_id == 1 ? "Synaptics" : "unknown",
- f01->properties.product_id);
+ dev_info(&fn->dev, "found RMI device, manufacturer: %s, product: %s, fw build: %d.\n",
+ f01->properties.manufacturer_id == 1 ?
+ "Synaptics" : "unknown",
+ f01->properties.product_id, f01->properties.build_id);
/* Advance to interrupt control registers, then skip over them. */
ctrl_base_addr++;
diff --git a/drivers/input/rmi4/rmi_f01.h b/drivers/input/rmi4/rmi_f01.h
index 9e5cc2b..af82fb7 100644
--- a/drivers/input/rmi4/rmi_f01.h
+++ b/drivers/input/rmi4/rmi_f01.h
@@ -21,6 +21,9 @@
/* Force a firmware reset of the sensor */
#define RMI_F01_CMD_DEVICE_RESET 1
+#define RMI_F01_DEFAULT_RESET_DELAY_MS 100
+
+#define F01_SERIALIZATION_SIZE 7
/* Various F01_RMI_QueryX bits */
@@ -41,20 +44,118 @@
#define RMI_F01_BASIC_QUERY_LEN 21 /* From Query 00 through 20 */
+#define RMI_F01_QRY21_SLAVE_ROWS_MASK 0x07
+#define RMI_F01_QRY21_SLAVE_COLUMNS_MASK 0x38
+
+#define RMI_F01_LTS_RESERVED_SIZE 19
+
+#define RMI_F01_QRY42_DS4_QUERIES (1 << 0)
+#define RMI_F01_QRY42_MULTI_PHYS (1 << 1)
+#define RMI_F01_QRY42_GUEST (1 << 2)
+#define RMI_F01_QRY42_SWR (1 << 3)
+#define RMI_F01_QRY42_NOMINAL_REPORT (1 << 4)
+#define RMI_F01_QRY42_RECAL_INTERVAL (1 << 5)
+
+#define RMI_F01_QRY43_01_PACKAGE_ID (1 << 0)
+#define RMI_F01_QRY43_01_BUILD_ID (1 << 1)
+#define RMI_F01_QRY43_01_RESET (1 << 2)
+#define RMI_F01_QRY43_01_MASK_REV (1 << 3)
+
+#define RMI_F01_QRY43_02_I2C_CTL (1 << 0)
+#define RMI_F01_QRY43_02_SPI_CTL (1 << 1)
+#define RMI_F01_QRY43_02_ATTN_CTL (1 << 2)
+#define RMI_F01_QRY43_02_WIN8 (1 << 3)
+#define RMI_F01_QRY43_02_TIMESTAMP (1 << 4)
+
+#define RMI_F01_QRY43_03_TOOL_ID (1 << 0)
+#define RMI_F01_QRY43_03_FW_REVISION (1 << 1)
+
+#define RMI_F01_QRY44_RST_ENABLED (1 << 0)
+#define RMI_F01_QRY44_RST_POLARITY (1 << 1)
+#define RMI_F01_QRY44_PULLUP_ENABLED (1 << 2)
+#define RMI_F01_QRY44_RST_PIN_MASK 0xF0
+
+#define RMI_TOOL_ID_LENGTH 16
+#define RMI_FW_REVISION_LENGTH 16
+
struct f01_basic_properties {
u8 manufacturer_id;
bool has_lts;
+ bool has_sensor_id;
bool has_adjustable_doze;
bool has_adjustable_doze_holdoff;
+ bool has_query42;
char dom[11]; /* YYYY/MM/DD + '\0' */
u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
u16 productinfo;
+ u16 package_id;
+ u16 package_rev;
+ u32 build_id;
+
+ /* These are meaningful only if has_lts is true. */
+ u8 slave_asic_rows;
+ u8 slave_asic_columns;
+
+ /* This is meaningful only if has_sensor_id is true. */
+ u8 sensor_id;
+
+ /* These are meaningful only if has_query42 is true. */
+ bool has_ds4_queries;
+ bool has_multi_physical;
+ bool has_guest;
+ bool has_swr;
+ bool has_nominal_report_rate;
+ bool has_recalibration_interval;
+
+ /* Tells how many of the Query43.xx registers are present.
+ */
+ u8 ds4_query_length;
+
+ /* Query 43.1 */
+ bool has_package_id_query;
+ bool has_build_id_query;
+ bool has_reset_query;
+ bool has_maskrev_query;
+
+ /* Query 43.2 */
+ bool has_i2c_control;
+ bool has_spi_control;
+ bool has_attn_control;
+ bool has_win8_vendor_info;
+ bool has_timestamp;
+
+ /* Query 43.3 */
+ bool has_tool_id_query;
+ bool has_fw_revision_query;
+
+ /* Query 44 */
+ bool reset_enabled;
+ bool reset_polarity;
+ bool pullup_enabled;
+ u8 reset_pin;
+
+ /* Query 45 */
+ char tool_id[RMI_TOOL_ID_LENGTH + 1];
+
+ /* Query 46 */
+ char fw_revision[RMI_FW_REVISION_LENGTH + 1];
};
+
+/** Read the F01 query registers and populate the basic_properties structure.
+ * @rmi_dev - the device to be queries.
+ * @query_base_addr - address of the start of the query registers.
+ * @props - pointer to the structure to be filled in.
+ */
+int rmi_f01_read_properties(struct rmi_device *rmi_dev, u16 query_base_addr,
+ struct f01_basic_properties *props);
+
/* F01 device status bits */
/* Most recent device status event */
#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
+/* Indicates that flash programming is enabled (bootloader mode). */
+#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40))
/* The device has lost its configuration for some reason. */
#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
^ permalink raw reply related
* [PATCH v2 05/06] input synaptics-rmi4: Add firmware update support
From: Christopher Heiny @ 2014-03-13 1:53 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
In-Reply-To: <1394675637-23853-1-git-send-email-cheiny@synaptics.com>
Add support for updating firmware on RMI4 devices with V5 bootloader.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Signed-off-by: Vincent Huang <vincent.huang@tw.synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
The next patch in this series converts this code to use
request_firmware_nowait(). We split that change off to
make it easier to check differences between this and the
v1 patch.
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index d0c7b6e..ac0c2b1 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -25,6 +25,24 @@ config RMI4_DEBUG
If unsure, say N.
+config RMI4_FW_UPDATE
+ bool "RMI4 Firmware Update"
+ depends on RMI4_CORE
+ help
+ Say Y here to enable in-kernel firmware update capability.
+
+ This allows you to update an RMI4 device's firmware using the
+ kernel request_firmware() facility. Control is provided via
+ a sysfs interface. Write 1 to /sys/devices/sensorXX/update_fw
+ to cause an update. The image file name will be derived from
+ the F01 product ID value; write a different name to
+ .../sensorXX/fw_img_name override that. The update will only
+ happen if the provided image appears to be newer than the
+ one currently running on the RMI4 device; write 1 to
+ ../sensorXX/fw_force_update to override that (this might be
+ required for older devices that don't implement the
+ necessary RMI4 queries).
+
config RMI4_I2C
tristate "RMI4 I2C Support"
depends on RMI4_CORE && I2C
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
index 5c6bad5..ce9a7b6 100644
--- a/drivers/input/rmi4/Makefile
+++ b/drivers/input/rmi4/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_RMI4_CORE) += rmi_core.o
rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o
+rmi_core-$(CONFIG_RMI4_FW_UPDATE) += rmi_fw_update.o
# Function drivers
obj-$(CONFIG_RMI4_F11) += rmi_f11.o
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index 6e0454a..aaff1e9 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -117,6 +117,8 @@ int rmi_register_transport_device(struct rmi_transport_dev *xport)
if (error)
goto err_put_device;
+ rmi_fw_update_init(rmi_dev);
+
dev_dbg(xport->dev, "%s: Registered %s as %s.\n", __func__,
pdata->sensor_name, dev_name(&rmi_dev->dev));
@@ -139,6 +141,7 @@ void rmi_unregister_transport_device(struct rmi_transport_dev *xport)
struct rmi_device *rmi_dev = xport->rmi_dev;
device_del(&rmi_dev->dev);
+ rmi_fw_update_cleanup(rmi_dev);
rmi_physical_teardown_debugfs(rmi_dev);
put_device(&rmi_dev->dev);
}
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 70410e8..822aa43 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -833,7 +833,7 @@ static int rmi_driver_probe(struct device *dev)
* previous settings and force it into normal operation.
*
* We have to do this before actually building the PDT because
- * the reflash updates (if any) might cause various registers to move
+ * the firmware updates (if any) might cause various registers to move
* around.
*
* For a number of reasons, this initial reset may fail to return
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 2291591..f3c0605 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -29,6 +29,8 @@
#define RMI_PDT_PROPS_HAS_BSR 0x02
+struct rmi_fw_update_data;
+
struct rmi_driver_data {
struct list_head function_list;
@@ -81,6 +83,8 @@ struct rmi_driver_data {
u8 reg_debug_size;
#endif
+ struct rmi_fw_update_data *fw_update_data;
+
void *data;
};
@@ -114,5 +118,17 @@ int rmi_check_bootloader_mode(struct rmi_device *rmi_dev,
void rmi_free_function_list(struct rmi_device *rmi_dev);
int rmi_driver_detect_functions(struct rmi_device *rmi_dev);
+#ifdef CONFIG_RMI4_FW_UPDATE
+void rmi_fw_update_init(struct rmi_device *rmi_dev);
+void rmi_fw_update_cleanup(struct rmi_device *rmi_dev);
+#else
+static inline void rmi_fw_update_init(struct rmi_device *rmi_dev)
+{
+}
+
+static inline void rmi_fw_update_cleanup(struct rmi_device *rmi_dev)
+{
+}
+#endif
#endif
diff --git a/drivers/input/rmi4/rmi_fw_update.c b/drivers/input/rmi4/rmi_fw_update.c
new file mode 100644
index 0000000..382aff3
--- /dev/null
+++ b/drivers/input/rmi4/rmi_fw_update.c
@@ -0,0 +1,942 @@
+/*
+ * Copyright (c) 2012-2014 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+
+/* F34 Query register defs. */
+
+#define RMI_F34_QUERY_SIZE 7
+#define RMI_F34_HAS_NEW_REG_MAP (1 << 0)
+#define RMI_F34_IS_UNLOCKED (1 << 1)
+#define RMI_F34_HAS_CONFIG_ID (1 << 2)
+#define RMI_F34_BLOCK_SIZE_OFFSET 1
+#define RMI_F34_FW_BLOCKS_OFFSET 3
+#define RMI_F34_CONFIG_BLOCKS_OFFSET 5
+
+struct rmi_f34_queries {
+ bool new_reg_map;
+ bool unlocked;
+ bool has_config_id;
+ u16 block_size;
+ u16 fw_block_count;
+ u16 config_block_count;
+};
+
+/* F34 Data register defs. */
+
+#define RMI_F34_BLOCK_DATA_OFFSET 2
+
+#define RMI_F34_COMMAND_MASK 0x0F
+#define RMI_F34_STATUS_MASK 0x07
+#define RMI_F34_STATUS_SHIFT 4
+#define RMI_F34_ENABLED_MASK 0x80
+
+#define RMI_F34_WRITE_FW_BLOCK 0x02
+#define RMI_F34_ERASE_ALL 0x03
+#define RMI_F34_WRITE_CONFIG_BLOCK 0x06
+#define RMI_F34_ENABLE_FLASH_PROG 0x0f
+
+struct rmi_f34_control_status {
+ u8 command;
+ u8 status;
+ bool program_enabled;
+};
+
+/* Timeouts for various F34 operations. */
+#define RMI_F34_ENABLE_WAIT_MS 300
+#define RMI_F34_ERASE_WAIT_MS (5 * 1000)
+#define RMI_F34_IDLE_WAIT_MS 500
+
+#define IS_IDLE(ctl_ptr) ((!ctl_ptr->status) && (!ctl_ptr->command))
+
+
+/* Image file defs. */
+#define RMI_IMG_CHECKSUM_OFFSET 0
+#define RMI_IMG_IO_OFFSET 0x06
+#define RMI_IMG_BOOTLOADER_VERSION_OFFSET 0x07
+#define RMI_IMG_IMAGE_SIZE_OFFSET 0x08
+#define RMI_IMG_CONFIG_SIZE_OFFSET 0x0C
+#define RMI_IMG_PACKAGE_ID_OFFSET 0x1A
+#define RMI_IMG_FW_BUILD_ID_OFFSET 0x50
+
+#define RMI_IMG_PRODUCT_INFO_LENGTH 2
+
+#define RMI_IMG_PRODUCT_ID_OFFSET 0x10
+#define RMI_IMG_PRODUCT_INFO_OFFSET 0x1E
+
+#define RMI_F34_FW_IMAGE_OFFSET 0x100
+
+/* Image file V5, Option 0 */
+struct rmi_image_header {
+ u32 checksum;
+ unsigned int image_size;
+ unsigned int config_size;
+ u8 options;
+ u8 io;
+ u32 fw_build_id;
+ u32 package_id;
+ u8 bootloader_version;
+ u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+ u8 product_info[RMI_IMG_PRODUCT_INFO_LENGTH];
+};
+
+static u32 rmi_extract_u32(const u8 *ptr)
+{
+ return (u32)ptr[0] +
+ (u32)ptr[1] * 0x100 +
+ (u32)ptr[2] * 0x10000 +
+ (u32)ptr[3] * 0x1000000;
+}
+
+#define RMI_NAME_BUFFER_SIZE 64
+
+struct rmi_fw_update_data {
+ struct rmi_device *rmi_dev;
+ bool force;
+ ulong busy;
+ char name_buf[RMI_NAME_BUFFER_SIZE];
+ const char *img_name;
+ struct pdt_entry f01_pdt;
+ struct f01_basic_properties f01_props;
+ u8 device_status;
+ struct pdt_entry f34_pdt;
+ u8 bootloader_id[2];
+ struct rmi_f34_queries f34_queries;
+ u16 f34_status_address;
+ struct rmi_f34_control_status f34_controls;
+ const u8 *firmware_data;
+ const u8 *config_data;
+ struct work_struct update_work;
+};
+
+static void rmi_extract_header(const u8 *data, int pos,
+ struct rmi_image_header *header)
+{
+ header->checksum =
+ rmi_extract_u32(&data[pos + RMI_IMG_CHECKSUM_OFFSET]);
+ header->io = data[pos + RMI_IMG_IO_OFFSET];
+ header->bootloader_version =
+ data[pos + RMI_IMG_BOOTLOADER_VERSION_OFFSET];
+ header->image_size =
+ rmi_extract_u32(&data[pos + RMI_IMG_IMAGE_SIZE_OFFSET]);
+ header->config_size =
+ rmi_extract_u32(&data[pos + RMI_IMG_CONFIG_SIZE_OFFSET]);
+ if (header->io == 1) {
+ header->fw_build_id =
+ rmi_extract_u32(&data[pos + RMI_IMG_FW_BUILD_ID_OFFSET]);
+ header->package_id =
+ rmi_extract_u32(&data[pos + RMI_IMG_PACKAGE_ID_OFFSET]);
+ }
+ memcpy(header->product_id, &data[pos + RMI_IMG_PRODUCT_ID_OFFSET],
+ RMI_PRODUCT_ID_LENGTH);
+ header->product_id[RMI_PRODUCT_ID_LENGTH] = 0;
+ memcpy(header->product_info, &data[pos + RMI_IMG_PRODUCT_INFO_OFFSET],
+ RMI_IMG_PRODUCT_INFO_LENGTH);
+}
+
+static int rmi_find_functions(struct rmi_device *rmi_dev,
+ void *ctx, const struct pdt_entry *pdt)
+{
+ struct rmi_fw_update_data *data = ctx;
+
+ if (pdt->page_start > 0)
+ return RMI_SCAN_DONE;
+
+ if (pdt->function_number == 0x01)
+ memcpy(&data->f01_pdt, pdt, sizeof(struct pdt_entry));
+ else if (pdt->function_number == 0x34)
+ memcpy(&data->f34_pdt, pdt, sizeof(struct pdt_entry));
+
+ return RMI_SCAN_CONTINUE;
+}
+
+static int rmi_find_f01_and_f34(struct rmi_fw_update_data *data)
+{
+ struct rmi_device *rmi_dev = data->rmi_dev;
+ int retval;
+
+ data->f01_pdt.function_number = data->f34_pdt.function_number = 0;
+ retval = rmi_scan_pdt(rmi_dev, data, rmi_find_functions);
+ if (retval < 0)
+ return retval;
+
+ if (!data->f01_pdt.function_number) {
+ dev_err(&rmi_dev->dev, "Failed to find F01 for fw update.\n");
+ return -ENODEV;
+ }
+
+ if (!data->f34_pdt.function_number) {
+ dev_err(&rmi_dev->dev, "Failed to find F34 for fw update.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int rmi_read_f34_controls(struct rmi_fw_update_data *data)
+{
+ int retval;
+ u8 buf;
+
+ retval = rmi_read(data->rmi_dev, data->f34_status_address, &buf);
+ if (retval)
+ return retval;
+
+ data->f34_controls.command = buf & RMI_F34_COMMAND_MASK;
+ data->f34_controls.status = (buf >> RMI_F34_STATUS_SHIFT)
+ & RMI_F34_STATUS_MASK;
+ data->f34_controls.program_enabled = !!(buf & RMI_F34_ENABLED_MASK);
+
+ return 0;
+}
+
+#define RMI_MIN_SLEEP_TIME_US 50
+#define RMI_MAX_SLEEP_TIME_US 100
+
+/*
+ * Wait until the status is idle and we're ready to continue.
+ *
+ * In order to indicate that the previous command has succeeded, the
+ * bootloader is supposed to signal idle state by:
+ *
+ * + atomically do the following by writing 0x80 to F34_FLASH_Data3
+ * - set status to 0,
+ * - set command to 0, and
+ * - leave program_enabled as 1
+ * + assert attn
+ *
+ * and then we're supposed to be able to see that we're in IDLE state.
+ * But some (most?) bootloaders do this
+ *
+ * + clear F34_FLASH_Data3
+ * + assert attn
+ * + set the program_enabled bit
+ *
+ * and a significant number of those don't even bother to assert
+ * ATTN, so you've got to poll them (which is what we're doing
+ * here). Regardless of whether you're polling or using ATTN,
+ * when this bug is present there is a race condition between
+ * clearing Data3 and setting program_enabled. So when we lose
+ * that race, we emit this warning (using dev_WARN_ONCE to avoid
+ * filling the log with complaints) and retry a few times. If a
+ * correct idle state is reached during the retries, then we just
+ * continue with the process. If it's not reached (that is, if
+ * Data3 contains anything but 0x80 after the timeout), then
+ * something has gone horribly wrong, and we abort the
+ * firmware update process.
+ *
+ */
+static int rmi_wait_for_idle(struct rmi_fw_update_data *data, int timeout_ms)
+{
+ int timeout_count = ((timeout_ms * 1000) / RMI_MAX_SLEEP_TIME_US) + 1;
+ int count = 0;
+ struct rmi_f34_control_status *controls = &data->f34_controls;
+ int retval;
+
+ do {
+ if (count || timeout_count == 1)
+ usleep_range(RMI_MIN_SLEEP_TIME_US,
+ RMI_MAX_SLEEP_TIME_US);
+ retval = rmi_read_f34_controls(data);
+ count++;
+ if (retval)
+ continue;
+ else if (IS_IDLE(controls)) {
+ if (dev_WARN_ONCE(&data->rmi_dev->dev,
+ !data->f34_controls.program_enabled,
+ "Bootloader is idle but program_enabled bit isn't set.\n"
+ ))
+ /*
+ * This works around a bug in certain device
+ * firmwares, where the idle state is reached,
+ * but the program_enabled bit is not yet set.
+ */
+ continue;
+ return 0;
+ }
+ } while (count < timeout_count);
+
+ dev_err(&data->rmi_dev->dev,
+ "ERROR: Timeout waiting for idle status.\n");
+ dev_err(&data->rmi_dev->dev, "Command: %#04x\n", controls->command);
+ dev_err(&data->rmi_dev->dev, "Status: %#04x\n", controls->status);
+ dev_err(&data->rmi_dev->dev, "Enabled: %d\n",
+ controls->program_enabled);
+ dev_err(&data->rmi_dev->dev, "Idle: %d\n", IS_IDLE(controls));
+ return -ETIMEDOUT;
+}
+
+static int rmi_read_f34_queries(struct rmi_fw_update_data *data)
+{
+ int retval;
+ u8 id_str[3];
+ u8 buf[RMI_F34_QUERY_SIZE];
+
+ retval = rmi_read_block(data->rmi_dev, data->f34_pdt.query_base_addr,
+ data->bootloader_id, 2);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev,
+ "Failed to read F34 bootloader_id (code %d).\n",
+ retval);
+ return retval;
+ }
+
+ retval = rmi_read_block(data->rmi_dev, data->f34_pdt.query_base_addr+2,
+ buf, RMI_F34_QUERY_SIZE);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev,
+ "Failed to read F34 queries (code %d).\n", retval);
+ return retval;
+ }
+
+ data->f34_queries.new_reg_map = buf[0] & RMI_F34_HAS_NEW_REG_MAP;
+ data->f34_queries.unlocked = buf[0] & RMI_F34_IS_UNLOCKED;
+ data->f34_queries.has_config_id = buf[0] & RMI_F34_HAS_CONFIG_ID;
+ data->f34_queries.block_size =
+ le16_to_cpu(*((u16 *)(buf + RMI_F34_BLOCK_SIZE_OFFSET)));
+ data->f34_queries.fw_block_count =
+ le16_to_cpu(*((u16 *)(buf + RMI_F34_FW_BLOCKS_OFFSET)));
+ data->f34_queries.config_block_count =
+ le16_to_cpu(*((u16 *)(buf + RMI_F34_CONFIG_BLOCKS_OFFSET)));
+
+ id_str[0] = data->bootloader_id[0];
+ id_str[1] = data->bootloader_id[1];
+ id_str[2] = 0;
+
+ dev_dbg(&data->rmi_dev->dev, "F34 bootloader id: %s (%#04x %#04x)\n",
+ id_str, data->bootloader_id[0], data->bootloader_id[1]);
+ dev_dbg(&data->rmi_dev->dev, "F34 has config id: %d\n",
+ data->f34_queries.has_config_id);
+ dev_dbg(&data->rmi_dev->dev, "F34 unlocked: %d\n",
+ data->f34_queries.unlocked);
+ dev_dbg(&data->rmi_dev->dev, "F34 new reg map: %d\n",
+ data->f34_queries.new_reg_map);
+ dev_dbg(&data->rmi_dev->dev, "F34 block size: %d\n",
+ data->f34_queries.block_size);
+ dev_dbg(&data->rmi_dev->dev, "F34 fw blocks: %d\n",
+ data->f34_queries.fw_block_count);
+ dev_dbg(&data->rmi_dev->dev, "F34 config blocks: %d\n",
+ data->f34_queries.config_block_count);
+
+ data->f34_status_address = data->f34_pdt.data_base_addr +
+ RMI_F34_BLOCK_DATA_OFFSET + data->f34_queries.block_size;
+
+ return 0;
+}
+
+static int rmi_write_bootloader_id(struct rmi_fw_update_data *data)
+{
+ int retval;
+ struct rmi_device *rmi_dev = data->rmi_dev;
+
+ retval = rmi_write_block(rmi_dev,
+ data->f34_pdt.data_base_addr + RMI_F34_BLOCK_DATA_OFFSET,
+ data->bootloader_id, ARRAY_SIZE(data->bootloader_id));
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to write bootloader ID. Code: %d.\n", retval);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int rmi_enter_flash_programming(struct rmi_fw_update_data *data)
+{
+ int retval;
+ struct rmi_device *rmi_dev = data->rmi_dev;
+ u8 f01_control_0;
+ const u8 enable_prog = RMI_F34_ENABLE_FLASH_PROG;
+
+ retval = rmi_write_bootloader_id(data);
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&rmi_dev->dev, "Enabling flash programming.\n");
+ retval = rmi_write(rmi_dev, data->f34_status_address, enable_prog);
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to enable flash programming. Code: %d.\n",
+ retval);
+ return retval;
+ }
+ if (retval < 0)
+ return retval;
+
+ retval = rmi_wait_for_idle(data, RMI_F34_ENABLE_WAIT_MS);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "Did not reach idle state after %d ms. Code: %d.\n",
+ RMI_F34_ENABLE_WAIT_MS, retval);
+ return retval;
+ }
+ if (!data->f34_controls.program_enabled) {
+ dev_err(&rmi_dev->dev, "Reached idle, but programming not enabled.\n");
+ return -EINVAL;
+ }
+ dev_dbg(&rmi_dev->dev, "HOORAY! Programming is enabled!\n");
+
+ retval = rmi_find_f01_and_f34(data);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "Failed to rescan pdt. Code: %d.\n",
+ retval);
+ return retval;
+ }
+
+ retval = rmi_read(data->rmi_dev, data->f01_pdt.data_base_addr,
+ &data->device_status);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "Failed to read F01 status after enabling flash programming. Code: %d.\n",
+ retval);
+ return retval;
+ }
+ if (!RMI_F01_STATUS_BOOTLOADER(data->device_status)) {
+ dev_err(&rmi_dev->dev, "Device reports as not in flash programming mode.\n");
+ return -EINVAL;
+ }
+
+ retval = rmi_read_f34_queries(data);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+ retval);
+ return retval;
+ }
+
+ retval = rmi_read(rmi_dev, data->f01_pdt.control_base_addr,
+ &f01_control_0);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "F01_CTRL_0 read failed, code = %d.\n",
+ retval);
+ return retval;
+ }
+ f01_control_0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
+ f01_control_0 = (f01_control_0 & ~RMI_F01_CTRL0_SLEEP_MODE_MASK)
+ | RMI_SLEEP_MODE_NORMAL;
+
+ retval = rmi_write(rmi_dev, data->f01_pdt.control_base_addr,
+ f01_control_0);
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev, "F01_CTRL_0 write failed, code = %d.\n",
+ retval);
+ return retval;
+ }
+
+ return 0;
+}
+
+static void rmi_reset_device(struct rmi_fw_update_data *data)
+{
+ int retval;
+ const struct rmi_device_platform_data *pdata =
+ rmi_get_platform_data(data->rmi_dev);
+
+ dev_dbg(&data->rmi_dev->dev, "Resetting...\n");
+ retval = rmi_write(data->rmi_dev, data->f01_pdt.command_base_addr,
+ RMI_F01_CMD_DEVICE_RESET);
+ if (retval < 0)
+ dev_warn(&data->rmi_dev->dev,
+ "WARNING - post-update reset failed, code: %d.\n",
+ retval);
+ msleep(pdata->reset_delay_ms ?: RMI_F01_DEFAULT_RESET_DELAY_MS);
+ dev_dbg(&data->rmi_dev->dev, "Reset completed.\n");
+}
+
+/*
+ * Send data to the device one block at a time.
+ */
+static int rmi_write_blocks(struct rmi_fw_update_data *data, u8 *block_ptr,
+ u16 block_count, u8 cmd)
+{
+ int block_num;
+ u8 zeros[] = {0, 0};
+ int retval;
+ u16 addr = data->f34_pdt.data_base_addr + RMI_F34_BLOCK_DATA_OFFSET;
+
+ retval = rmi_write_block(data->rmi_dev, data->f34_pdt.data_base_addr,
+ zeros, ARRAY_SIZE(zeros));
+ if (retval < 0) {
+ dev_err(&data->rmi_dev->dev, "Failed to write initial zeros. Code=%d.\n",
+ retval);
+ return retval;
+ }
+
+ for (block_num = 0; block_num < block_count; ++block_num) {
+ retval = rmi_write_block(data->rmi_dev, addr, block_ptr,
+ data->f34_queries.block_size);
+ if (retval < 0) {
+ dev_err(&data->rmi_dev->dev, "Failed to write block %d. Code=%d.\n",
+ block_num, retval);
+ return retval;
+ }
+
+ retval = rmi_write(data->rmi_dev, data->f34_status_address,
+ cmd);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev, "Failed to write command for block %d. Code=%d.\n",
+ block_num, retval);
+ return retval;
+ }
+
+
+ retval = rmi_wait_for_idle(data, RMI_F34_IDLE_WAIT_MS);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev, "Failed to go idle after writing block %d. Code=%d.\n",
+ block_num, retval);
+ return retval;
+ }
+
+ block_ptr += data->f34_queries.block_size;
+ }
+
+ return 0;
+}
+
+static void rmi_update_firmware(struct rmi_fw_update_data *data)
+{
+ struct timespec start;
+ struct timespec end;
+ s64 duration_ns;
+ int retval = 0;
+ const u8 erase_all = RMI_F34_ERASE_ALL;
+
+ retval = rmi_enter_flash_programming(data);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev, "Failed to enter flash programming (code: %d).\n",
+ retval);
+ return;
+ }
+
+ retval = rmi_write_bootloader_id(data);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev, "Failed to enter write bootloader ID (code: %d).\n",
+ retval);
+ return;
+ }
+
+ dev_dbg(&data->rmi_dev->dev, "Erasing FW...\n");
+ getnstimeofday(&start);
+ retval = rmi_write(data->rmi_dev, data->f34_status_address, erase_all);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev, "Erase failed (code: %d).\n",
+ retval);
+ return;
+ }
+
+ retval = rmi_wait_for_idle(data, RMI_F34_ERASE_WAIT_MS);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev,
+ "Failed to reach idle state. Code: %d.\n", retval);
+ return;
+ }
+ getnstimeofday(&end);
+ duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+ dev_dbg(&data->rmi_dev->dev,
+ "Erase complete, time: %lld ns.\n", duration_ns);
+
+ if (data->firmware_data) {
+ dev_dbg(&data->rmi_dev->dev, "Writing firmware...\n");
+ getnstimeofday(&start);
+ retval = rmi_write_blocks(data, (u8 *) data->firmware_data,
+ data->f34_queries.fw_block_count,
+ RMI_F34_WRITE_FW_BLOCK);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev,
+ "Failed to write FW (code: %d).\n", retval);
+ return;
+ }
+ getnstimeofday(&end);
+ duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+ dev_dbg(&data->rmi_dev->dev,
+ "Done writing FW, time: %lld ns.\n", duration_ns);
+ }
+
+ if (data->config_data) {
+ dev_dbg(&data->rmi_dev->dev, "Writing configuration...\n");
+ getnstimeofday(&start);
+ retval = rmi_write_blocks(data, (u8 *) data->config_data,
+ data->f34_queries.config_block_count,
+ RMI_F34_WRITE_CONFIG_BLOCK);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev,
+ "Failed to write config (code: %d).\n", retval);
+ return;
+ }
+ getnstimeofday(&end);
+ duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+ dev_dbg(&data->rmi_dev->dev,
+ "Done writing config, time: %lld ns.\n", duration_ns);
+ }
+}
+
+static bool rmi_go_nogo(struct rmi_fw_update_data *data,
+ struct rmi_image_header *header)
+{
+ if (data->force) {
+ dev_dbg(&data->rmi_dev->dev, "Fw update force flag in effect.\n");
+ return true;
+ }
+
+ if (header->io == 1) {
+ if (header->fw_build_id > data->f01_props.build_id) {
+ dev_dbg(&data->rmi_dev->dev, "Image file has newer Packrat.\n");
+ return true;
+ } else {
+ dev_dbg(&data->rmi_dev->dev, "Image file has lower Packrat ID than device.\n");
+ }
+ }
+
+ return false;
+}
+
+static const char rmi_fw_name_format[] = "%s.img";
+
+static void rmi_fw_update(struct rmi_device *rmi_dev)
+{
+ struct timespec start;
+ struct timespec end;
+ s64 duration_ns;
+ char *firmware_name;
+ const struct firmware *fw_entry = NULL;
+ int retval;
+ struct rmi_image_header *header = NULL;
+ u8 pdt_props;
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_fw_update_data *data = drv_data->fw_update_data;
+ const struct rmi_device_platform_data *pdata =
+ rmi_get_platform_data(rmi_dev);
+
+ dev_dbg(&rmi_dev->dev, "%s called.\n", __func__);
+ dev_dbg(&rmi_dev->dev, "force: %d\n", data->force);
+ dev_dbg(&rmi_dev->dev, "img_name: %s\n", data->img_name);
+ dev_dbg(&rmi_dev->dev, "firmware_name: %s\n", pdata->firmware_name);
+
+ getnstimeofday(&start);
+
+ firmware_name = kcalloc(RMI_NAME_BUFFER_SIZE, sizeof(char), GFP_KERNEL);
+ if (!firmware_name) {
+ dev_err(&rmi_dev->dev, "Failed to allocate firmware_name.\n");
+ goto done;
+ }
+ header = kzalloc(sizeof(struct rmi_image_header), GFP_KERNEL);
+ if (!header) {
+ dev_err(&rmi_dev->dev, "Failed to allocate header.\n");
+ goto done;
+ }
+
+ retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &pdt_props);
+ if (retval) {
+ dev_warn(&rmi_dev->dev,
+ "Failed to read PDT props at %#06x (code %d). Assuming 0x00.\n",
+ PDT_PROPERTIES_LOCATION, retval);
+ }
+ if (pdt_props & RMI_PDT_PROPS_HAS_BSR) {
+ dev_warn(&rmi_dev->dev,
+ "Firmware update for LTS not currently supported.\n");
+ goto done;
+ }
+
+ retval = rmi_f01_read_properties(rmi_dev, data->f01_pdt.query_base_addr,
+ &data->f01_props);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "F01 queries failed, code = %d.\n",
+ retval);
+ goto done;
+ }
+ retval = rmi_read_f34_queries(data);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+ retval);
+ goto done;
+ }
+ if (data->img_name && data->img_name[0])
+ snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+ rmi_fw_name_format, data->img_name);
+ else if (pdata->firmware_name && pdata->firmware_name[0])
+ snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+ rmi_fw_name_format, pdata->firmware_name);
+ else {
+ if (!data->f01_props.product_id[0]) {
+ dev_err(&rmi_dev->dev, "Could not determine fw image name - will not update fw.\n");
+ goto done;
+ }
+ snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+ rmi_fw_name_format, data->f01_props.product_id);
+ }
+ dev_info(&rmi_dev->dev, "Requesting %s.\n", firmware_name);
+ retval = request_firmware(&fw_entry, firmware_name, &rmi_dev->dev);
+ if (retval != 0) {
+ dev_err(&rmi_dev->dev, "Firmware %s not available, code = %d\n",
+ firmware_name, retval);
+ goto done;
+ }
+
+ dev_dbg(&rmi_dev->dev, "Got firmware %s, size: %d.\n", firmware_name,
+ fw_entry->size);
+ rmi_extract_header(fw_entry->data, 0, header);
+ dev_dbg(&rmi_dev->dev, "Img checksum: %#08X\n",
+ header->checksum);
+ dev_dbg(&rmi_dev->dev, "Img io: %#04X\n",
+ header->io);
+ dev_dbg(&rmi_dev->dev, "Img image size: %d\n",
+ header->image_size);
+ dev_dbg(&rmi_dev->dev, "Img config size: %d\n",
+ header->config_size);
+ dev_dbg(&rmi_dev->dev, "Img bootloader version: %d\n",
+ header->bootloader_version);
+ dev_dbg(&rmi_dev->dev, "Img product id: %s\n",
+ header->product_id);
+ dev_dbg(&rmi_dev->dev, "Img product info: %#04x %#04x\n",
+ header->product_info[0], header->product_info[1]);
+ if (header->io == 1) {
+ dev_dbg(&rmi_dev->dev, "Img Packrat: %d\n",
+ header->fw_build_id);
+ dev_dbg(&rmi_dev->dev, "Img package: %d\n",
+ header->package_id);
+ }
+
+ if (header->image_size)
+ data->firmware_data = fw_entry->data + RMI_F34_FW_IMAGE_OFFSET;
+ if (header->config_size)
+ data->config_data = fw_entry->data + RMI_F34_FW_IMAGE_OFFSET +
+ header->image_size;
+
+ if (rmi_go_nogo(data, header)) {
+ dev_dbg(&rmi_dev->dev, "Go/NoGo said go.\n");
+ rmi_free_function_list(rmi_dev);
+ rmi_update_firmware(data);
+ rmi_reset_device(data);
+ rmi_driver_detect_functions(rmi_dev);
+ } else {
+ dev_dbg(&rmi_dev->dev, "Go/NoGo said don't update.\n");
+ }
+
+ if (fw_entry)
+ release_firmware(fw_entry);
+
+
+done:
+ getnstimeofday(&end);
+ duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+ dev_dbg(&rmi_dev->dev, "Time to update fw: %lld ns.\n", duration_ns);
+
+ kfree(firmware_name);
+ kfree(header);
+ return;
+}
+
+static int rmi_device_update_firmware(struct rmi_device *rmi_dev)
+{
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_driver_data *drv_data = dev_get_drvdata(dev);
+ struct rmi_fw_update_data *data = drv_data->fw_update_data;
+ int retval;
+
+ retval = rmi_find_f01_and_f34(data);
+ if (retval < 0)
+ return retval;
+
+ rmi_fw_update(rmi_dev);
+
+ clear_bit(0, &data->busy);
+
+ return 0;
+}
+
+static ssize_t rmi_fw_update_img_name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_fw_update_data *data = drv_data->fw_update_data;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", data->img_name);
+}
+
+static ssize_t rmi_fw_update_img_name_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_fw_update_data *data = drv_data->fw_update_data;
+
+ if (test_and_set_bit(0, &data->busy))
+ return -EBUSY;
+
+ if (!count) {
+ data->img_name = NULL;
+ } else {
+ strlcpy(data->name_buf, buf, count);
+ data->img_name = strstrip(data->name_buf);
+ }
+
+ clear_bit(0, &data->busy);
+ return count;
+}
+
+static ssize_t rmi_fw_update_force_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_fw_update_data *data = drv_data->fw_update_data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->force);
+}
+
+static ssize_t rmi_fw_update_force_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_fw_update_data *data = drv_data->fw_update_data;
+ int retval;
+ unsigned long val;
+
+ if (test_and_set_bit(0, &data->busy))
+ return -EBUSY;
+
+ retval = kstrtoul(buf, 10, &val);
+ if (retval)
+ count = retval;
+ else if (val > 1)
+ return -EINVAL;
+ else
+ data->force = !!val;
+
+ clear_bit(0, &data->busy);
+
+ return count;
+}
+
+static ssize_t rmi_fw_update_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_fw_update_data *data = drv_data->fw_update_data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", test_bit(0, &data->busy));
+}
+
+static ssize_t rmi_fw_update_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ unsigned long val;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_fw_update_data *data = drv_data->fw_update_data;
+
+ retval = kstrtoul(buf, 10, &val);
+ if (retval)
+ return retval;
+
+ if (val > 1)
+ return -EINVAL;
+
+ if (test_and_set_bit(0, &data->busy))
+ return -EBUSY;
+
+ if (val)
+ /*
+ * TODO: Here we start a work thread to go do the update, but
+ * maybe we can just use request_firmware_timeout().
+ */
+ schedule_work(&data->update_work);
+ else
+ clear_bit(0, &data->busy);
+
+ return count;
+}
+
+static void rmi_update_work(struct work_struct *work)
+{
+ struct rmi_fw_update_data *data =
+ container_of(work, struct rmi_fw_update_data, update_work);
+ struct rmi_device *rmi_dev = data->rmi_dev;
+ int error;
+
+ dev_dbg(&rmi_dev->dev, "%s runs.\n", __func__);
+ error = rmi_device_update_firmware(rmi_dev);
+ if (error < 0)
+ dev_err(&rmi_dev->dev, "Firmware update attempt failed with code: %d.",
+ error);
+ clear_bit(0, &data->busy);
+}
+
+static DEVICE_ATTR(fw_force_update,
+ (S_IRUGO | S_IWUGO),
+ rmi_fw_update_force_show, rmi_fw_update_force_store);
+static DEVICE_ATTR(fw_img_name,
+ (S_IRUGO | S_IWUGO),
+ rmi_fw_update_img_name_show,
+ rmi_fw_update_img_name_store);
+static DEVICE_ATTR(fw_update,
+ (S_IRUGO | S_IWUGO),
+ rmi_fw_update_show, rmi_fw_update_store);
+
+static struct attribute *rmi_fw_update_attrs[] = {
+ &dev_attr_fw_force_update.attr,
+ &dev_attr_fw_img_name.attr,
+ &dev_attr_fw_update.attr,
+ NULL
+};
+
+static const struct attribute_group rmi_fw_update_attributes = {
+ .attrs = rmi_fw_update_attrs,
+};
+
+/*
+ * Allocate data needed by firmware update and initialize relevant
+ * structures (like sysfs, work, and so on). You'll need to call
+ * rmi_fw_update_cleanup() to free the storage and tear down the
+ * structures.
+ */
+void rmi_fw_update_init(struct rmi_device *rmi_dev)
+{
+ int error;
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_fw_update_data *data;
+
+ dev_dbg(&rmi_dev->dev, "%s called.\n", __func__);
+
+ data = kzalloc(sizeof(struct rmi_fw_update_data), GFP_KERNEL);
+
+ error = sysfs_create_group(&rmi_dev->dev.kobj,
+ &rmi_fw_update_attributes);
+ if (error) {
+ dev_warn(&rmi_dev->dev, "Failed to create fw update sysfs attributes.\n");
+ return;
+ }
+
+ INIT_WORK(&data->update_work, rmi_update_work);
+ data->rmi_dev = rmi_dev;
+ drv_data->fw_update_data = data;
+}
+
+void rmi_fw_update_cleanup(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_fw_update_data *data = drv_data->fw_update_data;
+
+ sysfs_remove_group(&rmi_dev->dev.kobj, &rmi_fw_update_attributes);
+ kfree(data);
+}
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index 735e978..83d2e52 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -196,8 +196,8 @@ struct rmi_device_platform_data_spi {
*
* @sensor_name - this is used for various diagnostic messages.
*
- * @firmware_name - if specified will override default firmware name,
- * for reflashing.
+ * @firmware_name - if specified will override default firmware image name
+ * used by the firmware update feature.
*
* @attn_gpio - the index of a GPIO that will be used to provide the ATTN
* interrupt from the touch sensor.
@@ -270,7 +270,7 @@ struct rmi_device_platform_data {
struct rmi_f30_gpioled_map *gpioled_map;
struct rmi_button_map *f41_button_map;
-#ifdef CONFIG_RMI4_FWLIB
+#ifdef CONFIG_RMI4_FW_UPDATE
char *firmware_name;
#endif
^ permalink raw reply related
* [PATCH v2 06/06] input synaptics-rmi4: Use request_firmware_nowait
From: Christopher Heiny @ 2014-03-13 1:53 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
In-Reply-To: <1394675637-23853-1-git-send-email-cheiny@synaptics.com>
Replace request_firmware() and our own work_queue entry, and use
request_firmware_nowait() instead.
Suggested-by: Courtney Cavin <courtney.cavin@sonymobile.com>
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Signed-off-by: Vincent Huang <vincent.huang@tw.synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/rmi_fw_update.c | 233 +++++++++++++++++--------------------
1 file changed, 109 insertions(+), 124 deletions(-)
diff --git a/drivers/input/rmi4/rmi_fw_update.c b/drivers/input/rmi4/rmi_fw_update.c
index a990f9a..c84f33f 100644
--- a/drivers/input/rmi4/rmi_fw_update.c
+++ b/drivers/input/rmi4/rmi_fw_update.c
@@ -120,7 +120,6 @@ struct rmi_fw_update_data {
struct rmi_f34_control_status f34_controls;
const u8 *firmware_data;
const u8 *config_data;
- struct work_struct update_work;
};
static void rmi_extract_header(const u8 *data, int pos,
@@ -260,11 +259,6 @@ static int rmi_wait_for_idle(struct rmi_fw_update_data *data, int timeout_ms)
!data->f34_controls.program_enabled,
"Bootloader is idle but program_enabled bit isn't set.\n"
))
- /*
- * This works around a bug in certain device
- * firmwares, where the idle state is reached,
- * but the program_enabled bit is not yet set.
- */
continue;
return 0;
}
@@ -482,7 +476,8 @@ static int rmi_write_blocks(struct rmi_fw_update_data *data, u8 *block_ptr,
return retval;
}
- retval = rmi_write(data->rmi_dev, data->f34_status_address, cmd);
+ retval = rmi_write(data->rmi_dev, data->f34_status_address,
+ cmd);
if (retval) {
dev_err(&data->rmi_dev->dev, "Failed to write command for block %d. Code=%d.\n",
block_num, retval);
@@ -600,89 +595,30 @@ static bool rmi_go_nogo(struct rmi_fw_update_data *data,
return false;
}
-static const char rmi_fw_name_format[] = "%s.img";
-
-static void rmi_fw_update(struct rmi_device *rmi_dev)
+static void rmi_do_update(const struct firmware *fw_entry, void *context)
{
+ struct rmi_device *rmi_dev = context;
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_fw_update_data *data = drv_data->fw_update_data;
+ struct rmi_image_header *header = NULL;
struct timespec start;
struct timespec end;
s64 duration_ns;
- char *firmware_name;
- const struct firmware *fw_entry = NULL;
- int retval;
- struct rmi_image_header *header = NULL;
- u8 pdt_props;
- struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
- struct rmi_fw_update_data *data = drv_data->fw_update_data;
- const struct rmi_device_platform_data *pdata =
- rmi_get_platform_data(rmi_dev);
-
- dev_dbg(&rmi_dev->dev, "%s called.\n", __func__);
- dev_dbg(&rmi_dev->dev, "force: %d\n", data->force);
- dev_dbg(&rmi_dev->dev, "img_name: %s\n", data->img_name);
- dev_dbg(&rmi_dev->dev, "firmware_name: %s\n", pdata->firmware_name);
- getnstimeofday(&start);
-
- firmware_name = kcalloc(RMI_NAME_BUFFER_SIZE, sizeof(char), GFP_KERNEL);
- if (!firmware_name) {
- dev_err(&rmi_dev->dev, "Failed to allocate firmware_name.\n");
- goto done;
+ if (!fw_entry) {
+ dev_err(&rmi_dev->dev, "Firmware was not available.\n");
+ goto error_exit;
}
+
header = kzalloc(sizeof(struct rmi_image_header), GFP_KERNEL);
if (!header) {
- dev_err(&rmi_dev->dev, "Failed to allocate header.\n");
- goto done;
+ dev_err(&rmi_dev->dev, "Failed to allocate image header.\n");
+ goto error_exit;
}
- retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &pdt_props);
- if (retval) {
- dev_warn(&rmi_dev->dev,
- "Failed to read PDT props at %#06x (code %d). Assuming 0x00.\n",
- PDT_PROPERTIES_LOCATION, retval);
- }
- if (pdt_props & RMI_PDT_PROPS_HAS_BSR) {
- dev_warn(&rmi_dev->dev,
- "Firmware update for LTS not currently supported.\n");
- goto done;
- }
-
- retval = rmi_f01_read_properties(rmi_dev, data->f01_pdt.query_base_addr,
- &data->f01_props);
- if (retval) {
- dev_err(&rmi_dev->dev, "F01 queries failed, code = %d.\n",
- retval);
- goto done;
- }
- retval = rmi_read_f34_queries(data);
- if (retval) {
- dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
- retval);
- goto done;
- }
- if (data->img_name && data->img_name[0])
- snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
- rmi_fw_name_format, data->img_name);
- else if (pdata->firmware_name && pdata->firmware_name[0])
- snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
- rmi_fw_name_format, pdata->firmware_name);
- else {
- if (!data->f01_props.product_id[0]) {
- dev_err(&rmi_dev->dev, "Could not determine fw image name - will not update fw.\n");
- goto done;
- }
- snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
- rmi_fw_name_format, data->f01_props.product_id);
- }
- dev_info(&rmi_dev->dev, "Requesting %s.\n", firmware_name);
- retval = request_firmware(&fw_entry, firmware_name, &rmi_dev->dev);
- if (retval != 0) {
- dev_err(&rmi_dev->dev, "Firmware %s not available, code = %d\n",
- firmware_name, retval);
- goto done;
- }
+ getnstimeofday(&start);
- dev_dbg(&rmi_dev->dev, "Got firmware %s, size: %d.\n", firmware_name,
+ dev_dbg(&rmi_dev->dev, "Got firmware, size: %d.\n",
fw_entry->size);
rmi_extract_header(fw_entry->data, 0, header);
dev_dbg(&rmi_dev->dev, "Img checksum: %#08X\n",
@@ -722,36 +658,98 @@ static void rmi_fw_update(struct rmi_device *rmi_dev)
dev_dbg(&rmi_dev->dev, "Go/NoGo said don't update.\n");
}
- if (fw_entry)
- release_firmware(fw_entry);
+ release_firmware(fw_entry);
-
-done:
getnstimeofday(&end);
duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
dev_dbg(&rmi_dev->dev, "Time to update fw: %lld ns.\n", duration_ns);
- kfree(firmware_name);
kfree(header);
+error_exit:
+ clear_bit(0, &data->busy);
return;
}
-static int rmi_device_update_firmware(struct rmi_device *rmi_dev)
+static const char rmi_fw_name_format[] = "%s.img";
+
+static int rmi_request_fw(struct rmi_device *rmi_dev)
{
- struct device *dev = &rmi_dev->dev;
- struct rmi_driver_data *drv_data = dev_get_drvdata(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
struct rmi_fw_update_data *data = drv_data->fw_update_data;
- int retval;
+ const struct rmi_device_platform_data *pdata =
+ rmi_get_platform_data(rmi_dev);
+ char *firmware_name;
+ int error;
+ u8 pdt_props;
- retval = rmi_find_f01_and_f34(data);
- if (retval < 0)
- return retval;
+ error = rmi_find_f01_and_f34(data);
+ if (error < 0)
+ return error;
- rmi_fw_update(rmi_dev);
+ dev_dbg(&rmi_dev->dev, "%s called.\n", __func__);
+ dev_dbg(&rmi_dev->dev, "force: %d\n", data->force);
+ dev_dbg(&rmi_dev->dev, "img_name: %s\n", data->img_name);
+ dev_dbg(&rmi_dev->dev, "firmware_name: %s\n", pdata->firmware_name);
- clear_bit(0, &data->busy);
- return 0;
+ error = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &pdt_props);
+ if (error) {
+ dev_warn(&rmi_dev->dev,
+ "Failed to read PDT props at %#06x (code %d). Assuming 0x00.\n",
+ PDT_PROPERTIES_LOCATION, error);
+ }
+ if (pdt_props & RMI_PDT_PROPS_HAS_BSR) {
+ dev_warn(&rmi_dev->dev,
+ "Firmware update for LTS not currently supported.\n");
+ return -EINVAL;
+ }
+
+ error = rmi_f01_read_properties(rmi_dev, data->f01_pdt.query_base_addr,
+ &data->f01_props);
+ if (error) {
+ dev_err(&rmi_dev->dev, "F01 queries failed, code = %d.\n",
+ error);
+ return error;
+ }
+ error = rmi_read_f34_queries(data);
+ if (error) {
+ dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+ error);
+ return error;
+ }
+
+ firmware_name = kcalloc(RMI_NAME_BUFFER_SIZE, sizeof(char), GFP_KERNEL);
+ if (!firmware_name) {
+ dev_err(&rmi_dev->dev, "Failed to allocate firmware_name.\n");
+ return -ENOMEM;
+ }
+
+ if (data->img_name && data->img_name[0])
+ snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+ rmi_fw_name_format, data->img_name);
+ else if (pdata->firmware_name && pdata->firmware_name[0])
+ snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+ rmi_fw_name_format, pdata->firmware_name);
+ else {
+ if (!data->f01_props.product_id[0]) {
+ dev_err(&rmi_dev->dev, "Could not determine fw image name - will not update fw.\n");
+ error = -EINVAL;
+ goto done;
+ }
+ snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+ rmi_fw_name_format, data->f01_props.product_id);
+ }
+ dev_info(&rmi_dev->dev, "Requesting %s.\n", firmware_name);
+ error = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ firmware_name, &rmi_dev->dev,
+ GFP_KERNEL, rmi_dev, rmi_do_update);
+ if (error)
+ dev_err(&rmi_dev->dev, "Request firmware failed for %s, code = %d\n",
+ firmware_name, error);
+
+done:
+ kfree(firmware_name);
+ return error;
}
static ssize_t rmi_fw_update_img_name_show(struct device *dev,
@@ -788,7 +786,8 @@ static ssize_t rmi_fw_update_img_name_store(struct device *dev,
}
static ssize_t rmi_fw_update_force_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
struct rmi_device *rmi_dev = to_rmi_device(dev);
struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
@@ -853,34 +852,21 @@ static ssize_t rmi_fw_update_store(struct device *dev,
if (test_and_set_bit(0, &data->busy))
return -EBUSY;
- if (val)
- /*
- * TODO: Here we start a work thread to go do the update, but
- * maybe we can just use request_firmware_timeout().
- */
- schedule_work(&data->update_work);
- else
+ if (val) {
+ retval = rmi_request_fw(rmi_dev);
+ if (retval) {
+ dev_err(dev, "Firmware request failed, code: %d",
+ retval);
+ clear_bit(0, &data->busy);
+ }
+ } else {
clear_bit(0, &data->busy);
+ }
return count;
}
-static void rmi_update_work(struct work_struct *work)
-{
- struct rmi_fw_update_data *data =
- container_of(work, struct rmi_fw_update_data, update_work);
- struct rmi_device *rmi_dev = data->rmi_dev;
- int error;
-
- dev_dbg(&rmi_dev->dev, "%s runs.\n", __func__);
- error = rmi_device_update_firmware(rmi_dev);
- if (error < 0)
- dev_err(&rmi_dev->dev, "Firmware update attempt failed with code: %d.",
- error);
- clear_bit(0, &data->busy);
-}
-
-static DEVICE_ATTR(update_force,
+static DEVICE_ATTR(fw_force_update,
(S_IRUGO | S_IWUGO),
rmi_fw_update_force_show, rmi_fw_update_force_store);
static DEVICE_ATTR(fw_img_name,
@@ -892,7 +878,7 @@ static DEVICE_ATTR(fw_update,
rmi_fw_update_show, rmi_fw_update_store);
static struct attribute *rmi_fw_update_attrs[] = {
- &dev_attr_update_force.attr,
+ &dev_attr_fw_force_update.attr,
&dev_attr_fw_img_name.attr,
&dev_attr_fw_update.attr,
NULL
@@ -904,9 +890,8 @@ static const struct attribute_group rmi_fw_update_attributes = {
/*
* Allocate data needed by firmware update and initialize relevant
- * structures (like sysfs, work, and so on). You'll need to call
- * rmi_fw_update_cleanup() to free the storage and tear down the
- * structures.
+ * structures (like sysfs). You'll need to call rmi_fw_update_cleanup()
+ * to free the storage and tear down the structures.
*/
void rmi_fw_update_init(struct rmi_device *rmi_dev)
{
@@ -918,13 +903,13 @@ void rmi_fw_update_init(struct rmi_device *rmi_dev)
data = kzalloc(sizeof(struct rmi_fw_update_data), GFP_KERNEL);
- error = sysfs_create_group(&rmi_dev->dev.kobj, &rmi_fw_update_attributes);
+ error = sysfs_create_group(&rmi_dev->dev.kobj,
+ &rmi_fw_update_attributes);
if (error) {
dev_warn(&rmi_dev->dev, "Failed to create fw update sysfs attributes.\n");
return;
}
- INIT_WORK(&data->update_work, rmi_update_work);
data->rmi_dev = rmi_dev;
drv_data->fw_update_data = data;
}
^ permalink raw reply related
* Re: [PATCH v2 2/3] mfd: max8997: handle IRQs using regmap
From: Chanwoo Choi @ 2014-03-13 2:03 UTC (permalink / raw)
To: Robert Baldyga
Cc: sameo, lee.jones, myungjoo.ham, dmitry.torokhov, cooloney,
rpurdie, dbaryshkov, dwmw2, lgirdwood, broonie, a.zummo,
paul.gortmaker, linux-kernel, linux-input, linux-leds, rtc-linux,
m.szyprowski, k.kozlowski
In-Reply-To: <1394631466-6429-3-git-send-email-r.baldyga@samsung.com>
Hi Robert,
On 03/12/2014 10:37 PM, Robert Baldyga wrote:
> This patch modifies mfd driver to use regmap for handling interrupts.
> It allows to simplify irq handling process. This modifications needed
> to make small changes in function drivers, which use interrupts.
>
> Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
> ---
> drivers/extcon/extcon-max8997.c | 35 ++--
> drivers/mfd/Kconfig | 2 +-
> drivers/mfd/Makefile | 2 +-
> drivers/mfd/max8997-irq.c | 373 -----------------------------------
> drivers/mfd/max8997.c | 113 ++++++++++-
> drivers/rtc/rtc-max8997.c | 2 +-
> include/linux/mfd/max8997-private.h | 65 +++++-
> 7 files changed, 183 insertions(+), 409 deletions(-)
> delete mode 100644 drivers/mfd/max8997-irq.c
>
> diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
> index f258c08..15fc5c0 100644
> --- a/drivers/extcon/extcon-max8997.c
> +++ b/drivers/extcon/extcon-max8997.c
> @@ -46,15 +46,15 @@ struct max8997_muic_irq {
> };
>
> static struct max8997_muic_irq muic_irqs[] = {
> - { MAX8997_MUICIRQ_ADCError, "muic-ADCERROR" },
> - { MAX8997_MUICIRQ_ADCLow, "muic-ADCLOW" },
> - { MAX8997_MUICIRQ_ADC, "muic-ADC" },
> - { MAX8997_MUICIRQ_VBVolt, "muic-VBVOLT" },
> - { MAX8997_MUICIRQ_DBChg, "muic-DBCHG" },
> - { MAX8997_MUICIRQ_DCDTmr, "muic-DCDTMR" },
> - { MAX8997_MUICIRQ_ChgDetRun, "muic-CHGDETRUN" },
> - { MAX8997_MUICIRQ_ChgTyp, "muic-CHGTYP" },
> - { MAX8997_MUICIRQ_OVP, "muic-OVP" },
> + { MAX8997_MUICIRQ_ADCERROR, "MUIC-ADCERROR" },
> + { MAX8997_MUICIRQ_ADCLOW, "MUIC-ADCLOW" },
> + { MAX8997_MUICIRQ_ADC, "MUIC-ADC" },
> + { MAX8997_MUICIRQ_VBVOLT, "MUIC-VBVOLT" },
> + { MAX8997_MUICIRQ_DBCHG, "MUIC-DBCHG" },
> + { MAX8997_MUICIRQ_DCDTMR, "MUIC-DCDTMR" },
> + { MAX8997_MUICIRQ_CHGDETRUN, "MUIC-CHGDETRUN" },
> + { MAX8997_MUICIRQ_CHGTYP, "MUIC-CHGTYP" },
> + { MAX8997_MUICIRQ_OVP, "MUIC-OVP" },
> };
Why did you modify interrput name? Did you have some reason?
I think this modification don't need it.
>
> /* Define supported cable type */
> @@ -553,17 +553,17 @@ static void max8997_muic_irq_work(struct work_struct *work)
> }
>
> switch (irq_type) {
> - case MAX8997_MUICIRQ_ADCError:
> - case MAX8997_MUICIRQ_ADCLow:
> + case MAX8997_MUICIRQ_ADCERROR:
> + case MAX8997_MUICIRQ_ADCLOW:
> case MAX8997_MUICIRQ_ADC:
> /* Handle all of cable except for charger cable */
> ret = max8997_muic_adc_handler(info);
> break;
> - case MAX8997_MUICIRQ_VBVolt:
> - case MAX8997_MUICIRQ_DBChg:
> - case MAX8997_MUICIRQ_DCDTmr:
> - case MAX8997_MUICIRQ_ChgDetRun:
> - case MAX8997_MUICIRQ_ChgTyp:
> + case MAX8997_MUICIRQ_VBVOLT:
> + case MAX8997_MUICIRQ_DBCHG:
> + case MAX8997_MUICIRQ_DCDTMR:
> + case MAX8997_MUICIRQ_CHGDETRUN:
> + case MAX8997_MUICIRQ_CHGTYP:
I don't agree the modification of MUIC itnerrput.
> /* Handle charger cable */
> ret = max8997_muic_chg_handler(info);
> break;
> @@ -679,7 +679,8 @@ static int max8997_muic_probe(struct platform_device *pdev)
> struct max8997_muic_irq *muic_irq = &muic_irqs[i];
> unsigned int virq = 0;
>
> - virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);
> + virq = regmap_irq_get_virq(max8997->irq_data_muic,
> + muic_irq->irq);
> if (!virq) {
> ret = -EINVAL;
> goto err_irq;
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 2871a65..2273574 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -388,7 +388,7 @@ config MFD_MAX8997
> depends on I2C=y
> select MFD_CORE
> select REGMAP_I2C
> - select IRQ_DOMAIN
> + select REGMAP_IRQ
> help
> Say yes here to add support for Maxim Semiconductor MAX8997/8966.
> This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index f5a7b2c..4cec8ad 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -119,7 +119,7 @@ obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
> obj-$(CONFIG_MFD_MAX8907) += max8907.o
> max8925-objs := max8925-core.o max8925-i2c.o
> obj-$(CONFIG_MFD_MAX8925) += max8925.o
> -obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
> +obj-$(CONFIG_MFD_MAX8997) += max8997.o
> obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
>
> pcf50633-objs := pcf50633-core.o pcf50633-irq.o
> diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c
> deleted file mode 100644
> index 0e7ff39..0000000
> --- a/drivers/mfd/max8997-irq.c
> +++ /dev/null
> @@ -1,373 +0,0 @@
> -/*
> - * max8997-irq.c - Interrupt controller support for MAX8997
> - *
> - * Copyright (C) 2011 Samsung Electronics Co.Ltd
> - * MyungJoo Ham <myungjoo.ham@samsung.com>
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - *
> - * This driver is based on max8998-irq.c
> - */
> -
> -#include <linux/err.h>
> -#include <linux/irq.h>
> -#include <linux/interrupt.h>
> -#include <linux/mfd/max8997.h>
> -#include <linux/mfd/max8997-private.h>
> -#include <linux/regmap.h>
> -
> -static const u8 max8997_mask_reg[] = {
> - [PMIC_INT1] = MAX8997_REG_INT1MSK,
> - [PMIC_INT2] = MAX8997_REG_INT2MSK,
> - [PMIC_INT3] = MAX8997_REG_INT3MSK,
> - [PMIC_INT4] = MAX8997_REG_INT4MSK,
> - [FUEL_GAUGE] = MAX8997_REG_INVALID,
> - [MUIC_INT1] = MAX8997_MUIC_REG_INTMASK1,
> - [MUIC_INT2] = MAX8997_MUIC_REG_INTMASK2,
> - [MUIC_INT3] = MAX8997_MUIC_REG_INTMASK3,
> - [GPIO_LOW] = MAX8997_REG_INVALID,
> - [GPIO_HI] = MAX8997_REG_INVALID,
> - [FLASH_STATUS] = MAX8997_REG_INVALID,
> -};
> -
> -struct max8997_irq_data {
> - int mask;
> - enum max8997_irq_source group;
> -};
> -
> -#define DECLARE_IRQ(idx, _group, _mask) \
> - [(idx)] = { .group = (_group), .mask = (_mask) }
> -static const struct max8997_irq_data max8997_irqs[] = {
> - DECLARE_IRQ(MAX8997_PMICIRQ_PWRONR, PMIC_INT1, 1 << 0),
> - DECLARE_IRQ(MAX8997_PMICIRQ_PWRONF, PMIC_INT1, 1 << 1),
> - DECLARE_IRQ(MAX8997_PMICIRQ_PWRON1SEC, PMIC_INT1, 1 << 3),
> - DECLARE_IRQ(MAX8997_PMICIRQ_JIGONR, PMIC_INT1, 1 << 4),
> - DECLARE_IRQ(MAX8997_PMICIRQ_JIGONF, PMIC_INT1, 1 << 5),
> - DECLARE_IRQ(MAX8997_PMICIRQ_LOWBAT2, PMIC_INT1, 1 << 6),
> - DECLARE_IRQ(MAX8997_PMICIRQ_LOWBAT1, PMIC_INT1, 1 << 7),
> -
> - DECLARE_IRQ(MAX8997_PMICIRQ_JIGR, PMIC_INT2, 1 << 0),
> - DECLARE_IRQ(MAX8997_PMICIRQ_JIGF, PMIC_INT2, 1 << 1),
> - DECLARE_IRQ(MAX8997_PMICIRQ_MR, PMIC_INT2, 1 << 2),
> - DECLARE_IRQ(MAX8997_PMICIRQ_DVS1OK, PMIC_INT2, 1 << 3),
> - DECLARE_IRQ(MAX8997_PMICIRQ_DVS2OK, PMIC_INT2, 1 << 4),
> - DECLARE_IRQ(MAX8997_PMICIRQ_DVS3OK, PMIC_INT2, 1 << 5),
> - DECLARE_IRQ(MAX8997_PMICIRQ_DVS4OK, PMIC_INT2, 1 << 6),
> -
> - DECLARE_IRQ(MAX8997_PMICIRQ_CHGINS, PMIC_INT3, 1 << 0),
> - DECLARE_IRQ(MAX8997_PMICIRQ_CHGRM, PMIC_INT3, 1 << 1),
> - DECLARE_IRQ(MAX8997_PMICIRQ_DCINOVP, PMIC_INT3, 1 << 2),
> - DECLARE_IRQ(MAX8997_PMICIRQ_TOPOFFR, PMIC_INT3, 1 << 3),
> - DECLARE_IRQ(MAX8997_PMICIRQ_CHGRSTF, PMIC_INT3, 1 << 5),
> - DECLARE_IRQ(MAX8997_PMICIRQ_MBCHGTMEXPD, PMIC_INT3, 1 << 7),
> -
> - DECLARE_IRQ(MAX8997_PMICIRQ_RTC60S, PMIC_INT4, 1 << 0),
> - DECLARE_IRQ(MAX8997_PMICIRQ_RTCA1, PMIC_INT4, 1 << 1),
> - DECLARE_IRQ(MAX8997_PMICIRQ_RTCA2, PMIC_INT4, 1 << 2),
> - DECLARE_IRQ(MAX8997_PMICIRQ_SMPL_INT, PMIC_INT4, 1 << 3),
> - DECLARE_IRQ(MAX8997_PMICIRQ_RTC1S, PMIC_INT4, 1 << 4),
> - DECLARE_IRQ(MAX8997_PMICIRQ_WTSR, PMIC_INT4, 1 << 5),
> -
> - DECLARE_IRQ(MAX8997_MUICIRQ_ADCError, MUIC_INT1, 1 << 2),
> - DECLARE_IRQ(MAX8997_MUICIRQ_ADCLow, MUIC_INT1, 1 << 1),
> - DECLARE_IRQ(MAX8997_MUICIRQ_ADC, MUIC_INT1, 1 << 0),
> -
> - DECLARE_IRQ(MAX8997_MUICIRQ_VBVolt, MUIC_INT2, 1 << 4),
> - DECLARE_IRQ(MAX8997_MUICIRQ_DBChg, MUIC_INT2, 1 << 3),
> - DECLARE_IRQ(MAX8997_MUICIRQ_DCDTmr, MUIC_INT2, 1 << 2),
> - DECLARE_IRQ(MAX8997_MUICIRQ_ChgDetRun, MUIC_INT2, 1 << 1),
> - DECLARE_IRQ(MAX8997_MUICIRQ_ChgTyp, MUIC_INT2, 1 << 0),
> -
> - DECLARE_IRQ(MAX8997_MUICIRQ_OVP, MUIC_INT3, 1 << 2),
> -};
> -
> -static void max8997_irq_lock(struct irq_data *data)
> -{
> - struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
> -
> - mutex_lock(&max8997->irqlock);
> -}
> -
> -static void max8997_irq_sync_unlock(struct irq_data *data)
> -{
> - struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
> - int i;
> -
> - for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
> - struct regmap *map;
> - u8 mask_reg = max8997_mask_reg[i];
> -
> - if (i >= MUIC_INT1 && i <= MUIC_INT3)
> - map = max8997->regmap_muic;
> - else
> - map = max8997->regmap;
> -
> - if (mask_reg == MAX8997_REG_INVALID ||
> - IS_ERR_OR_NULL(map))
> - continue;
> - max8997->irq_masks_cache[i] = max8997->irq_masks_cur[i];
> -
> - regmap_write(map, max8997_mask_reg[i],
> - max8997->irq_masks_cur[i]);
> - }
> -
> - mutex_unlock(&max8997->irqlock);
> -}
> -
> -static const inline struct max8997_irq_data *
> -irq_to_max8997_irq(struct max8997_dev *max8997, int irq)
> -{
> - struct irq_data *data = irq_get_irq_data(irq);
> - return &max8997_irqs[data->hwirq];
> -}
> -
> -static void max8997_irq_mask(struct irq_data *data)
> -{
> - struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
> - const struct max8997_irq_data *irq_data = irq_to_max8997_irq(max8997,
> - data->irq);
> -
> - max8997->irq_masks_cur[irq_data->group] |= irq_data->mask;
> -}
> -
> -static void max8997_irq_unmask(struct irq_data *data)
> -{
> - struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
> - const struct max8997_irq_data *irq_data = irq_to_max8997_irq(max8997,
> - data->irq);
> -
> - max8997->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
> -}
> -
> -static struct irq_chip max8997_irq_chip = {
> - .name = "max8997",
> - .irq_bus_lock = max8997_irq_lock,
> - .irq_bus_sync_unlock = max8997_irq_sync_unlock,
> - .irq_mask = max8997_irq_mask,
> - .irq_unmask = max8997_irq_unmask,
> -};
> -
> -#define MAX8997_IRQSRC_PMIC (1 << 1)
> -#define MAX8997_IRQSRC_FUELGAUGE (1 << 2)
> -#define MAX8997_IRQSRC_MUIC (1 << 3)
> -#define MAX8997_IRQSRC_GPIO (1 << 4)
> -#define MAX8997_IRQSRC_FLASH (1 << 5)
> -static irqreturn_t max8997_irq_thread(int irq, void *data)
> -{
> - struct max8997_dev *max8997 = data;
> - u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {};
> - unsigned int irq_src;
> - int ret;
> - int i, cur_irq;
> -
> - ret = regmap_read(max8997->regmap, MAX8997_REG_INTSRC, &irq_src);
> - if (ret < 0) {
> - dev_err(max8997->dev, "Failed to read interrupt source: %d\n",
> - ret);
> - return IRQ_NONE;
> - }
> -
> - if (irq_src & MAX8997_IRQSRC_PMIC) {
> - /* PMIC INT1 ~ INT4 */
> - regmap_bulk_read(max8997->regmap, MAX8997_REG_INT1,
> - &irq_reg[PMIC_INT1], 4);
> - }
> - if (irq_src & MAX8997_IRQSRC_FUELGAUGE) {
> - /*
> - * TODO: FUEL GAUGE
> - *
> - * This is to be supported by Max17042 driver. When
> - * an interrupt incurs here, it should be relayed to a
> - * Max17042 device that is connected (probably by
> - * platform-data). However, we do not have interrupt
> - * handling in Max17042 driver currently. The Max17042 IRQ
> - * driver should be ready to be used as a stand-alone device and
> - * a Max8997-dependent device. Because it is not ready in
> - * Max17042-side and it is not too critical in operating
> - * Max8997, we do not implement this in initial releases.
> - */
> - irq_reg[FUEL_GAUGE] = 0;
> - }
> - if (irq_src & MAX8997_IRQSRC_MUIC) {
> - /* MUIC INT1 ~ INT3 */
> - regmap_bulk_read(max8997->regmap_muic, MAX8997_MUIC_REG_INT1,
> - &irq_reg[MUIC_INT1], 3);
> - }
> - if (irq_src & MAX8997_IRQSRC_GPIO) {
> - /* GPIO Interrupt */
> - u8 gpio_info[MAX8997_NUM_GPIO];
> -
> - irq_reg[GPIO_LOW] = 0;
> - irq_reg[GPIO_HI] = 0;
> -
> - regmap_bulk_read(max8997->regmap, MAX8997_REG_GPIOCNTL1,
> - gpio_info, MAX8997_NUM_GPIO);
> - for (i = 0; i < MAX8997_NUM_GPIO; i++) {
> - bool interrupt = false;
> -
> - switch (gpio_info[i] & MAX8997_GPIO_INT_MASK) {
> - case MAX8997_GPIO_INT_BOTH:
> - if (max8997->gpio_status[i] != gpio_info[i])
> - interrupt = true;
> - break;
> - case MAX8997_GPIO_INT_RISE:
> - if ((max8997->gpio_status[i] != gpio_info[i]) &&
> - (gpio_info[i] & MAX8997_GPIO_DATA_MASK))
> - interrupt = true;
> - break;
> - case MAX8997_GPIO_INT_FALL:
> - if ((max8997->gpio_status[i] != gpio_info[i]) &&
> - !(gpio_info[i] & MAX8997_GPIO_DATA_MASK))
> - interrupt = true;
> - break;
> - default:
> - break;
> - }
> -
> - if (interrupt) {
> - if (i < 8)
> - irq_reg[GPIO_LOW] |= (1 << i);
> - else
> - irq_reg[GPIO_HI] |= (1 << (i - 8));
> - }
> -
> - }
> - }
> - if (irq_src & MAX8997_IRQSRC_FLASH) {
> - /* Flash Status Interrupt */
> - unsigned int data;
> - ret = regmap_read(max8997->regmap,
> - MAX8997_REG_FLASHSTATUS, &data);
> - irq_reg[FLASH_STATUS] = data;
> - }
> -
> - /* Apply masking */
> - for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++)
> - irq_reg[i] &= ~max8997->irq_masks_cur[i];
> -
> - /* Report */
> - for (i = 0; i < MAX8997_IRQ_NR; i++) {
> - if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) {
> - cur_irq = irq_find_mapping(max8997->irq_domain, i);
> - if (cur_irq)
> - handle_nested_irq(cur_irq);
> - }
> - }
> -
> - return IRQ_HANDLED;
> -}
> -
> -int max8997_irq_resume(struct max8997_dev *max8997)
> -{
> - if (max8997->irq && max8997->irq_domain)
> - max8997_irq_thread(0, max8997);
> - return 0;
> -}
> -
> -static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq,
> - irq_hw_number_t hw)
> -{
> - struct max8997_dev *max8997 = d->host_data;
> -
> - irq_set_chip_data(irq, max8997);
> - irq_set_chip_and_handler(irq, &max8997_irq_chip, handle_edge_irq);
> - irq_set_nested_thread(irq, 1);
> -#ifdef CONFIG_ARM
> - set_irq_flags(irq, IRQF_VALID);
> -#else
> - irq_set_noprobe(irq);
> -#endif
> - return 0;
> -}
> -
> -static struct irq_domain_ops max8997_irq_domain_ops = {
> - .map = max8997_irq_domain_map,
> -};
> -
> -int max8997_irq_init(struct max8997_dev *max8997)
> -{
> - struct irq_domain *domain;
> - int i;
> - int ret;
> - unsigned int val;
> -
> - if (!max8997->irq) {
> - dev_warn(max8997->dev, "No interrupt specified.\n");
> - return 0;
> - }
> -
> - mutex_init(&max8997->irqlock);
> -
> - /* Mask individual interrupt sources */
> - for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
> - max8997->irq_masks_cur[i] = 0xff;
> - max8997->irq_masks_cache[i] = 0xff;
> -
> - if (IS_ERR_OR_NULL(max8997->regmap))
> - continue;
> - if (max8997_mask_reg[i] == MAX8997_REG_INVALID)
> - continue;
> -
> - regmap_write(max8997->regmap, max8997_mask_reg[i], 0xff);
> - }
> -
> - for (i = 0; i < MAX8997_NUM_GPIO; i++) {
> - max8997->gpio_status[i] = (regmap_read(max8997->regmap,
> - MAX8997_REG_GPIOCNTL1 + i,
> - &val)
> - & MAX8997_GPIO_DATA_MASK) ?
> - true : false;
> - }
> -
> - domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR,
> - &max8997_irq_domain_ops, max8997);
> - if (!domain) {
> - dev_err(max8997->dev, "could not create irq domain\n");
> - return -ENODEV;
> - }
> - max8997->irq_domain = domain;
> -
> - ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread,
> - IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> - "max8997-irq", max8997);
> -
> - if (ret) {
> - dev_err(max8997->dev, "Failed to request IRQ %d: %d\n",
> - max8997->irq, ret);
> - return ret;
> - }
> -
> - if (!max8997->ono)
> - return 0;
> -
> - ret = request_threaded_irq(max8997->ono, NULL, max8997_irq_thread,
> - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
> - IRQF_ONESHOT, "max8997-ono", max8997);
> -
> - if (ret)
> - dev_err(max8997->dev, "Failed to request ono-IRQ %d: %d\n",
> - max8997->ono, ret);
> -
> - return 0;
> -}
> -
> -void max8997_irq_exit(struct max8997_dev *max8997)
> -{
> - if (max8997->ono)
> - free_irq(max8997->ono, max8997);
> -
> - if (max8997->irq)
> - free_irq(max8997->irq, max8997);
> -}
> diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
> index 782d7c9..26a360d 100644
> --- a/drivers/mfd/max8997.c
> +++ b/drivers/mfd/max8997.c
> @@ -64,6 +64,49 @@ static const struct regmap_config max8997_regmap_config = {
> .max_register = MAX8997_REG_PMIC_END,
> };
>
> +static const struct regmap_irq max8997_irqs[] = {
> + /* PMIC_INT1 interrupts */
> + { .reg_offset = 0, .mask = PMIC_INT1_PWRONR_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_PWRONF_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_PWRON1SEC_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_JIGONR_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_JIGONF_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_LOWBAT2_MASK, },
> + { .reg_offset = 0, .mask = PMIC_INT1_LOWBAT1_MASK, },
> + /* PMIC_INT2 interrupts */
> + { .reg_offset = 1, .mask = PMIC_INT2_JIGR_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_JIGF_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_MR_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_DVS1OK_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_DVS2OK_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_DVS3OK_MASK, },
> + { .reg_offset = 1, .mask = PMIC_INT2_DVS4OK_MASK, },
> + /* PMIC_INT3 interrupts */
> + { .reg_offset = 2, .mask = PMIC_INT3_CHGINS_MASK, },
> + { .reg_offset = 2, .mask = PMIC_INT3_CHGRM_MASK, },
> + { .reg_offset = 2, .mask = PMIC_INT3_DCINOVP_MASK, },
> + { .reg_offset = 2, .mask = PMIC_INT3_TOPOFFR_MASK, },
> + { .reg_offset = 2, .mask = PMIC_INT3_CHGRSTF_MASK, },
> + { .reg_offset = 2, .mask = PMIC_INT3_MBCHGTMEXPD_MASK, },
> + /* PMIC_INT4 interrupts */
> + { .reg_offset = 3, .mask = PMIC_INT4_RTC60S_MASK, },
> + { .reg_offset = 3, .mask = PMIC_INT4_RTCA1_MASK, },
> + { .reg_offset = 3, .mask = PMIC_INT4_RTCA2_MASK, },
> + { .reg_offset = 3, .mask = PMIC_INT4_SMPL_INT_MASK, },
> + { .reg_offset = 3, .mask = PMIC_INT4_RTC1S_MASK, },
> + { .reg_offset = 3, .mask = PMIC_INT4_WTSR_MASK, },
> +};
> +
> +static const struct regmap_irq_chip max8997_irq_chip = {
> + .name = "max8997",
> + .status_base = MAX8997_REG_INT1,
> + .mask_base = MAX8997_REG_INT1MSK,
> + .mask_invert = false,
> + .num_regs = 4,
> + .irqs = max8997_irqs,
> + .num_irqs = ARRAY_SIZE(max8997_irqs),
> +};
> +
> static const struct regmap_config max8997_regmap_rtc_config = {
> .reg_bits = 8,
> .val_bits = 8,
> @@ -82,6 +125,31 @@ static const struct regmap_config max8997_regmap_muic_config = {
> .max_register = MAX8997_MUIC_REG_END,
> };
>
> +static const struct regmap_irq max8997_irqs_muic[] = {
> + /* MUIC_INT1 interrupts */
> + { .reg_offset = 0, .mask = MUIC_INT1_ADC_MASK, },
> + { .reg_offset = 0, .mask = MUIC_INT1_ADCLOW_MASK, },
> + { .reg_offset = 0, .mask = MUIC_INT1_ADCERROR_MASK, },
> + /* MUIC_INT2 interrupts */
> + { .reg_offset = 1, .mask = MUIC_INT2_CHGTYP_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_CHGDETRUN_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_DCDTMR_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_DBCHG_MASK, },
> + { .reg_offset = 1, .mask = MUIC_INT2_VBVOLT_MASK, },
> + /* MUIC_INT3 interrupts */
> + { .reg_offset = 2, .mask = MUIC_INT3_OVP_MASK, },
> +};
> +
> +static const struct regmap_irq_chip max8997_irq_chip_muic = {
> + .name = "max8997-muic",
> + .status_base = MAX8997_MUIC_REG_INT1,
> + .mask_base = MAX8997_MUIC_REG_INTMASK1,
> + .mask_invert = true,
> + .num_regs = 3,
> + .irqs = max8997_irqs_muic,
> + .num_irqs = ARRAY_SIZE(max8997_irqs_muic),
> +};
> +
> /*
> * Only the common platform data elements for max8997 are parsed here from the
> * device tree. Other sub-modules of max8997 such as pmic, rtc and others have
> @@ -214,9 +282,26 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
> goto err_regmap;
> }
>
> - pm_runtime_set_active(max8997->dev);
> + ret = regmap_add_irq_chip(max8997->regmap, max8997->irq,
> + IRQF_ONESHOT | IRQF_SHARED |
> + IRQF_TRIGGER_FALLING, 0,
> + &max8997_irq_chip, &max8997->irq_data);
> + if (ret) {
> + dev_err(max8997->dev, "failed to add irq chip: %d\n", ret);
> + goto err_irq;
> + }
>
> - max8997_irq_init(max8997);
> + ret = regmap_add_irq_chip(max8997->regmap_muic, max8997->irq,
> + IRQF_ONESHOT | IRQF_SHARED |
> + IRQF_TRIGGER_FALLING, 0,
> + &max8997_irq_chip_muic,
> + &max8997->irq_data_muic);
> + if (ret) {
> + dev_err(max8997->dev, "failed to add irq chip: %d\n", ret);
> + goto err_irq_muic;
> + }
> +
> + pm_runtime_set_active(max8997->dev);
>
> ret = mfd_add_devices(max8997->dev, -1, max8997_devs,
> ARRAY_SIZE(max8997_devs),
> @@ -238,6 +323,10 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
>
> err_mfd:
> mfd_remove_devices(max8997->dev);
> +err_irq_muic:
> + regmap_del_irq_chip(max8997->irq, max8997->irq_data_muic);
> +err_irq:
> + regmap_del_irq_chip(max8997->irq, max8997->irq_data);
> err_regmap:
> i2c_unregister_device(max8997->muic);
> err_i2c_muic:
> @@ -252,6 +341,10 @@ static int max8997_i2c_remove(struct i2c_client *i2c)
> struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
>
> mfd_remove_devices(max8997->dev);
> +
> + regmap_del_irq_chip(max8997->irq, max8997->irq_data_muic);
> + regmap_del_irq_chip(max8997->irq, max8997->irq_data);
> +
> i2c_unregister_device(max8997->muic);
> i2c_unregister_device(max8997->haptic);
> i2c_unregister_device(max8997->rtc);
> @@ -468,8 +561,11 @@ static int max8997_suspend(struct device *dev)
> struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
> struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
>
> - if (device_may_wakeup(dev))
> - irq_set_irq_wake(max8997->irq, 1);
> + if (device_may_wakeup(dev)) {
> + enable_irq_wake(max8997->irq);
> + disable_irq(max8997->irq);
> + }
> +
> return 0;
> }
>
> @@ -478,9 +574,12 @@ static int max8997_resume(struct device *dev)
> struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
> struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
>
> - if (device_may_wakeup(dev))
> - irq_set_irq_wake(max8997->irq, 0);
> - return max8997_irq_resume(max8997);
> + if (device_may_wakeup(dev)) {
> + disable_irq_wake(max8997->irq);
> + enable_irq(max8997->irq);
> + }
> +
> + return 0;
> }
>
> static const struct dev_pm_ops max8997_pm = {
> diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c
> index b866f7d5..22769ea 100644
> --- a/drivers/rtc/rtc-max8997.c
> +++ b/drivers/rtc/rtc-max8997.c
> @@ -494,7 +494,7 @@ static int max8997_rtc_probe(struct platform_device *pdev)
> return ret;
> }
>
> - virq = irq_create_mapping(max8997->irq_domain, MAX8997_PMICIRQ_RTCA1);
> + virq = regmap_irq_get_virq(max8997->irq_data, MAX8997_PMICIRQ_RTCA1);
> if (!virq) {
> dev_err(&pdev->dev, "Failed to create mapping alarm IRQ\n");
> ret = -ENXIO;
> diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h
> index ea80ef8..2817fa6 100644
> --- a/include/linux/mfd/max8997-private.h
> +++ b/include/linux/mfd/max8997-private.h
> @@ -333,6 +333,48 @@ enum max8997_irq_source {
> MAX8997_IRQ_GROUP_NR,
> };
>
> +#define PMIC_INT1_PWRONR_MASK (0x1 << 0)
> +#define PMIC_INT1_PWRONF_MASK (0x1 << 1)
> +#define PMIC_INT1_PWRON1SEC_MASK (0x1 << 3)
> +#define PMIC_INT1_JIGONR_MASK (0x1 << 4)
> +#define PMIC_INT1_JIGONF_MASK (0x1 << 5)
> +#define PMIC_INT1_LOWBAT2_MASK (0x1 << 6)
> +#define PMIC_INT1_LOWBAT1_MASK (0x1 << 7)
> +
> +#define PMIC_INT2_JIGR_MASK (0x1 << 0)
> +#define PMIC_INT2_JIGF_MASK (0x1 << 1)
> +#define PMIC_INT2_MR_MASK (0x1 << 2)
> +#define PMIC_INT2_DVS1OK_MASK (0x1 << 3)
> +#define PMIC_INT2_DVS2OK_MASK (0x1 << 4)
> +#define PMIC_INT2_DVS3OK_MASK (0x1 << 5)
> +#define PMIC_INT2_DVS4OK_MASK (0x1 << 6)
> +
> +#define PMIC_INT3_CHGINS_MASK (0x1 << 0)
> +#define PMIC_INT3_CHGRM_MASK (0x1 << 1)
> +#define PMIC_INT3_DCINOVP_MASK (0x1 << 2)
> +#define PMIC_INT3_TOPOFFR_MASK (0x1 << 3)
> +#define PMIC_INT3_CHGRSTF_MASK (0x1 << 5)
> +#define PMIC_INT3_MBCHGTMEXPD_MASK (0x1 << 7)
> +
> +#define PMIC_INT4_RTC60S_MASK (0x1 << 0)
> +#define PMIC_INT4_RTCA1_MASK (0x1 << 1)
> +#define PMIC_INT4_RTCA2_MASK (0x1 << 2)
> +#define PMIC_INT4_SMPL_INT_MASK (0x1 << 3)
> +#define PMIC_INT4_RTC1S_MASK (0x1 << 4)
> +#define PMIC_INT4_WTSR_MASK (0x1 << 5)
> +
> +#define MUIC_INT1_ADC_MASK (0x1 << 0)
> +#define MUIC_INT1_ADCLOW_MASK (0x1 << 1)
> +#define MUIC_INT1_ADCERROR_MASK (0x1 << 2)
> +
> +#define MUIC_INT2_CHGTYP_MASK (0x1 << 0)
> +#define MUIC_INT2_CHGDETRUN_MASK (0x1 << 1)
> +#define MUIC_INT2_DCDTMR_MASK (0x1 << 2)
> +#define MUIC_INT2_DBCHG_MASK (0x1 << 3)
> +#define MUIC_INT2_VBVOLT_MASK (0x1 << 4)
> +
> +#define MUIC_INT3_OVP_MASK (0x1 << 2)
> +
> enum max8997_irq {
> MAX8997_PMICIRQ_PWRONR,
> MAX8997_PMICIRQ_PWRONF,
> @@ -364,19 +406,23 @@ enum max8997_irq {
> MAX8997_PMICIRQ_RTC1S,
> MAX8997_PMICIRQ_WTSR,
>
> - MAX8997_MUICIRQ_ADCError,
> - MAX8997_MUICIRQ_ADCLow,
> + MAX8997_PMICIRQ_NR,
> +};
> +
> +enum max8997_irq_muic {
> MAX8997_MUICIRQ_ADC,
> + MAX8997_MUICIRQ_ADCLOW,
> + MAX8997_MUICIRQ_ADCERROR,
>
> - MAX8997_MUICIRQ_VBVolt,
> - MAX8997_MUICIRQ_DBChg,
> - MAX8997_MUICIRQ_DCDTmr,
> - MAX8997_MUICIRQ_ChgDetRun,
> - MAX8997_MUICIRQ_ChgTyp,
> + MAX8997_MUICIRQ_CHGTYP,
> + MAX8997_MUICIRQ_CHGDETRUN,
> + MAX8997_MUICIRQ_DCDTMR,
> + MAX8997_MUICIRQ_DBCHG,
> + MAX8997_MUICIRQ_VBVOLT,
ditto.
I don't understand why do you modify interrnut name/macro.
>
> MAX8997_MUICIRQ_OVP,
>
> - MAX8997_IRQ_NR,
> + MAX8997_MUCIRQ_NR,
ditto.
> };
>
> #define MAX8997_NUM_GPIO 12
> @@ -397,9 +443,10 @@ struct max8997_dev {
> struct regmap *regmap_haptic;
> struct regmap *regmap_muic;
>
> + struct regmap_irq_chip_data *irq_data;
> + struct regmap_irq_chip_data *irq_data_muic;
> int irq;
> int ono;
> - struct irq_domain *irq_domain;
> struct mutex irqlock;
> int irq_masks_cur[MAX8997_IRQ_GROUP_NR];
> int irq_masks_cache[MAX8997_IRQ_GROUP_NR];
>
Thanks,
Chanwoo Choi
^ permalink raw reply
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