Merge tag 'arm-soc/for-4.19/drivers' of https://github.com/Broadcom/stblinux into...
authorOlof Johansson <olof@lixom.net>
Sat, 14 Jul 2018 21:14:47 +0000 (14:14 -0700)
committerOlof Johansson <olof@lixom.net>
Sat, 14 Jul 2018 21:14:47 +0000 (14:14 -0700)
This pull request contains Broadcom ARM/ARM64/MIPS SoCs drivers changes
for 4.19, please pull the following:

- Doug updates the low-level suspend/resume code for ARM SoCs to support
  the latest rev B3.0 memory controllers found on newer chips with an
  appropriate match structure to perform the correct entry sequencing

- Florian updates the Device Tree binding document for these memory
  controllers to list all possible compatible strings that exist given
   the supported memory controllers.

- Stefan adds the GET_THROTTLED firmware property value that is required
  for the Rasperry Pi voltage monitoring driver and updates the
  Raspberry Pi firmware driver accordingly to register such a device
  using the HWMON subsystem. Finally he adds support for reporting under
  voltage conditions using a specialized HWMON driver.

* tag 'arm-soc/for-4.19/drivers' of https://github.com/Broadcom/stblinux:
  firmware: raspberrypi: Remove VLA usage
  firmware: raspberrypi: Register hwmon driver
  hwmon: Add support for RPi voltage sensor
  soc: bcm: brcmstb: Add missing DDR MEMC compatible strings
  soc: bcm: brcmstb: pm: Add support for newer rev B3.0 controllers
  ARM: bcm2835: Add GET_THROTTLED firmware property

Signed-off-by: Olof Johansson <olof@lixom.net>
Documentation/devicetree/bindings/arm/bcm/brcm,brcmstb.txt
Documentation/hwmon/raspberrypi-hwmon [new file with mode: 0644]
drivers/firmware/raspberrypi.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/raspberrypi-hwmon.c [new file with mode: 0644]
drivers/soc/bcm/brcmstb/pm/pm-arm.c
include/soc/bcm2835/raspberrypi-firmware.h

index c052caad36e806907988fa20e8250f2dce7d90d6..104cc9b41df46bebb2d8db5cdd4fb7843a677f9c 100644 (file)
@@ -189,7 +189,11 @@ Power-Down (SRPD), among other things.
 
 Required properties:
 - compatible     : should contain one of these
+       "brcm,brcmstb-memc-ddr-rev-b.2.1"
        "brcm,brcmstb-memc-ddr-rev-b.2.2"
+       "brcm,brcmstb-memc-ddr-rev-b.2.3"
+       "brcm,brcmstb-memc-ddr-rev-b.3.0"
+       "brcm,brcmstb-memc-ddr-rev-b.3.1"
        "brcm,brcmstb-memc-ddr"
 - reg            : the MEMC DDR register range
 
diff --git a/Documentation/hwmon/raspberrypi-hwmon b/Documentation/hwmon/raspberrypi-hwmon
new file mode 100644 (file)
index 0000000..3c92e2c
--- /dev/null
@@ -0,0 +1,22 @@
+Kernel driver raspberrypi-hwmon
+===============================
+
+Supported boards:
+  * Raspberry Pi A+ (via GPIO on SoC)
+  * Raspberry Pi B+ (via GPIO on SoC)
+  * Raspberry Pi 2 B (via GPIO on SoC)
+  * Raspberry Pi 3 B (via GPIO on port expander)
+  * Raspberry Pi 3 B+ (via PMIC)
+
+Author: Stefan Wahren <stefan.wahren@i2se.com>
+
+Description
+-----------
+
+This driver periodically polls a mailbox property of the VC4 firmware to detect
+undervoltage conditions.
+
+Sysfs entries
+-------------
+
+in0_lcrit_alarm                Undervoltage alarm
index 6692888f04cfb8be4824f64efb3222f398937c3c..a200a217461191b796105682e599582bb736683e 100644 (file)
 #define MBOX_DATA28(msg)               ((msg) & ~0xf)
 #define MBOX_CHAN_PROPERTY             8
 
