1 From 83a8df1b7fff284fc3c2277c8051f53acde2e64f Mon Sep 17 00:00:00 2001
2 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
3 Date: Sat, 24 Feb 2018 13:41:25 +0100
4 Subject: [PATCH 234/454] firmware/raspberrypi: Add a get_throttled sysfs file
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
9 Under-voltage due to inadequate power supplies is a recurring problem for
10 new Raspberry Pi users. There are visual indications that an
11 under-voltage situation is occuring like blinking power led and a
12 lightning icon on the desktop (not shown when using the vc4 driver), but
13 for new users it's not obvious that this signifies a critical situation.
15 This patch provides a twofold improvement to the situation:
17 Firstly it logs under-voltage events to the kernel log. This provides
18 information also for headless installations.
20 Secondly it provides a sysfs file to read the value. This improves on
21 'vcgencmd' by providing change notification. Userspace can poll on the
22 file and be notified of changes to the value.
23 A script can poll the file and use dbus notification to put a windows on
24 the desktop with information about the severity with a recommendation to
25 change the power supply. A link to more information can also be provided.
26 Only changes to the sticky bits are reported (cleared between readings).
28 Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
30 drivers/firmware/raspberrypi.c | 108 +++++++++++++++++++++
31 include/soc/bcm2835/raspberrypi-firmware.h | 1 +
32 2 files changed, 109 insertions(+)
34 --- a/drivers/firmware/raspberrypi.c
35 +++ b/drivers/firmware/raspberrypi.c
37 #include <linux/module.h>
38 #include <linux/of_platform.h>
39 #include <linux/platform_device.h>
40 +#include <linux/workqueue.h>
41 #include <soc/bcm2835/raspberrypi-firmware.h>
43 #define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf))
45 #define MBOX_DATA28(msg) ((msg) & ~0xf)
46 #define MBOX_CHAN_PROPERTY 8
48 +#define UNDERVOLTAGE_BIT BIT(0)
51 struct mbox_client cl;
52 struct mbox_chan *chan; /* The property channel. */
55 + struct delayed_work get_throttled_poll_work;
58 static struct platform_device *g_pdev;
59 @@ -166,6 +170,101 @@ int rpi_firmware_property(struct rpi_fir
61 EXPORT_SYMBOL_GPL(rpi_firmware_property);
63 +static int rpi_firmware_get_throttled(struct rpi_firmware *fw, u32 *value)
65 + static ktime_t old_timestamp;
66 + static u32 old_value;
67 + u32 new_sticky, old_sticky, new_uv, old_uv;
68 + ktime_t new_timestamp;
76 + * We can't run faster than the sticky shift (100ms) since we get
77 + * flipping in the sticky bits that are cleared.
78 + * This happens on polling, so just return the previous value.
80 + new_timestamp = ktime_get();
81 + elapsed_ms = ktime_ms_delta(new_timestamp, old_timestamp);
82 + if (elapsed_ms < 150) {
86 + old_timestamp = new_timestamp;
88 + /* Clear sticky bits */
91 + ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_THROTTLED,
92 + value, sizeof(*value));
96 + new_sticky = *value >> 16;
97 + old_sticky = old_value >> 16;
100 + /* Only notify about changes in the sticky bits */
101 + if (new_sticky == old_sticky)
104 + new_uv = new_sticky & UNDERVOLTAGE_BIT;
105 + old_uv = old_sticky & UNDERVOLTAGE_BIT;
107 + if (new_uv != old_uv) {
109 + pr_crit("Under-voltage detected! (0x%08x)\n", *value);
111 + pr_info("Voltage normalised (0x%08x)\n", *value);
114 + sysfs_notify(&fw->cl.dev->kobj, NULL, "get_throttled");
119 +static void get_throttled_poll(struct work_struct *work)
121 + struct rpi_firmware *fw = container_of(work, struct rpi_firmware,
122 + get_throttled_poll_work.work);
126 + ret = rpi_firmware_get_throttled(fw, &dummy);
128 + pr_debug("%s: Failed to read value (%d)", __func__, ret);
130 + schedule_delayed_work(&fw->get_throttled_poll_work, 2 * HZ);
133 +static ssize_t get_throttled_show(struct device *dev,
134 + struct device_attribute *attr, char *buf)
136 + struct rpi_firmware *fw = dev_get_drvdata(dev);
140 + ret = rpi_firmware_get_throttled(fw, &value);
144 + return sprintf(buf, "%x\n", value);
147 +static DEVICE_ATTR_RO(get_throttled);
149 +static struct attribute *rpi_firmware_dev_attrs[] = {
150 + &dev_attr_get_throttled.attr,
154 +static const struct attribute_group rpi_firmware_dev_group = {
155 + .attrs = rpi_firmware_dev_attrs,
159 rpi_firmware_print_firmware_revision(struct rpi_firmware *fw)
161 @@ -190,6 +289,11 @@ static int rpi_firmware_probe(struct pla
163 struct device *dev = &pdev->dev;
164 struct rpi_firmware *fw;
167 + ret = devm_device_add_group(dev, &rpi_firmware_dev_group);
171 fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL);
173 @@ -208,12 +312,15 @@ static int rpi_firmware_probe(struct pla
176 init_completion(&fw->c);
177 + INIT_DELAYED_WORK(&fw->get_throttled_poll_work, get_throttled_poll);
179 platform_set_drvdata(pdev, fw);
182 rpi_firmware_print_firmware_revision(fw);
184 + schedule_delayed_work(&fw->get_throttled_poll_work, 0);
189 @@ -221,6 +328,7 @@ static int rpi_firmware_remove(struct pl
191 struct rpi_firmware *fw = platform_get_drvdata(pdev);
193 + cancel_delayed_work_sync(&fw->get_throttled_poll_work);
194 mbox_free_channel(fw->chan);
197 --- a/include/soc/bcm2835/raspberrypi-firmware.h
198 +++ b/include/soc/bcm2835/raspberrypi-firmware.h
199 @@ -77,6 +77,7 @@ enum rpi_firmware_property_tag {
200 RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020,
201 RPI_FIRMWARE_GET_CUSTOMER_OTP = 0x00030021,
202 RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030,
203 + RPI_FIRMWARE_GET_THROTTLED = 0x00030046,
204 RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001,
205 RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002,
206 RPI_FIRMWARE_SET_VOLTAGE = 0x00038003,