Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Driver for Broadcom BCM2835 SoC temperature sensor
0004  *
0005  * Copyright (C) 2016 Martin Sperl
0006  */
0007 
0008 #include <linux/clk.h>
0009 #include <linux/debugfs.h>
0010 #include <linux/device.h>
0011 #include <linux/err.h>
0012 #include <linux/io.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/of.h>
0016 #include <linux/of_address.h>
0017 #include <linux/of_device.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/thermal.h>
0020 
0021 #include "../thermal_hwmon.h"
0022 
0023 #define BCM2835_TS_TSENSCTL         0x00
0024 #define BCM2835_TS_TSENSSTAT            0x04
0025 
0026 #define BCM2835_TS_TSENSCTL_PRWDW       BIT(0)
0027 #define BCM2835_TS_TSENSCTL_RSTB        BIT(1)
0028 
0029 /*
0030  * bandgap reference voltage in 6 mV increments
0031  * 000b = 1178 mV, 001b = 1184 mV, ... 111b = 1220 mV
0032  */
0033 #define BCM2835_TS_TSENSCTL_CTRL_BITS       3
0034 #define BCM2835_TS_TSENSCTL_CTRL_SHIFT      2
0035 #define BCM2835_TS_TSENSCTL_CTRL_MASK           \
0036     GENMASK(BCM2835_TS_TSENSCTL_CTRL_BITS +     \
0037         BCM2835_TS_TSENSCTL_CTRL_SHIFT - 1, \
0038         BCM2835_TS_TSENSCTL_CTRL_SHIFT)
0039 #define BCM2835_TS_TSENSCTL_CTRL_DEFAULT    1
0040 #define BCM2835_TS_TSENSCTL_EN_INT      BIT(5)
0041 #define BCM2835_TS_TSENSCTL_DIRECT      BIT(6)
0042 #define BCM2835_TS_TSENSCTL_CLR_INT     BIT(7)
0043 #define BCM2835_TS_TSENSCTL_THOLD_SHIFT     8
0044 #define BCM2835_TS_TSENSCTL_THOLD_BITS      10
0045 #define BCM2835_TS_TSENSCTL_THOLD_MASK           \
0046     GENMASK(BCM2835_TS_TSENSCTL_THOLD_BITS +     \
0047         BCM2835_TS_TSENSCTL_THOLD_SHIFT - 1, \
0048         BCM2835_TS_TSENSCTL_THOLD_SHIFT)
0049 /*
0050  * time how long the block to be asserted in reset
0051  * which based on a clock counter (TSENS clock assumed)
0052  */
0053 #define BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT  18
0054 #define BCM2835_TS_TSENSCTL_RSTDELAY_BITS   8
0055 #define BCM2835_TS_TSENSCTL_REGULEN     BIT(26)
0056 
0057 #define BCM2835_TS_TSENSSTAT_DATA_BITS      10
0058 #define BCM2835_TS_TSENSSTAT_DATA_SHIFT     0
0059 #define BCM2835_TS_TSENSSTAT_DATA_MASK           \
0060     GENMASK(BCM2835_TS_TSENSSTAT_DATA_BITS +     \
0061         BCM2835_TS_TSENSSTAT_DATA_SHIFT - 1, \
0062         BCM2835_TS_TSENSSTAT_DATA_SHIFT)
0063 #define BCM2835_TS_TSENSSTAT_VALID      BIT(10)
0064 #define BCM2835_TS_TSENSSTAT_INTERRUPT      BIT(11)
0065 
0066 struct bcm2835_thermal_data {
0067     struct thermal_zone_device *tz;
0068     void __iomem *regs;
0069     struct clk *clk;
0070     struct dentry *debugfsdir;
0071 };
0072 
0073 static int bcm2835_thermal_adc2temp(u32 adc, int offset, int slope)
0074 {
0075     return offset + slope * adc;
0076 }
0077 
0078 static int bcm2835_thermal_temp2adc(int temp, int offset, int slope)
0079 {
0080     temp -= offset;
0081     temp /= slope;
0082 
0083     if (temp < 0)
0084         temp = 0;
0085     if (temp >= BIT(BCM2835_TS_TSENSSTAT_DATA_BITS))
0086         temp = BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1;
0087 
0088     return temp;
0089 }
0090 
0091 static int bcm2835_thermal_get_temp(void *d, int *temp)
0092 {
0093     struct bcm2835_thermal_data *data = d;
0094     u32 val = readl(data->regs + BCM2835_TS_TSENSSTAT);
0095 
0096     if (!(val & BCM2835_TS_TSENSSTAT_VALID))
0097         return -EIO;
0098 
0099     val &= BCM2835_TS_TSENSSTAT_DATA_MASK;
0100 
0101     *temp = bcm2835_thermal_adc2temp(
0102         val,
0103         thermal_zone_get_offset(data->tz),
0104         thermal_zone_get_slope(data->tz));
0105 
0106     return 0;
0107 }
0108 
0109 static const struct debugfs_reg32 bcm2835_thermal_regs[] = {
0110     {
0111         .name = "ctl",
0112         .offset = 0
0113     },
0114     {
0115         .name = "stat",
0116         .offset = 4
0117     }
0118 };
0119 
0120 static void bcm2835_thermal_debugfs(struct platform_device *pdev)
0121 {
0122     struct bcm2835_thermal_data *data = platform_get_drvdata(pdev);
0123     struct debugfs_regset32 *regset;
0124 
0125     data->debugfsdir = debugfs_create_dir("bcm2835_thermal", NULL);
0126 
0127     regset = devm_kzalloc(&pdev->dev, sizeof(*regset), GFP_KERNEL);
0128     if (!regset)
0129         return;
0130 
0131     regset->regs = bcm2835_thermal_regs;
0132     regset->nregs = ARRAY_SIZE(bcm2835_thermal_regs);
0133     regset->base = data->regs;
0134 
0135     debugfs_create_regset32("regset", 0444, data->debugfsdir, regset);
0136 }
0137 
0138 static const struct thermal_zone_of_device_ops bcm2835_thermal_ops = {
0139     .get_temp = bcm2835_thermal_get_temp,
0140 };
0141 
0142 /*
0143  * Note: as per Raspberry Foundation FAQ
0144  * (https://www.raspberrypi.org/help/faqs/#performanceOperatingTemperature)
0145  * the recommended temperature range for the SoC -40C to +85C
0146  * so the trip limit is set to 80C.
0147  * this applies to all the BCM283X SoC
0148  */
0149 
0150 static const struct of_device_id bcm2835_thermal_of_match_table[] = {
0151     {
0152         .compatible = "brcm,bcm2835-thermal",
0153     },
0154     {
0155         .compatible = "brcm,bcm2836-thermal",
0156     },
0157     {
0158         .compatible = "brcm,bcm2837-thermal",
0159     },
0160     {},
0161 };
0162 MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table);
0163 
0164 static int bcm2835_thermal_probe(struct platform_device *pdev)
0165 {
0166     const struct of_device_id *match;
0167     struct thermal_zone_device *tz;
0168     struct bcm2835_thermal_data *data;
0169     struct resource *res;
0170     int err = 0;
0171     u32 val;
0172     unsigned long rate;
0173 
0174     data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
0175     if (!data)
0176         return -ENOMEM;
0177 
0178     match = of_match_device(bcm2835_thermal_of_match_table,
0179                 &pdev->dev);
0180     if (!match)
0181         return -EINVAL;
0182 
0183     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0184     data->regs = devm_ioremap_resource(&pdev->dev, res);
0185     if (IS_ERR(data->regs)) {
0186         err = PTR_ERR(data->regs);
0187         return err;
0188     }
0189 
0190     data->clk = devm_clk_get(&pdev->dev, NULL);
0191     if (IS_ERR(data->clk)) {
0192         err = PTR_ERR(data->clk);
0193         if (err != -EPROBE_DEFER)
0194             dev_err(&pdev->dev, "Could not get clk: %d\n", err);
0195         return err;
0196     }
0197 
0198     err = clk_prepare_enable(data->clk);
0199     if (err)
0200         return err;
0201 
0202     rate = clk_get_rate(data->clk);
0203     if ((rate < 1920000) || (rate > 5000000))
0204         dev_warn(&pdev->dev,
0205              "Clock %pCn running at %lu Hz is outside of the recommended range: 1.92 to 5MHz\n",
0206              data->clk, rate);
0207 
0208     /* register of thermal sensor and get info from DT */
0209     tz = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
0210                          &bcm2835_thermal_ops);
0211     if (IS_ERR(tz)) {
0212         err = PTR_ERR(tz);
0213         dev_err(&pdev->dev,
0214             "Failed to register the thermal device: %d\n",
0215             err);
0216         goto err_clk;
0217     }
0218 
0219     /*
0220      * right now the FW does set up the HW-block, so we are not
0221      * touching the configuration registers.
0222      * But if the HW is not enabled, then set it up
0223      * using "sane" values used by the firmware right now.
0224      */
0225     val = readl(data->regs + BCM2835_TS_TSENSCTL);
0226     if (!(val & BCM2835_TS_TSENSCTL_RSTB)) {
0227         int trip_temp, offset, slope;
0228 
0229         slope = thermal_zone_get_slope(tz);
0230         offset = thermal_zone_get_offset(tz);
0231         /*
0232          * For now we deal only with critical, otherwise
0233          * would need to iterate
0234          */
0235         err = tz->ops->get_trip_temp(tz, 0, &trip_temp);
0236         if (err < 0) {
0237             dev_err(&pdev->dev,
0238                 "Not able to read trip_temp: %d\n",
0239                 err);
0240             goto err_tz;
0241         }
0242 
0243         /* set bandgap reference voltage and enable voltage regulator */
0244         val = (BCM2835_TS_TSENSCTL_CTRL_DEFAULT <<
0245                BCM2835_TS_TSENSCTL_CTRL_SHIFT) |
0246               BCM2835_TS_TSENSCTL_REGULEN;
0247 
0248         /* use the recommended reset duration */
0249         val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT);
0250 
0251         /*  trip_adc value from info */
0252         val |= bcm2835_thermal_temp2adc(trip_temp,
0253                         offset,
0254                         slope)
0255             << BCM2835_TS_TSENSCTL_THOLD_SHIFT;
0256 
0257         /* write the value back to the register as 2 steps */
0258         writel(val, data->regs + BCM2835_TS_TSENSCTL);
0259         val |= BCM2835_TS_TSENSCTL_RSTB;
0260         writel(val, data->regs + BCM2835_TS_TSENSCTL);
0261     }
0262 
0263     data->tz = tz;
0264 
0265     platform_set_drvdata(pdev, data);
0266 
0267     /*
0268      * Thermal_zone doesn't enable hwmon as default,
0269      * enable it here
0270      */
0271     tz->tzp->no_hwmon = false;
0272     err = thermal_add_hwmon_sysfs(tz);
0273     if (err)
0274         goto err_tz;
0275 
0276     bcm2835_thermal_debugfs(pdev);
0277 
0278     return 0;
0279 err_tz:
0280     thermal_zone_of_sensor_unregister(&pdev->dev, tz);
0281 err_clk:
0282     clk_disable_unprepare(data->clk);
0283 
0284     return err;
0285 }
0286 
0287 static int bcm2835_thermal_remove(struct platform_device *pdev)
0288 {
0289     struct bcm2835_thermal_data *data = platform_get_drvdata(pdev);
0290     struct thermal_zone_device *tz = data->tz;
0291 
0292     debugfs_remove_recursive(data->debugfsdir);
0293     thermal_zone_of_sensor_unregister(&pdev->dev, tz);
0294     clk_disable_unprepare(data->clk);
0295 
0296     return 0;
0297 }
0298 
0299 static struct platform_driver bcm2835_thermal_driver = {
0300     .probe = bcm2835_thermal_probe,
0301     .remove = bcm2835_thermal_remove,
0302     .driver = {
0303         .name = "bcm2835_thermal",
0304         .of_match_table = bcm2835_thermal_of_match_table,
0305     },
0306 };
0307 module_platform_driver(bcm2835_thermal_driver);
0308 
0309 MODULE_AUTHOR("Martin Sperl");
0310 MODULE_DESCRIPTION("Thermal driver for bcm2835 chip");
0311 MODULE_LICENSE("GPL");