1 /* 2 * R-Car THS/TSC thermal sensor driver 3 * 4 * Copyright (C) 2012 Renesas Solutions Corp. 5 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; version 2 of the License. 10 * 11 * This program is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License along 17 * with this program; if not, write to the Free Software Foundation, Inc., 18 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 19 */ 20 #include <linux/delay.h> 21 #include <linux/err.h> 22 #include <linux/io.h> 23 #include <linux/module.h> 24 #include <linux/platform_device.h> 25 #include <linux/slab.h> 26 #include <linux/spinlock.h> 27 #include <linux/thermal.h> 28 29 #define THSCR 0x2c 30 #define THSSR 0x30 31 32 /* THSCR */ 33 #define CPTAP 0xf 34 35 /* THSSR */ 36 #define CTEMP 0x3f 37 38 39 struct rcar_thermal_priv { 40 void __iomem *base; 41 struct device *dev; 42 spinlock_t lock; 43 u32 comp; 44 }; 45 46 /* 47 * basic functions 48 */ 49 static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) 50 { 51 unsigned long flags; 52 u32 ret; 53 54 spin_lock_irqsave(&priv->lock, flags); 55 56 ret = ioread32(priv->base + reg); 57 58 spin_unlock_irqrestore(&priv->lock, flags); 59 60 return ret; 61 } 62 63 #if 0 /* no user at this point */ 64 static void rcar_thermal_write(struct rcar_thermal_priv *priv, 65 u32 reg, u32 data) 66 { 67 unsigned long flags; 68 69 spin_lock_irqsave(&priv->lock, flags); 70 71 iowrite32(data, priv->base + reg); 72 73 spin_unlock_irqrestore(&priv->lock, flags); 74 } 75 #endif 76 77 static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, 78 u32 mask, u32 data) 79 { 80 unsigned long flags; 81 u32 val; 82 83 spin_lock_irqsave(&priv->lock, flags); 84 85 val = ioread32(priv->base + reg); 86 val &= ~mask; 87 val |= (data & mask); 88 iowrite32(val, priv->base + reg); 89 90 spin_unlock_irqrestore(&priv->lock, flags); 91 } 92 93 /* 94 * zone device functions 95 */ 96 static int rcar_thermal_get_temp(struct thermal_zone_device *zone, 97 unsigned long *temp) 98 { 99 struct rcar_thermal_priv *priv = zone->devdata; 100 int val, min, max, tmp; 101 102 tmp = -200; /* default */ 103 while (1) { 104 if (priv->comp < 1 || priv->comp > 12) { 105 dev_err(priv->dev, 106 "THSSR invalid data (%d)\n", priv->comp); 107 priv->comp = 4; /* for next thermal */ 108 return -EINVAL; 109 } 110 111 /* 112 * THS comparator offset and the reference temperature 113 * 114 * Comparator | reference | Temperature field 115 * offset | temperature | measurement 116 * | (degrees C) | (degrees C) 117 * -------------+---------------+------------------- 118 * 1 | -45 | -45 to -30 119 * 2 | -30 | -30 to -15 120 * 3 | -15 | -15 to 0 121 * 4 | 0 | 0 to +15 122 * 5 | +15 | +15 to +30 123 * 6 | +30 | +30 to +45 124 * 7 | +45 | +45 to +60 125 * 8 | +60 | +60 to +75 126 * 9 | +75 | +75 to +90 127 * 10 | +90 | +90 to +105 128 * 11 | +105 | +105 to +120 129 * 12 | +120 | +120 to +135 130 */ 131 132 /* calculate thermal limitation */ 133 min = (priv->comp * 15) - 60; 134 max = min + 15; 135 136 /* 137 * we need to wait 300us after changing comparator offset 138 * to get stable temperature. 139 * see "Usage Notes" on datasheet 140 */ 141 rcar_thermal_bset(priv, THSCR, CPTAP, priv->comp); 142 udelay(300); 143 144 /* calculate current temperature */ 145 val = rcar_thermal_read(priv, THSSR) & CTEMP; 146 val = (val * 5) - 65; 147 148 dev_dbg(priv->dev, "comp/min/max/val = %d/%d/%d/%d\n", 149 priv->comp, min, max, val); 150 151 /* 152 * If val is same as min/max, then, 153 * it should try again on next comparator. 154 * But the val might be correct temperature. 155 * Keep it on "tmp" and compare with next val. 156 */ 157 if (tmp == val) 158 break; 159 160 if (val <= min) { 161 tmp = min; 162 priv->comp--; /* try again */ 163 } else if (val >= max) { 164 tmp = max; 165 priv->comp++; /* try again */ 166 } else { 167 tmp = val; 168 break; 169 } 170 } 171 172 *temp = tmp; 173 return 0; 174 } 175 176 static struct thermal_zone_device_ops rcar_thermal_zone_ops = { 177 .get_temp = rcar_thermal_get_temp, 178 }; 179 180 /* 181 * platform functions 182 */ 183 static int rcar_thermal_probe(struct platform_device *pdev) 184 { 185 struct thermal_zone_device *zone; 186 struct rcar_thermal_priv *priv; 187 struct resource *res; 188 int ret; 189 190 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 191 if (!res) { 192 dev_err(&pdev->dev, "Could not get platform resource\n"); 193 return -ENODEV; 194 } 195 196 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 197 if (!priv) { 198 dev_err(&pdev->dev, "Could not allocate priv\n"); 199 return -ENOMEM; 200 } 201 202 priv->comp = 4; /* basic setup */ 203 priv->dev = &pdev->dev; 204 spin_lock_init(&priv->lock); 205 priv->base = devm_ioremap_nocache(&pdev->dev, 206 res->start, resource_size(res)); 207 if (!priv->base) { 208 dev_err(&pdev->dev, "Unable to ioremap thermal register\n"); 209 ret = -ENOMEM; 210 goto error_free_priv; 211 } 212 213 zone = thermal_zone_device_register("rcar_thermal", 0, 0, priv, 214 &rcar_thermal_zone_ops, 0, 0); 215 if (IS_ERR(zone)) { 216 dev_err(&pdev->dev, "thermal zone device is NULL\n"); 217 ret = PTR_ERR(zone); 218 goto error_iounmap; 219 } 220 221 platform_set_drvdata(pdev, zone); 222 223 dev_info(&pdev->dev, "proved\n"); 224 225 return 0; 226 227 error_iounmap: 228 devm_iounmap(&pdev->dev, priv->base); 229 error_free_priv: 230 devm_kfree(&pdev->dev, priv); 231 232 return ret; 233 } 234 235 static int rcar_thermal_remove(struct platform_device *pdev) 236 { 237 struct thermal_zone_device *zone = platform_get_drvdata(pdev); 238 struct rcar_thermal_priv *priv = zone->devdata; 239 240 thermal_zone_device_unregister(zone); 241 platform_set_drvdata(pdev, NULL); 242 243 devm_iounmap(&pdev->dev, priv->base); 244 devm_kfree(&pdev->dev, priv); 245 246 return 0; 247 } 248 249 static struct platform_driver rcar_thermal_driver = { 250 .driver = { 251 .name = "rcar_thermal", 252 }, 253 .probe = rcar_thermal_probe, 254 .remove = rcar_thermal_remove, 255 }; 256 module_platform_driver(rcar_thermal_driver); 257 258 MODULE_LICENSE("GPL"); 259 MODULE_DESCRIPTION("R-Car THS/TSC thermal sensor driver"); 260 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 261