diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,brcmstb.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,brcmstb.txt index c052caad36e806907988fa20e8250f2dce7d90d6..104cc9b41df46bebb2d8db5cdd4fb7843a677f9c 100644 --- a/Documentation/devicetree/bindings/arm/bcm/brcm,brcmstb.txt +++ b/Documentation/devicetree/bindings/arm/bcm/brcm,brcmstb.txt @@ -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 index 0000000000000000000000000000000000000000..3c92e2cb52d6068fa4fa65254a827bec77c6c667 --- /dev/null +++ b/Documentation/hwmon/raspberrypi-hwmon @@ -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 + +Description +----------- + +This driver periodically polls a mailbox property of the VC4 firmware to detect +undervoltage conditions. + +Sysfs entries +------------- + +in0_lcrit_alarm Undervoltage alarm diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index 6692888f04cfb8be4824f64efb3222f398937c3c..a200a217461191b796105682e599582bb736683e 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c @@ -21,6 +21,10 @@ #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; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f10840ad465c2bc468f0457bcaa8ec8bbf1c7d0f..195154178affc28d9ff214f5b0d8e9d17f5e453b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -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 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index e7d52a36e6c4f1dcea766a35d8ffa2bee1d7ae8e..a9297703fd6e41fd763c73cbab5c04c6f95fc036 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -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 index 0000000000000000000000000000000000000000..fb4e4a6bb1f63c2b34749a31ad4b18105f297d5b --- /dev/null +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -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 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/bcm/brcmstb/pm/pm-arm.c b/drivers/soc/bcm/brcmstb/pm/pm-arm.c index dcf8c8065508e0ddf8d182a909bfa401ac916e79..a5577dd5eb0870bccff391f38c69f47c8d708fde 100644 --- a/drivers/soc/bcm/brcmstb/pm/pm-arm.c +++ b/drivers/soc/bcm/brcmstb/pm/pm-arm.c @@ -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, diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h index 8ee8991aa099af3a1c9d8435bab12821dfef8928..c4a5c9e9fb478133d3fa518b0503fc7f074a93a9 100644 --- a/include/soc/bcm2835/raspberrypi-firmware.h +++ b/include/soc/bcm2835/raspberrypi-firmware.h @@ -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,