Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Raspberry Pi voltage sensor driver
0004  *
0005  * Based on firmware/raspberrypi.c by Noralf Trønnes
0006  *
0007  * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
0008  */
0009 #include <linux/device.h>
0010 #include <linux/devm-helpers.h>
0011 #include <linux/err.h>
0012 #include <linux/hwmon.h>
0013 #include <linux/module.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/slab.h>
0016 #include <linux/workqueue.h>
0017 #include <soc/bcm2835/raspberrypi-firmware.h>
0018 
0019 #define UNDERVOLTAGE_STICKY_BIT BIT(16)
0020 
0021 struct rpi_hwmon_data {
0022     struct device *hwmon_dev;
0023     struct rpi_firmware *fw;
0024     u32 last_throttled;
0025     struct delayed_work get_values_poll_work;
0026 };
0027 
0028 static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data)
0029 {
0030     u32 new_uv, old_uv, value;
0031     int ret;
0032 
0033     /* Request firmware to clear sticky bits */
0034     value = 0xffff;
0035 
0036     ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED,
0037                     &value, sizeof(value));
0038     if (ret) {
0039         dev_err_once(data->hwmon_dev, "Failed to get throttled (%d)\n",
0040                  ret);
0041         return;
0042     }
0043 
0044     new_uv = value & UNDERVOLTAGE_STICKY_BIT;
0045     old_uv = data->last_throttled & UNDERVOLTAGE_STICKY_BIT;
0046     data->last_throttled = value;
0047 
0048     if (new_uv == old_uv)
0049         return;
0050 
0051     if (new_uv)
0052         dev_crit(data->hwmon_dev, "Undervoltage detected!\n");
0053     else
0054         dev_info(data->hwmon_dev, "Voltage normalised\n");
0055 
0056     hwmon_notify_event(data->hwmon_dev, hwmon_in, hwmon_in_lcrit_alarm, 0);
0057 }
0058 
0059 static void get_values_poll(struct work_struct *work)
0060 {
0061     struct rpi_hwmon_data *data;
0062 
0063     data = container_of(work, struct rpi_hwmon_data,
0064                 get_values_poll_work.work);
0065 
0066     rpi_firmware_get_throttled(data);
0067 
0068     /*
0069      * We can't run faster than the sticky shift (100ms) since we get
0070      * flipping in the sticky bits that are cleared.
0071      */
0072     schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
0073 }
0074 
0075 static int rpi_read(struct device *dev, enum hwmon_sensor_types type,
0076             u32 attr, int channel, long *val)
0077 {
0078     struct rpi_hwmon_data *data = dev_get_drvdata(dev);
0079 
0080     *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
0081     return 0;
0082 }
0083 
0084 static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type,
0085                   u32 attr, int channel)
0086 {
0087     return 0444;
0088 }
0089 
0090 static const struct hwmon_channel_info *rpi_info[] = {
0091     HWMON_CHANNEL_INFO(in,
0092                HWMON_I_LCRIT_ALARM),
0093     NULL
0094 };
0095 
0096 static const struct hwmon_ops rpi_hwmon_ops = {
0097     .is_visible = rpi_is_visible,
0098     .read = rpi_read,
0099 };
0100 
0101 static const struct hwmon_chip_info rpi_chip_info = {
0102     .ops = &rpi_hwmon_ops,
0103     .info = rpi_info,
0104 };
0105 
0106 static int rpi_hwmon_probe(struct platform_device *pdev)
0107 {
0108     struct device *dev = &pdev->dev;
0109     struct rpi_hwmon_data *data;
0110     int ret;
0111 
0112     data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
0113     if (!data)
0114         return -ENOMEM;
0115 
0116     /* Parent driver assure that firmware is correct */
0117     data->fw = dev_get_drvdata(dev->parent);
0118 
0119     data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt",
0120                                    data,
0121                                    &rpi_chip_info,
0122                                    NULL);
0123     if (IS_ERR(data->hwmon_dev))
0124         return PTR_ERR(data->hwmon_dev);
0125 
0126     ret = devm_delayed_work_autocancel(dev, &data->get_values_poll_work,
0127                        get_values_poll);
0128     if (ret)
0129         return ret;
0130     platform_set_drvdata(pdev, data);
0131 
0132     schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
0133 
0134     return 0;
0135 }
0136 
0137 static struct platform_driver rpi_hwmon_driver = {
0138     .probe = rpi_hwmon_probe,
0139     .driver = {
0140         .name = "raspberrypi-hwmon",
0141     },
0142 };
0143 module_platform_driver(rpi_hwmon_driver);
0144 
0145 MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>");
0146 MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
0147 MODULE_LICENSE("GPL v2");
0148 MODULE_ALIAS("platform:raspberrypi-hwmon");