LCOV - code coverage report
Current view: top level - drivers/thermal/broadcom - bcm2835_thermal.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_real_modules_combined.info Lines: 43 73 58.9 %
Date: 2020-09-30 20:25:40 Functions: 4 6 66.7 %
Branches: 13 30 43.3 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0+
       2                 :            : /*
       3                 :            :  * Driver for Broadcom BCM2835 SoC temperature sensor
       4                 :            :  *
       5                 :            :  * Copyright (C) 2016 Martin Sperl
       6                 :            :  */
       7                 :            : 
       8                 :            : #include <linux/clk.h>
       9                 :            : #include <linux/debugfs.h>
      10                 :            : #include <linux/device.h>
      11                 :            : #include <linux/err.h>
      12                 :            : #include <linux/io.h>
      13                 :            : #include <linux/kernel.h>
      14                 :            : #include <linux/module.h>
      15                 :            : #include <linux/of.h>
      16                 :            : #include <linux/of_address.h>
      17                 :            : #include <linux/of_device.h>
      18                 :            : #include <linux/platform_device.h>
      19                 :            : #include <linux/thermal.h>
      20                 :            : 
      21                 :            : #include "../thermal_hwmon.h"
      22                 :            : 
      23                 :            : #define BCM2835_TS_TSENSCTL                     0x00
      24                 :            : #define BCM2835_TS_TSENSSTAT                    0x04
      25                 :            : 
      26                 :            : #define BCM2835_TS_TSENSCTL_PRWDW               BIT(0)
      27                 :            : #define BCM2835_TS_TSENSCTL_RSTB                BIT(1)
      28                 :            : 
      29                 :            : /*
      30                 :            :  * bandgap reference voltage in 6 mV increments
      31                 :            :  * 000b = 1178 mV, 001b = 1184 mV, ... 111b = 1220 mV
      32                 :            :  */
      33                 :            : #define BCM2835_TS_TSENSCTL_CTRL_BITS           3
      34                 :            : #define BCM2835_TS_TSENSCTL_CTRL_SHIFT          2
      35                 :            : #define BCM2835_TS_TSENSCTL_CTRL_MASK               \
      36                 :            :         GENMASK(BCM2835_TS_TSENSCTL_CTRL_BITS +     \
      37                 :            :                 BCM2835_TS_TSENSCTL_CTRL_SHIFT - 1, \
      38                 :            :                 BCM2835_TS_TSENSCTL_CTRL_SHIFT)
      39                 :            : #define BCM2835_TS_TSENSCTL_CTRL_DEFAULT        1
      40                 :            : #define BCM2835_TS_TSENSCTL_EN_INT              BIT(5)
      41                 :            : #define BCM2835_TS_TSENSCTL_DIRECT              BIT(6)
      42                 :            : #define BCM2835_TS_TSENSCTL_CLR_INT             BIT(7)
      43                 :            : #define BCM2835_TS_TSENSCTL_THOLD_SHIFT         8
      44                 :            : #define BCM2835_TS_TSENSCTL_THOLD_BITS          10
      45                 :            : #define BCM2835_TS_TSENSCTL_THOLD_MASK               \
      46                 :            :         GENMASK(BCM2835_TS_TSENSCTL_THOLD_BITS +     \
      47                 :            :                 BCM2835_TS_TSENSCTL_THOLD_SHIFT - 1, \
      48                 :            :                 BCM2835_TS_TSENSCTL_THOLD_SHIFT)
      49                 :            : /*
      50                 :            :  * time how long the block to be asserted in reset
      51                 :            :  * which based on a clock counter (TSENS clock assumed)
      52                 :            :  */
      53                 :            : #define BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT      18
      54                 :            : #define BCM2835_TS_TSENSCTL_RSTDELAY_BITS       8
      55                 :            : #define BCM2835_TS_TSENSCTL_REGULEN             BIT(26)
      56                 :            : 
      57                 :            : #define BCM2835_TS_TSENSSTAT_DATA_BITS          10
      58                 :            : #define BCM2835_TS_TSENSSTAT_DATA_SHIFT         0
      59                 :            : #define BCM2835_TS_TSENSSTAT_DATA_MASK               \
      60                 :            :         GENMASK(BCM2835_TS_TSENSSTAT_DATA_BITS +     \
      61                 :            :                 BCM2835_TS_TSENSSTAT_DATA_SHIFT - 1, \
      62                 :            :                 BCM2835_TS_TSENSSTAT_DATA_SHIFT)
      63                 :            : #define BCM2835_TS_TSENSSTAT_VALID              BIT(10)
      64                 :            : #define BCM2835_TS_TSENSSTAT_INTERRUPT          BIT(11)
      65                 :            : 
      66                 :            : struct bcm2835_thermal_data {
      67                 :            :         struct thermal_zone_device *tz;
      68                 :            :         void __iomem *regs;
      69                 :            :         struct clk *clk;
      70                 :            :         struct dentry *debugfsdir;
      71                 :            : };
      72                 :            : 
      73                 :            : static int bcm2835_thermal_adc2temp(u32 adc, int offset, int slope)
      74                 :            : {
      75                 :        207 :         return offset + slope * adc;
      76                 :            : }
      77                 :            : 
      78                 :            : static int bcm2835_thermal_temp2adc(int temp, int offset, int slope)
      79                 :            : {
      80                 :          0 :         temp -= offset;
      81                 :          0 :         temp /= slope;
      82                 :            : 
      83         [ #  # ]:          0 :         if (temp < 0)
      84                 :            :                 temp = 0;
      85         [ #  # ]:          0 :         if (temp >= BIT(BCM2835_TS_TSENSSTAT_DATA_BITS))
      86                 :            :                 temp = BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1;
      87                 :            : 
      88                 :            :         return temp;
      89                 :            : }
      90                 :            : 
      91                 :        207 : static int bcm2835_thermal_get_temp(void *d, int *temp)
      92                 :            : {
      93                 :            :         struct bcm2835_thermal_data *data = d;
      94                 :        414 :         u32 val = readl(data->regs + BCM2835_TS_TSENSSTAT);
      95                 :            : 
      96         [ +  - ]:        207 :         if (!(val & BCM2835_TS_TSENSSTAT_VALID))
      97                 :            :                 return -EIO;
      98                 :            : 
      99                 :        207 :         val &= BCM2835_TS_TSENSSTAT_DATA_MASK;
     100                 :            : 
     101                 :        414 :         *temp = bcm2835_thermal_adc2temp(
     102                 :            :                 val,
     103                 :            :                 thermal_zone_get_offset(data->tz),
     104                 :            :                 thermal_zone_get_slope(data->tz));
     105                 :            : 
     106                 :        207 :         return 0;
     107                 :            : }
     108                 :            : 
     109                 :            : static const struct debugfs_reg32 bcm2835_thermal_regs[] = {
     110                 :            :         {
     111                 :            :                 .name = "ctl",
     112                 :            :                 .offset = 0
     113                 :            :         },
     114                 :            :         {
     115                 :            :                 .name = "stat",
     116                 :            :                 .offset = 4
     117                 :            :         }
     118                 :            : };
     119                 :            : 
     120                 :        207 : static void bcm2835_thermal_debugfs(struct platform_device *pdev)
     121                 :            : {
     122                 :            :         struct bcm2835_thermal_data *data = platform_get_drvdata(pdev);
     123                 :            :         struct debugfs_regset32 *regset;
     124                 :            : 
     125                 :        207 :         data->debugfsdir = debugfs_create_dir("bcm2835_thermal", NULL);
     126                 :            : 
     127                 :        207 :         regset = devm_kzalloc(&pdev->dev, sizeof(*regset), GFP_KERNEL);
     128         [ +  - ]:        207 :         if (!regset)
     129                 :        207 :                 return;
     130                 :            : 
     131                 :        207 :         regset->regs = bcm2835_thermal_regs;
     132                 :        207 :         regset->nregs = ARRAY_SIZE(bcm2835_thermal_regs);
     133                 :        207 :         regset->base = data->regs;
     134                 :            : 
     135                 :        207 :         debugfs_create_regset32("regset", 0444, data->debugfsdir, regset);
     136                 :            : }
     137                 :            : 
     138                 :            : static const struct thermal_zone_of_device_ops bcm2835_thermal_ops = {
     139                 :            :         .get_temp = bcm2835_thermal_get_temp,
     140                 :            : };
     141                 :            : 
     142                 :            : /*
     143                 :            :  * Note: as per Raspberry Foundation FAQ
     144                 :            :  * (https://www.raspberrypi.org/help/faqs/#performanceOperatingTemperature)
     145                 :            :  * the recommended temperature range for the SoC -40C to +85C
     146                 :            :  * so the trip limit is set to 80C.
     147                 :            :  * this applies to all the BCM283X SoC
     148                 :            :  */
     149                 :            : 
     150                 :            : static const struct of_device_id bcm2835_thermal_of_match_table[] = {
     151                 :            :         {
     152                 :            :                 .compatible = "brcm,bcm2835-thermal",
     153                 :            :         },
     154                 :            :         {
     155                 :            :                 .compatible = "brcm,bcm2836-thermal",
     156                 :            :         },
     157                 :            :         {
     158                 :            :                 .compatible = "brcm,bcm2837-thermal",
     159                 :            :         },
     160                 :            :         {},
     161                 :            : };
     162                 :            : MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table);
     163                 :            : 
     164                 :        414 : static int bcm2835_thermal_probe(struct platform_device *pdev)
     165                 :            : {
     166                 :            :         const struct of_device_id *match;
     167                 :            :         struct thermal_zone_device *tz;
     168                 :            :         struct bcm2835_thermal_data *data;
     169                 :            :         struct resource *res;
     170                 :            :         int err = 0;
     171                 :            :         u32 val;
     172                 :            :         unsigned long rate;
     173                 :            : 
     174                 :        414 :         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
     175         [ +  - ]:        414 :         if (!data)
     176                 :            :                 return -ENOMEM;
     177                 :            : 
     178                 :        414 :         match = of_match_device(bcm2835_thermal_of_match_table,
     179                 :            :                                 &pdev->dev);
     180         [ +  - ]:        414 :         if (!match)
     181                 :            :                 return -EINVAL;
     182                 :            : 
     183                 :        414 :         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
     184                 :        414 :         data->regs = devm_ioremap_resource(&pdev->dev, res);
     185         [ -  + ]:        414 :         if (IS_ERR(data->regs)) {
     186                 :            :                 err = PTR_ERR(data->regs);
     187                 :          0 :                 dev_err(&pdev->dev, "Could not get registers: %d\n", err);
     188                 :          0 :                 return err;
     189                 :            :         }
     190                 :            : 
     191                 :        414 :         data->clk = devm_clk_get(&pdev->dev, NULL);
     192         [ +  + ]:        414 :         if (IS_ERR(data->clk)) {
     193                 :            :                 err = PTR_ERR(data->clk);
     194         [ -  + ]:        207 :                 if (err != -EPROBE_DEFER)
     195                 :          0 :                         dev_err(&pdev->dev, "Could not get clk: %d\n", err);
     196                 :        207 :                 return err;
     197                 :            :         }
     198                 :            : 
     199                 :        207 :         err = clk_prepare_enable(data->clk);
     200         [ +  - ]:        207 :         if (err)
     201                 :            :                 return err;
     202                 :            : 
     203                 :        207 :         rate = clk_get_rate(data->clk);
     204         [ -  + ]:        207 :         if ((rate < 1920000) || (rate > 5000000))
     205                 :          0 :                 dev_warn(&pdev->dev,
     206                 :            :                          "Clock %pCn running at %lu Hz is outside of the recommended range: 1.92 to 5MHz\n",
     207                 :            :                          data->clk, rate);
     208                 :            : 
     209                 :            :         /* register of thermal sensor and get info from DT */
     210                 :        207 :         tz = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
     211                 :            :                                              &bcm2835_thermal_ops);
     212         [ -  + ]:        207 :         if (IS_ERR(tz)) {
     213                 :            :                 err = PTR_ERR(tz);
     214                 :          0 :                 dev_err(&pdev->dev,
     215                 :            :                         "Failed to register the thermal device: %d\n",
     216                 :            :                         err);
     217                 :          0 :                 goto err_clk;
     218                 :            :         }
     219                 :            : 
     220                 :            :         /*
     221                 :            :          * right now the FW does set up the HW-block, so we are not
     222                 :            :          * touching the configuration registers.
     223                 :            :          * But if the HW is not enabled, then set it up
     224                 :            :          * using "sane" values used by the firmware right now.
     225                 :            :          */
     226                 :        414 :         val = readl(data->regs + BCM2835_TS_TSENSCTL);
     227         [ -  + ]:        207 :         if (!(val & BCM2835_TS_TSENSCTL_RSTB)) {
     228                 :            :                 int trip_temp, offset, slope;
     229                 :            : 
     230                 :          0 :                 slope = thermal_zone_get_slope(tz);
     231                 :          0 :                 offset = thermal_zone_get_offset(tz);
     232                 :            :                 /*
     233                 :            :                  * For now we deal only with critical, otherwise
     234                 :            :                  * would need to iterate
     235                 :            :                  */
     236                 :          0 :                 err = tz->ops->get_trip_temp(tz, 0, &trip_temp);
     237         [ #  # ]:          0 :                 if (err < 0) {
     238                 :          0 :                         dev_err(&pdev->dev,
     239                 :            :                                 "Not able to read trip_temp: %d\n",
     240                 :            :                                 err);
     241                 :          0 :                         goto err_tz;
     242                 :            :                 }
     243                 :            : 
     244                 :            :                 /* set bandgap reference voltage and enable voltage regulator */
     245                 :            :                 val = (BCM2835_TS_TSENSCTL_CTRL_DEFAULT <<
     246                 :            :                        BCM2835_TS_TSENSCTL_CTRL_SHIFT) |
     247                 :            :                       BCM2835_TS_TSENSCTL_REGULEN;
     248                 :            : 
     249                 :            :                 /* use the recommended reset duration */
     250                 :            :                 val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT);
     251                 :            : 
     252                 :            :                 /*  trip_adc value from info */
     253                 :          0 :                 val |= bcm2835_thermal_temp2adc(trip_temp,
     254                 :            :                                                 offset,
     255                 :            :                                                 slope)
     256                 :          0 :                         << BCM2835_TS_TSENSCTL_THOLD_SHIFT;
     257                 :            : 
     258                 :            :                 /* write the value back to the register as 2 steps */
     259                 :          0 :                 writel(val, data->regs + BCM2835_TS_TSENSCTL);
     260                 :          0 :                 val |= BCM2835_TS_TSENSCTL_RSTB;
     261                 :          0 :                 writel(val, data->regs + BCM2835_TS_TSENSCTL);
     262                 :            :         }
     263                 :            : 
     264                 :        207 :         data->tz = tz;
     265                 :            : 
     266                 :            :         platform_set_drvdata(pdev, data);
     267                 :            : 
     268                 :            :         /*
     269                 :            :          * Thermal_zone doesn't enable hwmon as default,
     270                 :            :          * enable it here
     271                 :            :          */
     272                 :        207 :         tz->tzp->no_hwmon = false;
     273                 :        207 :         err = thermal_add_hwmon_sysfs(tz);
     274         [ +  - ]:        207 :         if (err)
     275                 :            :                 goto err_tz;
     276                 :            : 
     277                 :        207 :         bcm2835_thermal_debugfs(pdev);
     278                 :            : 
     279                 :        207 :         return 0;
     280                 :            : err_tz:
     281                 :          0 :         thermal_zone_of_sensor_unregister(&pdev->dev, tz);
     282                 :            : err_clk:
     283                 :          0 :         clk_disable_unprepare(data->clk);
     284                 :            : 
     285                 :          0 :         return err;
     286                 :            : }
     287                 :            : 
     288                 :          0 : static int bcm2835_thermal_remove(struct platform_device *pdev)
     289                 :            : {
     290                 :            :         struct bcm2835_thermal_data *data = platform_get_drvdata(pdev);
     291                 :          0 :         struct thermal_zone_device *tz = data->tz;
     292                 :            : 
     293                 :          0 :         debugfs_remove_recursive(data->debugfsdir);
     294                 :          0 :         thermal_zone_of_sensor_unregister(&pdev->dev, tz);
     295                 :          0 :         clk_disable_unprepare(data->clk);
     296                 :            : 
     297                 :          0 :         return 0;
     298                 :            : }
     299                 :            : 
     300                 :            : static struct platform_driver bcm2835_thermal_driver = {
     301                 :            :         .probe = bcm2835_thermal_probe,
     302                 :            :         .remove = bcm2835_thermal_remove,
     303                 :            :         .driver = {
     304                 :            :                 .name = "bcm2835_thermal",
     305                 :            :                 .of_match_table = bcm2835_thermal_of_match_table,
     306                 :            :         },
     307                 :            : };
     308                 :        207 : module_platform_driver(bcm2835_thermal_driver);
     309                 :            : 
     310                 :            : MODULE_AUTHOR("Martin Sperl");
     311                 :            : MODULE_DESCRIPTION("Thermal driver for bcm2835 chip");
     312                 :            : MODULE_LICENSE("GPL");

Generated by: LCOV version 1.14