aboutsummaryrefslogtreecommitdiff
path: root/target/linux/bcm27xx/patches-6.1/950-0925-Input-Add-raspberrypi-button-firmware-driver.patch
blob: 76a9261a68b464136d4f72b39b0748ca456fcccd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
From 7c0d40384b0648030d5202114d90239b8db7d4e0 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Wed, 2 Aug 2023 11:30:48 +0100
Subject: [PATCH] Input: Add raspberrypi-button firmware driver

Raspberry Pi 5s have a power/suspend button that is only accessible to
the firmware. Add a driver to read it and generate key events.

Signed-off-by: Phil Elwell <phil@raspberrypi.com>
---
 drivers/input/misc/Kconfig                 |  10 ++
 drivers/input/misc/Makefile                |   1 +
 drivers/input/misc/raspberrypi-button.c    | 138 +++++++++++++++++++++
 include/soc/bcm2835/raspberrypi-firmware.h |   1 +
 4 files changed, 150 insertions(+)
 create mode 100644 drivers/input/misc/raspberrypi-button.c

--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -918,6 +918,16 @@ config INPUT_RT5120_PWRKEY
 	  To compile this driver as a module, choose M here. the module will
 	  be called rt5120-pwrkey.
 
+config INPUT_RASPBERRYPI_BUTTON
+	tristate "Raspberry Pi button support"
+	depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
+	help
+	  This enables support for firmware-controlled buttons on Raspberry
+	  Pi devices.
+
+	  To compile this driver as a module, choose M here. the module will
+	  be called raspberrypi-button.
+
 config INPUT_STPMIC1_ONKEY
 	tristate "STPMIC1 PMIC Onkey support"
 	depends on MFD_STPMIC1
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON)	+=
 obj-$(CONFIG_INPUT_RB532_BUTTON)	+= rb532_button.o
 obj-$(CONFIG_INPUT_REGULATOR_HAPTIC)	+= regulator-haptic.o
 obj-$(CONFIG_INPUT_RETU_PWRBUTTON)	+= retu-pwrbutton.o
+obj-$(CONFIG_INPUT_RASPBERRYPI_BUTTON)	+= raspberrypi-button.o
 obj-$(CONFIG_INPUT_RT5120_PWRKEY)	+= rt5120-pwrkey.o
 obj-$(CONFIG_INPUT_AXP20X_PEK)		+= axp20x-pek.o
 obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)	+= rotary_encoder.o
--- /dev/null
+++ b/drivers/input/misc/raspberrypi-button.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Driver for Raspberry Pi power button
+ *
+ * Copyright (C) 2023 Raspberry Pi Ltd.
+ *
+ * This driver is based on drivers/hwmon/raspberrypi-hwmon.c and
+ * input/misc/pm8941-pwrkey.c/ - see original files for copyright information
+ */
+
+#include <linux/delay.h>
+#include <linux/devm-helpers.h>
+#include <dt-bindings/input/raspberrypi-button.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+struct rpi_button {
+	struct device *dev;
+	struct rpi_firmware *fw;
+	struct input_dev *input;
+	struct delayed_work poll_work;
+	unsigned long poll_rate;
+	const char *name;
+	u32 id;
+	u32 code;
+};
+
+static void button_poll(struct work_struct *work)
+{
+	struct rpi_button *button;
+	u32 value;
+	int err;
+
+	button = container_of(work, struct rpi_button,
+			      poll_work.work);
+
+	value = BIT(button->id);
+	err = rpi_firmware_property(button->fw, RPI_FIRMWARE_GET_BUTTONS_PRESSED,
+				    &value, sizeof(value));
+	if (err) {
+		dev_err_once(button->dev, "GET_BUTTON_PRESSED not implemented?\n");
+		return;
+	}
+
+	if (value & BIT(button->id)) {
+		input_event(button->input, EV_KEY, button->code, 1);
+		input_sync(button->input);
+		input_event(button->input, EV_KEY, button->code, 0);
+		input_sync(button->input);
+	}
+
+	schedule_delayed_work(&button->poll_work, button->poll_rate);
+}
+
+static int rpi_button_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rpi_button *button;
+	int err;
+
+	button = devm_kzalloc(dev, sizeof(*button), GFP_KERNEL);
+	if (!button)
+		return -ENOMEM;
+
+	button->dev = dev;
+
+	/* Get the firmware pointer from our parent */
+	button->fw = dev_get_drvdata(dev->parent);
+
+	if (device_property_read_u32(dev, "id", &button->id))
+		button->id = RASPBERRYPI_BUTTON_POWER;
+
+	if (device_property_read_string(dev, "label", &button->name))
+		button->name = "raspberrypi-button";
+
+	if (device_property_read_u32(dev, "linux,code", &button->code)) {
+		dev_err(&pdev->dev, "no linux,code property\n");
+		return -EINVAL;
+	}
+
+	button->input = devm_input_allocate_device(dev);
+	if (!button->input) {
+		dev_dbg(&pdev->dev, "unable to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	input_set_capability(button->input, EV_KEY, button->code);
+
+	button->input->name = button->name;
+	button->input->phys = "raspberrypi-button/input0";
+	button->input->dev.parent = dev;
+	button->poll_rate = HZ;
+
+	err = input_register_device(button->input);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register input device: %d\n",
+			err);
+		return err;
+	}
+
+	err = devm_delayed_work_autocancel(dev, &button->poll_work,
+					   button_poll);
+	if (err)
+		return err;
+
+	platform_set_drvdata(pdev, button);
+	schedule_delayed_work(&button->poll_work, button->poll_rate);
+
+	return 0;
+}
+
+static const struct of_device_id rpi_button_match[] = {
+	{ .compatible = "raspberrypi,firmware-button", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rpi_button_match);
+
+static struct platform_driver rpi_button_driver = {
+	.probe = rpi_button_probe,
+	.driver = {
+		.name = "raspberrypi-button",
+		.of_match_table = of_match_ptr(rpi_button_match),
+	},
+};
+module_platform_driver(rpi_button_driver);
+
+MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
+MODULE_DESCRIPTION("Raspberry Pi button driver");
+MODULE_LICENSE("Dual BSD/GPL");
--- a/include/soc/bcm2835/raspberrypi-firmware.h
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
@@ -98,6 +98,7 @@ enum rpi_firmware_property_tag {
 	RPI_FIRMWARE_GET_REBOOT_FLAGS =                       0x00030064,
 	RPI_FIRMWARE_SET_REBOOT_FLAGS =                       0x00038064,
 	RPI_FIRMWARE_NOTIFY_DISPLAY_DONE =                    0x00030066,
+	RPI_FIRMWARE_GET_BUTTONS_PRESSED =                    0x00030088,
 
 	/* Dispmanx TAGS */
 	RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE =                   0x00040001,