LCOV - code coverage report
Current view: top level - drivers/hwmon - raspberrypi-hwmon.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_real_modules_combined.info Lines: 26 37 70.3 %
Date: 2020-09-30 20:25:40 Functions: 5 8 62.5 %
Branches: 4 16 25.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0+
       2                 :            : /*
       3                 :            :  * Raspberry Pi voltage sensor driver
       4                 :            :  *
       5                 :            :  * Based on firmware/raspberrypi.c by Noralf Trønnes
       6                 :            :  *
       7                 :            :  * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
       8                 :            :  */
       9                 :            : #include <linux/device.h>
      10                 :            : #include <linux/err.h>
      11                 :            : #include <linux/hwmon.h>
      12                 :            : #include <linux/module.h>
      13                 :            : #include <linux/platform_device.h>
      14                 :            : #include <linux/slab.h>
      15                 :            : #include <linux/workqueue.h>
      16                 :            : #include <soc/bcm2835/raspberrypi-firmware.h>
      17                 :            : 
      18                 :            : /*
      19                 :            :  * This section defines some rate limited logging that prevent
      20                 :            :  * repeated messages at much lower Hz than the default kernel settings.
      21                 :            :  * It's usually 5s, this is 5 minutes.
      22                 :            :  * Burst 3 means you may get three messages 'quickly', before
      23                 :            :  * the ratelimiting kicks in.
      24                 :            :  */
      25                 :            : #define LOCAL_RATELIMIT_INTERVAL (5 * 60 * HZ)
      26                 :            : #define LOCAL_RATELIMIT_BURST 3
      27                 :            : 
      28                 :            : #ifdef CONFIG_PRINTK
      29                 :            : #define printk_ratelimited_local(fmt, ...)      \
      30                 :            : ({                                              \
      31                 :            :         static DEFINE_RATELIMIT_STATE(_rs,      \
      32                 :            :                 LOCAL_RATELIMIT_INTERVAL,       \
      33                 :            :                 LOCAL_RATELIMIT_BURST);         \
      34                 :            :                                                 \
      35                 :            :         if (__ratelimit(&_rs))                      \
      36                 :            :                 printk(fmt, ##__VA_ARGS__);     \
      37                 :            : })
      38                 :            : #else
      39                 :            : #define printk_ratelimited_local(fmt, ...)      \
      40                 :            :         no_printk(fmt, ##__VA_ARGS__)
      41                 :            : #endif
      42                 :            : 
      43                 :            : #define pr_crit_ratelimited_local(fmt, ...)              \
      44                 :            :         printk_ratelimited_local(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
      45                 :            : #define pr_info_ratelimited_local(fmt, ...)              \
      46                 :            : printk_ratelimited_local(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
      47                 :            : 
      48                 :            : #define UNDERVOLTAGE_STICKY_BIT BIT(16)
      49                 :            : 
      50                 :            : struct rpi_hwmon_data {
      51                 :            :         struct device *hwmon_dev;
      52                 :            :         struct rpi_firmware *fw;
      53                 :            :         u32 last_throttled;
      54                 :            :         struct delayed_work get_values_poll_work;
      55                 :            : };
      56                 :            : 
      57                 :      10409 : static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data)
      58                 :            : {
      59                 :            :         u32 new_uv, old_uv, value;
      60                 :            :         int ret;
      61                 :            : 
      62                 :            :         /* Request firmware to clear sticky bits */
      63                 :      10409 :         value = 0xffff;
      64                 :            : 
      65                 :      10409 :         ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED,
      66                 :            :                                     &value, sizeof(value));
      67         [ -  + ]:      10409 :         if (ret) {
      68         [ #  # ]:          0 :                 dev_err_once(data->hwmon_dev, "Failed to get throttled (%d)\n",
      69                 :            :                              ret);
      70                 :      10409 :                 return;
      71                 :            :         }
      72                 :            : 
      73                 :      10409 :         new_uv = value & UNDERVOLTAGE_STICKY_BIT;
      74                 :      10409 :         old_uv = data->last_throttled & UNDERVOLTAGE_STICKY_BIT;
      75                 :      10409 :         data->last_throttled = value;
      76                 :            : 
      77         [ -  + ]:      10409 :         if (new_uv == old_uv)
      78                 :            :                 return;
      79                 :            : 
      80         [ #  # ]:          0 :         if (new_uv) {
      81         [ #  # ]:          0 :                 pr_crit_ratelimited_local("Under-voltage detected! (0x%08x)\n",
      82                 :            :                                           value);
      83                 :            :         } else {
      84         [ #  # ]:          0 :                 pr_info_ratelimited_local("Voltage normalised (0x%08x)\n",
      85                 :            :                                           value);
      86                 :            :         }
      87                 :            : 
      88                 :          0 :         sysfs_notify(&data->hwmon_dev->kobj, NULL, "in0_lcrit_alarm");
      89                 :            : }
      90                 :            : 
      91                 :      10409 : static void get_values_poll(struct work_struct *work)
      92                 :            : {
      93                 :            :         struct rpi_hwmon_data *data;
      94                 :            : 
      95                 :      10409 :         data = container_of(work, struct rpi_hwmon_data,
      96                 :            :                             get_values_poll_work.work);
      97                 :            : 
      98                 :      10409 :         rpi_firmware_get_throttled(data);
      99                 :            : 
     100                 :            :         /*
     101                 :            :          * We can't run faster than the sticky shift (100ms) since we get
     102                 :            :          * flipping in the sticky bits that are cleared.
     103                 :            :          */
     104                 :      10409 :         schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
     105                 :      10409 : }
     106                 :            : 
     107                 :          0 : static int rpi_read(struct device *dev, enum hwmon_sensor_types type,
     108                 :            :                     u32 attr, int channel, long *val)
     109                 :            : {
     110                 :            :         struct rpi_hwmon_data *data = dev_get_drvdata(dev);
     111                 :            : 
     112                 :          0 :         *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
     113                 :          0 :         return 0;
     114                 :            : }
     115                 :            : 
     116                 :        207 : static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type,
     117                 :            :                               u32 attr, int channel)
     118                 :            : {
     119                 :        207 :         return 0444;
     120                 :            : }
     121                 :            : 
     122                 :            : static const struct hwmon_channel_info *rpi_info[] = {
     123                 :            :         HWMON_CHANNEL_INFO(in,
     124                 :            :                            HWMON_I_LCRIT_ALARM),
     125                 :            :         NULL
     126                 :            : };
     127                 :            : 
     128                 :            : static const struct hwmon_ops rpi_hwmon_ops = {
     129                 :            :         .is_visible = rpi_is_visible,
     130                 :            :         .read = rpi_read,
     131                 :            : };
     132                 :            : 
     133                 :            : static const struct hwmon_chip_info rpi_chip_info = {
     134                 :            :         .ops = &rpi_hwmon_ops,
     135                 :            :         .info = rpi_info,
     136                 :            : };
     137                 :            : 
     138                 :        207 : static int rpi_hwmon_probe(struct platform_device *pdev)
     139                 :            : {
     140                 :        207 :         struct device *dev = &pdev->dev;
     141                 :            :         struct rpi_hwmon_data *data;
     142                 :            : 
     143                 :            :         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
     144         [ +  - ]:        207 :         if (!data)
     145                 :            :                 return -ENOMEM;
     146                 :            : 
     147                 :            :         /* Parent driver assure that firmware is correct */
     148                 :        414 :         data->fw = dev_get_drvdata(dev->parent);
     149                 :            : 
     150                 :        207 :         data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt",
     151                 :            :                                                                data,
     152                 :            :                                                                &rpi_chip_info,
     153                 :            :                                                                NULL);
     154                 :            : 
     155                 :        414 :         INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll);
     156                 :            :         platform_set_drvdata(pdev, data);
     157                 :            : 
     158         [ +  - ]:        414 :         if (!PTR_ERR_OR_ZERO(data->hwmon_dev))
     159                 :        207 :                 schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
     160                 :            : 
     161                 :        414 :         return PTR_ERR_OR_ZERO(data->hwmon_dev);
     162                 :            : }
     163                 :            : 
     164                 :          0 : static int rpi_hwmon_remove(struct platform_device *pdev)
     165                 :            : {
     166                 :            :         struct rpi_hwmon_data *data = platform_get_drvdata(pdev);
     167                 :            : 
     168                 :          0 :         cancel_delayed_work_sync(&data->get_values_poll_work);
     169                 :            : 
     170                 :          0 :         return 0;
     171                 :            : }
     172                 :            : 
     173                 :            : static struct platform_driver rpi_hwmon_driver = {
     174                 :            :         .probe = rpi_hwmon_probe,
     175                 :            :         .remove = rpi_hwmon_remove,
     176                 :            :         .driver = {
     177                 :            :                 .name = "raspberrypi-hwmon",
     178                 :            :         },
     179                 :            : };
     180                 :        207 : module_platform_driver(rpi_hwmon_driver);
     181                 :            : 
     182                 :            : MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>");
     183                 :            : MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
     184                 :            : MODULE_LICENSE("GPL v2");
     185                 :            : MODULE_ALIAS("platform:raspberrypi-hwmon");

Generated by: LCOV version 1.14