11e426ffdSKuninori Morimoto /* 21e426ffdSKuninori Morimoto * R-Car THS/TSC thermal sensor driver 31e426ffdSKuninori Morimoto * 41e426ffdSKuninori Morimoto * Copyright (C) 2012 Renesas Solutions Corp. 51e426ffdSKuninori Morimoto * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 61e426ffdSKuninori Morimoto * 71e426ffdSKuninori Morimoto * This program is free software; you can redistribute it and/or modify 81e426ffdSKuninori Morimoto * it under the terms of the GNU General Public License as published by 91e426ffdSKuninori Morimoto * the Free Software Foundation; version 2 of the License. 101e426ffdSKuninori Morimoto * 111e426ffdSKuninori Morimoto * This program is distributed in the hope that it will be useful, but 121e426ffdSKuninori Morimoto * WITHOUT ANY WARRANTY; without even the implied warranty of 131e426ffdSKuninori Morimoto * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 141e426ffdSKuninori Morimoto * General Public License for more details. 151e426ffdSKuninori Morimoto * 161e426ffdSKuninori Morimoto * You should have received a copy of the GNU General Public License along 171e426ffdSKuninori Morimoto * with this program; if not, write to the Free Software Foundation, Inc., 181e426ffdSKuninori Morimoto * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 191e426ffdSKuninori Morimoto */ 201e426ffdSKuninori Morimoto #include <linux/delay.h> 211e426ffdSKuninori Morimoto #include <linux/err.h> 221e426ffdSKuninori Morimoto #include <linux/io.h> 231e426ffdSKuninori Morimoto #include <linux/module.h> 241e426ffdSKuninori Morimoto #include <linux/platform_device.h> 25d2a73e22Skuninori.morimoto.gx@renesas.com #include <linux/reboot.h> 261e426ffdSKuninori Morimoto #include <linux/slab.h> 271e426ffdSKuninori Morimoto #include <linux/spinlock.h> 281e426ffdSKuninori Morimoto #include <linux/thermal.h> 291e426ffdSKuninori Morimoto 30d2a73e22Skuninori.morimoto.gx@renesas.com #define IDLE_INTERVAL 5000 31d2a73e22Skuninori.morimoto.gx@renesas.com 321e426ffdSKuninori Morimoto #define THSCR 0x2c 331e426ffdSKuninori Morimoto #define THSSR 0x30 341e426ffdSKuninori Morimoto 351e426ffdSKuninori Morimoto /* THSCR */ 361e426ffdSKuninori Morimoto #define CPTAP 0xf 371e426ffdSKuninori Morimoto 381e426ffdSKuninori Morimoto /* THSSR */ 391e426ffdSKuninori Morimoto #define CTEMP 0x3f 401e426ffdSKuninori Morimoto 411e426ffdSKuninori Morimoto 421e426ffdSKuninori Morimoto struct rcar_thermal_priv { 431e426ffdSKuninori Morimoto void __iomem *base; 441e426ffdSKuninori Morimoto struct device *dev; 451e426ffdSKuninori Morimoto spinlock_t lock; 461e426ffdSKuninori Morimoto u32 comp; 471e426ffdSKuninori Morimoto }; 481e426ffdSKuninori Morimoto 49c499703eSKuninori Morimoto #define MCELSIUS(temp) ((temp) * 1000) 509dde8f86SKuninori Morimoto #define rcar_zone_to_priv(zone) ((zone)->devdata) 51c499703eSKuninori Morimoto 521e426ffdSKuninori Morimoto /* 531e426ffdSKuninori Morimoto * basic functions 541e426ffdSKuninori Morimoto */ 551e426ffdSKuninori Morimoto static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) 561e426ffdSKuninori Morimoto { 571e426ffdSKuninori Morimoto unsigned long flags; 581e426ffdSKuninori Morimoto u32 ret; 591e426ffdSKuninori Morimoto 601e426ffdSKuninori Morimoto spin_lock_irqsave(&priv->lock, flags); 611e426ffdSKuninori Morimoto 621e426ffdSKuninori Morimoto ret = ioread32(priv->base + reg); 631e426ffdSKuninori Morimoto 641e426ffdSKuninori Morimoto spin_unlock_irqrestore(&priv->lock, flags); 651e426ffdSKuninori Morimoto 661e426ffdSKuninori Morimoto return ret; 671e426ffdSKuninori Morimoto } 681e426ffdSKuninori Morimoto 691e426ffdSKuninori Morimoto #if 0 /* no user at this point */ 701e426ffdSKuninori Morimoto static void rcar_thermal_write(struct rcar_thermal_priv *priv, 711e426ffdSKuninori Morimoto u32 reg, u32 data) 721e426ffdSKuninori Morimoto { 731e426ffdSKuninori Morimoto unsigned long flags; 741e426ffdSKuninori Morimoto 751e426ffdSKuninori Morimoto spin_lock_irqsave(&priv->lock, flags); 761e426ffdSKuninori Morimoto 771e426ffdSKuninori Morimoto iowrite32(data, priv->base + reg); 781e426ffdSKuninori Morimoto 791e426ffdSKuninori Morimoto spin_unlock_irqrestore(&priv->lock, flags); 801e426ffdSKuninori Morimoto } 811e426ffdSKuninori Morimoto #endif 821e426ffdSKuninori Morimoto 831e426ffdSKuninori Morimoto static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, 841e426ffdSKuninori Morimoto u32 mask, u32 data) 851e426ffdSKuninori Morimoto { 861e426ffdSKuninori Morimoto unsigned long flags; 871e426ffdSKuninori Morimoto u32 val; 881e426ffdSKuninori Morimoto 891e426ffdSKuninori Morimoto spin_lock_irqsave(&priv->lock, flags); 901e426ffdSKuninori Morimoto 911e426ffdSKuninori Morimoto val = ioread32(priv->base + reg); 921e426ffdSKuninori Morimoto val &= ~mask; 931e426ffdSKuninori Morimoto val |= (data & mask); 941e426ffdSKuninori Morimoto iowrite32(val, priv->base + reg); 951e426ffdSKuninori Morimoto 961e426ffdSKuninori Morimoto spin_unlock_irqrestore(&priv->lock, flags); 971e426ffdSKuninori Morimoto } 981e426ffdSKuninori Morimoto 991e426ffdSKuninori Morimoto /* 1001e426ffdSKuninori Morimoto * zone device functions 1011e426ffdSKuninori Morimoto */ 1021e426ffdSKuninori Morimoto static int rcar_thermal_get_temp(struct thermal_zone_device *zone, 1031e426ffdSKuninori Morimoto unsigned long *temp) 1041e426ffdSKuninori Morimoto { 105d12250efSKuninori Morimoto struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); 1061e426ffdSKuninori Morimoto int val, min, max, tmp; 1071e426ffdSKuninori Morimoto 1081e426ffdSKuninori Morimoto tmp = -200; /* default */ 1091e426ffdSKuninori Morimoto while (1) { 1101e426ffdSKuninori Morimoto if (priv->comp < 1 || priv->comp > 12) { 1111e426ffdSKuninori Morimoto dev_err(priv->dev, 1121e426ffdSKuninori Morimoto "THSSR invalid data (%d)\n", priv->comp); 1131e426ffdSKuninori Morimoto priv->comp = 4; /* for next thermal */ 1141e426ffdSKuninori Morimoto return -EINVAL; 1151e426ffdSKuninori Morimoto } 1161e426ffdSKuninori Morimoto 1171e426ffdSKuninori Morimoto /* 1181e426ffdSKuninori Morimoto * THS comparator offset and the reference temperature 1191e426ffdSKuninori Morimoto * 1201e426ffdSKuninori Morimoto * Comparator | reference | Temperature field 1211e426ffdSKuninori Morimoto * offset | temperature | measurement 1221e426ffdSKuninori Morimoto * | (degrees C) | (degrees C) 1231e426ffdSKuninori Morimoto * -------------+---------------+------------------- 1241e426ffdSKuninori Morimoto * 1 | -45 | -45 to -30 1251e426ffdSKuninori Morimoto * 2 | -30 | -30 to -15 1261e426ffdSKuninori Morimoto * 3 | -15 | -15 to 0 1271e426ffdSKuninori Morimoto * 4 | 0 | 0 to +15 1281e426ffdSKuninori Morimoto * 5 | +15 | +15 to +30 1291e426ffdSKuninori Morimoto * 6 | +30 | +30 to +45 1301e426ffdSKuninori Morimoto * 7 | +45 | +45 to +60 1311e426ffdSKuninori Morimoto * 8 | +60 | +60 to +75 1321e426ffdSKuninori Morimoto * 9 | +75 | +75 to +90 1331e426ffdSKuninori Morimoto * 10 | +90 | +90 to +105 1341e426ffdSKuninori Morimoto * 11 | +105 | +105 to +120 1351e426ffdSKuninori Morimoto * 12 | +120 | +120 to +135 1361e426ffdSKuninori Morimoto */ 1371e426ffdSKuninori Morimoto 1381e426ffdSKuninori Morimoto /* calculate thermal limitation */ 1391e426ffdSKuninori Morimoto min = (priv->comp * 15) - 60; 1401e426ffdSKuninori Morimoto max = min + 15; 1411e426ffdSKuninori Morimoto 1421e426ffdSKuninori Morimoto /* 1431e426ffdSKuninori Morimoto * we need to wait 300us after changing comparator offset 1441e426ffdSKuninori Morimoto * to get stable temperature. 1451e426ffdSKuninori Morimoto * see "Usage Notes" on datasheet 1461e426ffdSKuninori Morimoto */ 1471e426ffdSKuninori Morimoto rcar_thermal_bset(priv, THSCR, CPTAP, priv->comp); 1481e426ffdSKuninori Morimoto udelay(300); 1491e426ffdSKuninori Morimoto 1501e426ffdSKuninori Morimoto /* calculate current temperature */ 1511e426ffdSKuninori Morimoto val = rcar_thermal_read(priv, THSSR) & CTEMP; 1521e426ffdSKuninori Morimoto val = (val * 5) - 65; 1531e426ffdSKuninori Morimoto 1541e426ffdSKuninori Morimoto dev_dbg(priv->dev, "comp/min/max/val = %d/%d/%d/%d\n", 1551e426ffdSKuninori Morimoto priv->comp, min, max, val); 1561e426ffdSKuninori Morimoto 1571e426ffdSKuninori Morimoto /* 1581e426ffdSKuninori Morimoto * If val is same as min/max, then, 1591e426ffdSKuninori Morimoto * it should try again on next comparator. 1601e426ffdSKuninori Morimoto * But the val might be correct temperature. 1611e426ffdSKuninori Morimoto * Keep it on "tmp" and compare with next val. 1621e426ffdSKuninori Morimoto */ 1631e426ffdSKuninori Morimoto if (tmp == val) 1641e426ffdSKuninori Morimoto break; 1651e426ffdSKuninori Morimoto 1661e426ffdSKuninori Morimoto if (val <= min) { 1671e426ffdSKuninori Morimoto tmp = min; 1681e426ffdSKuninori Morimoto priv->comp--; /* try again */ 1691e426ffdSKuninori Morimoto } else if (val >= max) { 1701e426ffdSKuninori Morimoto tmp = max; 1711e426ffdSKuninori Morimoto priv->comp++; /* try again */ 1721e426ffdSKuninori Morimoto } else { 1731e426ffdSKuninori Morimoto tmp = val; 1741e426ffdSKuninori Morimoto break; 1751e426ffdSKuninori Morimoto } 1761e426ffdSKuninori Morimoto } 1771e426ffdSKuninori Morimoto 178c499703eSKuninori Morimoto *temp = MCELSIUS(tmp); 1791e426ffdSKuninori Morimoto return 0; 1801e426ffdSKuninori Morimoto } 1811e426ffdSKuninori Morimoto 182d2a73e22Skuninori.morimoto.gx@renesas.com static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, 183d2a73e22Skuninori.morimoto.gx@renesas.com int trip, enum thermal_trip_type *type) 184d2a73e22Skuninori.morimoto.gx@renesas.com { 185d2a73e22Skuninori.morimoto.gx@renesas.com struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); 186d2a73e22Skuninori.morimoto.gx@renesas.com 187d2a73e22Skuninori.morimoto.gx@renesas.com /* see rcar_thermal_get_temp() */ 188d2a73e22Skuninori.morimoto.gx@renesas.com switch (trip) { 189d2a73e22Skuninori.morimoto.gx@renesas.com case 0: /* +90 <= temp */ 190d2a73e22Skuninori.morimoto.gx@renesas.com *type = THERMAL_TRIP_CRITICAL; 191d2a73e22Skuninori.morimoto.gx@renesas.com break; 192d2a73e22Skuninori.morimoto.gx@renesas.com default: 193d2a73e22Skuninori.morimoto.gx@renesas.com dev_err(priv->dev, "rcar driver trip error\n"); 194d2a73e22Skuninori.morimoto.gx@renesas.com return -EINVAL; 195d2a73e22Skuninori.morimoto.gx@renesas.com } 196d2a73e22Skuninori.morimoto.gx@renesas.com 197d2a73e22Skuninori.morimoto.gx@renesas.com return 0; 198d2a73e22Skuninori.morimoto.gx@renesas.com } 199d2a73e22Skuninori.morimoto.gx@renesas.com 200d2a73e22Skuninori.morimoto.gx@renesas.com static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, 201d2a73e22Skuninori.morimoto.gx@renesas.com int trip, unsigned long *temp) 202d2a73e22Skuninori.morimoto.gx@renesas.com { 203d2a73e22Skuninori.morimoto.gx@renesas.com struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); 204d2a73e22Skuninori.morimoto.gx@renesas.com 205d2a73e22Skuninori.morimoto.gx@renesas.com /* see rcar_thermal_get_temp() */ 206d2a73e22Skuninori.morimoto.gx@renesas.com switch (trip) { 207d2a73e22Skuninori.morimoto.gx@renesas.com case 0: /* +90 <= temp */ 208d2a73e22Skuninori.morimoto.gx@renesas.com *temp = MCELSIUS(90); 209d2a73e22Skuninori.morimoto.gx@renesas.com break; 210d2a73e22Skuninori.morimoto.gx@renesas.com default: 211d2a73e22Skuninori.morimoto.gx@renesas.com dev_err(priv->dev, "rcar driver trip error\n"); 212d2a73e22Skuninori.morimoto.gx@renesas.com return -EINVAL; 213d2a73e22Skuninori.morimoto.gx@renesas.com } 214d2a73e22Skuninori.morimoto.gx@renesas.com 215d2a73e22Skuninori.morimoto.gx@renesas.com return 0; 216d2a73e22Skuninori.morimoto.gx@renesas.com } 217d2a73e22Skuninori.morimoto.gx@renesas.com 218d2a73e22Skuninori.morimoto.gx@renesas.com static int rcar_thermal_notify(struct thermal_zone_device *zone, 219d2a73e22Skuninori.morimoto.gx@renesas.com int trip, enum thermal_trip_type type) 220d2a73e22Skuninori.morimoto.gx@renesas.com { 221d2a73e22Skuninori.morimoto.gx@renesas.com struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); 222d2a73e22Skuninori.morimoto.gx@renesas.com 223d2a73e22Skuninori.morimoto.gx@renesas.com switch (type) { 224d2a73e22Skuninori.morimoto.gx@renesas.com case THERMAL_TRIP_CRITICAL: 225d2a73e22Skuninori.morimoto.gx@renesas.com /* FIXME */ 226d2a73e22Skuninori.morimoto.gx@renesas.com dev_warn(priv->dev, 227d2a73e22Skuninori.morimoto.gx@renesas.com "Thermal reached to critical temperature\n"); 228d2a73e22Skuninori.morimoto.gx@renesas.com machine_power_off(); 229d2a73e22Skuninori.morimoto.gx@renesas.com break; 230d2a73e22Skuninori.morimoto.gx@renesas.com default: 231d2a73e22Skuninori.morimoto.gx@renesas.com break; 232d2a73e22Skuninori.morimoto.gx@renesas.com } 233d2a73e22Skuninori.morimoto.gx@renesas.com 234d2a73e22Skuninori.morimoto.gx@renesas.com return 0; 235d2a73e22Skuninori.morimoto.gx@renesas.com } 236d2a73e22Skuninori.morimoto.gx@renesas.com 2371e426ffdSKuninori Morimoto static struct thermal_zone_device_ops rcar_thermal_zone_ops = { 2381e426ffdSKuninori Morimoto .get_temp = rcar_thermal_get_temp, 239d2a73e22Skuninori.morimoto.gx@renesas.com .get_trip_type = rcar_thermal_get_trip_type, 240d2a73e22Skuninori.morimoto.gx@renesas.com .get_trip_temp = rcar_thermal_get_trip_temp, 241d2a73e22Skuninori.morimoto.gx@renesas.com .notify = rcar_thermal_notify, 2421e426ffdSKuninori Morimoto }; 2431e426ffdSKuninori Morimoto 2441e426ffdSKuninori Morimoto /* 2451e426ffdSKuninori Morimoto * platform functions 2461e426ffdSKuninori Morimoto */ 2471e426ffdSKuninori Morimoto static int rcar_thermal_probe(struct platform_device *pdev) 2481e426ffdSKuninori Morimoto { 2491e426ffdSKuninori Morimoto struct thermal_zone_device *zone; 2501e426ffdSKuninori Morimoto struct rcar_thermal_priv *priv; 2511e426ffdSKuninori Morimoto struct resource *res; 2521e426ffdSKuninori Morimoto 2531e426ffdSKuninori Morimoto res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2541e426ffdSKuninori Morimoto if (!res) { 2551e426ffdSKuninori Morimoto dev_err(&pdev->dev, "Could not get platform resource\n"); 2561e426ffdSKuninori Morimoto return -ENODEV; 2571e426ffdSKuninori Morimoto } 2581e426ffdSKuninori Morimoto 2591e426ffdSKuninori Morimoto priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 2601e426ffdSKuninori Morimoto if (!priv) { 2611e426ffdSKuninori Morimoto dev_err(&pdev->dev, "Could not allocate priv\n"); 2621e426ffdSKuninori Morimoto return -ENOMEM; 2631e426ffdSKuninori Morimoto } 2641e426ffdSKuninori Morimoto 2651e426ffdSKuninori Morimoto priv->comp = 4; /* basic setup */ 2661e426ffdSKuninori Morimoto priv->dev = &pdev->dev; 2671e426ffdSKuninori Morimoto spin_lock_init(&priv->lock); 2681e426ffdSKuninori Morimoto priv->base = devm_ioremap_nocache(&pdev->dev, 2691e426ffdSKuninori Morimoto res->start, resource_size(res)); 2701e426ffdSKuninori Morimoto if (!priv->base) { 2711e426ffdSKuninori Morimoto dev_err(&pdev->dev, "Unable to ioremap thermal register\n"); 2724e8e2f64SKuninori Morimoto return -ENOMEM; 2731e426ffdSKuninori Morimoto } 2741e426ffdSKuninori Morimoto 275d2a73e22Skuninori.morimoto.gx@renesas.com zone = thermal_zone_device_register("rcar_thermal", 1, 0, priv, 276d2a73e22Skuninori.morimoto.gx@renesas.com &rcar_thermal_zone_ops, NULL, 0, 277d2a73e22Skuninori.morimoto.gx@renesas.com IDLE_INTERVAL); 2781e426ffdSKuninori Morimoto if (IS_ERR(zone)) { 2791e426ffdSKuninori Morimoto dev_err(&pdev->dev, "thermal zone device is NULL\n"); 2804e8e2f64SKuninori Morimoto return PTR_ERR(zone); 2811e426ffdSKuninori Morimoto } 2821e426ffdSKuninori Morimoto 2831e426ffdSKuninori Morimoto platform_set_drvdata(pdev, zone); 2841e426ffdSKuninori Morimoto 2851e426ffdSKuninori Morimoto dev_info(&pdev->dev, "proved\n"); 2861e426ffdSKuninori Morimoto 2871e426ffdSKuninori Morimoto return 0; 2881e426ffdSKuninori Morimoto } 2891e426ffdSKuninori Morimoto 2901e426ffdSKuninori Morimoto static int rcar_thermal_remove(struct platform_device *pdev) 2911e426ffdSKuninori Morimoto { 2921e426ffdSKuninori Morimoto struct thermal_zone_device *zone = platform_get_drvdata(pdev); 2931e426ffdSKuninori Morimoto 2941e426ffdSKuninori Morimoto thermal_zone_device_unregister(zone); 2951e426ffdSKuninori Morimoto platform_set_drvdata(pdev, NULL); 2961e426ffdSKuninori Morimoto 2971e426ffdSKuninori Morimoto return 0; 2981e426ffdSKuninori Morimoto } 2991e426ffdSKuninori Morimoto 3001e426ffdSKuninori Morimoto static struct platform_driver rcar_thermal_driver = { 3011e426ffdSKuninori Morimoto .driver = { 3021e426ffdSKuninori Morimoto .name = "rcar_thermal", 3031e426ffdSKuninori Morimoto }, 3041e426ffdSKuninori Morimoto .probe = rcar_thermal_probe, 3051e426ffdSKuninori Morimoto .remove = rcar_thermal_remove, 3061e426ffdSKuninori Morimoto }; 3071e426ffdSKuninori Morimoto module_platform_driver(rcar_thermal_driver); 3081e426ffdSKuninori Morimoto 3091e426ffdSKuninori Morimoto MODULE_LICENSE("GPL"); 3101e426ffdSKuninori Morimoto MODULE_DESCRIPTION("R-Car THS/TSC thermal sensor driver"); 3111e426ffdSKuninori Morimoto MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 312