+#define MAX_RPI_FW_PROP_BUF_SIZE       32
+
+static struct platform_device *rpi_hwmon;
+
 struct rpi_firmware {
        struct mbox_client cl;
        struct mbox_chan *chan; /* The property channel. */
@@ -143,18 +147,22 @@ int rpi_firmware_property(struct rpi_firmware *fw,
        /* Single tags are very small (generally 8 bytes), so the
         * stack should be safe.
         */
-       u8 data[buf_size + sizeof(struct rpi_firmware_property_tag_header)];
+       u8 data[sizeof(struct rpi_firmware_property_tag_header) +
+               MAX_RPI_FW_PROP_BUF_SIZE];
        struct rpi_firmware_property_tag_header *header =
                (struct rpi_firmware_property_tag_header *)data;
        int ret;
 
+       if (WARN_ON(buf_size > sizeof(data) - sizeof(*header)))
+               return -EINVAL;
+
        header->tag = tag;
        header->buf_size = buf_size;
        header->req_resp_size = 0;
        memcpy(data + sizeof(struct rpi_firmware_property_tag_header),
               tag_data, buf_size);
 
-       ret = rpi_firmware_property_list(fw, &data, sizeof(data));
+       ret = rpi_firmware_property_list(fw, &data, buf_size + sizeof(*header));
        memcpy(tag_data,
               data + sizeof(struct rpi_firmware_property_tag_header),
               buf_size);
@@ -183,6 +191,20 @@ rpi_firmware_print_firmware_revision(struct rpi_firmware *fw)
        }
 }
 
+static void
+rpi_register_hwmon_driver(struct device *dev, struct rpi_firmware *fw)
+{
+       u32 packet;
+       int ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_THROTTLED,
+                                       &packet, sizeof(packet));
+
+       if (ret)
+               return;
+
+       rpi_hwmon = platform_device_register_data(dev, "raspberrypi-hwmon",
+                                                 -1, NULL, 0);
+}
+
 static int rpi_firmware_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -209,6 +231,7 @@ static int rpi_firmware_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, fw);
 
        rpi_firmware_print_firmware_revision(fw);
+       rpi_register_hwmon_driver(dev, fw);
 
        return 0;
 }
@@ -217,6 +240,8 @@ static int rpi_firmware_remove(struct platform_device *pdev)
 {
        struct rpi_firmware *fw = platform_get_drvdata(pdev);
 
+       platform_device_unregister(rpi_hwmon);
+       rpi_hwmon = NULL;
        mbox_free_channel(fw->chan);
 
        return 0;
index f10840ad465c2bc468f0457bcaa8ec8bbf1c7d0f..195154178affc28d9ff214f5b0d8e9d17f5e453b 100644 (file)
@@ -1298,6 +1298,16 @@ config SENSORS_PWM_FAN
          This driver can also be built as a module.  If so, the module
          will be called pwm-fan.
 
+config SENSORS_RASPBERRYPI_HWMON
+       tristate "Raspberry Pi voltage monitor"
+       depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
+       help
+         If you say yes here you get support for voltage sensor on the
+         Raspberry Pi.
+
+         This driver can also be built as a module. If so, the module
+         will be called raspberrypi-hwmon.
+
 config SENSORS_SHT15
        tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
        depends on GPIOLIB || COMPILE_TEST
index e7d52a36e6c4f1dcea766a35d8ffa2bee1d7ae8e..a9297703fd6e41fd763c73cbab5c04c6f95fc036 100644 (file)
@@ -141,6 +141,7 @@ obj-$(CONFIG_SENSORS_PC87427)       += pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)  += pcf8591.o
 obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
 obj-$(CONFIG_SENSORS_PWM_FAN)  += pwm-fan.o
+obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)        += raspberrypi-hwmon.o
 obj-$(CONFIG_SENSORS_S3C)      += s3c-hwmon.o
 obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
 obj-$(CONFIG_SENSORS_SCH5627)  += sch5627.o
diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c
new file mode 100644 (file)
index 0000000..fb4e4a6
--- /dev/null
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Raspberry Pi voltage sensor driver
+ *
+ * Based on firmware/raspberrypi.c by Noralf Trønnes
+ *
+ * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define UNDERVOLTAGE_STICKY_BIT        BIT(16)
+
+struct rpi_hwmon_data {
+       struct device *hwmon_dev;
+       struct rpi_firmware *fw;
+       u32 last_throttled;
+       struct delayed_work get_values_poll_work;
+};
+
+static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data)
+{
+       u32 new_uv, old_uv, value;
+       int ret;
+
+       /* Request firmware to clear sticky bits */
+       value = 0xffff;
+
+       ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED,
+                                   &value, sizeof(value));
+       if (ret) {
+               dev_err_once(data->hwmon_dev, "Failed to get throttled (%d)\n",
+                            ret);
+               return;
+       }
+
+       new_uv = value & UNDERVOLTAGE_STICKY_BIT;
+       old_uv = data->last_throttled & UNDERVOLTAGE_STICKY_BIT;
+       data->last_throttled = value;
+
+       if (new_uv == old_uv)
+               return;
+
+       if (new_uv)
+               dev_crit(data->hwmon_dev, "Undervoltage detected!\n");
+       else
+               dev_info(data->hwmon_dev, "Voltage normalised\n");
+
+       sysfs_notify(&data->hwmon_dev->kobj, NULL, "in0_lcrit_alarm");
+}
+
+static void get_values_poll(struct work_struct *work)
+{
+       struct rpi_hwmon_data *data;
+
+       data = container_of(work, struct rpi_hwmon_data,
+                           get_values_poll_work.work);
+
+       rpi_firmware_get_throttled(data);
+
+       /*
+        * We can't run faster than the sticky shift (100ms) since we get
+        * flipping in the sticky bits that are cleared.
+        */
+       schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
+}
+
+static int rpi_read(struct device *dev, enum hwmon_sensor_types type,
+                   u32 attr, int channel, long *val)
+{
+       struct rpi_hwmon_data *data = dev_get_drvdata(dev);
+
+       *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
+       return 0;
+}
+
+static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type,
+                             u32 attr, int channel)
+{
+       return 0444;
+}
+
+static const u32 rpi_in_config[] = {
+       HWMON_I_LCRIT_ALARM,
+       0
+};
+
+static const struct hwmon_channel_info rpi_in = {
+       .type = hwmon_in,
+       .config = rpi_in_config,
+};
+
+static const struct hwmon_channel_info *rpi_info[] = {
+       &rpi_in,
+       NULL
+};
+
+static const struct hwmon_ops rpi_hwmon_ops = {
+       .is_visible = rpi_is_visible,
+       .read = rpi_read,
+};
+
+static const struct hwmon_chip_info rpi_chip_info = {
+       .ops = &rpi_hwmon_ops,
+       .info = rpi_info,
+};
+
+static int rpi_hwmon_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rpi_hwmon_data *data;
+       int ret;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       /* Parent driver assure that firmware is correct */
+       data->fw = dev_get_drvdata(dev->parent);
+
+       /* Init throttled */
+       ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED,
+                                   &data->last_throttled,
+                                   sizeof(data->last_throttled));
+
+       data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt",
+                                                              data,
+                                                              &rpi_chip_info,
+                                                              NULL);
+
+       INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll);
+       platform_set_drvdata(pdev, data);
+
+       if (!PTR_ERR_OR_ZERO(data->hwmon_dev))
+               schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
+
+       return PTR_ERR_OR_ZERO(data->hwmon_dev);
+}
+
+static int rpi_hwmon_remove(struct platform_device *pdev)
+{
+       struct rpi_hwmon_data *data = platform_get_drvdata(pdev);
+
+       cancel_delayed_work_sync(&data->get_values_poll_work);
+
+       return 0;
+}
+
+static struct platform_driver rpi_hwmon_driver = {
+       .probe = rpi_hwmon_probe,
+       .remove = rpi_hwmon_remove,
+       .driver = {
+               .name = "raspberrypi-hwmon",
+       },
+};
+module_platform_driver(rpi_hwmon_driver);
+
+MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
+MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
+MODULE_LICENSE("GPL v2");
index dcf8c8065508e0ddf8d182a909bfa401ac916e79..a5577dd5eb0870bccff391f38c69f47c8d708fde 100644 (file)
@@ -627,10 +627,26 @@ static const struct of_device_id ddr_shimphy_dt_ids[] = {
 };
 
 static const struct of_device_id brcmstb_memc_of_match[] = {
+       {
+               .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.1",
+               .data = &ddr_seq,
+       },
        {
                .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.2",
                .data = &ddr_seq_b22,
        },
+       {
+               .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.3",
+               .data = &ddr_seq_b22,
+       },
+       {
+               .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.0",
+               .data = &ddr_seq_b22,
+       },
+       {
+               .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.1",
+               .data = &ddr_seq_b22,
+       },
        {
                .compatible = "brcm,brcmstb-memc-ddr",
                .data = &ddr_seq,
index 8ee8991aa099af3a1c9d8435bab12821dfef8928..c4a5c9e9fb478133d3fa518b0503fc7f074a93a9 100644 (file)
@@ -75,6 +75,7 @@ enum rpi_firmware_property_tag {
        RPI_FIRMWARE_GET_EDID_BLOCK =                         0x00030020,
        RPI_FIRMWARE_GET_CUSTOMER_OTP =                       0x00030021,
        RPI_FIRMWARE_GET_DOMAIN_STATE =                       0x00030030,
+       RPI_FIRMWARE_GET_THROTTLED =                          0x00030046,
        RPI_FIRMWARE_SET_CLOCK_STATE =                        0x00038001,
        RPI_FIRMWARE_SET_CLOCK_RATE =                         0x00038002,
        RPI_FIRMWARE_SET_VOLTAGE =                            0x00038003,