[PATCH 3/3] Input: add generic gpio brownout driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



A brownout can be detected in several ways e.g. a deticated pin on the
soc, a external pmic or by another external hardware which informs the
host via a gpio line.

This patch adds the support for a generic gpio-based brownout
detection. Upon a brownout the host system gets informed and the driver
sends a keycode signal to the userspace. Per default this signal is
mapped to KEY_POWER, so the system will shoutdown.

Additional the driver supports releasing registered devices from their
drivers, see Documentation/devicetree/bindings/input/gpio-brownout.txt
for more details.

Signed-off-by: Marco Felsch <m.felsch@xxxxxxxxxxxxxx>
---
 .../bindings/input/gpio-brownout.txt          |  36 ++++
 drivers/input/misc/Kconfig                    |  12 ++
 drivers/input/misc/Makefile                   |   1 +
 drivers/input/misc/gpio-brownout.c            | 166 ++++++++++++++++++
 4 files changed, 215 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/gpio-brownout.txt
 create mode 100644 drivers/input/misc/gpio-brownout.c

diff --git a/Documentation/devicetree/bindings/input/gpio-brownout.txt b/Documentation/devicetree/bindings/input/gpio-brownout.txt
new file mode 100644
index 000000000000..55fbe2aa52a9
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/gpio-brownout.txt
@@ -0,0 +1,36 @@
+Device-Tree bindings for input/gpio_brownout.c driver
+
+Required properties:
+- compatible: Must be "gpio-brownout"
+- interrupt-parent: The phandle to the interrupt controller. For more details
+  see ../interrupt-controller/interrupts.txt.
+- interrupts: The interrupt line for a brownout detection. For more details
+  see ../interrupt-controller/interrupts.txt.
+
+Optional properties:
+- linux,code: Keycode to emit upon a brownout detection, default: KEY_POWER.
+- release-devices: A list of i2c or spi device phandles. All listed devices
+  will be released from their drivers in the order they listed upon a brownout
+  detection. This can be helpful to avoid a interrupt flood, because some
+  system designs power off all external devices immediately and keep the host
+  on for a certain time.
+
+Example:
+
+i2c3 {
+	temp_core: lm75@48 { };
+	temp_chassis: lm75@49 { };
+};
+
+spi1 {
+	ts: ad7879@1 { };
+};
+
+/ {
+	gpio_brownout_det {
+		compatible = "gpio-brownout";
+		interrupts-parent = <&gpio3>;
+		interrupts = <3 IRQ_TYPE_EDGE_LOW>:
+		release-devices = <&temp_core &ts>;
+	};
+};
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ca59a2be9bc5..6b49e681cca7 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -268,6 +268,18 @@ config INPUT_GPIO_BEEPER
 	  To compile this driver as a module, choose M here: the
 	  module will be called gpio-beeper.
 
+config INPUT_GPIO_BROWNOUT
+	tristate "Generic GPIO Brownout detection support"
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  Say Y here if you have a brownout signal connected to a GPIO pin
+	  and want to report a keycode signal on a brownout detection.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gpio-brownout.
+
 config INPUT_GPIO_DECODER
 	tristate "Polled GPIO Decoder Input driver"
 	depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9d0f9d1ff68f..8b872b5fc84a 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_INPUT_DRV2665_HAPTICS)	+= drv2665.o
 obj-$(CONFIG_INPUT_DRV2667_HAPTICS)	+= drv2667.o
 obj-$(CONFIG_INPUT_GP2A)		+= gp2ap002a00f.o
 obj-$(CONFIG_INPUT_GPIO_BEEPER)		+= gpio-beeper.o
+obj-$(CONFIG_INPUT_GPIO_BROWNOUT)	+= gpio-brownout.o
 obj-$(CONFIG_INPUT_GPIO_DECODER)	+= gpio_decoder.o
 obj-$(CONFIG_INPUT_HISI_POWERKEY)	+= hisi_powerkey.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
