Max77620 ONOFF
diff mbox series

Message ID CAFUUQHMwfg1qs3mGvr3+q=kwh=HqY60RTfueG9KfWJH7=THZMg@mail.gmail.com
State New
Headers show
Series
  • Max77620 ONOFF
Related show

Commit Message

Billy Laws March 10, 2019, 8:55 p.m. UTC
I'm currently working of a driver for the onoff block of the max77620
for the nintendo switches powerkey basing my work off of
https://datasheets.maximintegrated.com/en/ds/MAX77714.pdf which seems
to be a similar chip.
However I don't seem to ever get the rising interrupt, just the
falling one and i get that only once. Another issue i had is that the
mask register is at a different offset to the top mask register than
the status is to the top status register, i had to hack a mask_offset
property to regmap-irq to fix this.

If anyone here with access to the real datasheet can confirm if my
code is correct that would be very helpful :)

Attached below is my diff:

--------------

               .resources = gpio_resources,
               .num_resources = ARRAY_SIZE(gpio_resources),
@@ -183,12 +196,19 @@ static int max77620_irq_global_mask(void *irq_drv_data)
{
       struct max77620_chip *chip = irq_drv_data;
       int ret;
+       unsigned int val;

       ret = regmap_update_bits(chip->rmap, MAX77620_REG_INTENLBT,
                                MAX77620_GLBLM_MASK, MAX77620_GLBLM_MASK);
       if (ret < 0)
               dev_err(chip->dev, "Failed to set GLBLM: %d\n", ret);

+       regmap_read(chip->rmap, MAX77620_REG_IRQTOP, &val);
+       dev_err(chip->dev, "1top status reg %x", val);
+       regmap_read(chip->rmap, MAX77620_REG_ONOFFIRQ, &val);
+       dev_err(chip->dev, "status reg %x", val);
+       regmap_read(chip->rmap, MAX77620_REG_ONOFFIRQM, &val);
+       dev_err(chip->dev, "mask reg %x", val);
       return ret;
}

@@ -196,12 +216,19 @@ static int max77620_irq_global_unmask(void *irq_drv_data)
{
       struct max77620_chip *chip = irq_drv_data;
       int ret;
+       unsigned int val;

       ret = regmap_update_bits(chip->rmap, MAX77620_REG_INTENLBT,
                                MAX77620_GLBLM_MASK, 0);
       if (ret < 0)
               dev_err(chip->dev, "Failed to reset GLBLM: %d\n", ret);

+       regmap_read(chip->rmap, MAX77620_REG_IRQTOP, &val);
+       dev_err(chip->dev, "1top status reg %x", val);
+       regmap_read(chip->rmap, MAX77620_REG_ONOFFIRQ, &val);
+       dev_err(chip->dev, "1status reg %x", val);
+       regmap_read(chip->rmap, MAX77620_REG_ONOFFIRQM, &val);
+       dev_err(chip->dev, "1mask reg %x", val);
       return ret;
}

