* AW: [PATCH 2/3] Input: st1232 - add support for st1633
From: Matthias Fend @ 2019-01-29 7:07 UTC (permalink / raw)
To: Martin Kepplinger, Dmitry Torokhov
Cc: devicetree@vger.kernel.org, linux-input@vger.kernel.org,
robh+dt@kernel.org, mark.rutland@arm.com,
linux-kernel@vger.kernel.org, Martin Kepplinger
In-Reply-To: <894cb7d4749835325c4e0f9c65d31a63@posteo.de>
Hi Martin,
Matthias Fend
R&D Electronics
Wolfvision GmbH
Oberes Ried 14 | 6833 Klaus | Austria
Tel: +43 5523 52250 | Mail: Matthias.Fend@wolfvision.net
Webpage: www.wolfvision.com | www.wolfvision.com/green
Firmenbuch / Commercial Register: FN283521v Feldkirch/Austria
> -----Ursprüngliche Nachricht-----
> Von: linux-input-owner@vger.kernel.org <linux-input-
> owner@vger.kernel.org> Im Auftrag von Martin Kepplinger
> Gesendet: Montag, 28. Jänner 2019 20:10
> An: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: devicetree@vger.kernel.org; linux-input@vger.kernel.org;
> robh+dt@kernel.org; mark.rutland@arm.com; linux-kernel@vger.kernel.org;
> Martin Kepplinger <martin.kepplinger@ginzinger.com>
> Betreff: Re: [PATCH 2/3] Input: st1232 - add support for st1633
>
> Am 28.01.2019 20:03 schrieb Dmitry Torokhov:
> > Hi Martin,
> >
> > On Mon, Jan 28, 2019 at 09:44:48AM +0100, Martin Kepplinger wrote:
> >> From: Martin Kepplinger <martin.kepplinger@ginzinger.com>
> >>
> >> Add support for the Sitronix ST1633 touchscreen controller to the
> >> st1232
> >> driver. A protocol spec can be found here:
> >>
> https://emea01.safelinks.protection.outlook.com/?url=www.ampdisplay.co
> m%2Fdocuments%2Fpdf%2FAM-320480B6TZQW-
> TC0H.pdf&data=01%7C01%7CMatthias.Fend%40wolfvision.net%7C4f9d
> 56475f674b1e3b4008d685543b35%7Ce94ec9da9183471e83b351baa8eb804f%
> 7C0&sdata=YTw33BPKKpdN%2BBFrufVk8e4YqXOzR6VQKuzRMOjcwWk
> %3D&reserved=0
> >>
> >> Signed-off-by: Martin Kepplinger <martin.kepplinger@ginzinger.com>
> >> ---
> >> drivers/input/touchscreen/Kconfig | 6 +-
> >> drivers/input/touchscreen/st1232.c | 122
> >> +++++++++++++++++++++--------
> >> 2 files changed, 94 insertions(+), 34 deletions(-)
> >>
> >> diff --git a/drivers/input/touchscreen/Kconfig
> >> b/drivers/input/touchscreen/Kconfig
> >> index 068dbbc610fc..7c597a49c265 100644
> >> --- a/drivers/input/touchscreen/Kconfig
> >> +++ b/drivers/input/touchscreen/Kconfig
> >> @@ -1168,11 +1168,11 @@ config TOUCHSCREEN_SIS_I2C
> >> module will be called sis_i2c.
> >>
> >> config TOUCHSCREEN_ST1232
> >> - tristate "Sitronix ST1232 touchscreen controllers"
> >> + tristate "Sitronix ST1232 or ST1633 touchscreen controllers"
> >> depends on I2C
> >> help
> >> - Say Y here if you want to support Sitronix ST1232
> >> - touchscreen controller.
> >> + Say Y here if you want to support the Sitronix ST1232
> >> + or ST1633 touchscreen controller.
> >>
> >> If unsure, say N.
> >>
> >> diff --git a/drivers/input/touchscreen/st1232.c
> >> b/drivers/input/touchscreen/st1232.c
> >> index 11ff32c68025..19a665d48dad 100644
> >> --- a/drivers/input/touchscreen/st1232.c
> >> +++ b/drivers/input/touchscreen/st1232.c
> >> @@ -23,13 +23,15 @@
> >> #include <linux/types.h>
> >>
> >> #define ST1232_TS_NAME "st1232-ts"
> >> +#define ST1633_TS_NAME "st1633-ts"
> >> +
> >> +enum {
> >> + st1232,
> >> + st1633,
> >> +};
> >
> > This enum seems no longer needed, I dropped it.
> >
> >>
> >> #define MIN_X 0x00
> >> #define MIN_Y 0x00
> >
> > Given we no longer have constant MAX_X/Y I simply used 0 in
> > input_set_abs_params() and dropped MIN-X/Y.
> >
> >> -#define MAX_X 0x31f /* (800 - 1) */
> >> -#define MAX_Y 0x1df /* (480 - 1) */
> >> -#define MAX_AREA 0xff
> >> -#define MAX_FINGERS 2
> >>
> >> struct st1232_ts_finger {
> >> u16 x;
> >> @@ -38,12 +40,24 @@ struct st1232_ts_finger {
> >> bool is_valid;
> >> };
> >>
> >> +struct st_chip_info {
> >> + bool have_z;
> >> + u16 max_x;
> >> + u16 max_y;
> >> + u16 max_area;
> >> + u16 max_fingers;
> >> + u8 start_reg;
> >> +};
> >> +
> >> struct st1232_ts_data {
> >> struct i2c_client *client;
> >> struct input_dev *input_dev;
> >> - struct st1232_ts_finger finger[MAX_FINGERS];
> >> struct dev_pm_qos_request low_latency_req;
> >> int reset_gpio;
> >
> > Could you please create an additional patch converting this to gpiod?
> > Instead of doing of_get_gpio()/gpio_is_valid()/devm_gpio_request()
> > smply
> > do
> >
> > ts->reset_gpio = devm_gpiod_get_optional();
> > if (IS_ERR(ts->reset_gpio)) {
> > ...
> > }
> >
> > and later
> >
> > if (ts->reset_gpio)
> > ...
> >
> > Looking at the code it looks like reset is as usual active low, so you
> > will need to invert the logic as gpiod takes care of convertion logical
> > value to proper level (active low or high).
>
> I'll test your applied changes and get back to this tomorrow.
>
> thanks.
>
> >
> >> + const struct st_chip_info *chip_info;
> >> + int read_buf_len;
> >> + u8 *read_buf;
> >> + struct st1232_ts_finger *finger;
> >> };
> >>
> >> static int st1232_ts_read_data(struct st1232_ts_data *ts)
> >> @@ -52,40 +66,35 @@ static int st1232_ts_read_data(struct
> >> st1232_ts_data *ts)
> >> struct i2c_client *client = ts->client;
> >> struct i2c_msg msg[2];
> >> int error;
> >> - u8 start_reg;
> >> - u8 buf[10];
> >> + int i, y;
> >> + u8 start_reg = ts->chip_info->start_reg;
> >> + u8 *buf = ts->read_buf;
> >>
> >> - /* read touchscreen data from ST1232 */
> >> + /* read touchscreen data */
> >> msg[0].addr = client->addr;
> >> msg[0].flags = 0;
> >> msg[0].len = 1;
> >> msg[0].buf = &start_reg;
> >> - start_reg = 0x10;
> >>
> >> msg[1].addr = ts->client->addr;
> >> msg[1].flags = I2C_M_RD;
> >> - msg[1].len = sizeof(buf);
> >> + msg[1].len = ts->read_buf_len;
> >> msg[1].buf = buf;
> >>
> >> error = i2c_transfer(client->adapter, msg, 2);
> >> if (error < 0)
> >> return error;
> >>
> >> - /* get "valid" bits */
> >> - finger[0].is_valid = buf[2] >> 7;
> >> - finger[1].is_valid = buf[5] >> 7;
> >> + for (i = 0, y = 0; i < ts->chip_info->max_fingers; i++, y += 3) {
> >> + finger[i].is_valid = buf[i + y] >> 7;
> >> + if (finger[i].is_valid) {
> >> + finger[i].x = ((buf[i + y] & 0x0070) << 4) | buf[i + 1];
> >> + finger[i].y = ((buf[i + y] & 0x0007) << 8) | buf[i + 2];
> >>
> >> - /* get xy coordinate */
> >> - if (finger[0].is_valid) {
> >> - finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
> >> - finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
> >> - finger[0].t = buf[8];
> >> - }
> >> -
> >> - if (finger[1].is_valid) {
> >> - finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
> >> - finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
> >> - finger[1].t = buf[9];
> >> + /* st1232 includes a z-axis / touch strength */
> >> + if (ts->chip_info->have_z)
> >> + finger[i].t = buf[i + 6];
> >> + }
> >> }
> >>
> >> return 0;
> >> @@ -104,11 +113,14 @@ static irqreturn_t st1232_ts_irq_handler(int
> >> irq, void *dev_id)
> >> goto end;
> >>
> >> /* multi touch protocol */
> >> - for (i = 0; i < MAX_FINGERS; i++) {
> >> + for (i = 0; i < ts->chip_info->max_fingers; i++) {
> >> if (!finger[i].is_valid)
> >> continue;
> >>
> >> - input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
> finger[i].t);
> >> + if (ts->chip_info->have_z)
> >> + input_report_abs(input_dev,
> ABS_MT_TOUCH_MAJOR,
> >> + finger[i].t);
> >> +
> >> input_report_abs(input_dev, ABS_MT_POSITION_X,
> finger[i].x);
> >> input_report_abs(input_dev, ABS_MT_POSITION_Y,
> finger[i].y);
> >> input_mt_sync(input_dev);
> >> @@ -142,12 +154,40 @@ static void st1232_ts_power(struct
> >> st1232_ts_data *ts, bool poweron)
> >> gpio_direction_output(ts->reset_gpio, poweron);
> >> }
> >>
> >> +static const struct st_chip_info st1232_chip_info = {
> >> + .have_z = true,
> >> + .max_x = 0x31f, /* 800 - 1 */
> >> + .max_y = 0x1df, /* 480 -1 */
> >> + .max_area = 0xff,
> >> + .max_fingers = 2,
> >> + .start_reg = 0x12,
> >> +};
> >> +
> >> +static const struct st_chip_info st1633_chip_info = {
> >> + .have_z = false,
> >> + .max_x = 0x13f, /* 320 - 1 */
> >> + .max_y = 0x1df, /* 480 -1 */
I guess these values are the dimensions of the referenced TFT display and not any limits of the touch controller. I wonder which values are correct here?
Maybe it would also be easier to just use the decimal representation and drop the comment, what do you think?
Thanks,
~Matthias
> >> + .max_area = 0x00,
> >> + .max_fingers = 5,
> >> + .start_reg = 0x12,
> >> +};
> >> +
> >> static int st1232_ts_probe(struct i2c_client *client,
> >> const struct i2c_device_id *id)
> >> {
> >> struct st1232_ts_data *ts;
> >> + struct st1232_ts_finger *finger;
> >> struct input_dev *input_dev;
> >> int error;
> >> + const struct st_chip_info *match = NULL;
> >
> > There is no need to initialize with NULL given that we do unconditional
> > assignment below. I removed initialization.
> >
> >> +
> >> + match = device_get_match_data(&client->dev);
> >> + if (!match && id)
> >> + match = (const void *)id->driver_data;
> >> + if (!match) {
> >> + dev_err(&client->dev, "unknown device model\n");
> >> + return -ENODEV;
> >> + }
> >>
> >> if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
> >> dev_err(&client->dev, "need I2C_FUNC_I2C\n");
> >> @@ -163,6 +203,19 @@ static int st1232_ts_probe(struct i2c_client
> >> *client,
> >> if (!ts)
> >> return -ENOMEM;
> >>
> >> + ts->chip_info = match;
> >> + ts->finger = devm_kzalloc(&client->dev,
> >> + sizeof(*finger) * ts->chip_info-
> >max_fingers,
> >> + GFP_KERNEL);
> >
> > I replaced it with devm_kcalloc(&client->dev,
> > ts->chip_info->max_fingers, sizeof(*finger),
> > GFP_KERNEL);
> >
> > and applied.
> >
> >> + if (!ts->finger)
> >> + return -ENOMEM;
> >> +
> >> + /* allocate a buffer according to the number of registers to read */
> >> + ts->read_buf_len = ts->chip_info->max_fingers * 4;
> >> + ts->read_buf = devm_kzalloc(&client->dev, ts->read_buf_len,
> >> GFP_KERNEL);
> >> + if (!ts->read_buf)
> >> + return -ENOMEM;
> >> +
> >> input_dev = devm_input_allocate_device(&client->dev);
> >> if (!input_dev)
> >> return -ENOMEM;
> >> @@ -192,9 +245,14 @@ static int st1232_ts_probe(struct i2c_client
> >> *client,
> >> __set_bit(EV_KEY, input_dev->evbit);
> >> __set_bit(EV_ABS, input_dev->evbit);
> >>
> >> - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
> MAX_AREA, 0,
> >> 0);
> >> - input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X,
> MAX_X, 0,
> >> 0);
> >> - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y,
> MAX_Y, 0,
> >> 0);
> >> + if (ts->chip_info->have_z)
> >> + input_set_abs_params(input_dev,
> ABS_MT_TOUCH_MAJOR, 0,
> >> + ts->chip_info->max_area, 0, 0);
> >> +
> >> + input_set_abs_params(input_dev, ABS_MT_POSITION_X,
> >> + MIN_X, ts->chip_info->max_x, 0, 0);
> >> + input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
> >> + MIN_Y, ts->chip_info->max_y, 0, 0);
> >>
> >> error = devm_request_threaded_irq(&client->dev, client->irq,
> >> NULL, st1232_ts_irq_handler,
> >> @@ -261,13 +319,15 @@ static
> SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops,
> >> st1232_ts_suspend, st1232_ts_resume);
> >>
> >> static const struct i2c_device_id st1232_ts_id[] = {
> >> - { ST1232_TS_NAME, 0 },
> >> + { ST1232_TS_NAME, (unsigned long)&st1232_chip_info },
> >> + { ST1633_TS_NAME, (unsigned long)&st1633_chip_info },
> >> { }
> >> };
> >> MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
> >>
> >> static const struct of_device_id st1232_ts_dt_ids[] = {
> >> - { .compatible = "sitronix,st1232", },
> >> + { .compatible = "sitronix,st1232", .data = &st1232_chip_info },
> >> + { .compatible = "sitronix,st1633", .data = &st1633_chip_info },
> >> { }
> >> };
> >> MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids);
> >> --
> >> 2.20.1
> >>
> >
> > Thanks.
^ permalink raw reply
* Re: [PATCH] input: goodix - support Goodix gt5688
From: Guido Günther @ 2019-01-29 9:16 UTC (permalink / raw)
To: Bastien Nocera
Cc: Mark Rutland, devicetree, Dmitry Torokhov, linux-kernel,
Rob Herring, linux-mediatek, linux-input, Matthias Brugger,
linux-arm-kernel
In-Reply-To: <7e289e6b3410a3fdea99d7d6becfe5180a69f0c5.camel@hadess.net>
Hi,
On Mon, Jan 28, 2019 at 07:24:33PM +0100, Bastien Nocera wrote:
> On Mon, 2019-01-28 at 18:03 +0100, Guido Günther wrote:
> > From what I've seen in vendor trees it's fine to treat this as gt1x¹.
> >
> > [1]: https://github.com/TadiT7/android_kernel_mtk-4.4/tree/master/drivers/input/touchscreen/mediatek/GT5688
>
> Can you please point to the exact line of code that makes you say that?
> I'm not saying it's not compatible, but it's not the same driver that
> the current goodix.c was based on, or even goodix.c.
>
> Can you please elaborate?
I basically looked at the registers uses for the config update:
https://github.com/TadiT7/android_kernel_mtk-4.4/blob/master/drivers/input/touchscreen/mediatek/GT5688/include/gt1x_tpd_common.h#L152
https://github.com/TadiT7/android_kernel_mtk-4.4/blob/master/drivers/input/touchscreen/mediatek/GT5688/gt1x_generic.c#L430
That and the fact that the driver is doing it's job well made me believe
that's good for base support. Things like hotknot, gesture wakeup and
proximity sensor will need additional work.
> Finding that data in the specs would also be fine:
> https://github.com/hadess/gt9xx/tree/master/specifications
https://github.com/hadess/gt9xx/pull/3
Cheers,
-- Guido
^ permalink raw reply
* Re: [PATCH v2] HID: debug: fix the ring buffer implementation
From: Benjamin Tissoires @ 2019-01-29 9:33 UTC (permalink / raw)
To: Oleg Nesterov, Vladis Dronov
Cc: Jiri Kosina, open list:HID CORE LAYER, lkml, 3.8+
In-Reply-To: <20190128111818.GA22143@redhat.com>
Vlad,
On Mon, Jan 28, 2019 at 12:18 PM Oleg Nesterov <oleg@redhat.com> wrote:
>
> On 01/28, Benjamin Tissoires wrote:
> >
> > Oleg, can you provide some feedback before I push this?
>
> Looks good to me, feel free to add
>
> Reviewed-by: Oleg Nesterov <oleg@redhat.com>
>
> > > + set_current_state(TASK_RUNNING);
>
> I still think that
>
> __set_current_state(TASK_RUNNING);
>
> will look a bit better, but this is really minor.
Would you mind sending a v3 with this change? I'll apply it ASAP.
Cheers,
Benjamin
^ permalink raw reply
* Re: [PATCH v3] HID: i2c-hid: Ignore input report if there's no data present on Elan touchpanels
From: Benjamin Tissoires @ 2019-01-29 10:05 UTC (permalink / raw)
To: Kai-Heng Feng; +Cc: Jiri Kosina, open list:HID CORE LAYER, lkml
In-Reply-To: <D7C6B90F-641A-495B-9E05-78BF4F56B552@canonical.com>
On Mon, Jan 21, 2019 at 4:30 AM Kai-Heng Feng
<kai.heng.feng@canonical.com> wrote:
>
>
>
> > On Jan 18, 2019, at 23:50, Benjamin Tissoires <benjamin.tissoires@redhat.com> wrote:
> >
> > Hi Kai-Heng,
> >
> > On Mon, Jan 7, 2019 at 8:24 AM Kai-Heng Feng
> > <kai.heng.feng@canonical.com> wrote:
> >>
> >> While using Elan touchpads, the message floods:
> >> [ 136.138487] i2c_hid i2c-DELL08D6:00: i2c_hid_get_input: incomplete report (14/65535)
> >>
> >> Though the message flood is annoying, the device it self works without
> >> any issue. I suspect that the device in question takes too much time to
> >> pull the IRQ back to high after I2C host has done reading its data.
> >>
> >> Since the host receives all useful data, let's ignore the input report
> >> when there's no data.
> >>
> >> Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
> >
> > Thanks for the v3.
> >
> > This patch has just been brought to my attention, and I have one
> > question before applying it:
> > are those messages (without your patch) occurring all the time, or
> > just after resume?
>
> All the time.
>
> The touchpad works fine though. The entire report is 0xff, so it can be
> safely ignored.
Couple of things:
- I have forgotten to tell you that I have applied this patch in
for-5.1/i2c-hid, it will be pushed to linux-next today.
- Dell told me that a BIOS fix was solving the issue. We can still
quarry the patch, but there should not be a strong need for it when
users upgrade their BIOS.
Cheers,
Benjamin
>
> >
> > I wonder if the pm_suspend delay we talked about in the other thread
> > would fix that issue in a cleaner way.
>
> I’ve replied in another thread, unfortunately it can’t.
>
> We can introduce msleep() between each commands though, but I don’t
> think it’s good either.
>
> Kai-Heng
>
> >
> > Cheers,
> > Benjamin
> >
> >> ---
> >> v3:
> >> Fix compiler error/warnings.
> >>
> >> v2:
> >> Use dev_warn_once() to warn the user about the situation.
> >>
> >> drivers/hid/i2c-hid/i2c-hid-core.c | 9 +++++++++
> >> 1 file changed, 9 insertions(+)
> >>
> >> diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
> >> index 8555ce7e737b..2f940c1de616 100644
> >> --- a/drivers/hid/i2c-hid/i2c-hid-core.c
> >> +++ b/drivers/hid/i2c-hid/i2c-hid-core.c
> >> @@ -50,6 +50,7 @@
> >> #define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1)
> >> #define I2C_HID_QUIRK_NO_RUNTIME_PM BIT(2)
> >> #define I2C_HID_QUIRK_DELAY_AFTER_SLEEP BIT(3)
> >> +#define I2C_HID_QUIRK_BOGUS_IRQ BIT(4)
> >>
> >> /* flags */
> >> #define I2C_HID_STARTED 0
> >> @@ -179,6 +180,8 @@ static const struct i2c_hid_quirks {
> >> I2C_HID_QUIRK_DELAY_AFTER_SLEEP },
> >> { USB_VENDOR_ID_LG, I2C_DEVICE_ID_LG_8001,
> >> I2C_HID_QUIRK_NO_RUNTIME_PM },
> >> + { USB_VENDOR_ID_ELAN, HID_ANY_ID,
> >> + I2C_HID_QUIRK_BOGUS_IRQ },
> >> { 0, 0 }
> >> };
> >>
> >> @@ -503,6 +506,12 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
> >> return;
> >> }
> >>
> >> + if (ihid->quirks & I2C_HID_QUIRK_BOGUS_IRQ && ret_size == 0xffff) {
> >> + dev_warn_once(&ihid->client->dev, "%s: IRQ triggered but "
> >> + "there's no data\n", __func__);
> >> + return;
> >> + }
> >> +
> >> if ((ret_size > size) || (ret_size < 2)) {
> >> dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
> >> __func__, size, ret_size);
> >> --
> >> 2.17.1
> >>
>
^ permalink raw reply
* [PATCH 1/2] dt-bindings: input: sitronix-st1232: document optional reset-gpios property
From: Martin Kepplinger @ 2019-01-29 10:23 UTC (permalink / raw)
To: devicetree, linux-input
Cc: dmitry.torokhov, robh+dt, mark.rutland, linux-kernel,
Martin Kepplinger
From: Martin Kepplinger <martin.kepplinger@ginzinger.com>
The st1232 driver reads this via gpiod.
Signed-off-by: Martin Kepplinger <martin.kepplinger@ginzinger.com>
---
.../devicetree/bindings/input/touchscreen/sitronix-st1232.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt b/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt
index e73e826e0f2a..365b32d30d4b 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt
@@ -8,7 +8,7 @@ Required properties:
- interrupts: interrupt to which the chip is connected
Optional properties:
-- gpios: a phandle to the reset GPIO
+- reset-gpios: a phandle to the reset GPIO
Example:
@@ -19,7 +19,7 @@ Example:
compatible = "sitronix,st1232";
reg = <0x55>;
interrupts = <2 0>;
- gpios = <&gpio1 166 0>;
+ reset-gpios = <&gpio1 166 0>;
};
/* ... */
--
2.20.1
^ permalink raw reply related
* [PATCH 2/2] Input: st1232 - switch to gpiod API
From: Martin Kepplinger @ 2019-01-29 10:23 UTC (permalink / raw)
To: devicetree, linux-input
Cc: dmitry.torokhov, robh+dt, mark.rutland, linux-kernel,
Martin Kepplinger
In-Reply-To: <20190129102347.27754-1-martink@posteo.de>
From: Martin Kepplinger <martin.kepplinger@ginzinger.com>
Use devm_gpiod_get_optional() and gpiod_set_value_cansleep() instead
of the old API. The st1232_ts_power() now passes on the inverted "poweron"
value to reflect the correct logical value.
Signed-off-by: Martin Kepplinger <martin.kepplinger@ginzinger.com>
---
Tested and works. thanks for your help Dmitry,
martin
drivers/input/touchscreen/st1232.c | 22 ++++++++++------------
1 file changed, 10 insertions(+), 12 deletions(-)
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
index 777df903605d..04d75b08be44 100644
--- a/drivers/input/touchscreen/st1232.c
+++ b/drivers/input/touchscreen/st1232.c
@@ -45,7 +45,7 @@ struct st1232_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
struct dev_pm_qos_request low_latency_req;
- int reset_gpio;
+ struct gpio_desc *reset_gpio;
const struct st_chip_info *chip_info;
int read_buf_len;
u8 *read_buf;
@@ -142,8 +142,8 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron)
{
- if (gpio_is_valid(ts->reset_gpio))
- gpio_direction_output(ts->reset_gpio, poweron);
+ if (ts->reset_gpio)
+ gpiod_set_value_cansleep(ts->reset_gpio, !poweron);
}
static const struct st_chip_info st1232_chip_info = {
@@ -215,15 +215,13 @@ static int st1232_ts_probe(struct i2c_client *client,
ts->client = client;
ts->input_dev = input_dev;
- ts->reset_gpio = of_get_gpio(client->dev.of_node, 0);
- if (gpio_is_valid(ts->reset_gpio)) {
- error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL);
- if (error) {
- dev_err(&client->dev,
- "Unable to request GPIO pin %d.\n",
- ts->reset_gpio);
- return error;
- }
+ ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ts->reset_gpio)) {
+ error = PTR_ERR(ts->reset_gpio);
+ dev_err(&client->dev, "Unable to request GPIO pin: %d.\n",
+ error);
+ return error;
}
st1232_ts_power(ts, true);
--
2.20.1
^ permalink raw reply related
* Re: [PATCH] input: goodix - support Goodix gt5688
From: Bastien Nocera @ 2019-01-29 10:30 UTC (permalink / raw)
To: Guido Günther
Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Matthias Brugger,
linux-input, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <20190129091651.GA8497@bogon.m.sigxcpu.org>
On Tue, 2019-01-29 at 10:16 +0100, Guido Günther wrote:
> Hi,
> On Mon, Jan 28, 2019 at 07:24:33PM +0100, Bastien Nocera wrote:
> > On Mon, 2019-01-28 at 18:03 +0100, Guido Günther wrote:
> > > From what I've seen in vendor trees it's fine to treat this as
> > > gt1x¹.
> > >
> > > [1]:
> > > https://github.com/TadiT7/android_kernel_mtk-4.4/tree/master/drivers/input/touchscreen/mediatek/GT5688
> >
> > Can you please point to the exact line of code that makes you say
> > that?
> > I'm not saying it's not compatible, but it's not the same driver
> > that
> > the current goodix.c was based on, or even goodix.c.
> >
> > Can you please elaborate?
>
> I basically looked at the registers uses for the config update:
>
> https://github.com/TadiT7/android_kernel_mtk-4.4/blob/master/drivers/input/touchscreen/mediatek/GT5688/include/gt1x_tpd_common.h#L152
> https://github.com/TadiT7/android_kernel_mtk-4.4/blob/master/drivers/input/touchscreen/mediatek/GT5688/gt1x_generic.c#L430
I'm not sure that's good enough to say that the touchscreen models are
compatible.
> That and the fact that the driver is doing it's job well made me
> believe
> that's good for base support.
If you've tested it, that's better. Can you please add a reference to
the device that you've tested this on in the commit message?
> Things like hotknot, gesture wakeup and
> proximity sensor will need additional work.
>
> > Finding that data in the specs would also be fine:
> > https://github.com/hadess/gt9xx/tree/master/specifications
>
> https://github.com/hadess/gt9xx/pull/3
Merged that, thanks
Cheers
^ permalink raw reply
* [PATCH v3] HID: debug: fix the ring buffer implementation
From: Vladis Dronov @ 2019-01-29 10:58 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input, linux-kernel
Cc: Vladis Dronov, stable
Ring buffer implementation in hid_debug_event() and hid_debug_events_read()
is strange allowing lost or corrupted data. After commit 717adfdaf147
("HID: debug: check length before copy_to_user()") it is possible to enter
an infinite loop in hid_debug_events_read() by providing 0 as count, this
locks up a system. Fix this by rewriting the ring buffer implementation
with kfifo and simplify the code.
This fixes CVE-2019-3819.
v2: fix an execution logic and add a comment
v3: use __set_current_state() instead of set_current_state()
Link: https://bugzilla.redhat.com/show_bug.cgi?id=1669187
Cc: stable@vger.kernel.org # v4.18+
Fixes: cd667ce24796 ("HID: use debugfs for events/reports dumping")
Fixes: 717adfdaf147 ("HID: debug: check length before copy_to_user()")
Signed-off-by: Vladis Dronov <vdronov@redhat.com>
---
drivers/hid/hid-debug.c | 116 ++++++++++++++------------------------
include/linux/hid-debug.h | 9 ++-
2 files changed, 47 insertions(+), 78 deletions(-)
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index c530476edba6..08870c909268 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -30,6 +30,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/kfifo.h>
#include <linux/sched/signal.h>
#include <linux/export.h>
#include <linux/slab.h>
@@ -661,17 +662,12 @@ EXPORT_SYMBOL_GPL(hid_dump_device);
/* enqueue string to 'events' ring buffer */
void hid_debug_event(struct hid_device *hdev, char *buf)
{
- unsigned i;
struct hid_debug_list *list;
unsigned long flags;
spin_lock_irqsave(&hdev->debug_list_lock, flags);
- list_for_each_entry(list, &hdev->debug_list, node) {
- for (i = 0; buf[i]; i++)
- list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] =
- buf[i];
- list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
- }
+ list_for_each_entry(list, &hdev->debug_list, node)
+ kfifo_in(&list->hid_debug_fifo, buf, strlen(buf));
spin_unlock_irqrestore(&hdev->debug_list_lock, flags);
wake_up_interruptible(&hdev->debug_wait);
@@ -722,8 +718,7 @@ void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 valu
hid_debug_event(hdev, buf);
kfree(buf);
- wake_up_interruptible(&hdev->debug_wait);
-
+ wake_up_interruptible(&hdev->debug_wait);
}
EXPORT_SYMBOL_GPL(hid_dump_input);
@@ -1083,8 +1078,8 @@ static int hid_debug_events_open(struct inode *inode, struct file *file)
goto out;
}
- if (!(list->hid_debug_buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_KERNEL))) {
- err = -ENOMEM;
+ err = kfifo_alloc(&list->hid_debug_fifo, HID_DEBUG_FIFOSIZE, GFP_KERNEL);
+ if (err) {
kfree(list);
goto out;
}
@@ -1104,77 +1099,57 @@ static ssize_t hid_debug_events_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct hid_debug_list *list = file->private_data;
- int ret = 0, len;
+ int ret = 0, copied;
DECLARE_WAITQUEUE(wait, current);
mutex_lock(&list->read_mutex);
- while (ret == 0) {
- if (list->head == list->tail) {
- add_wait_queue(&list->hdev->debug_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
- while (list->head == list->tail) {
- if (file->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
-
- if (!list->hdev || !list->hdev->debug) {
- ret = -EIO;
- set_current_state(TASK_RUNNING);
- goto out;
- }
-
- /* allow O_NONBLOCK from other threads */
- mutex_unlock(&list->read_mutex);
- schedule();
- mutex_lock(&list->read_mutex);
- set_current_state(TASK_INTERRUPTIBLE);
- }
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&list->hdev->debug_wait, &wait);
- }
-
- if (ret)
- goto out;
+ if (kfifo_is_empty(&list->hid_debug_fifo)) {
+ add_wait_queue(&list->hdev->debug_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (kfifo_is_empty(&list->hid_debug_fifo)) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ /* if list->hdev is NULL we cannot remove_wait_queue().
+ * if list->hdev->debug is 0 then hid_debug_unregister()
+ * was already called and list->hdev is being destroyed.
+ * if we add remove_wait_queue() here we can hit a race.
+ */
+ if (!list->hdev || !list->hdev->debug) {
+ ret = -EIO;
+ set_current_state(TASK_RUNNING);
+ goto out;
+ }
+
+ /* allow O_NONBLOCK from other threads */
+ mutex_unlock(&list->read_mutex);
+ schedule();
+ mutex_lock(&list->read_mutex);
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&list->hdev->debug_wait, &wait);
+
+ if (ret)
+ goto out;
+ }
- /* pass the ringbuffer contents to userspace */
-copy_rest:
- if (list->tail == list->head)
- goto out;
- if (list->tail > list->head) {
- len = list->tail - list->head;
- if (len > count)
- len = count;
-
- if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) {
- ret = -EFAULT;
- goto out;
- }
- ret += len;
- list->head += len;
- } else {
- len = HID_DEBUG_BUFSIZE - list->head;
- if (len > count)
- len = count;
-
- if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) {
- ret = -EFAULT;
- goto out;
- }
- list->head = 0;
- ret += len;
- count -= len;
- if (count > 0)
- goto copy_rest;
- }
-
- }
+ /* pass the fifo content to userspace, locking is not needed with only
+ * one concurrent reader and one concurrent writer
+ */
+ ret = kfifo_to_user(&list->hid_debug_fifo, buffer, count, &copied);
+ if (ret)
+ goto out;
+ ret = copied;
out:
mutex_unlock(&list->read_mutex);
return ret;
@@ -1185,7 +1160,7 @@ static __poll_t hid_debug_events_poll(struct file *file, poll_table *wait)
struct hid_debug_list *list = file->private_data;
poll_wait(file, &list->hdev->debug_wait, wait);
- if (list->head != list->tail)
+ if (!kfifo_is_empty(&list->hid_debug_fifo))
return EPOLLIN | EPOLLRDNORM;
if (!list->hdev->debug)
return EPOLLERR | EPOLLHUP;
@@ -1200,7 +1175,7 @@ static int hid_debug_events_release(struct inode *inode, struct file *file)
spin_lock_irqsave(&list->hdev->debug_list_lock, flags);
list_del(&list->node);
spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags);
- kfree(list->hid_debug_buf);
+ kfifo_free(&list->hid_debug_fifo);
kfree(list);
return 0;
@@ -1246,4 +1221,3 @@ void hid_debug_exit(void)
{
debugfs_remove_recursive(hid_debug_root);
}
-
diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h
index 8663f216c563..e7a7c92aaf09 100644
--- a/include/linux/hid-debug.h
+++ b/include/linux/hid-debug.h
@@ -24,7 +24,10 @@
#ifdef CONFIG_DEBUG_FS
+#include <linux/kfifo.h>
+
#define HID_DEBUG_BUFSIZE 512
+#define HID_DEBUG_FIFOSIZE 512
void hid_dump_input(struct hid_device *, struct hid_usage *, __s32);
void hid_dump_report(struct hid_device *, int , u8 *, int);
@@ -38,10 +41,7 @@ void hid_debug_event(struct hid_device *, char *);
void hid_debug_event(struct hid_device *, char *);
-
struct hid_debug_list {
- char *hid_debug_buf;
- int head;
- int tail;
+ DECLARE_KFIFO_PTR(hid_debug_fifo, char);
struct fasync_struct *fasync;
struct hid_device *hdev;
struct list_head node;
@@ -64,4 +64,3 @@ struct hid_debug_list {
#endif
#endif
-
^ permalink raw reply related
* Re: [PATCH v2] HID: debug: fix the ring buffer implementation
From: Vladis Dronov @ 2019-01-29 10:59 UTC (permalink / raw)
To: Benjamin Tissoires; +Cc: Jiri Kosina, open list:HID CORE LAYER, lkml, 3.8+
In-Reply-To: <CAO-hwJK_WoimsUMR19GR_qEUCk0ZMaKGham3DSJ5SDqtc8oCzw@mail.gmail.com>
> > I still think that
> >
> > __set_current_state(TASK_RUNNING);
> >
> > will look a bit better, but this is really minor.
>
> Would you mind sending a v3 with this change? I'll apply it ASAP.
Done, please, see inbox.
Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security | Senior Software Engineer
^ permalink raw reply
* Re: [PATCH 10/13] gpio: max77650: add GPIO support
From: Bartosz Golaszewski @ 2019-01-29 11:00 UTC (permalink / raw)
To: Linus Walleij
Cc: Brian Masney, Rob Herring, Mark Rutland, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman,
linux-kernel@vger.kernel.org, open list:GPIO SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Linux Input, Linux LED Subsystem, Linux PM list
In-Reply-To: <CACRpkdacfOLSs1GPew4gMtL_BX0OTXqBQNWzWZMwAkk+Ah2ETQ@mail.gmail.com>
czw., 24 sty 2019 o 11:30 Linus Walleij <linus.walleij@linaro.org> napisał(a):
>
> On Mon, Jan 21, 2019 at 6:07 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
>
> > Thank you for your review. While I think you're right about the issue
> > being present in this driver, I'm not sure it's really a problem. Do
> > we actually require every gpio-controller to also be a stand-alone
> > interrupt-controller?
>
> Absolutely not :D
>
> Just GPIO is fine.
>
> > The binding document for the GPIO module doesn't
> > mention this - it only requires the gpio-controller property. Without
> > the "interrupt-controller" property dtc will bail-out if anyone uses
> > this node as the interrupt parent.
> >
> > If I'm wrong and we do require it, then I think we need to update
> > Documentation/devicetree/bindings/gpio/gpio.txt.
>
> What is weird is if a driver with DT bindings not mentioning IRQ
> and only probing from DT start implementing IRQ support, that
> becomes quite inconsistent. So then max77650_gpio_to_irq()
> should just return -ENOTSUPP
> or something for now, then it's fine.
>
I don't see it as weird at all. I see the need to define the register
and interrupt resources in DT for SoC peripherals becaue SoCs often
reuse IPs. But in the case of a self-contained i2c PMIC - the modules
such as GPIO are tightly coupled with the core functionality. In the
case of this device for example: there isn't even a separate set of
mask/status registers for GPIO interrupts.
Most mfd devices setup the resources in a hard-coded manner.
> We can add the (complicated) IRQ handling later.
>
> I am trying to eat my own dogfood here, I was sweating all
> last night trying to implement a hierarchical IRQ controller.
> There is no running away from that now. :/
>
> Apparently doing hierarchical IRQs demand that all irq
> controllers up to the top-level SoC IRQ controller support
> hierarchical interrupts using the v2 version of the irqdomain
> API, and currently it seems like the ARM
> GIC seems like the only top level IRQ controller that can
> do that.
>
Yep, and for that reason I can't use the regmap irq_chip abstraction
for now because it doesn't implement support for hierarchical
interrupts either.
How about the cascaded gpiochip irq_chip?
Best regards,
Bartosz
^ permalink raw reply
* [PATCH v2] input: goodix - support Goodix gt5688
From: Guido Günther @ 2019-01-29 11:11 UTC (permalink / raw)
To: Bastien Nocera
Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Matthias Brugger,
Guido Günther, linux-input, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <12e21befacf68861ddf34020838ff7a6e6e078ad.camel@hadess.net>
>From what I've seen in vendor trees it's fine to treat this as gt1x¹.
Tested on the Purism Librem 5 Devkit (Rocktech JH057N00900 panel).
[1]: https://github.com/TadiT7/android_kernel_mtk-4.4/tree/master/drivers/input/touchscreen/mediatek/GT5688
Signed-off-by: Guido Günther <agx@sigxcpu.org>
---
Changes from v1:
* Add tested board to commit message
Documentation/devicetree/bindings/input/touchscreen/goodix.txt | 1 +
drivers/input/touchscreen/goodix.c | 2 ++
2 files changed, 3 insertions(+)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
index f7e95c52f3c7..57d3d8870a09 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
@@ -3,6 +3,7 @@ Device tree bindings for Goodix GT9xx series touchscreen controller
Required properties:
- compatible : Should be "goodix,gt1151"
+ or "goodix,gt5688"
or "goodix,gt911"
or "goodix,gt9110"
or "goodix,gt912"
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index f2d9c2c41885..47b1ced41576 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -216,6 +216,7 @@ static const struct goodix_chip_data *goodix_get_chip_data(u16 id)
{
switch (id) {
case 1151:
+ case 5688:
return >1x_chip_data;
case 911:
@@ -942,6 +943,7 @@ MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
#ifdef CONFIG_OF
static const struct of_device_id goodix_of_match[] = {
{ .compatible = "goodix,gt1151" },
+ { .compatible = "goodix,gt5688" },
{ .compatible = "goodix,gt911" },
{ .compatible = "goodix,gt9110" },
{ .compatible = "goodix,gt912" },
--
2.20.1
^ permalink raw reply related
* Re: [RESEND PATCH v2 8/8] Input: sx8654 - convert #defined flags to BIT(x)
From: Richard Leitner @ 2019-01-29 11:23 UTC (permalink / raw)
To: Joe Perches, Dmitry Torokhov
Cc: mark.rutland, robh+dt, linux-input, devicetree, linux-kernel
In-Reply-To: <533e48bd35d360cec8d749f3b70425c1eb5e6e9d.camel@perches.com>
Hi Joe,
On 29/01/2019 06:40, Joe Perches wrote:
> On Mon, 2019-01-28 at 16:25 -0800, Dmitry Torokhov wrote:
>> On Tue, Dec 18, 2018 at 09:40:02AM +0100, Richard Leitner wrote:
>>> Some of the #defined register values are one-bit flags. Convert them to
>>> use the BIT(x) macro instead of 1 byte hexadecimal values. This improves
>>> readability and clarifies the intent.
>>>
>>> Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
>>
>> Applied, thank you.
>
> Not so sure this should be applied.
>
>>> diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
> []
>>> @@ -46,7 +47,7 @@
> []
>>> /* bits for I2C_REG_IRQSRC */
>>> -#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08
>>> -#define IRQ_PENRELEASE 0x04
>>> +#define IRQ_PENTOUCH_TOUCHCONVDONE BIT(7)
>>> +#define IRQ_PENRELEASE BIT(6)
>
> Shouldn't this be BIT(3) and BIT(2)
> or did you mean to change the values too?
>
> If so, this change should be noted in the commit message.
>
That's true, those values should stay the same. Thanks for the catch!
@Dimitry: Should I send an updated version or do you fix it yourself?
regards;Richard.L
^ permalink raw reply
* Re: [PATCH v2] input: goodix - support Goodix gt5688
From: Bastien Nocera @ 2019-01-29 11:35 UTC (permalink / raw)
To: Guido Günther
Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Matthias Brugger,
linux-input, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <20190129111113.GA32222@bogon.m.sigxcpu.org>
On Tue, 2019-01-29 at 12:11 +0100, Guido Günther wrote:
> From what I've seen in vendor trees it's fine to treat this as gt1x¹.
> Tested on the Purism Librem 5 Devkit (Rocktech JH057N00900 panel).
>
> [1]:
> https://github.com/TadiT7/android_kernel_mtk-4.4/tree/master/drivers/input/touchscreen/mediatek/GT5688
>
> Signed-off-by: Guido Günther <agx@sigxcpu.org>
Not super fond of the casual commit message, but the code and
explanation are fine.
Reviewed-by: Bastien Nocera <hadess@hadess.net>
> ---
> Changes from v1:
> * Add tested board to commit message
>
> Documentation/devicetree/bindings/input/touchscreen/goodix.txt | 1 +
> drivers/input/touchscreen/goodix.c | 2
> ++
> 2 files changed, 3 insertions(+)
>
> diff --git
> a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> index f7e95c52f3c7..57d3d8870a09 100644
> --- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
> @@ -3,6 +3,7 @@ Device tree bindings for Goodix GT9xx series
> touchscreen controller
> Required properties:
>
> - compatible : Should be "goodix,gt1151"
> + or "goodix,gt5688"
> or "goodix,gt911"
> or "goodix,gt9110"
> or "goodix,gt912"
> diff --git a/drivers/input/touchscreen/goodix.c
> b/drivers/input/touchscreen/goodix.c
> index f2d9c2c41885..47b1ced41576 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -216,6 +216,7 @@ static const struct goodix_chip_data
> *goodix_get_chip_data(u16 id)
> {
> switch (id) {
> case 1151:
> + case 5688:
> return >1x_chip_data;
>
> case 911:
> @@ -942,6 +943,7 @@ MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
> #ifdef CONFIG_OF
> static const struct of_device_id goodix_of_match[] = {
> { .compatible = "goodix,gt1151" },
> + { .compatible = "goodix,gt5688" },
> { .compatible = "goodix,gt911" },
> { .compatible = "goodix,gt9110" },
> { .compatible = "goodix,gt912" },
^ permalink raw reply
* Re: [PATCH v3] HID: debug: fix the ring buffer implementation
From: Benjamin Tissoires @ 2019-01-29 12:55 UTC (permalink / raw)
To: Vladis Dronov; +Cc: Jiri Kosina, open list:HID CORE LAYER, lkml, 3.8+
In-Reply-To: <20190129105835.4723-1-vdronov@redhat.com>
On Tue, Jan 29, 2019 at 11:58 AM Vladis Dronov <vdronov@redhat.com> wrote:
>
> Ring buffer implementation in hid_debug_event() and hid_debug_events_read()
> is strange allowing lost or corrupted data. After commit 717adfdaf147
> ("HID: debug: check length before copy_to_user()") it is possible to enter
> an infinite loop in hid_debug_events_read() by providing 0 as count, this
> locks up a system. Fix this by rewriting the ring buffer implementation
> with kfifo and simplify the code.
>
> This fixes CVE-2019-3819.
>
> v2: fix an execution logic and add a comment
> v3: use __set_current_state() instead of set_current_state()
>
> Link: https://bugzilla.redhat.com/show_bug.cgi?id=1669187
> Cc: stable@vger.kernel.org # v4.18+
> Fixes: cd667ce24796 ("HID: use debugfs for events/reports dumping")
> Fixes: 717adfdaf147 ("HID: debug: check length before copy_to_user()")
> Signed-off-by: Vladis Dronov <vdronov@redhat.com>
> ---
Thanks for the quick v3.
I have now applied this to for-5.0/upstream-fixes with Oleg's rev-by.
Cheers,
Benjamin
> drivers/hid/hid-debug.c | 116 ++++++++++++++------------------------
> include/linux/hid-debug.h | 9 ++-
> 2 files changed, 47 insertions(+), 78 deletions(-)
>
> diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
> index c530476edba6..08870c909268 100644
> --- a/drivers/hid/hid-debug.c
> +++ b/drivers/hid/hid-debug.c
> @@ -30,6 +30,7 @@
>
> #include <linux/debugfs.h>
> #include <linux/seq_file.h>
> +#include <linux/kfifo.h>
> #include <linux/sched/signal.h>
> #include <linux/export.h>
> #include <linux/slab.h>
> @@ -661,17 +662,12 @@ EXPORT_SYMBOL_GPL(hid_dump_device);
> /* enqueue string to 'events' ring buffer */
> void hid_debug_event(struct hid_device *hdev, char *buf)
> {
> - unsigned i;
> struct hid_debug_list *list;
> unsigned long flags;
>
> spin_lock_irqsave(&hdev->debug_list_lock, flags);
> - list_for_each_entry(list, &hdev->debug_list, node) {
> - for (i = 0; buf[i]; i++)
> - list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] =
> - buf[i];
> - list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
> - }
> + list_for_each_entry(list, &hdev->debug_list, node)
> + kfifo_in(&list->hid_debug_fifo, buf, strlen(buf));
> spin_unlock_irqrestore(&hdev->debug_list_lock, flags);
>
> wake_up_interruptible(&hdev->debug_wait);
> @@ -722,8 +718,7 @@ void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 valu
> hid_debug_event(hdev, buf);
>
> kfree(buf);
> - wake_up_interruptible(&hdev->debug_wait);
> -
> + wake_up_interruptible(&hdev->debug_wait);
> }
> EXPORT_SYMBOL_GPL(hid_dump_input);
>
> @@ -1083,8 +1078,8 @@ static int hid_debug_events_open(struct inode *inode, struct file *file)
> goto out;
> }
>
> - if (!(list->hid_debug_buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_KERNEL))) {
> - err = -ENOMEM;
> + err = kfifo_alloc(&list->hid_debug_fifo, HID_DEBUG_FIFOSIZE, GFP_KERNEL);
> + if (err) {
> kfree(list);
> goto out;
> }
> @@ -1104,77 +1099,57 @@ static ssize_t hid_debug_events_read(struct file *file, char __user *buffer,
> size_t count, loff_t *ppos)
> {
> struct hid_debug_list *list = file->private_data;
> - int ret = 0, len;
> + int ret = 0, copied;
> DECLARE_WAITQUEUE(wait, current);
>
> mutex_lock(&list->read_mutex);
> - while (ret == 0) {
> - if (list->head == list->tail) {
> - add_wait_queue(&list->hdev->debug_wait, &wait);
> - set_current_state(TASK_INTERRUPTIBLE);
> -
> - while (list->head == list->tail) {
> - if (file->f_flags & O_NONBLOCK) {
> - ret = -EAGAIN;
> - break;
> - }
> - if (signal_pending(current)) {
> - ret = -ERESTARTSYS;
> - break;
> - }
> -
> - if (!list->hdev || !list->hdev->debug) {
> - ret = -EIO;
> - set_current_state(TASK_RUNNING);
> - goto out;
> - }
> -
> - /* allow O_NONBLOCK from other threads */
> - mutex_unlock(&list->read_mutex);
> - schedule();
> - mutex_lock(&list->read_mutex);
> - set_current_state(TASK_INTERRUPTIBLE);
> - }
> -
> - set_current_state(TASK_RUNNING);
> - remove_wait_queue(&list->hdev->debug_wait, &wait);
> - }
> -
> - if (ret)
> - goto out;
> + if (kfifo_is_empty(&list->hid_debug_fifo)) {
> + add_wait_queue(&list->hdev->debug_wait, &wait);
> + set_current_state(TASK_INTERRUPTIBLE);
> +
> + while (kfifo_is_empty(&list->hid_debug_fifo)) {
> + if (file->f_flags & O_NONBLOCK) {
> + ret = -EAGAIN;
> + break;
> + }
> +
> + if (signal_pending(current)) {
> + ret = -ERESTARTSYS;
> + break;
> + }
> +
> + /* if list->hdev is NULL we cannot remove_wait_queue().
> + * if list->hdev->debug is 0 then hid_debug_unregister()
> + * was already called and list->hdev is being destroyed.
> + * if we add remove_wait_queue() here we can hit a race.
> + */
> + if (!list->hdev || !list->hdev->debug) {
> + ret = -EIO;
> + set_current_state(TASK_RUNNING);
> + goto out;
> + }
> +
> + /* allow O_NONBLOCK from other threads */
> + mutex_unlock(&list->read_mutex);
> + schedule();
> + mutex_lock(&list->read_mutex);
> + set_current_state(TASK_INTERRUPTIBLE);
> + }
> +
> + __set_current_state(TASK_RUNNING);
> + remove_wait_queue(&list->hdev->debug_wait, &wait);
> +
> + if (ret)
> + goto out;
> + }
>
> - /* pass the ringbuffer contents to userspace */
> -copy_rest:
> - if (list->tail == list->head)
> - goto out;
> - if (list->tail > list->head) {
> - len = list->tail - list->head;
> - if (len > count)
> - len = count;
> -
> - if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) {
> - ret = -EFAULT;
> - goto out;
> - }
> - ret += len;
> - list->head += len;
> - } else {
> - len = HID_DEBUG_BUFSIZE - list->head;
> - if (len > count)
> - len = count;
> -
> - if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) {
> - ret = -EFAULT;
> - goto out;
> - }
> - list->head = 0;
> - ret += len;
> - count -= len;
> - if (count > 0)
> - goto copy_rest;
> - }
> -
> - }
> + /* pass the fifo content to userspace, locking is not needed with only
> + * one concurrent reader and one concurrent writer
> + */
> + ret = kfifo_to_user(&list->hid_debug_fifo, buffer, count, &copied);
> + if (ret)
> + goto out;
> + ret = copied;
> out:
> mutex_unlock(&list->read_mutex);
> return ret;
> @@ -1185,7 +1160,7 @@ static __poll_t hid_debug_events_poll(struct file *file, poll_table *wait)
> struct hid_debug_list *list = file->private_data;
>
> poll_wait(file, &list->hdev->debug_wait, wait);
> - if (list->head != list->tail)
> + if (!kfifo_is_empty(&list->hid_debug_fifo))
> return EPOLLIN | EPOLLRDNORM;
> if (!list->hdev->debug)
> return EPOLLERR | EPOLLHUP;
> @@ -1200,7 +1175,7 @@ static int hid_debug_events_release(struct inode *inode, struct file *file)
> spin_lock_irqsave(&list->hdev->debug_list_lock, flags);
> list_del(&list->node);
> spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags);
> - kfree(list->hid_debug_buf);
> + kfifo_free(&list->hid_debug_fifo);
> kfree(list);
>
> return 0;
> @@ -1246,4 +1221,3 @@ void hid_debug_exit(void)
> {
> debugfs_remove_recursive(hid_debug_root);
> }
> -
> diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h
> index 8663f216c563..e7a7c92aaf09 100644
> --- a/include/linux/hid-debug.h
> +++ b/include/linux/hid-debug.h
> @@ -24,7 +24,10 @@
>
> #ifdef CONFIG_DEBUG_FS
>
> +#include <linux/kfifo.h>
> +
> #define HID_DEBUG_BUFSIZE 512
> +#define HID_DEBUG_FIFOSIZE 512
>
> void hid_dump_input(struct hid_device *, struct hid_usage *, __s32);
> void hid_dump_report(struct hid_device *, int , u8 *, int);
> @@ -38,10 +41,7 @@ void hid_debug_event(struct hid_device *, char *);
> void hid_debug_event(struct hid_device *, char *);
>
> -
> struct hid_debug_list {
> - char *hid_debug_buf;
> - int head;
> - int tail;
> + DECLARE_KFIFO_PTR(hid_debug_fifo, char);
> struct fasync_struct *fasync;
> struct hid_device *hdev;
> struct list_head node;
> @@ -64,4 +64,3 @@ struct hid_debug_list {
> #endif
>
> #endif
> -
^ permalink raw reply
* Re: [PATCH 10/13] gpio: max77650: add GPIO support
From: Bartosz Golaszewski @ 2019-01-29 13:22 UTC (permalink / raw)
To: Linus Walleij
Cc: Brian Masney, Rob Herring, Mark Rutland, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman,
linux-kernel@vger.kernel.org, open list:GPIO SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Linux Input, Linux LED Subsystem, Linux PM list
In-Reply-To: <CAMRc=MftwsuooC8vLmnGYiiKj2rVShXeuekTrho8JRihjiBJ+w@mail.gmail.com>
wt., 29 sty 2019 o 12:00 Bartosz Golaszewski <brgl@bgdev.pl> napisał(a):
>
> czw., 24 sty 2019 o 11:30 Linus Walleij <linus.walleij@linaro.org> napisał(a):
> >
> > On Mon, Jan 21, 2019 at 6:07 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> >
> > > Thank you for your review. While I think you're right about the issue
> > > being present in this driver, I'm not sure it's really a problem. Do
> > > we actually require every gpio-controller to also be a stand-alone
> > > interrupt-controller?
> >
> > Absolutely not :D
> >
> > Just GPIO is fine.
> >
> > > The binding document for the GPIO module doesn't
> > > mention this - it only requires the gpio-controller property. Without
> > > the "interrupt-controller" property dtc will bail-out if anyone uses
> > > this node as the interrupt parent.
> > >
> > > If I'm wrong and we do require it, then I think we need to update
> > > Documentation/devicetree/bindings/gpio/gpio.txt.
> >
> > What is weird is if a driver with DT bindings not mentioning IRQ
> > and only probing from DT start implementing IRQ support, that
> > becomes quite inconsistent. So then max77650_gpio_to_irq()
> > should just return -ENOTSUPP
> > or something for now, then it's fine.
> >
>
> I don't see it as weird at all. I see the need to define the register
> and interrupt resources in DT for SoC peripherals becaue SoCs often
> reuse IPs. But in the case of a self-contained i2c PMIC - the modules
> such as GPIO are tightly coupled with the core functionality. In the
> case of this device for example: there isn't even a separate set of
> mask/status registers for GPIO interrupts.
>
> Most mfd devices setup the resources in a hard-coded manner.
>
> > We can add the (complicated) IRQ handling later.
> >
> > I am trying to eat my own dogfood here, I was sweating all
> > last night trying to implement a hierarchical IRQ controller.
> > There is no running away from that now. :/
> >
> > Apparently doing hierarchical IRQs demand that all irq
> > controllers up to the top-level SoC IRQ controller support
> > hierarchical interrupts using the v2 version of the irqdomain
> > API, and currently it seems like the ARM
> > GIC seems like the only top level IRQ controller that can
> > do that.
> >
>
> Yep, and for that reason I can't use the regmap irq_chip abstraction
> for now because it doesn't implement support for hierarchical
> interrupts either.
>
> How about the cascaded gpiochip irq_chip?
>
> Best regards,
> Bartosz
Nah that won't work either without a proper hierarchy...
In that case, let's leave out the irq support for now. I'll send v2.
Bart
^ permalink raw reply
* [PATCH v2 00/13] mfd: add support for max77650 PMIC
From: Bartosz Golaszewski @ 2019-01-29 13:35 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman
Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
linux-pm, Bartosz Golaszewski
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This series adds support for max77650 ultra low-power PMIC. It provides
the core mfd driver and a set of five sub-drivers for the regulator,
power supply, gpio, leds and input subsystems.
Patches 1-6 add the DT binding documents. Patches 7-12 add all drivers.
Last patch adds a MAINTAINERS entry for this device.
Note that patch 8/13 depends on commit 03c87b95ac04 ("regulator: provide
rdev_get_regmap()") which was picked up by Mark Brown for v5.1.
v1 -> v2:
General:
- use C++ style comments for the SPDX license identifier and the
copyright header
- s/MODULE_LICENSE("GPL")/MODULE_LICENSE("GPL v2")/
- lookup the virtual interrupt numbers in the MFD driver, setup
resources for child devices and use platform_get_irq_byname()
in sub-drivers
- picked up review tags
- use devm_request_any_context_irq() for interrupt requests
LEDs:
- changed the max77650_leds_ prefix to max77650_led_
- drop the max77650_leds structure as the only field it held was the
regmap pointer, move said pointer to struct max77650_led
- change the driver name to "max77650-led"
- drop the last return value check and return the result of
regmap_write() directly
- change the labeling scheme to one consistent with other LED drivers
ONKEY:
- drop the key reporting helper and call the input functions directly
from interrupt handlers
- rename the rv local variable to error
- drop parent device asignment
Regulator:
- drop the unnecessary init_data lookup from the driver code
- drop unnecessary include
Charger:
- disable the charger on driver remove
- change the power supply type to POWER_SUPPLY_TYPE_USB
GPIO:
- drop interrupt support until we have correct implementation of hierarchical
irqs in gpiolib
Bartosz Golaszewski (13):
dt-bindings: mfd: add DT bindings for max77650
dt-bindings: regulator: add DT bindings for max77650
dt-bindings: power: supply: add DT bindings for max77650
dt-bindings: gpio: add DT bindings for max77650
dt-bindings: leds: add DT bindings for max77650
dt-bindings: input: add DT bindings for max77650
mfd: max77650: new core mfd driver
regulator: max77650: add regulator support
power: supply: max77650: add support for battery charger
gpio: max77650: add GPIO support
leds: max77650: add LEDs support
input: max77650: add onkey support
MAINTAINERS: add an entry for max77650 mfd driver
.../bindings/gpio/gpio-max77650.txt | 34 ++
.../bindings/input/max77650-onkey.txt | 26 +
.../bindings/leds/leds-max77650.txt | 57 ++
.../devicetree/bindings/mfd/max77650.txt | 28 +
.../power/supply/max77650-charger.txt | 27 +
.../bindings/regulator/max77650-regulator.txt | 41 ++
MAINTAINERS | 14 +
drivers/gpio/Kconfig | 7 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-max77650.c | 189 +++++++
drivers/input/misc/Kconfig | 9 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/max77650-onkey.c | 127 +++++
drivers/leds/Kconfig | 6 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-max77650.c | 152 +++++
drivers/mfd/Kconfig | 11 +
drivers/mfd/Makefile | 1 +
drivers/mfd/max77650.c | 338 ++++++++++++
drivers/power/supply/Kconfig | 7 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/max77650-charger.c | 355 ++++++++++++
drivers/regulator/Kconfig | 8 +
drivers/regulator/Makefile | 1 +
drivers/regulator/max77650-regulator.c | 518 ++++++++++++++++++
include/linux/mfd/max77650.h | 59 ++
26 files changed, 2019 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/gpio-max77650.txt
create mode 100644 Documentation/devicetree/bindings/input/max77650-onkey.txt
create mode 100644 Documentation/devicetree/bindings/leds/leds-max77650.txt
create mode 100644 Documentation/devicetree/bindings/mfd/max77650.txt
create mode 100644 Documentation/devicetree/bindings/power/supply/max77650-charger.txt
create mode 100644 Documentation/devicetree/bindings/regulator/max77650-regulator.txt
create mode 100644 drivers/gpio/gpio-max77650.c
create mode 100644 drivers/input/misc/max77650-onkey.c
create mode 100644 drivers/leds/leds-max77650.c
create mode 100644 drivers/mfd/max77650.c
create mode 100644 drivers/power/supply/max77650-charger.c
create mode 100644 drivers/regulator/max77650-regulator.c
create mode 100644 include/linux/mfd/max77650.h
--
2.20.1
^ permalink raw reply
* [PATCH v2 01/13] dt-bindings: mfd: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-01-29 13:35 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman
Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
linux-pm, Bartosz Golaszewski
In-Reply-To: <20190129133545.1931-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add a DT binding document for max77650 ultra-low power PMIC. This
describes the core mfd device.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../devicetree/bindings/mfd/max77650.txt | 28 +++++++++++++++++++
1 file changed, 28 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/max77650.txt
diff --git a/Documentation/devicetree/bindings/mfd/max77650.txt b/Documentation/devicetree/bindings/mfd/max77650.txt
new file mode 100644
index 000000000000..84631d3b1e14
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/max77650.txt
@@ -0,0 +1,28 @@
+MAX77650 ultra low-power PMIC from Maxim Integrated.
+
+Required properties:
+-------------------
+- compatible: Must be "maxim,max77650"
+- reg: I2C device address.
+- interrupts: The interrupt on the parent the controller is
+ connected to.
+- interrupt-parent: phandle of the parent interrupt controller.
+- interrupt-controller: Marks the device node as an interrupt controller.
+- #interrupt-cells: Must be <2>.
+
+For device-tree bindings of sub-modules (regulator, power supply, GPIO, LEDs
+and onkey) refer to the binding documents under the respective sub-system
+directories.
+
+Example:
+--------
+
+ pmic: max77650@48 {
+ compatible = "maxim,max77650";
+ reg = <0x48>;
+
+ interrupt-controller;
+ interrupt-parent = <&gpio2>;
+ #interrupt-cells = <2>;
+ interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+ };
--
2.20.1
^ permalink raw reply related
* [PATCH] gpio fixup domain
From: Bartosz Golaszewski @ 2019-01-29 13:35 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman
Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
linux-pm, Bartosz Golaszewski
In-Reply-To: <20190129133545.1931-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
arch/arm/boot/dts/am335x-bone-common.dtsi | 4 +
drivers/gpio/gpio-max77650.c | 138 +++++++++++++++++++++-
2 files changed, 136 insertions(+), 6 deletions(-)
diff --git a/arch/arm/boot/dts/am335x-bone-common.dtsi b/arch/arm/boot/dts/am335x-bone-common.dtsi
index 62f7f2ac191c..320b4c21fdf3 100644
--- a/arch/arm/boot/dts/am335x-bone-common.dtsi
+++ b/arch/arm/boot/dts/am335x-bone-common.dtsi
@@ -276,6 +276,10 @@
gpio-controller;
#gpio-cells = <2>;
+
+ interrupt-parent = <&pmic>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "GPI";
};
leds {
diff --git a/drivers/gpio/gpio-max77650.c b/drivers/gpio/gpio-max77650.c
index 3f03f4e8956c..03a5a2ed07f8 100644
--- a/drivers/gpio/gpio-max77650.c
+++ b/drivers/gpio/gpio-max77650.c
@@ -9,6 +9,7 @@
#include <linux/i2c.h>
#include <linux/mfd/max77650.h>
#include <linux/module.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -34,7 +35,8 @@
struct max77650_gpio_chip {
struct regmap *map;
struct gpio_chip gc;
- int irq;
+ struct fwnode_handle *fwnode;
+ struct irq_domain *domain;
};
static int max77650_gpio_direction_input(struct gpio_chip *gc,
@@ -130,20 +132,114 @@ static int max77650_gpio_set_config(struct gpio_chip *gc,
}
}
+static struct irq_chip max77650_gpio_irq_chip = {
+ .name = "max77650-gpio",
+ .irq_ack = irq_chip_ack_parent,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_set_type = irq_chip_set_type_parent,
+};
+
+static int max77650_gpio_irq_domain_activate(struct irq_domain *domain,
+ struct irq_data *data,
+ bool reserve)
+{
+ struct max77650_gpio_chip *chip = domain->host_data;
+
+ return gpiochip_lock_as_irq(&chip->gc, data->hwirq);
+}
+
+static void max77650_gpio_irq_domain_deactivate(struct irq_domain *domain,
+ struct irq_data *data)
+{
+ struct max77650_gpio_chip *chip = domain->host_data;
+
+ return gpiochip_unlock_as_irq(&chip->gc, data->hwirq);
+}
+
+static int max77650_gpio_domain_translate(struct irq_domain *domain,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
+{
+ struct max77650_gpio_chip *chip = domain->host_data;
+
+ if (fwspec->param_count != 2 || fwspec->param[0] >= chip->gc.ngpio)
+ return -EINVAL;
+
+ *hwirq = fwspec->param[0];
+ *type = fwspec->param[1];
+
+ return 0;
+}
+
+static int max77650_gpio_domain_alloc(struct irq_domain *domain,
+ unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct max77650_gpio_chip *chip = domain->host_data;
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ int ret;
+
+ if (nr_irqs != 1)
+ return -EINVAL;
+
+ ret = max77650_gpio_domain_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ irq_domain_set_info(domain, virq, hwirq,
+ &max77650_gpio_irq_chip, chip,
+ handle_level_irq, NULL, NULL);
+
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ parent_fwspec.param_count = 4;
+ parent_fwspec.param[0] = 0;
+ parent_fwspec.param[1] = hwirq;
+ parent_fwspec.param[2] = 0;
+ parent_fwspec.param[3] = fwspec->param[1];
+
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
+
+ return 0;
+}
+
+static const struct irq_domain_ops max77650_gpio_irq_domain_ops = {
+ .activate = max77650_gpio_irq_domain_activate,
+ .deactivate = max77650_gpio_irq_domain_deactivate,
+ .alloc = max77650_gpio_domain_alloc,
+ .free = irq_domain_free_irqs_common,
+ .translate = max77650_gpio_domain_translate,
+};
+
static int max77650_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
{
struct max77650_gpio_chip *chip = gpiochip_get_data(gc);
+ struct irq_fwspec fwspec;
+
+ fwspec.fwnode = chip->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = 0;
+ fwspec.param[1] = IRQ_TYPE_LEVEL_LOW;
- return chip->irq;
+ return irq_create_fwspec_mapping(&fwspec);
}
static int max77650_gpio_probe(struct platform_device *pdev)
{
+ struct device_node *of_node, *parent_node;
+ struct irq_domain *parent_domain;
struct max77650_gpio_chip *chip;
struct device *dev, *parent;
struct i2c_client *i2c;
+ int rv;
dev = &pdev->dev;
+ of_node = dev->of_node;
parent = dev->parent;
i2c = to_i2c_client(parent);
@@ -151,13 +247,29 @@ static int max77650_gpio_probe(struct platform_device *pdev)
if (!chip)
return -ENOMEM;
+ chip->fwnode = dev->fwnode;
+ platform_set_drvdata(pdev, chip);
+
chip->map = dev_get_regmap(parent, NULL);
if (!chip->map)
return -ENODEV;
- chip->irq = platform_get_irq_byname(pdev, "GPI");
- if (chip->irq < 0)
- return chip->irq;
+ parent_node = of_irq_find_parent(of_node);
+ if (!parent_node)
+ return -ENXIO;
+
+ parent_domain = irq_find_host(parent_node);
+ of_node_put(parent_node);
+ if (!parent_domain)
+ return -ENXIO;
+
+ chip->fwnode = of_node_to_fwnode(of_node);
+ chip->domain = irq_domain_create_hierarchy(parent_domain, 0, 1,
+ chip->fwnode,
+ &max77650_gpio_irq_domain_ops,
+ chip);
+ if (!chip->domain)
+ return -ENODEV;
chip->gc.base = -1;
chip->gc.ngpio = 1;
@@ -174,7 +286,20 @@ static int max77650_gpio_probe(struct platform_device *pdev)
chip->gc.set_config = max77650_gpio_set_config;
chip->gc.to_irq = max77650_gpio_to_irq;
- return devm_gpiochip_add_data(dev, &chip->gc, chip);
+ rv = devm_gpiochip_add_data(dev, &chip->gc, chip);
+ if (rv)
+ irq_domain_remove(chip->domain);
+
+ return rv;
+}
+
+static int max77650_gpio_remove(struct platform_device *pdev)
+{
+ struct max77650_gpio_chip *chip = platform_get_drvdata(pdev);
+
+ irq_domain_remove(chip->domain);
+
+ return 0;
}
static struct platform_driver max77650_gpio_driver = {
@@ -182,6 +307,7 @@ static struct platform_driver max77650_gpio_driver = {
.name = "max77650-gpio",
},
.probe = max77650_gpio_probe,
+ .remove = max77650_gpio_remove,
};
module_platform_driver(max77650_gpio_driver);
--
2.20.1
^ permalink raw reply related
* [PATCH v2 02/13] dt-bindings: regulator: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-01-29 13:35 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman
Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
linux-pm, Bartosz Golaszewski
In-Reply-To: <20190129133545.1931-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add the DT binding document for max77650 regulators.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../bindings/regulator/max77650-regulator.txt | 41 +++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regulator/max77650-regulator.txt
diff --git a/Documentation/devicetree/bindings/regulator/max77650-regulator.txt b/Documentation/devicetree/bindings/regulator/max77650-regulator.txt
new file mode 100644
index 000000000000..f1cbe813c30f
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/max77650-regulator.txt
@@ -0,0 +1,41 @@
+Regulator driver for MAX77650 PMIC from Maxim Integrated.
+
+This module is part of the MAX77650 MFD device. For more details
+see Documentation/devicetree/bindings/mfd/max77650.txt.
+
+The regulator controller is represented as a sub-node of the PMIC node
+on the device tree.
+
+The device has a single LDO regulator and a SIMO buck-boost regulator with
+three independent power rails.
+
+Required properties:
+--------------------
+- compatible: Must be "maxim,max77650-regulator"
+
+Each rail must be instantiated under the regulators subnode of the top PMIC
+node. Up to four regulators can be defined. For standard regulator properties
+refer to Documentation/devicetree/bindings/regulator/regulator.txt.
+
+Available regulator compatible strings are: "ldo", "sbb0", "sbb1", "sbb2".
+
+Example:
+--------
+
+ regulators {
+ compatible = "maxim,max77650-regulator";
+
+ max77650_ldo: regulator@0 {
+ regulator-compatible = "ldo";
+ regulator-name = "max77650-ldo";
+ regulator-min-microvolt = <1350000>;
+ regulator-max-microvolt = <2937500>;
+ };
+
+ max77650_sbb0: regulator@1 {
+ regulator-compatible = "sbb0";
+ regulator-name = "max77650-sbb0";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1587500>;
+ };
+ };
--
2.20.1
^ permalink raw reply related
* [PATCH v2 03/13] dt-bindings: power: supply: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-01-29 13:35 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman
Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
linux-pm, Bartosz Golaszewski
In-Reply-To: <20190129133545.1931-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add the DT binding document for the battery charger module of max77650.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../power/supply/max77650-charger.txt | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/supply/max77650-charger.txt
diff --git a/Documentation/devicetree/bindings/power/supply/max77650-charger.txt b/Documentation/devicetree/bindings/power/supply/max77650-charger.txt
new file mode 100644
index 000000000000..f3e00d41e299
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/max77650-charger.txt
@@ -0,0 +1,27 @@
+Battery charger driver for MAX77650 PMIC from Maxim Integrated.
+
+This module is part of the MAX77650 MFD device. For more details
+see Documentation/devicetree/bindings/mfd/max77650.txt.
+
+The charger is represented as a sub-node of the PMIC node on the device tree.
+
+Required properties:
+--------------------
+- compatible: Must be "maxim,max77650-charger"
+
+Optional properties:
+--------------------
+- maxim,vchgin-min: Minimum CHGIN regulation voltage (in microvolts). Must be
+ one of: 4000000, 4100000, 4200000, 4300000, 4400000,
+ 4500000, 4600000, 4700000.
+- maxim,ichgin-lim: CHGIN input current limit (in microamps). Must be one of:
+ 95000, 190000, 285000, 380000, 475000.
+
+Example:
+--------
+
+ charger {
+ compatible = "maxim,max77650-charger";
+ maxim,vchgin-min = <4200000>;
+ maxim,ichgin-lim = <285000>;
+ };
--
2.20.1
^ permalink raw reply related
* [PATCH v2 04/13] dt-bindings: gpio: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-01-29 13:35 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman
Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
linux-pm, Bartosz Golaszewski
In-Reply-To: <20190129133545.1931-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add the DT binding document for the GPIO module of max77650.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
.../bindings/gpio/gpio-max77650.txt | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/gpio-max77650.txt
diff --git a/Documentation/devicetree/bindings/gpio/gpio-max77650.txt b/Documentation/devicetree/bindings/gpio/gpio-max77650.txt
new file mode 100644
index 000000000000..b5dbbe934deb
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-max77650.txt
@@ -0,0 +1,34 @@
+GPIO driver for MAX77650 PMIC from Maxim Integrated.
+
+This module is part of the MAX77650 MFD device. For more details
+see Documentation/devicetree/bindings/mfd/max77650.txt.
+
+The GPIO controller is represented as a sub-node of the PMIC node on
+the device tree.
+
+This device has a single GPIO pin.
+
+Required properties:
+--------------------
+- compatible: Must be "maxim,max77650-gpio"
+- gpio-controller : Marks the device node as a gpio controller.
+- #gpio-cells : Must be <2>. The first cell is the pin number and
+ the second cell is used to specify the gpio active
+ state.
+
+Optional properties:
+--------------------
+gpio-line-names: Single string containing the name of the GPIO line.
+
+For more details, please refer to the generic GPIO DT binding document
+<devicetree/bindings/gpio/gpio.txt>.
+
+Example:
+--------
+
+ gpio {
+ compatible = "maxim,max77650-gpio";
+ gpio-line-names = "max77650-charger";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
--
2.20.1
^ permalink raw reply related
* [PATCH v2 05/13] dt-bindings: leds: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-01-29 13:35 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman
Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
linux-pm, Bartosz Golaszewski
In-Reply-To: <20190129133545.1931-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add the DT binding document for the LEDs module of max77650.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../bindings/leds/leds-max77650.txt | 57 +++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/leds-max77650.txt
diff --git a/Documentation/devicetree/bindings/leds/leds-max77650.txt b/Documentation/devicetree/bindings/leds/leds-max77650.txt
new file mode 100644
index 000000000000..b2fac8e719a6
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-max77650.txt
@@ -0,0 +1,57 @@
+LED driver for MAX77650 PMIC from Maxim Integrated.
+
+This module is part of the MAX77650 MFD device. For more details
+see Documentation/devicetree/bindings/mfd/max77650.txt.
+
+The LED controller is represented as a sub-node of the PMIC node on
+the device tree.
+
+This device has three current sinks.
+
+Required properties:
+--------------------
+- compatible: Must be "maxim,max77650-led"
+- #address-cells: Must be <1>.
+- #size-cells: Must be <0>.
+
+Each LED is represented as a sub-node of the LED-controller node. Up to
+three sub-nodes can be defined.
+
+Required properties of the sub-node:
+------------------------------------
+
+- reg: Must be <0>, <1> or <2>.
+
+Optional properties of the sub-node:
+------------------------------------
+
+- label: See Documentation/devicetree/bindings/leds/common.txt
+- linux,default-trigger: See Documentation/devicetree/bindings/leds/common.txt
+
+For more details, please refer to the generic GPIO DT binding document
+<devicetree/bindings/gpio/gpio.txt>.
+
+Example:
+--------
+
+ leds {
+ compatible = "maxim,max77650-leds";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@0 {
+ reg = <0>;
+ label = "blue:usr0";
+ };
+
+ led@1 {
+ reg = <1>;
+ label = "red:usr1";
+ linux,default-trigger = "heartbeat";
+ };
+
+ led@2 {
+ reg = <2>;
+ label = "green:usr2";
+ };
+ };
--
2.20.1
^ permalink raw reply related
* [PATCH v2 06/13] dt-bindings: input: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-01-29 13:35 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman
Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
linux-pm, Bartosz Golaszewski
In-Reply-To: <20190129133545.1931-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add the DT binding document for the onkey module of max77650.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../bindings/input/max77650-onkey.txt | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/max77650-onkey.txt
diff --git a/Documentation/devicetree/bindings/input/max77650-onkey.txt b/Documentation/devicetree/bindings/input/max77650-onkey.txt
new file mode 100644
index 000000000000..37c80898be4d
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/max77650-onkey.txt
@@ -0,0 +1,26 @@
+Onkey driver for MAX77650 PMIC from Maxim Integrated.
+
+This module is part of the MAX77650 MFD device. For more details
+see Documentation/devicetree/bindings/mfd/max77650.txt.
+
+The onkey controller is represented as a sub-node of the PMIC node on
+the device tree.
+
+Required properties:
+--------------------
+- compatible: Must be "maxim,max77650-onkey".
+
+Optional properties:
+- linux,code: The key-code to be reported when the key is pressed.
+ Defaults to KEY_POWER.
+- maxim,onkey-mode: Must be "push" or "slide" depending on the type of
+ button used by the system. Defaults to "push".
+
+Example:
+--------
+
+ onkey {
+ compatible = "maxim,max77650-onkey";
+ linux,code = <KEY_END>;
+ maxim,onkey-mode = "slide";
+ };
--
2.20.1
^ permalink raw reply related
* [PATCH v2 07/13] mfd: max77650: new core mfd driver
From: Bartosz Golaszewski @ 2019-01-29 13:35 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman
Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
linux-pm, Bartosz Golaszewski
In-Reply-To: <20190129133545.1931-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add the core mfd driver for max77650 PMIC. We define five sub-devices
for which the drivers will be added in subsequent patches.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/mfd/Kconfig | 11 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/max77650.c | 338 +++++++++++++++++++++++++++++++++++
include/linux/mfd/max77650.h | 59 ++++++
4 files changed, 409 insertions(+)
create mode 100644 drivers/mfd/max77650.c
create mode 100644 include/linux/mfd/max77650.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f461460a2aeb..828fd193b4ee 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -734,6 +734,17 @@ config MFD_MAX77620
provides common support for accessing the device; additional drivers
must be enabled in order to use the functionality of the device.
+config MFD_MAX77650
+ tristate "Maxim MAX77650/77651 PMIC Support"
+ depends on I2C
+ depends on OF || COMPILE_TEST
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ Say yes here to add support for Maxim Semiconductor MAX77650 and
+ MAX77651 Power Management ICs. This is the core multifunction
+ driver for interacting with the device.
+
config MFD_MAX77686
tristate "Maxim Semiconductor MAX77686/802 PMIC Support"
depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 12980a4ad460..3b912a4015d1 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -151,6 +151,7 @@ obj-$(CONFIG_MFD_DA9150) += da9150-core.o
obj-$(CONFIG_MFD_MAX14577) += max14577.o
obj-$(CONFIG_MFD_MAX77620) += max77620.o
+obj-$(CONFIG_MFD_MAX77650) += max77650.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o
obj-$(CONFIG_MFD_MAX77843) += max77843.o
diff --git a/drivers/mfd/max77650.c b/drivers/mfd/max77650.c
new file mode 100644
index 000000000000..a89a5ea421f8
--- /dev/null
+++ b/drivers/mfd/max77650.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2018 BayLibre SAS
+// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+//
+// Core MFD driver for MAXIM 77650/77651 charger/power-supply.
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77650.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define MAX77650_INT_GPI_F_MSK BIT(0)
+#define MAX77650_INT_GPI_R_MSK BIT(1)
+#define MAX77650_INT_GPI_MSK \
+ (MAX77650_INT_GPI_F_MSK | MAX77650_INT_GPI_R_MSK)
+#define MAX77650_INT_nEN_F_MSK BIT(2)
+#define MAX77650_INT_nEN_R_MSK BIT(3)
+#define MAX77650_INT_TJAL1_R_MSK BIT(4)
+#define MAX77650_INT_TJAL2_R_MSK BIT(5)
+#define MAX77650_INT_DOD_R_MSK BIT(6)
+
+#define MAX77650_INT_THM_MSK BIT(0)
+#define MAX77650_INT_CHG_MSK BIT(1)
+#define MAX77650_INT_CHGIN_MSK BIT(2)
+#define MAX77650_INT_TJ_REG_MSK BIT(3)
+#define MAX77650_INT_CHGIN_CTRL_MSK BIT(4)
+#define MAX77650_INT_SYS_CTRL_MSK BIT(5)
+#define MAX77650_INT_SYS_CNFG_MSK BIT(6)
+
+#define MAX77650_INT_GLBL_OFFSET 0
+#define MAX77650_INT_CHG_OFFSET 1
+
+#define MAX77650_SBIA_LPM_MASK BIT(5)
+#define MAX77650_SBIA_LPM_DISABLED 0x00
+
+#define MAX77650_INT_GPI 0
+#define MAX77650_INT_nEN_F 1
+#define MAX77650_INT_nEN_R 2
+#define MAX77650_INT_TJAL1_R 3
+#define MAX77650_INT_TJAL2_R 4
+#define MAX77650_INT_DOD_R 5
+#define MAX77650_INT_THM 6
+#define MAX77650_INT_CHG 7
+#define MAX77650_INT_CHGIN 8
+#define MAX77650_INT_TJ_REG 9
+#define MAX77650_INT_CHGIN_CTRL 10
+#define MAX77650_INT_SYS_CTRL 11
+#define MAX77650_INT_SYS_CNFG 12
+
+enum {
+ MAX77650_CELL_REGULATOR = 0,
+ MAX77650_CELL_CHARGER,
+ MAX77650_CELL_GPIO,
+ MAX77650_CELL_LED,
+ MAX77650_CELL_ONKEY,
+ MAX77650_NUM_CELLS,
+};
+
+struct max77650_irq_mapping {
+ int cell_num;
+ const int *irqs;
+ const char **irq_names;
+ unsigned int num_irqs;
+};
+
+static const int max77650_charger_irqs[] = {
+ MAX77650_INT_CHG,
+ MAX77650_INT_CHGIN,
+};
+
+static const int max77650_gpio_irqs[] = {
+ MAX77650_INT_GPI,
+};
+
+static const int max77650_onkey_irqs[] = {
+ MAX77650_INT_nEN_F,
+ MAX77650_INT_nEN_R,
+};
+
+static const char *max77650_charger_irq_names[] = {
+ "CHG",
+ "CHGIN",
+};
+
+static const char *max77650_gpio_irq_names[] = {
+ "GPI",
+};
+
+static const char *max77650_onkey_irq_names[] = {
+ "nEN_F",
+ "nEN_R",
+};
+
+static const struct max77650_irq_mapping max77650_irq_mapping_table[] = {
+ {
+ .cell_num = MAX77650_CELL_CHARGER,
+ .irqs = max77650_charger_irqs,
+ .irq_names = max77650_charger_irq_names,
+ .num_irqs = ARRAY_SIZE(max77650_charger_irqs),
+ },
+ {
+ .cell_num = MAX77650_CELL_GPIO,
+ .irqs = max77650_gpio_irqs,
+ .irq_names = max77650_gpio_irq_names,
+ .num_irqs = ARRAY_SIZE(max77650_gpio_irqs),
+ },
+ {
+ .cell_num = MAX77650_CELL_ONKEY,
+ .irqs = max77650_onkey_irqs,
+ .irq_names = max77650_onkey_irq_names,
+ .num_irqs = ARRAY_SIZE(max77650_onkey_irqs),
+ },
+};
+
+static const struct mfd_cell max77650_cells[] = {
+ [MAX77650_CELL_REGULATOR] = {
+ .name = "max77650-regulator",
+ .of_compatible = "maxim,max77650-regulator",
+ },
+ [MAX77650_CELL_CHARGER] = {
+ .name = "max77650-charger",
+ .of_compatible = "maxim,max77650-charger",
+ },
+ [MAX77650_CELL_GPIO] = {
+ .name = "max77650-gpio",
+ .of_compatible = "maxim,max77650-gpio",
+ },
+ [MAX77650_CELL_LED] = {
+ .name = "max77650-led",
+ .of_compatible = "maxim,max77650-led",
+ },
+ [MAX77650_CELL_ONKEY] = {
+ .name = "max77650-onkey",
+ .of_compatible = "maxim,max77650-onkey",
+ },
+};
+
+static const struct regmap_irq max77650_irqs[] = {
+ [MAX77650_INT_GPI] = {
+ .reg_offset = MAX77650_INT_GLBL_OFFSET,
+ .mask = MAX77650_INT_GPI_MSK,
+ .type = {
+ .type_falling_val = MAX77650_INT_GPI_F_MSK,
+ .type_rising_val = MAX77650_INT_GPI_R_MSK,
+ .types_supported = IRQ_TYPE_EDGE_BOTH,
+ },
+ },
+ [MAX77650_INT_nEN_F] = {
+ .reg_offset = MAX77650_INT_GLBL_OFFSET,
+ .mask = MAX77650_INT_nEN_F_MSK,
+ },
+ [MAX77650_INT_nEN_R] = {
+ .reg_offset = MAX77650_INT_GLBL_OFFSET,
+ .mask = MAX77650_INT_nEN_R_MSK,
+ },
+ [MAX77650_INT_TJAL1_R] = {
+ .reg_offset = MAX77650_INT_GLBL_OFFSET,
+ .mask = MAX77650_INT_TJAL1_R_MSK,
+ },
+ [MAX77650_INT_TJAL2_R] = {
+ .reg_offset = MAX77650_INT_GLBL_OFFSET,
+ .mask = MAX77650_INT_TJAL2_R_MSK,
+ },
+ [MAX77650_INT_DOD_R] = {
+ .reg_offset = MAX77650_INT_GLBL_OFFSET,
+ .mask = MAX77650_INT_DOD_R_MSK,
+ },
+ [MAX77650_INT_THM] = {
+ .reg_offset = MAX77650_INT_CHG_OFFSET,
+ .mask = MAX77650_INT_THM_MSK,
+ },
+ [MAX77650_INT_CHG] = {
+ .reg_offset = MAX77650_INT_CHG_OFFSET,
+ .mask = MAX77650_INT_CHG_MSK,
+ },
+ [MAX77650_INT_CHGIN] = {
+ .reg_offset = MAX77650_INT_CHG_OFFSET,
+ .mask = MAX77650_INT_CHGIN_MSK,
+ },
+ [MAX77650_INT_TJ_REG] = {
+ .reg_offset = MAX77650_INT_CHG_OFFSET,
+ .mask = MAX77650_INT_TJ_REG_MSK,
+ },
+ [MAX77650_INT_CHGIN_CTRL] = {
+ .reg_offset = MAX77650_INT_CHG_OFFSET,
+ .mask = MAX77650_INT_CHGIN_CTRL_MSK,
+ },
+ [MAX77650_INT_SYS_CTRL] = {
+ .reg_offset = MAX77650_INT_CHG_OFFSET,
+ .mask = MAX77650_INT_SYS_CTRL_MSK,
+ },
+ [MAX77650_INT_SYS_CNFG] = {
+ .reg_offset = MAX77650_INT_CHG_OFFSET,
+ .mask = MAX77650_INT_SYS_CNFG_MSK,
+ },
+};
+
+static const struct regmap_irq_chip max77650_irq_chip = {
+ .name = "max77650-irq",
+ .irqs = max77650_irqs,
+ .num_irqs = ARRAY_SIZE(max77650_irqs),
+ .num_regs = 2,
+ .status_base = MAX77650_REG_INT_GLBL,
+ .mask_base = MAX77650_REG_INTM_GLBL,
+ .type_in_mask = true,
+ .type_invert = true,
+ .init_ack_masked = true,
+ .clear_on_unmask = true,
+};
+
+static const struct regmap_config max77650_regmap_config = {
+ .name = "max77650",
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int max77650_setup_irqs(struct device *dev, struct mfd_cell *cells)
+{
+ const struct max77650_irq_mapping *mapping;
+ struct regmap_irq_chip_data *irq_data;
+ struct i2c_client *i2c;
+ struct mfd_cell *cell;
+ struct resource *res;
+ struct regmap *map;
+ int i, j, irq, rv;
+
+ i2c = to_i2c_client(dev);
+
+ map = dev_get_regmap(dev, NULL);
+ if (!map)
+ return -ENODEV;
+
+ rv = devm_regmap_add_irq_chip(dev, map, i2c->irq,
+ IRQF_ONESHOT | IRQF_SHARED, -1,
+ &max77650_irq_chip, &irq_data);
+ if (rv)
+ return rv;
+
+ for (i = 0; i < ARRAY_SIZE(max77650_irq_mapping_table); i++) {
+ mapping = &max77650_irq_mapping_table[i];
+ cell = &cells[mapping->cell_num];
+
+ res = devm_kcalloc(dev, sizeof(*res),
+ mapping->num_irqs, GFP_KERNEL);
+ if (!res)
+ return -ENOMEM;
+
+ cell->resources = res;
+ cell->num_resources = mapping->num_irqs;
+
+ for (j = 0; j < mapping->num_irqs; j++) {
+ irq = regmap_irq_get_virq(irq_data, mapping->irqs[j]);
+ if (irq < 0)
+ return irq;
+
+ res[j].start = res[j].end = irq;
+ res[j].flags = IORESOURCE_IRQ;
+ res[j].name = mapping->irq_names[j];
+ }
+ }
+
+ return 0;
+}
+
+static int max77650_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct mfd_cell *cells;
+ struct regmap *map;
+ unsigned int val;
+ int rv;
+
+ map = devm_regmap_init_i2c(i2c, &max77650_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ rv = regmap_read(map, MAX77650_REG_CID, &val);
+ if (rv)
+ return rv;
+
+ switch (MAX77650_CID_BITS(val)) {
+ case MAX77650_CID_77650A:
+ case MAX77650_CID_77650C:
+ case MAX77650_CID_77651A:
+ case MAX77650_CID_77651B:
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ /*
+ * This IC has a low-power mode which reduces the quiescent current
+ * consumption to ~5.6uA but is only suitable for systems consuming
+ * less than ~2mA. Since this is not likely the case even on
+ * linux-based wearables - keep the chip in normal power mode.
+ */
+ rv = regmap_update_bits(map,
+ MAX77650_REG_CNFG_GLBL,
+ MAX77650_SBIA_LPM_MASK,
+ MAX77650_SBIA_LPM_DISABLED);
+ if (rv)
+ return rv;
+
+ cells = devm_kmemdup(dev, max77650_cells,
+ sizeof(max77650_cells), GFP_KERNEL);
+ if (!cells)
+ return -ENOMEM;
+
+ rv = max77650_setup_irqs(dev, cells);
+ if (rv)
+ return rv;
+
+ return devm_mfd_add_devices(dev, -1, cells,
+ MAX77650_NUM_CELLS, NULL, 0, NULL);
+}
+
+static const struct of_device_id max77650_of_match[] = {
+ { .compatible = "maxim,max77650", },
+};
+
+static struct i2c_driver max77650_i2c_driver = {
+ .driver = {
+ .name = "max77650",
+ .of_match_table = of_match_ptr(max77650_of_match),
+ },
+ .probe_new = max77650_i2c_probe,
+};
+module_i2c_driver(max77650_i2c_driver);
+
+MODULE_DESCRIPTION("MAXIM 77650/77651 multi-function core driver");
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/max77650.h b/include/linux/mfd/max77650.h
new file mode 100644
index 000000000000..c809e211a8cd
--- /dev/null
+++ b/include/linux/mfd/max77650.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 BayLibre SAS
+ * Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+ *
+ * Common definitions for MAXIM 77650/77651 charger/power-supply.
+ */
+
+#ifndef MAX77650_H
+#define MAX77650_H
+
+#include <linux/bits.h>
+
+#define MAX77650_REG_INT_GLBL 0x00
+#define MAX77650_REG_INT_CHG 0x01
+#define MAX77650_REG_STAT_CHG_A 0x02
+#define MAX77650_REG_STAT_CHG_B 0x03
+#define MAX77650_REG_ERCFLAG 0x04
+#define MAX77650_REG_STAT_GLBL 0x05
+#define MAX77650_REG_INTM_GLBL 0x06
+#define MAX77650_REG_INTM_CHG 0x07
+#define MAX77650_REG_CNFG_GLBL 0x10
+#define MAX77650_REG_CID 0x11
+#define MAX77650_REG_CNFG_GPIO 0x12
+#define MAX77650_REG_CNFG_CHG_A 0x18
+#define MAX77650_REG_CNFG_CHG_B 0x19
+#define MAX77650_REG_CNFG_CHG_C 0x1a
+#define MAX77650_REG_CNFG_CHG_D 0x1b
+#define MAX77650_REG_CNFG_CHG_E 0x1c
+#define MAX77650_REG_CNFG_CHG_F 0x1d
+#define MAX77650_REG_CNFG_CHG_G 0x1e
+#define MAX77650_REG_CNFG_CHG_H 0x1f
+#define MAX77650_REG_CNFG_CHG_I 0x20
+#define MAX77650_REG_CNFG_SBB_TOP 0x28
+#define MAX77650_REG_CNFG_SBB0_A 0x29
+#define MAX77650_REG_CNFG_SBB0_B 0x2a
+#define MAX77650_REG_CNFG_SBB1_A 0x2b
+#define MAX77650_REG_CNFG_SBB1_B 0x2c
+#define MAX77650_REG_CNFG_SBB2_A 0x2d
+#define MAX77650_REG_CNFG_SBB2_B 0x2e
+#define MAX77650_REG_CNFG_LDO_A 0x38
+#define MAX77650_REG_CNFG_LDO_B 0x39
+#define MAX77650_REG_CNFG_LED0_A 0x40
+#define MAX77650_REG_CNFG_LED1_A 0x41
+#define MAX77650_REG_CNFG_LED2_A 0x42
+#define MAX77650_REG_CNFG_LED0_B 0x43
+#define MAX77650_REG_CNFG_LED1_B 0x44
+#define MAX77650_REG_CNFG_LED2_B 0x45
+#define MAX77650_REG_CNFG_LED_TOP 0x46
+
+#define MAX77650_CID_MASK GENMASK(3, 0)
+#define MAX77650_CID_BITS(_reg) (_reg & MAX77650_CID_MASK)
+
+#define MAX77650_CID_77650A 0x03
+#define MAX77650_CID_77650C 0x0a
+#define MAX77650_CID_77651A 0x06
+#define MAX77650_CID_77651B 0x08
+
+#endif /* MAX77650_H */
--
2.20.1
^ permalink raw reply related
* [PATCH v2 08/13] regulator: max77650: add regulator support
From: Bartosz Golaszewski @ 2019-01-29 13:35 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
Liam Girdwood, Mark Brown, Greg Kroah-Hartman
Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
linux-pm, Bartosz Golaszewski
In-Reply-To: <20190129133545.1931-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add regulator support for max77650. We support all four variants of this
PMIC including non-linear voltage table for max77651 SBB1 rail.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/regulator/Kconfig | 8 +
drivers/regulator/Makefile | 1 +
drivers/regulator/max77650-regulator.c | 518 +++++++++++++++++++++++++
3 files changed, 527 insertions(+)
create mode 100644 drivers/regulator/max77650-regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index ee60a222f5eb..514f094f9444 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -457,6 +457,14 @@ config REGULATOR_MAX77620
chip to control Step-Down DC-DC and LDOs. Say Y here to
enable the regulator driver.
+config REGULATOR_MAX77650
+ tristate "Maxim MAX77650/77651 regulator support"
+ depends on MFD_MAX77650
+ help
+ Regulator driver for MAX77650/77651 PMIC from Maxim
+ Semiconductor. This device has a SIMO with three independent
+ power rails and an LDO.
+
config REGULATOR_MAX8649
tristate "Maxim 8649 voltage regulator"
depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index b12e1c9b2118..7de79a12b0b7 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_REGULATOR_LTC3676) += ltc3676.o
obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o
+obj-$(CONFIG_REGULATOR_MAX77650) += max77650-regulator.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o
diff --git a/drivers/regulator/max77650-regulator.c b/drivers/regulator/max77650-regulator.c
new file mode 100644
index 000000000000..474f2c02f2d5
--- /dev/null
+++ b/drivers/regulator/max77650-regulator.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2018 BayLibre SAS
+// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+//
+// Regulator driver for MAXIM 77650/77651 charger/power-supply.
+
+#include <linux/i2c.h>
+#include <linux/mfd/max77650.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+
+#define MAX77650_REGULATOR_EN_CTRL_MASK GENMASK(3, 0)
+#define MAX77650_REGULATOR_EN_CTRL_BITS(_reg) \
+ ((_reg) & MAX77650_REGULATOR_EN_CTRL_MASK)
+#define MAX77650_REGULATOR_ENABLED GENMASK(2, 1)
+#define MAX77650_REGULATOR_DISABLED BIT(2)
+
+#define MAX77650_REGULATOR_V_LDO_MASK GENMASK(6, 0)
+#define MAX77650_REGULATOR_V_SBB_MASK GENMASK(5, 0)
+
+#define MAX77650_REGULATOR_AD_MASK BIT(3)
+#define MAX77650_REGULATOR_AD_DISABLED 0x00
+#define MAX77650_REGULATOR_AD_ENABLED BIT(3)
+
+#define MAX77650_REGULATOR_CURR_LIM_MASK GENMASK(7, 6)
+#define MAX77650_REGULATOR_CURR_LIM_BITS(_reg) \
+ (((_reg) & MAX77650_REGULATOR_CURR_LIM_MASK) >> 6)
+#define MAX77650_REGULATOR_CURR_LIM_SHIFT(_val) ((_val) << 6)
+
+enum {
+ MAX77650_REGULATOR_ID_LDO = 0,
+ MAX77650_REGULATOR_ID_SBB0,
+ MAX77650_REGULATOR_ID_SBB1,
+ MAX77650_REGULATOR_ID_SBB2,
+ MAX77650_REGULATOR_NUM_REGULATORS,
+};
+
+struct max77650_regulator_desc {
+ struct regulator_desc desc;
+ unsigned int regA;
+ unsigned int regB;
+};
+
+static const u32 max77651_sbb1_regulator_volt_table[] = {
+ 2400000, 3200000, 4000000, 4800000,
+ 2450000, 3250000, 4050000, 4850000,
+ 2500000, 3300000, 4100000, 4900000,
+ 2550000, 3350000, 4150000, 4950000,
+ 2600000, 3400000, 4200000, 5000000,
+ 2650000, 3450000, 4250000, 5050000,
+ 2700000, 3500000, 4300000, 5100000,
+ 2750000, 3550000, 4350000, 5150000,
+ 2800000, 3600000, 4400000, 5200000,
+ 2850000, 3650000, 4450000, 5250000,
+ 2900000, 3700000, 4500000, 0,
+ 2950000, 3750000, 4550000, 0,
+ 3000000, 3800000, 4600000, 0,
+ 3050000, 3850000, 4650000, 0,
+ 3100000, 3900000, 4700000, 0,
+ 3150000, 3950000, 4750000, 0,
+};
+
+#define MAX77651_REGULATOR_SBB1_SEL_DEC(_val) \
+ (((_val & 0x3c) >> 2) | ((_val & 0x03) << 4))
+#define MAX77651_REGULATOR_SBB1_SEL_ENC(_val) \
+ (((_val & 0x30) >> 4) | ((_val & 0x0f) << 2))
+
+#define MAX77650_REGULATOR_SBB1_SEL_DECR(_val) \
+ do { \
+ _val = MAX77651_REGULATOR_SBB1_SEL_DEC(_val); \
+ _val--; \
+ _val = MAX77651_REGULATOR_SBB1_SEL_ENC(_val); \
+ } while (0)
+
+#define MAX77650_REGULATOR_SBB1_SEL_INCR(_val) \
+ do { \
+ _val = MAX77651_REGULATOR_SBB1_SEL_DEC(_val); \
+ _val++; \
+ _val = MAX77651_REGULATOR_SBB1_SEL_ENC(_val); \
+ } while (0)
+
+static const int max77650_current_limit_table[] = {
+ 1000000, 866000, 707000, 500000,
+};
+
+static int max77650_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct max77650_regulator_desc *rdesc;
+ struct regmap *map;
+ int val, rv, en;
+
+ rdesc = rdev_get_drvdata(rdev);
+ map = rdev_get_regmap(rdev);
+
+ rv = regmap_read(map, rdesc->regB, &val);
+ if (rv)
+ return rv;
+
+ en = MAX77650_REGULATOR_EN_CTRL_BITS(val);
+
+ return en != MAX77650_REGULATOR_DISABLED;
+}
+
+static int max77650_regulator_enable(struct regulator_dev *rdev)
+{
+ struct max77650_regulator_desc *rdesc;
+ struct regmap *map;
+
+ rdesc = rdev_get_drvdata(rdev);
+ map = rdev_get_regmap(rdev);
+
+ return regmap_update_bits(map, rdesc->regB,
+ MAX77650_REGULATOR_EN_CTRL_MASK,
+ MAX77650_REGULATOR_ENABLED);
+}
+
+static int max77650_regulator_disable(struct regulator_dev *rdev)
+{
+ struct max77650_regulator_desc *rdesc;
+ struct regmap *map;
+
+ rdesc = rdev_get_drvdata(rdev);
+ map = rdev_get_regmap(rdev);
+
+ return regmap_update_bits(map, rdesc->regB,
+ MAX77650_REGULATOR_EN_CTRL_MASK,
+ MAX77650_REGULATOR_DISABLED);
+}
+
+static int max77650_regulator_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int sel)
+{
+ int rv = 0, curr, diff;
+ bool ascending;
+
+ /*
+ * If the regulator is disabled, we can program the desired
+ * voltage right away.
+ */
+ if (!max77650_regulator_is_enabled(rdev))
+ return regulator_set_voltage_sel_regmap(rdev, sel);
+
+ /*
+ * Otherwise we need to manually ramp the output voltage up/down
+ * one step at a time.
+ */
+
+ curr = regulator_get_voltage_sel_regmap(rdev);
+ if (curr < 0)
+ return curr;
+
+ diff = curr - sel;
+ if (diff == 0)
+ return 0; /* Already there. */
+ else if (diff > 0)
+ ascending = false;
+ else
+ ascending = true;
+
+ /*
+ * Make sure we'll get to the right voltage and break the loop even if
+ * the selector equals 0.
+ */
+ for (ascending ? curr++ : curr--;; ascending ? curr++ : curr--) {
+ rv = regulator_set_voltage_sel_regmap(rdev, curr);
+ if (rv)
+ return rv;
+
+ if (curr == sel)
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Special case: non-linear voltage table for max77651 SBB1 - software
+ * must ensure the voltage is ramped in 50mV increments.
+ */
+static int max77651_regulator_sbb1_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int sel)
+{
+ int rv = 0, curr, vcurr, vdest, vdiff;
+
+ /*
+ * If the regulator is disabled, we can program the desired
+ * voltage right away.
+ */
+ if (!max77650_regulator_is_enabled(rdev))
+ return regulator_set_voltage_sel_regmap(rdev, sel);
+
+ curr = regulator_get_voltage_sel_regmap(rdev);
+ if (curr < 0)
+ return curr;
+
+ if (curr == sel)
+ return 0; /* Already there. */
+
+ vcurr = max77651_sbb1_regulator_volt_table[curr];
+ vdest = max77651_sbb1_regulator_volt_table[sel];
+ vdiff = vcurr - vdest;
+
+ for (;;) {
+ if (vdiff > 0)
+ MAX77650_REGULATOR_SBB1_SEL_DECR(curr);
+ else
+ MAX77650_REGULATOR_SBB1_SEL_INCR(curr);
+
+ rv = regulator_set_voltage_sel_regmap(rdev, curr);
+ if (rv)
+ return rv;
+
+ if (curr == sel)
+ break;
+ };
+
+ return 0;
+}
+
+static int max77650_regulator_get_current_limit(struct regulator_dev *rdev)
+{
+ struct max77650_regulator_desc *rdesc;
+ struct regmap *map;
+ int val, rv, limit;
+
+ rdesc = rdev_get_drvdata(rdev);
+ map = rdev_get_regmap(rdev);
+
+ rv = regmap_read(map, rdesc->regA, &val);
+ if (rv)
+ return rv;
+
+ limit = MAX77650_REGULATOR_CURR_LIM_BITS(val);
+
+ return max77650_current_limit_table[limit];
+}
+
+static int max77650_regulator_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ struct max77650_regulator_desc *rdesc;
+ struct regmap *map;
+ int rv, i, limit;
+
+ rdesc = rdev_get_drvdata(rdev);
+ map = rdev_get_regmap(rdev);
+
+ for (i = 0; i < ARRAY_SIZE(max77650_current_limit_table); i++) {
+ limit = max77650_current_limit_table[i];
+
+ if (limit >= min_uA && limit <= max_uA) {
+ rv = regmap_update_bits(map, rdesc->regA,
+ MAX77650_REGULATOR_CURR_LIM_MASK,
+ MAX77650_REGULATOR_CURR_LIM_SHIFT(i));
+ if (rv)
+ return rv;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct regulator_ops max77650_regulator_LDO_ops = {
+ .is_enabled = max77650_regulator_is_enabled,
+ .enable = max77650_regulator_enable,
+ .disable = max77650_regulator_disable,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = max77650_regulator_set_voltage_sel,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+static const struct regulator_ops max77650_regulator_SBB_ops = {
+ .is_enabled = max77650_regulator_is_enabled,
+ .enable = max77650_regulator_enable,
+ .disable = max77650_regulator_disable,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = max77650_regulator_set_voltage_sel,
+ .get_current_limit = max77650_regulator_get_current_limit,
+ .set_current_limit = max77650_regulator_set_current_limit,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+/* Special case for max77651 SBB1 - non-linear voltage mapping. */
+static const struct regulator_ops max77651_SBB1_regulator_ops = {
+ .is_enabled = max77650_regulator_is_enabled,
+ .enable = max77650_regulator_enable,
+ .disable = max77650_regulator_disable,
+ .list_voltage = regulator_list_voltage_table,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = max77651_regulator_sbb1_set_voltage_sel,
+ .get_current_limit = max77650_regulator_get_current_limit,
+ .set_current_limit = max77650_regulator_set_current_limit,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+static struct max77650_regulator_desc max77650_LDO_desc = {
+ .desc = {
+ .name = "ldo",
+ .of_match = of_match_ptr("ldo"),
+ .regulators_node = of_match_ptr("regulators"),
+ .supply_name = "in-ldo",
+ .id = MAX77650_REGULATOR_ID_LDO,
+ .ops = &max77650_regulator_LDO_ops,
+ .min_uV = 1350000,
+ .uV_step = 12500,
+ .n_voltages = 128,
+ .vsel_mask = MAX77650_REGULATOR_V_LDO_MASK,
+ .vsel_reg = MAX77650_REG_CNFG_LDO_A,
+ .active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
+ .active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
+ .active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
+ .active_discharge_reg = MAX77650_REG_CNFG_LDO_B,
+ .enable_time = 100,
+ .type = REGULATOR_VOLTAGE,
+ },
+ .regA = MAX77650_REG_CNFG_LDO_A,
+ .regB = MAX77650_REG_CNFG_LDO_B,
+};
+
+static struct max77650_regulator_desc max77650_SBB0_desc = {
+ .desc = {
+ .name = "sbb0",
+ .of_match = of_match_ptr("sbb0"),
+ .regulators_node = of_match_ptr("regulators"),
+ .supply_name = "in-sbb0",
+ .id = MAX77650_REGULATOR_ID_SBB0,
+ .ops = &max77650_regulator_SBB_ops,
+ .min_uV = 800000,
+ .uV_step = 25000,
+ .n_voltages = 64,
+ .vsel_mask = MAX77650_REGULATOR_V_SBB_MASK,
+ .vsel_reg = MAX77650_REG_CNFG_SBB0_A,
+ .active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
+ .active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
+ .active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
+ .active_discharge_reg = MAX77650_REG_CNFG_SBB0_B,
+ .enable_time = 100,
+ .type = REGULATOR_VOLTAGE,
+ },
+ .regA = MAX77650_REG_CNFG_SBB0_A,
+ .regB = MAX77650_REG_CNFG_SBB0_B,
+};
+
+static struct max77650_regulator_desc max77650_SBB1_desc = {
+ .desc = {
+ .name = "sbb1",
+ .of_match = of_match_ptr("sbb1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .supply_name = "in-sbb1",
+ .id = MAX77650_REGULATOR_ID_SBB1,
+ .ops = &max77650_regulator_SBB_ops,
+ .min_uV = 800000,
+ .uV_step = 12500,
+ .n_voltages = 64,
+ .vsel_mask = MAX77650_REGULATOR_V_SBB_MASK,
+ .vsel_reg = MAX77650_REG_CNFG_SBB1_A,
+ .active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
+ .active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
+ .active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
+ .active_discharge_reg = MAX77650_REG_CNFG_SBB1_B,
+ .enable_time = 100,
+ .type = REGULATOR_VOLTAGE,
+ },
+ .regA = MAX77650_REG_CNFG_SBB1_A,
+ .regB = MAX77650_REG_CNFG_SBB1_B,
+};
+
+static struct max77650_regulator_desc max77651_SBB1_desc = {
+ .desc = {
+ .name = "sbb1",
+ .of_match = of_match_ptr("sbb1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .supply_name = "in-sbb1",
+ .id = MAX77650_REGULATOR_ID_SBB1,
+ .ops = &max77651_SBB1_regulator_ops,
+ .volt_table = max77651_sbb1_regulator_volt_table,
+ .n_voltages = ARRAY_SIZE(max77651_sbb1_regulator_volt_table),
+ .vsel_mask = MAX77650_REGULATOR_V_SBB_MASK,
+ .vsel_reg = MAX77650_REG_CNFG_SBB1_A,
+ .active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
+ .active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
+ .active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
+ .active_discharge_reg = MAX77650_REG_CNFG_SBB1_B,
+ .enable_time = 100,
+ .type = REGULATOR_VOLTAGE,
+ },
+ .regA = MAX77650_REG_CNFG_SBB1_A,
+ .regB = MAX77650_REG_CNFG_SBB1_B,
+};
+
+static struct max77650_regulator_desc max77650_SBB2_desc = {
+ .desc = {
+ .name = "sbb2",
+ .of_match = of_match_ptr("sbb2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .supply_name = "in-sbb0",
+ .id = MAX77650_REGULATOR_ID_SBB2,
+ .ops = &max77650_regulator_SBB_ops,
+ .min_uV = 800000,
+ .uV_step = 50000,
+ .n_voltages = 64,
+ .vsel_mask = MAX77650_REGULATOR_V_SBB_MASK,
+ .vsel_reg = MAX77650_REG_CNFG_SBB2_A,
+ .active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
+ .active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
+ .active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
+ .active_discharge_reg = MAX77650_REG_CNFG_SBB2_B,
+ .enable_time = 100,
+ .type = REGULATOR_VOLTAGE,
+ },
+ .regA = MAX77650_REG_CNFG_SBB2_A,
+ .regB = MAX77650_REG_CNFG_SBB2_B,
+};
+
+static struct max77650_regulator_desc max77651_SBB2_desc = {
+ .desc = {
+ .name = "sbb2",
+ .of_match = of_match_ptr("sbb2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .supply_name = "in-sbb0",
+ .id = MAX77650_REGULATOR_ID_SBB2,
+ .ops = &max77650_regulator_SBB_ops,
+ .min_uV = 2400000,
+ .uV_step = 50000,
+ .n_voltages = 64,
+ .vsel_mask = MAX77650_REGULATOR_V_SBB_MASK,
+ .vsel_reg = MAX77650_REG_CNFG_SBB2_A,
+ .active_discharge_off = MAX77650_REGULATOR_AD_DISABLED,
+ .active_discharge_on = MAX77650_REGULATOR_AD_ENABLED,
+ .active_discharge_mask = MAX77650_REGULATOR_AD_MASK,
+ .active_discharge_reg = MAX77650_REG_CNFG_SBB2_B,
+ .enable_time = 100,
+ .type = REGULATOR_VOLTAGE,
+ },
+ .regA = MAX77650_REG_CNFG_SBB2_A,
+ .regB = MAX77650_REG_CNFG_SBB2_B,
+};
+
+static int max77650_regulator_probe(struct platform_device *pdev)
+{
+ struct max77650_regulator_desc **rdescs;
+ struct max77650_regulator_desc *rdesc;
+ struct regulator_config config = { };
+ struct device *dev, *parent;
+ struct regulator_dev *rdev;
+ struct regmap *map;
+ unsigned int val;
+ int i, rv;
+
+ dev = &pdev->dev;
+ parent = dev->parent;
+
+ if (!dev->of_node)
+ dev->of_node = parent->of_node;
+
+ rdescs = devm_kcalloc(dev, MAX77650_REGULATOR_NUM_REGULATORS,
+ sizeof(*rdescs), GFP_KERNEL);
+ if (!rdescs)
+ return -ENOMEM;
+
+ map = dev_get_regmap(parent, NULL);
+ if (!map)
+ return -ENODEV;
+
+ rv = regmap_read(map, MAX77650_REG_CID, &val);
+ if (rv)
+ return rv;
+
+ rdescs[MAX77650_REGULATOR_ID_LDO] = &max77650_LDO_desc;
+ rdescs[MAX77650_REGULATOR_ID_SBB0] = &max77650_SBB0_desc;
+
+ switch (MAX77650_CID_BITS(val)) {
+ case MAX77650_CID_77650A:
+ case MAX77650_CID_77650C:
+ rdescs[MAX77650_REGULATOR_ID_SBB1] = &max77650_SBB1_desc;
+ rdescs[MAX77650_REGULATOR_ID_SBB2] = &max77650_SBB2_desc;
+ break;
+ case MAX77650_CID_77651A:
+ case MAX77650_CID_77651B:
+ rdescs[MAX77650_REGULATOR_ID_SBB1] = &max77651_SBB1_desc;
+ rdescs[MAX77650_REGULATOR_ID_SBB2] = &max77651_SBB2_desc;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ config.dev = parent;
+
+ for (i = 0; i < MAX77650_REGULATOR_NUM_REGULATORS; i++) {
+ rdesc = rdescs[i];
+ config.driver_data = rdesc;
+
+ rdev = devm_regulator_register(dev, &rdesc->desc, &config);
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
+ }
+
+ return 0;
+}
+
+static struct platform_driver max77650_regulator_driver = {
+ .driver = {
+ .name = "max77650-regulator",
+ },
+ .probe = max77650_regulator_probe,
+};
+module_platform_driver(max77650_regulator_driver);
+
+MODULE_DESCRIPTION("MAXIM 77650/77651 regulator driver");
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
+MODULE_LICENSE("GPL v2");
--
2.20.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox