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 #define MCELSIUS(temp) ((temp) * 1000) 47 #define rcar_zone_to_priv(zone) (zone->devdata) 48 49 /* 50 * basic functions 51 */ 52 static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) 53 { 54 unsigned long flags; 55 u32 ret; 56 57 spin_lock_irqsave(&priv->lock, flags); 58 59 ret = ioread32(priv->base + reg); 60 61 spin_unlock_irqrestore(&priv->lock, flags); 62 63 return ret; 64 } 65 66 #if 0 /* no user at this point */ 67 static void rcar_thermal_write(struct rcar_thermal_priv *priv, 68 u32 reg, u32 data) 69 { 70 unsigned long flags; 71 72 spin_lock_irqsave(&priv->lock, flags); 73 74 iowrite32(data, priv->base + reg); 75 76 spin_unlock_irqrestore(&priv->lock, flags); 77 } 78 #endif 79 80 static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, 81 u32 mask, u32 data) 82 { 83 unsigned long flags; 84 u32 val; 85 86 spin_lock_irqsave(&priv->lock, flags); 87 88 val = ioread32(priv->base + reg); 89 val &= ~mask; 90 val |= (data & mask); 91 iowrite32(val, priv->base + reg); 92 93 spin_unlock_irqrestore(&priv->lock, flags); 94 } 95 96 /* 97 * zone device functions 98 */ 99 static int rcar_thermal_get_temp(struct thermal_zone_device *zone, 100 unsigned long *temp) 101 { 102 struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); 103 int val, min, max, tmp; 104 105 tmp = -200; /* default */ 106 while (1) { 107 if (priv->comp < 1 || priv->comp > 12) { 108 dev_err(priv->dev, 109 "THSSR invalid data (%d)\n", priv->comp); 110 priv->comp = 4; /* for next thermal */ 111 return -EINVAL; 112 } 113 114 /* 115 * THS comparator offset and the reference temperature 116 * 117 * Comparator | reference | Temperature field 118 * offset | temperature | measurement 119 * | (degrees C) | (degrees C) 120 * -------------+---------------+------------------- 121 * 1 | -45 | -45 to -30 122 * 2 | -30 | -30 to -15 123 * 3 | -15 | -15 to 0 124 * 4 | 0 | 0 to +15 125 * 5 | +15 | +15 to +30 126 * 6 | +30 | +30 to +45 127 * 7 | +45 | +45 to +60 128 * 8 | +60 | +60 to +75 129 * 9 | +75 | +75 to +90 130 * 10 | +90 | +90 to +105 131 * 11 | +105 | +105 to +120 132 * 12 | +120 | +120 to +135 133 */ 134 135 /* calculate thermal limitation */ 136 min = (priv->comp * 15) - 60; 137 max = min + 15; 138 139 /* 140 * we need to wait 300us after changing comparator offset 141 * to get stable temperature. 142 * see "Usage Notes" on datasheet 143 */ 144 rcar_thermal_bset(priv, THSCR, CPTAP, priv->comp); 145 udelay(300); 146 147 /* calculate current temperature */ 148 val = rcar_thermal_read(priv, THSSR) & CTEMP; 149 val = (val * 5) - 65; 150 151 dev_dbg(priv->dev, "comp/min/max/val = %d/%d/%d/%d\n", 152 priv->comp, min, max, val); 153 154 /* 155 * If val is same as min/max, then, 156 * it should try again on next comparator. 157 * But the val might be correct temperature. 158 * Keep it on "tmp" and compare with next val. 159 */ 160 if (tmp == val) 161 break; 162 163 if (val <= min) { 164 tmp = min; 165 priv->comp--; /* try again */ 166 } else if (val >= max) { 167 tmp = max; 168 priv->comp++; /* try again */ 169 } else { 170 tmp = val; 171 break; 172 } 173 } 174 175 *temp = MCELSIUS(tmp); 176 return 0; 177 } 178 179 static struct thermal_zone_device_ops rcar_thermal_zone_ops = { 180 .get_temp = rcar_thermal_get_temp, 181 }; 182 183 /* 184 * platform functions 185 */ 186 static int rcar_thermal_probe(struct platform_device *pdev) 187 { 188 struct thermal_zone_device *zone; 189 struct rcar_thermal_priv *priv; 190 struct resource *res; 191 192 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 193 if (!res) { 194 dev_err(&pdev->dev, "Could not get platform resource\n"); 195 return -ENODEV; 196 } 197 198 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 199 if (!priv) { 200 dev_err(&pdev->dev, "Could not allocate priv\n"); 201 return -ENOMEM; 202 } 203 204 priv->comp = 4; /* basic setup */ 205 priv->dev = &pdev->dev; 206 spin_lock_init(&priv->lock); 207 priv->base = devm_ioremap_nocache(&pdev->dev, 208 res->start, resource_size(res)); 209 if (!priv->base) { 210 dev_err(&pdev->dev, "Unable to ioremap thermal register\n"); 211 return -ENOMEM; 212 } 213 214 zone = thermal_zone_device_register("rcar_thermal", 0, 0, priv, 215 &rcar_thermal_zone_ops, NULL, 0, 0); 216 if (IS_ERR(zone)) { 217 dev_err(&pdev->dev, "thermal zone device is NULL\n"); 218 return PTR_ERR(zone); 219 } 220 221 platform_set_drvdata(pdev, zone); 222 223 dev_info(&pdev->dev, "proved\n"); 224 225 return 0; 226 } 227 228 static int rcar_thermal_remove(struct platform_device *pdev) 229 { 230 struct thermal_zone_device *zone = platform_get_drvdata(pdev); 231 232 thermal_zone_device_unregister(zone); 233 platform_set_drvdata(pdev, NULL); 234 235 return 0; 236 } 237 238 static struct platform_driver rcar_thermal_driver = { 239 .driver = { 240 .name = "rcar_thermal", 241 }, 242 .probe = rcar_thermal_probe, 243 .remove = rcar_thermal_remove, 244 }; 245 module_platform_driver(rcar_thermal_driver); 246 247 MODULE_LICENSE("GPL"); 248 MODULE_DESCRIPTION("R-Car THS/TSC thermal sensor driver"); 249 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 250