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