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