@@ -209,7 +236,7 @@ static struct regmap_irq_chip max77620_top_irq_chip = {
       .name = "max77620-top",
       .irqs = max77620_top_irqs,
       .num_irqs = ARRAY_SIZE(max77620_top_irqs),
-       .num_regs = 2,
+       .num_regs = 7,
       .status_base = MAX77620_REG_IRQTOP,
       .mask_base = MAX77620_REG_IRQTOPM,
       .handle_pre_irq = max77620_irq_global_mask,
@@ -431,6 +458,7 @@ static int max77620_probe(struct i2c_client *client,
       const struct mfd_cell *mfd_cells;
       int n_mfd_cells;
       int ret;
+       unsigned int val;

       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
       if (!chip)
@@ -478,7 +506,7 @@ static int max77620_probe(struct i2c_client *client,
               dev_err(chip->dev, "Failed to add regmap irq: %d\n", ret);
               return ret;
       }
-
+
       ret = max77620_initialise_fps(chip);
       if (ret < 0)
               return ret;
@@ -491,6 +519,34 @@ static int max77620_probe(struct i2c_client *client,
               return ret;
       }

+       regmap_read(chip->rmap, MAX77620_REG_IRQTOP, &val);
+       dev_err(chip->dev, "2top status reg %x", val);
+       regmap_read(chip->rmap, MAX77620_REG_ONOFFIRQ, &val);
+       dev_err(chip->dev, "1status reg %x", val);
+       regmap_read(chip->rmap, MAX77620_REG_ONOFFIRQM, &val);
+       dev_err(chip->dev, "1mask reg %x", val);
+
+       ret = regmap_update_bits(chip->rmap, MAX77620_REG_INTENLBT,
+                                MAX77620_GLBLM_MASK, MAX77620_GLBLM_MASK);
+       if (ret < 0)
+               dev_err(chip->dev, "Failed to set GLBLM: %d\n", ret);
+
+       udelay(1000);
+       ret = regmap_update_bits(chip->rmap, MAX77620_REG_INTENLBT,
+                                MAX77620_GLBLM_MASK, 0);
+       if (ret < 0)
+               dev_err(chip->dev, "Failed to set GLBLM: %d\n", ret);
+
+       regmap_read(chip->rmap, MAX77620_REG_IRQTOP, &val);
+       dev_err(chip->dev, "2top status reg %x", val);
+       regmap_read(chip->rmap, MAX77620_REG_ONOFFIRQ, &val);
+       dev_err(chip->dev, "1status reg %x", val);
+       regmap_read(chip->rmap, MAX77620_REG_ONOFFIRQM, &val);
+       dev_err(chip->dev, "1mask reg %x", val);
+
+
+
+
       return 0;
}

Patch
diff mbox series

diff --git a/arch/arm64/boot/dts/nvidia/tegra210-nintendo-switch.dts
b/arch/arm64/boot/dts/nvidia/tegra210-nintendo-switch.dts
index bfe4f4b1e091..19d189b58d91 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-nintendo-switch.dts
+++ b/arch/arm64/boot/dts/nvidia/tegra210-nintendo-switch.dts
@@ -1656,7 +1656,7 @@ 
               max77620: max77620@3c {
                       compatible = "maxim,max77620";
                       reg = <0x3c>;
-                       interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_LOW>;

                       #interrupt-cells = <2>;
                       interrupt-controller;
@@ -1945,8 +1945,8 @@ 
               max77621_cpu: pmic@1b {
                       compatible = "maxim,max77621";
                       reg = <0x1b>;
-                        interrupt-parent = <&gpio>;
-                        interrupts = <TEGRA_GPIO(K, 6) IRQ_TYPE_LEVEL_LOW>;
+                       interrupt-parent = <&gpio>;
+                       interrupts = <TEGRA_GPIO(K, 6) IRQ_TYPE_LEVEL_LOW>;
                       regulator-name = "PPVAR_CPU";
                       // smaug: 800000..1231250
                       regulator-min-microvolt = <800000>;
@@ -1969,8 +1969,8 @@ 
               max77621_gpu: pmic@1c {
                       compatible = "maxim,max77621";
                       reg = <0x1c>;
-                        interrupt-parent = <&gpio>;
-                        interrupts = <TEGRA_GPIO(K, 7) IRQ_TYPE_LEVEL_LOW>;
+                       interrupt-parent = <&gpio>;
+                       interrupts = <TEGRA_GPIO(K, 7) IRQ_TYPE_LEVEL_LOW>;
                       regulator-name = "PPVAR_GPU";
                       regulator-min-microvolt = <840000>;
                       regulator-max-microvolt = <1150000>;
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 330c1f7e9665..45f61660e1b4 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -240,7 +240,10 @@  static void regmap_irq_enable(struct irq_data *data)
       if (d->chip->clear_on_unmask)
               d->clear_status = true;

-       d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~mask;
+       if (!irq_data->mask_offset)
+               d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~mask;
+       else
+               d->mask_buf[irq_data->mask_offset / map->reg_stride] &= ~mask;
}

static void regmap_irq_disable(struct irq_data *data)
@@ -249,7 +252,10 @@  static void regmap_irq_disable(struct irq_data *data)
       struct regmap *map = d->map;
       const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);

-       d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
+       if (!irq_data->mask_offset)
+               d->mask_buf[irq_data->reg_offset / map->reg_stride] |=
irq_data->mask;
+       else
+               d->mask_buf[irq_data->mask_offset / map->reg_stride]
|= irq_data->mask;
}

static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
me@archlinux  ~/Development/tegra/linux   switch-5.0 ●✚  git diff
HEAD drivers/
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 330c1f7e9665..45f61660e1b4 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -240,7 +240,10 @@  static void regmap_irq_enable(struct irq_data *data)
       if (d->chip->clear_on_unmask)
               d->clear_status = true;

-       d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~mask;
+       if (!irq_data->mask_offset)
+               d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~mask;
+       else
+               d->mask_buf[irq_data->mask_offset / map->reg_stride] &= ~mask;
}

static void regmap_irq_disable(struct irq_data *data)
@@ -249,7 +252,10 @@  static void regmap_irq_disable(struct irq_data *data)
       struct regmap *map = d->map;
       const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);

-       d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
+       if (!irq_data->mask_offset)
+               d->mask_buf[irq_data->reg_offset / map->reg_stride] |=
irq_data->mask;
+       else
+               d->mask_buf[irq_data->mask_offset / map->reg_stride]
|= irq_data->mask;
}

static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
@@ -585,8 +591,10 @@  int regmap_add_irq_chip(struct regmap *map, int
irq, int irq_flags,
       mutex_init(&d->lock);

       for (i = 0; i < chip->num_irqs; i++)
-               d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride]
-                       |= chip->irqs[i].mask;
+               if (!chip->irqs[i].mask_offset)
+                       d->mask_buf_def[chip->irqs[i].reg_offset /
map->reg_stride] |= chip->irqs[i].mask;
+               else
+                       d->mask_buf_def[chip->irqs[i].mask_offset /
map->reg_stride] |= chip->irqs[i].mask;

       /* Mask all the interrupts by default */
       for (i = 0; i < chip->num_regs; i++) {
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ca59a2be9bc5..1cac5eb0aad1 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -191,6 +191,17 @@  config INPUT_MAX77693_HAPTIC
         To compile this driver as module, choose M here: the
         module will be called max77693-haptic.

+config INPUT_MAX77620_ONOFF
+       tristate "MAX77620 ONOFF support"
+       depends on MFD_MAX77620
+       help
+         Support the ONOFF block of MAX77620 PMICs as an input device
+         reporting power button status.
+
+         To compile this driver as a module, choose M here: the module
+         will be called max77620_onoff.
+
+
config INPUT_MAX8925_ONKEY
       tristate "MAX8925 ONKEY support"
       depends on MFD_MAX8925
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9d0f9d1ff68f..47f0eaf8b370 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -44,6 +44,7 @@  obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)    += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9)              += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP)          += m68kspkr.o
obj-$(CONFIG_INPUT_MAX77693_HAPTIC)    += max77693-haptic.o
+obj-$(CONFIG_INPUT_MAX77620_ONOFF)     += max77620-onoff.o
obj-$(CONFIG_INPUT_MAX8925_ONKEY)      += max8925_onkey.o
obj-$(CONFIG_INPUT_MAX8997_HAPTIC)     += max8997_haptic.o
obj-$(CONFIG_INPUT_MC13783_PWRBUTTON)  += mc13783-pwrbutton.o
diff --git a/drivers/input/misc/max77620-onoff.c
b/drivers/input/misc/max77620-onoff.c
new file mode 100644
index 000000000000..932536bc5680
--- /dev/null
+++ b/drivers/input/misc/max77620-onoff.c
@@ -0,0 +1,149 @@ 
+/**re--
+ * MAX77620 ONKEY driver
+ *ore--
+ * Copyright (C) 2009 Marvell International Ltd.
+ *      Haojian Zhuang <haojian.zhuang@marvell.com>
+ *ore--
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *ore--
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *ore--
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */re--
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/mfd/max77620.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#define POWERKEY_PRESSED       (1 << 2)        /* 0/1 -- up/down */
+
+struct max77620_onoff_info {
+       struct input_dev *input;
+       struct regmap *regmap;
+       unsigned int code;
+};
+
+/*
+ * MAX77620 gives us an interrupt when ONKEY is pressed or released.
+ * max77620_set_bits() operates I2C bus and may sleep. So implement
+ * it in thread IRQ handler.
+ */
+static irqreturn_t max77620_onoff_handler(int irq, void *data)
+{
+       struct max77620_onoff_info *info = data;
+       u32 state;
+
+       regmap_read(info->regmap, MAX77620_REG_ONOFFSTAT, &state);
+
+       input_report_key(info->input, info->code, state & POWERKEY_PRESSED);
+       input_sync(info->input);
+
+       return IRQ_HANDLED;
+}
+
+static int max77620_onoff_probe(struct platform_device *pdev)
+{
+       struct max77620_onoff_info *info;
+       int irq[2], error;
+
+       irq[0] = platform_get_irq(pdev, 0);
+       if (irq[0] < 0) {
+               dev_err(&pdev->dev, "No IRQ resource!\n");
+               return -EINVAL;
+       }
+
+       irq[1] = platform_get_irq(pdev, 1);
+       if (irq[1] < 0) {
+               dev_err(&pdev->dev, "No IRQ resource!\n");
+               return -EINVAL;
+       }
+
+       info = devm_kzalloc(&pdev->dev, sizeof(struct max77620_onoff_info),
+                           GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!info->regmap) {
+               dev_err(&pdev->dev, "Failed to get parent regmap\n");
+               return -ENODEV;
+       }
+
+       error = device_property_read_u32(&pdev->dev, "linux,code", &info->code);
+       if (error)
+               info->code = KEY_POWER;
+
+       info->input = devm_input_allocate_device(&pdev->dev);
+       if (!info->input)
+               return -ENOMEM;
+
+       info->input->name = "max77620_onoff";
+       info->input->phys = "max77620_onoff/input0";
+       info->input->id.bustype = BUS_I2C;
+       info->input->dev.parent = &pdev->dev;
+       input_set_capability(info->input, EV_KEY, info->code);
+
+       error = devm_request_threaded_irq(&pdev->dev, irq[0], NULL,
+                                         max77620_onoff_handler, IRQF_ONESHOT,
+                                         "en0-up", info);
+       if (error < 0) {
+               dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
+                       irq[0], error);
+               return error;
+       }
+
+       error = devm_request_threaded_irq(&pdev->dev, irq[1], NULL,
+                                         max77620_onoff_handler, IRQF_ONESHOT,
+                                         "en0-down", info);
+       if (error < 0) {
+               dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
+                       irq[1], error);
+               return error;
+       }
+
+       error = input_register_device(info->input);
+       if (error) {
+               dev_err(&pdev->dev, "Can't register input device: %d\n", error);
+               return error;
+       }
+
+       platform_set_drvdata(pdev, info);
+//     device_init_wakeup(&pdev->dev, 1);
+
+       return 0;
+}
+
+static int max77620_onoff_remove(struct platform_device *pdev)
+{
+       device_init_wakeup(&pdev->dev, 0);
+
+       return 0;
+}
+
+static struct platform_driver max77620_onoff_driver = {
+       .driver         = {
+               .name   = "max77620-onoff",
+       },
+       .probe          = max77620_onoff_probe,
+       .remove         = max77620_onoff_remove,
+};
+module_platform_driver(max77620_onoff_driver);
+
+MODULE_DESCRIPTION("Maxim MAX77620 ONOFF driver");
+MODULE_AUTHOR("Billy Laws <blaws05@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c
index d8ddd1a6f304..ef908af95a80 100644
--- a/drivers/mfd/max77620.c
+++ b/drivers/mfd/max77620.c
@@ -36,6 +36,12 @@ 
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/delay.h>
+
+static const struct resource onoff_resources[] = {
+       DEFINE_RES_IRQ(MAX77620_IRQ_ONOFF_EN0_R),
+       DEFINE_RES_IRQ(MAX77620_IRQ_ONOFF_EN0_F),
+};

static const struct resource gpio_resources[] = {
       DEFINE_RES_IRQ(MAX77620_IRQ_TOP_GPIO),
@@ -65,14 +71,21 @@  static const struct regmap_irq max77620_top_irqs[] = {
       REGMAP_IRQ_REG(MAX77620_IRQ_LBT_MBATLOW, 1, MAX77620_IRQ_LBM_MASK),
       REGMAP_IRQ_REG(MAX77620_IRQ_LBT_TJALRM1, 1, MAX77620_IRQ_TJALRM1_MASK),
       REGMAP_IRQ_REG(MAX77620_IRQ_LBT_TJALRM2, 1, MAX77620_IRQ_TJALRM2_MASK),
+       REGMAP_IRQ_REG2(MAX77620_IRQ_ONOFF_EN0_R, 6, 5,
MAX77620_IRQ_ONOFF_EN0_R_MASK),
+       REGMAP_IRQ_REG2(MAX77620_IRQ_ONOFF_EN0_F, 6, 5,
MAX77620_IRQ_ONOFF_EN0_F_MASK),
};

+
static const struct mfd_cell max77620_children[] = {
       { .name = "max77620-pinctrl", },
       { .name = "max77620-clock", },
       { .name = "max77620-pmic", },
       { .name = "max77620-watchdog", },
       {
+               .name = "max77620-onoff",
+               .resources = onoff_resources,
+               .num_resources = ARRAY_SIZE(onoff_resources),
+       }, {
               .name = "max77620-gpio",