diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 94a753fcb64f..3dbf6df9bce6 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -282,6 +282,13 @@ 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. + 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 415fc4e2918b..e8f9478f726e 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX7360_ROTARY) += max7360-rotary.o obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.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..b842a87e4275 --- /dev/null +++ b/drivers/input/misc/max77620-onoff.c @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Billy Laws + * Copyright (C) 2020-2022 CTCaer + * + * Author: + * Billy Laws + * CTCaer + * + * Description: + * ONOFF driver for MAXIM 77620 charger/power-supply. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ONOFF_IRQF 2 +#define ONOFF_IRQR 3 + +/* MAX77620 IRQ ONOFF interrupts */ +#define MAX77620_IRQ_ONOFF_MRWRN 0 +#define MAX77620_IRQ_ONOFF_EN0_1SEC 1 +#define MAX77620_IRQ_ONOFF_EN0_F 2 +#define MAX77620_IRQ_ONOFF_EN0_R 3 +#define MAX77620_IRQ_ONOFF_LID_F 4 +#define MAX77620_IRQ_ONOFF_LID_R 5 +#define MAX77620_IRQ_ONOFF_ACOK_F 6 +#define MAX77620_IRQ_ONOFF_ACOK_R 7 + +struct max77620_onoff { + struct regmap *rmap; + struct input_dev *input; + int irq_f, irq_r; + unsigned int code; + bool suspended; +}; + +static const struct regmap_irq max77620_onoff_irqs[] = { + REGMAP_IRQ_REG(MAX77620_IRQ_ONOFF_MRWRN, 0, MAX77620_IRQ_ONOFF_MRWRN_MASK), + REGMAP_IRQ_REG(MAX77620_IRQ_ONOFF_EN0_1SEC, 0, MAX77620_IRQ_ONOFF_EN0_1SEC_MASK), + REGMAP_IRQ_REG(MAX77620_IRQ_ONOFF_EN0_F, 0, MAX77620_IRQ_ONOFF_EN0_F_MASK), + REGMAP_IRQ_REG(MAX77620_IRQ_ONOFF_EN0_R, 0, MAX77620_IRQ_ONOFF_EN0_R_MASK), + REGMAP_IRQ_REG(MAX77620_IRQ_ONOFF_LID_F, 0, MAX77620_IRQ_ONOFF_LID_F_MASK), + REGMAP_IRQ_REG(MAX77620_IRQ_ONOFF_LID_R, 0, MAX77620_IRQ_ONOFF_LID_R_MASK), + REGMAP_IRQ_REG(MAX77620_IRQ_ONOFF_ACOK_F, 0, MAX77620_IRQ_ONOFF_ACOK_F_MASK), + REGMAP_IRQ_REG(MAX77620_IRQ_ONOFF_ACOK_R, 0, MAX77620_IRQ_ONOFF_ACOK_R_MASK), +}; + +static struct regmap_irq_chip max77620_onoff_irq_chip = { + .name = "max77620-onoff", + .irqs = max77620_onoff_irqs, + .num_irqs = ARRAY_SIZE(max77620_onoff_irqs), + .num_regs = 1, + .irq_reg_stride = 1, + .status_base = MAX77620_REG_ONOFFIRQ, + .mask_base = MAX77620_REG_ONOFFIRQM, +}; + +static irqreturn_t max77620_onoff_handler(int irq, void *data) +{ + struct max77620_onoff *onoff = data; + bool pressed = irq == onoff->irq_r; + + /* Skip release report in suspended state */ + if (onoff->suspended && !pressed) + return IRQ_HANDLED; + + input_report_key(onoff->input, onoff->code, pressed); + input_sync(onoff->input); + + return IRQ_HANDLED; +} + +static int max77620_onoff_probe(struct platform_device *pdev) +{ + struct max77620_onoff *onoff; + struct device *dev, *parent; + struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct device_node *np; + unsigned int code; + int irq, ret; + + dev = &pdev->dev; + parent = dev->parent; + + onoff = devm_kzalloc(dev, sizeof(*onoff), GFP_KERNEL); + if (!onoff) + return -ENOMEM; + + onoff->rmap = chip->rmap; + + ret = regmap_read(onoff->rmap, MAX77620_REG_ONOFFIRQ, &code); + if (ret < 0) + dev_err(dev, "Failed to clear onoff irq: %d\n", ret); + + np = of_get_child_by_name(pdev->dev.parent->of_node, "onoff"); + if (np && !of_device_is_available(np)) + np = NULL; + + if (np) + ret = of_property_read_u32(np, "maxim,onoff-keycode", + &code); + if (!np || ret < 0) + onoff->code = KEY_POWER; + else + onoff->code = code; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + onoff->input = devm_input_allocate_device(dev); + if (!onoff->input) + return -ENOMEM; + + onoff->input->name = "max77620_onoff"; + onoff->input->phys = "max77620_onoff/input0"; + onoff->input->id.bustype = BUS_I2C; + input_set_capability(onoff->input, EV_KEY, onoff->code); + + ret = devm_regmap_add_irq_chip(&pdev->dev, onoff->rmap, irq, + IRQF_ONESHOT, -1, + &max77620_onoff_irq_chip, + &chip->onoff_irq_data); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add onoff irq_chip %d\n", ret); + goto fail; + } + + onoff->irq_r = regmap_irq_get_virq(chip->onoff_irq_data, + MAX77620_IRQ_ONOFF_EN0_R); + onoff->irq_f = regmap_irq_get_virq(chip->onoff_irq_data, + MAX77620_IRQ_ONOFF_EN0_F); + + ret = devm_request_any_context_irq(dev, onoff->irq_r, + max77620_onoff_handler, + IRQF_ONESHOT, "en0-down", onoff); + if (ret < 0) + goto fail; + + ret = devm_request_any_context_irq(dev, onoff->irq_f, + max77620_onoff_handler, + IRQF_ONESHOT, "en0-up", onoff); + if (ret < 0) + goto fail; + + platform_set_drvdata(pdev, onoff); + input_set_drvdata(onoff->input, onoff); + device_init_wakeup(dev, 1); + + ret = input_register_device(onoff->input); + +fail: + return ret; +} + +#ifdef CONFIG_PM_SLEEP + +static int max77620_onoff_suspend(struct device *dev) +{ + struct max77620_onoff *onoff = dev_get_drvdata(dev); + + onoff->suspended = true; + + if (device_may_wakeup(dev)) + return enable_irq_wake(onoff->irq_f); + + return 0; +} + +static int max77620_onoff_resume(struct device *dev) +{ + struct max77620_onoff *onoff = dev_get_drvdata(dev); + unsigned int val, ret; + + /* Clear ON/OFF interrupts */ + ret = regmap_read(onoff->rmap, MAX77620_REG_ONOFFIRQ, &val); + if (ret < 0) + dev_err(dev, "Failed to clear onoff irq: %d\n", ret); + + onoff->suspended = false; + + /* If ON/OFF was pressed, report it */ + if (val & MAX77620_IRQ_ONOFF_EN0_R_MASK) { + input_report_key(onoff->input, onoff->code, 1); + input_sync(onoff->input); + } + + /* Unconditionally release after resume */ + input_report_key(onoff->input, onoff->code, 0); + input_sync(onoff->input); + + if (device_may_wakeup(dev)) + return disable_irq_wake(onoff->irq_f); + + return 0; +} + +static const struct dev_pm_ops max77620_onoff_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(max77620_onoff_suspend, + max77620_onoff_resume) +}; + +#endif /* CONFIG_PM_SLEEP */ + +static struct platform_driver max77620_onoff_driver = { + .driver = { + .name = "max77620-onoff", +#ifdef CONFIG_PM_SLEEP + .pm = &max77620_onoff_pm_ops, +#endif + }, + .probe = max77620_onoff_probe, +}; + +static int __init max77620_onoff_init(void) +{ + return platform_driver_register(&max77620_onoff_driver); +} +subsys_initcall(max77620_onoff_init); + +static void __exit max77620_onoff_exit(void) +{ + platform_driver_unregister(&max77620_onoff_driver); +} +module_exit(max77620_onoff_exit); + +MODULE_DESCRIPTION("MAXIM 77620 ONOFF driver"); +MODULE_AUTHOR("Billy Laws "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c index 3af2974b3023..02d385422244 100644 --- a/drivers/mfd/max77620.c +++ b/drivers/mfd/max77620.c @@ -53,6 +53,10 @@ static const struct resource thermal_resources[] = { DEFINE_RES_IRQ(MAX77620_IRQ_LBT_TJALRM2), }; +static const struct resource onoff_resources[] = { + DEFINE_RES_IRQ(MAX77620_IRQ_TOP_ONOFF), +}; + static const struct regmap_irq max77620_top_irqs[] = { REGMAP_IRQ_REG(MAX77620_IRQ_TOP_GLBL, 0, MAX77620_IRQ_TOP_GLBL_MASK), REGMAP_IRQ_REG(MAX77620_IRQ_TOP_SD, 0, MAX77620_IRQ_TOP_SD_MASK), @@ -87,6 +91,10 @@ static const struct mfd_cell max77620_children[] = { .name = "max77620-thermal", .resources = thermal_resources, .num_resources = ARRAY_SIZE(thermal_resources), + }, { + .name = "max77620-onoff", + .resources = onoff_resources, + .num_resources = ARRAY_SIZE(onoff_resources), }, }; diff --git a/include/linux/mfd/max77620.h b/include/linux/mfd/max77620.h index f552ef5b1100..607879512295 100644 --- a/include/linux/mfd/max77620.h +++ b/include/linux/mfd/max77620.h @@ -280,6 +280,15 @@ #define MAX77620_CNFGBBC_RESISTOR_MASK 0xC0 #define MAX77620_CNFGBBC_RESISTOR_SHIFT 6 +#define MAX77620_IRQ_ONOFF_MRWRN_MASK BIT(0) +#define MAX77620_IRQ_ONOFF_EN0_1SEC_MASK BIT(1) +#define MAX77620_IRQ_ONOFF_EN0_F_MASK BIT(2) +#define MAX77620_IRQ_ONOFF_EN0_R_MASK BIT(3) +#define MAX77620_IRQ_ONOFF_LID_F_MASK BIT(4) +#define MAX77620_IRQ_ONOFF_LID_R_MASK BIT(5) +#define MAX77620_IRQ_ONOFF_ACOK_F_MASK BIT(6) +#define MAX77620_IRQ_ONOFF_ACOK_R_MASK BIT(7) + #define MAX77620_FPS_COUNT 3 /* Interrupts */ @@ -340,6 +349,7 @@ struct max77620_chip { struct regmap_irq_chip_data *top_irq_data; struct regmap_irq_chip_data *gpio_irq_data; + struct regmap_irq_chip_data *onoff_irq_data; }; #endif /* _MFD_MAX77620_H_ */