diff --git a/drivers/input/misc/gpio-brownout.c b/drivers/input/misc/gpio-brownout.c
new file mode 100644
index 000000000000..23992b9e2814
--- /dev/null
+++ b/drivers/input/misc/gpio-brownout.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * gpio-brownout.c - Generic power fail driver
+ *
+ * Copyright (C) 2018 Pengutronix, Marco Felsch <kernel@xxxxxxxxxxxxxx>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#define GPIO_BROWNOUT_MOD_NAME "gpio-brownout"
+
+struct gpio_brownout_device {
+	struct list_head  list;
+	struct device *dev;
+};
+
+struct gpio_brownout {
+	struct device *dev;
+	struct input_dev *idev;
+	unsigned short kcode;
+	struct list_head devices;
+};
+
+static irqreturn_t gpio_brownout_isr(int irq, void *dev_id)
+{
+	struct gpio_brownout *gb = dev_id;
+	struct input_dev *idev = gb->idev;
+	struct gpio_brownout_device *bout_dev, *tmp;
+
+	/* first inform userspace */
+	input_report_key(idev, gb->kcode, 1);
+	input_sync(idev);
+
+	/* now unregister registered drivers */
+	list_for_each_entry_safe(bout_dev, tmp, &gb->devices, list) {
+		device_release_driver(bout_dev->dev);
+		list_del(&bout_dev->list);
+	}
+
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_OF
+static int gpio_brownout_probe_dt(struct gpio_brownout *gb)
+{
+	struct device_node *np = gb->dev->of_node;
+	struct of_phandle_iterator it;
+	unsigned int kcode;
+	int ret;
+
+	/* all dt-properties are optional */
+	of_property_read_u32(np, "linux,code", &kcode);
+	gb->kcode = kcode;
+
+	/*
+	 * Register all devices which should be unbinded upon a brownout
+	 * detection. At the moment only i2c and spi devices are supported
+	 */
+	of_for_each_phandle(&it, ret, np, "release-devices", NULL, 0) {
+		struct gpio_brownout_device *elem;
+		struct i2c_client *i2c_c;
+		struct spi_device *spi_c;
+
+		i2c_c = of_find_i2c_device_by_node(it.node);
+		spi_c = of_find_spi_device_by_node(it.node);
+
+		if (!i2c_c && !spi_c)
+			return -EPROBE_DEFER;
+		else if (i2c_c && spi_c)
+			return -EINVAL;
+
+		elem = devm_kzalloc(gb->dev, sizeof(*elem), GFP_KERNEL);
+		if (!elem)
+			return -ENOMEM;
+
+		elem->dev = i2c_c ? &i2c_c->dev : &spi_c->dev;
+
+		INIT_LIST_HEAD(&elem->list);
+		list_add_tail(&elem->list, &gb->devices);
+	}
+
+	return 0;
+}
+#endif
+
+static int gpio_brownout_probe(struct platform_device *pdev)
+{
+	struct gpio_brownout *gb;
+	struct input_dev *idev;
+	int ret, irq;
+
+	gb = devm_kzalloc(&pdev->dev, sizeof(*gb), GFP_KERNEL);
+	if (!gb)
+		return -ENOMEM;
+
+	idev = devm_input_allocate_device(&pdev->dev);
+	if (!idev)
+		return -ENOMEM;
+
+	gb->dev = &pdev->dev;
+	gb->idev = idev;
+	INIT_LIST_HEAD(&gb->devices);
+
+	if (IS_ENABLED(CONFIG_OF)) {
+		ret = gpio_brownout_probe_dt(gb);
+		if (ret) {
+			dev_err(&pdev->dev, "probe_dt failed: %d\n", ret);
+			return ret;
+		}
+	}
+
+	idev->name = pdev->name;
+	gb->kcode = gb->kcode == KEY_RESERVED ? KEY_POWER : gb->kcode;
+
+	input_set_capability(idev, EV_KEY, gb->kcode);
+
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+					gpio_brownout_isr, IRQF_ONESHOT,
+					GPIO_BROWNOUT_MOD_NAME, gb);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "IRQ request failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = input_register_device(idev);
+	if (ret) {
+		dev_err(&pdev->dev, "Input register failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id gpio_brownout_of_match[] = {
+	{ .compatible = GPIO_BROWNOUT_MOD_NAME, },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, arm_gpio_brownout_of_match);
+#endif
+
+static struct platform_driver gpio_brownout_driver = {
+	.driver = {
+		.name = GPIO_BROWNOUT_MOD_NAME,
+		.of_match_table = of_match_ptr(gpio_brownout_of_match)
+	},
+	.probe = gpio_brownout_probe,
+};
+
+module_platform_driver(gpio_brownout_driver);
+
+MODULE_AUTHOR("Marco Felsch <kernel@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("GPIO Brownout Detection");
+MODULE_LICENSE("GPL v2");
-- 
2.19.0




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux