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> 251e426ffdSKuninori Morimoto #include <linux/slab.h> 261e426ffdSKuninori Morimoto #include <linux/spinlock.h> 271e426ffdSKuninori Morimoto #include <linux/thermal.h> 281e426ffdSKuninori Morimoto 291e426ffdSKuninori Morimoto #define THSCR 0x2c 301e426ffdSKuninori Morimoto #define THSSR 0x30 311e426ffdSKuninori Morimoto 321e426ffdSKuninori Morimoto /* THSCR */ 331e426ffdSKuninori Morimoto #define CPTAP 0xf 341e426ffdSKuninori Morimoto 351e426ffdSKuninori Morimoto /* THSSR */ 361e426ffdSKuninori Morimoto #define CTEMP 0x3f 371e426ffdSKuninori Morimoto 381e426ffdSKuninori Morimoto 391e426ffdSKuninori Morimoto struct rcar_thermal_priv { 401e426ffdSKuninori Morimoto void __iomem *base; 411e426ffdSKuninori Morimoto struct device *dev; 421e426ffdSKuninori Morimoto spinlock_t lock; 431e426ffdSKuninori Morimoto u32 comp; 441e426ffdSKuninori Morimoto }; 451e426ffdSKuninori Morimoto 461e426ffdSKuninori Morimoto /* 471e426ffdSKuninori Morimoto * basic functions 481e426ffdSKuninori Morimoto */ 491e426ffdSKuninori Morimoto static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) 501e426ffdSKuninori Morimoto { 511e426ffdSKuninori Morimoto unsigned long flags; 521e426ffdSKuninori Morimoto u32 ret; 531e426ffdSKuninori Morimoto 541e426ffdSKuninori Morimoto spin_lock_irqsave(&priv->lock, flags); 551e426ffdSKuninori Morimoto 561e426ffdSKuninori Morimoto ret = ioread32(priv->base + reg); 571e426ffdSKuninori Morimoto 581e426ffdSKuninori Morimoto spin_unlock_irqrestore(&priv->lock, flags); 591e426ffdSKuninori Morimoto 601e426ffdSKuninori Morimoto return ret; 611e426ffdSKuninori Morimoto } 621e426ffdSKuninori Morimoto 631e426ffdSKuninori Morimoto #if 0 /* no user at this point */ 641e426ffdSKuninori Morimoto static void rcar_thermal_write(struct rcar_thermal_priv *priv, 651e426ffdSKuninori Morimoto u32 reg, u32 data) 661e426ffdSKuninori Morimoto { 671e426ffdSKuninori Morimoto unsigned long flags; 681e426ffdSKuninori Morimoto 691e426ffdSKuninori Morimoto spin_lock_irqsave(&priv->lock, flags); 701e426ffdSKuninori Morimoto 711e426ffdSKuninori Morimoto iowrite32(data, priv->base + reg); 721e426ffdSKuninori Morimoto 731e426ffdSKuninori Morimoto spin_unlock_irqrestore(&priv->lock, flags); 741e426ffdSKuninori Morimoto } 751e426ffdSKuninori Morimoto #endif 761e426ffdSKuninori Morimoto 771e426ffdSKuninori Morimoto static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, 781e426ffdSKuninori Morimoto u32 mask, u32 data) 791e426ffdSKuninori Morimoto { 801e426ffdSKuninori Morimoto unsigned long flags; 811e426ffdSKuninori Morimoto u32 val; 821e426ffdSKuninori Morimoto 831e426ffdSKuninori Morimoto spin_lock_irqsave(&priv->lock, flags); 841e426ffdSKuninori Morimoto 851e426ffdSKuninori Morimoto val = ioread32(priv->base + reg); 861e426ffdSKuninori Morimoto val &= ~mask; 871e426ffdSKuninori Morimoto val |= (data & mask); 881e426ffdSKuninori Morimoto iowrite32(val, priv->base + reg); 891e426ffdSKuninori Morimoto 901e426ffdSKuninori Morimoto spin_unlock_irqrestore(&priv->lock, flags); 911e426ffdSKuninori Morimoto } 921e426ffdSKuninori Morimoto 931e426ffdSKuninori Morimoto /* 941e426ffdSKuninori Morimoto * zone device functions 951e426ffdSKuninori Morimoto */ 961e426ffdSKuninori Morimoto static int rcar_thermal_get_temp(struct thermal_zone_device *zone, 971e426ffdSKuninori Morimoto unsigned long *temp) 981e426ffdSKuninori Morimoto { 991e426ffdSKuninori Morimoto struct rcar_thermal_priv *priv = zone->devdata; 1001e426ffdSKuninori Morimoto int val, min, max, tmp; 1011e426ffdSKuninori Morimoto 1021e426ffdSKuninori Morimoto tmp = -200; /* default */ 1031e426ffdSKuninori Morimoto while (1) { 1041e426ffdSKuninori Morimoto if (priv->comp < 1 || priv->comp > 12) { 1051e426ffdSKuninori Morimoto dev_err(priv->dev, 1061e426ffdSKuninori Morimoto "THSSR invalid data (%d)\n", priv->comp); 1071e426ffdSKuninori Morimoto priv->comp = 4; /* for next thermal */ 1081e426ffdSKuninori Morimoto return -EINVAL; 1091e426ffdSKuninori Morimoto } 1101e426ffdSKuninori Morimoto 1111e426ffdSKuninori Morimoto /* 1121e426ffdSKuninori Morimoto * THS comparator offset and the reference temperature 1131e426ffdSKuninori Morimoto * 1141e426ffdSKuninori Morimoto * Comparator | reference | Temperature field 1151e426ffdSKuninori Morimoto * offset | temperature | measurement 1161e426ffdSKuninori Morimoto * | (degrees C) | (degrees C) 1171e426ffdSKuninori Morimoto * -------------+---------------+------------------- 1181e426ffdSKuninori Morimoto * 1 | -45 | -45 to -30 1191e426ffdSKuninori Morimoto * 2 | -30 | -30 to -15 1201e426ffdSKuninori Morimoto * 3 | -15 | -15 to 0 1211e426ffdSKuninori Morimoto * 4 | 0 | 0 to +15 1221e426ffdSKuninori Morimoto * 5 | +15 | +15 to +30 1231e426ffdSKuninori Morimoto * 6 | +30 | +30 to +45 1241e426ffdSKuninori Morimoto * 7 | +45 | +45 to +60 1251e426ffdSKuninori Morimoto * 8 | +60 | +60 to +75 1261e426ffdSKuninori Morimoto * 9 | +75 | +75 to +90 1271e426ffdSKuninori Morimoto * 10 | +90 | +90 to +105 1281e426ffdSKuninori Morimoto * 11 | +105 | +105 to +120 1291e426ffdSKuninori Morimoto * 12 | +120 | +120 to +135 1301e426ffdSKuninori Morimoto */ 1311e426ffdSKuninori Morimoto 1321e426ffdSKuninori Morimoto /* calculate thermal limitation */ 1331e426ffdSKuninori Morimoto min = (priv->comp * 15) - 60; 1341e426ffdSKuninori Morimoto max = min + 15; 1351e426ffdSKuninori Morimoto 1361e426ffdSKuninori Morimoto /* 1371e426ffdSKuninori Morimoto * we need to wait 300us after changing comparator offset 1381e426ffdSKuninori Morimoto * to get stable temperature. 1391e426ffdSKuninori Morimoto * see "Usage Notes" on datasheet 1401e426ffdSKuninori Morimoto */ 1411e426ffdSKuninori Morimoto rcar_thermal_bset(priv, THSCR, CPTAP, priv->comp); 1421e426ffdSKuninori Morimoto udelay(300); 1431e426ffdSKuninori Morimoto 1441e426ffdSKuninori Morimoto /* calculate current temperature */ 1451e426ffdSKuninori Morimoto val = rcar_thermal_read(priv, THSSR) & CTEMP; 1461e426ffdSKuninori Morimoto val = (val * 5) - 65; 1471e426ffdSKuninori Morimoto 1481e426ffdSKuninori Morimoto dev_dbg(priv->dev, "comp/min/max/val = %d/%d/%d/%d\n", 1491e426ffdSKuninori Morimoto priv->comp, min, max, val); 1501e426ffdSKuninori Morimoto 1511e426ffdSKuninori Morimoto /* 1521e426ffdSKuninori Morimoto * If val is same as min/max, then, 1531e426ffdSKuninori Morimoto * it should try again on next comparator. 1541e426ffdSKuninori Morimoto * But the val might be correct temperature. 1551e426ffdSKuninori Morimoto * Keep it on "tmp" and compare with next val. 1561e426ffdSKuninori Morimoto */ 1571e426ffdSKuninori Morimoto if (tmp == val) 1581e426ffdSKuninori Morimoto break; 1591e426ffdSKuninori Morimoto 1601e426ffdSKuninori Morimoto if (val <= min) { 1611e426ffdSKuninori Morimoto tmp = min; 1621e426ffdSKuninori Morimoto priv->comp--; /* try again */ 1631e426ffdSKuninori Morimoto } else if (val >= max) { 1641e426ffdSKuninori Morimoto tmp = max; 1651e426ffdSKuninori Morimoto priv->comp++; /* try again */ 1661e426ffdSKuninori Morimoto } else { 1671e426ffdSKuninori Morimoto tmp = val; 1681e426ffdSKuninori Morimoto break; 1691e426ffdSKuninori Morimoto } 1701e426ffdSKuninori Morimoto } 1711e426ffdSKuninori Morimoto 1721e426ffdSKuninori Morimoto *temp = tmp; 1731e426ffdSKuninori Morimoto return 0; 1741e426ffdSKuninori Morimoto } 1751e426ffdSKuninori Morimoto 1761e426ffdSKuninori Morimoto static struct thermal_zone_device_ops rcar_thermal_zone_ops = { 1771e426ffdSKuninori Morimoto .get_temp = rcar_thermal_get_temp, 1781e426ffdSKuninori Morimoto }; 1791e426ffdSKuninori Morimoto 1801e426ffdSKuninori Morimoto /* 1811e426ffdSKuninori Morimoto * platform functions 1821e426ffdSKuninori Morimoto */ 1831e426ffdSKuninori Morimoto static int rcar_thermal_probe(struct platform_device *pdev) 1841e426ffdSKuninori Morimoto { 1851e426ffdSKuninori Morimoto struct thermal_zone_device *zone; 1861e426ffdSKuninori Morimoto struct rcar_thermal_priv *priv; 1871e426ffdSKuninori Morimoto struct resource *res; 1881e426ffdSKuninori Morimoto 1891e426ffdSKuninori Morimoto res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1901e426ffdSKuninori Morimoto if (!res) { 1911e426ffdSKuninori Morimoto dev_err(&pdev->dev, "Could not get platform resource\n"); 1921e426ffdSKuninori Morimoto return -ENODEV; 1931e426ffdSKuninori Morimoto } 1941e426ffdSKuninori Morimoto 1951e426ffdSKuninori Morimoto priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 1961e426ffdSKuninori Morimoto if (!priv) { 1971e426ffdSKuninori Morimoto dev_err(&pdev->dev, "Could not allocate priv\n"); 1981e426ffdSKuninori Morimoto return -ENOMEM; 1991e426ffdSKuninori Morimoto } 2001e426ffdSKuninori Morimoto 2011e426ffdSKuninori Morimoto priv->comp = 4; /* basic setup */ 2021e426ffdSKuninori Morimoto priv->dev = &pdev->dev; 2031e426ffdSKuninori Morimoto spin_lock_init(&priv->lock); 2041e426ffdSKuninori Morimoto priv->base = devm_ioremap_nocache(&pdev->dev, 2051e426ffdSKuninori Morimoto res->start, resource_size(res)); 2061e426ffdSKuninori Morimoto if (!priv->base) { 2071e426ffdSKuninori Morimoto dev_err(&pdev->dev, "Unable to ioremap thermal register\n"); 2084e8e2f64SKuninori Morimoto return -ENOMEM; 2091e426ffdSKuninori Morimoto } 2101e426ffdSKuninori Morimoto 211608f62b9SDevendra Naga zone = thermal_zone_device_register("rcar_thermal", 0, 0, priv, 21250125a9bSDurgadoss R &rcar_thermal_zone_ops, NULL, 0, 0); 2131e426ffdSKuninori Morimoto if (IS_ERR(zone)) { 2141e426ffdSKuninori Morimoto dev_err(&pdev->dev, "thermal zone device is NULL\n"); 2154e8e2f64SKuninori Morimoto return PTR_ERR(zone); 2161e426ffdSKuninori Morimoto } 2171e426ffdSKuninori Morimoto 2181e426ffdSKuninori Morimoto platform_set_drvdata(pdev, zone); 2191e426ffdSKuninori Morimoto 2201e426ffdSKuninori Morimoto dev_info(&pdev->dev, "proved\n"); 2211e426ffdSKuninori Morimoto 2221e426ffdSKuninori Morimoto return 0; 2231e426ffdSKuninori Morimoto } 2241e426ffdSKuninori Morimoto 2251e426ffdSKuninori Morimoto static int rcar_thermal_remove(struct platform_device *pdev) 2261e426ffdSKuninori Morimoto { 2271e426ffdSKuninori Morimoto struct thermal_zone_device *zone = platform_get_drvdata(pdev); 2281e426ffdSKuninori Morimoto 2291e426ffdSKuninori Morimoto thermal_zone_device_unregister(zone); 2301e426ffdSKuninori Morimoto platform_set_drvdata(pdev, NULL); 2311e426ffdSKuninori Morimoto 2321e426ffdSKuninori Morimoto return 0; 2331e426ffdSKuninori Morimoto } 2341e426ffdSKuninori Morimoto 2351e426ffdSKuninori Morimoto static struct platform_driver rcar_thermal_driver = { 2361e426ffdSKuninori Morimoto .driver = { 2371e426ffdSKuninori Morimoto .name = "rcar_thermal", 2381e426ffdSKuninori Morimoto }, 2391e426ffdSKuninori Morimoto .probe = rcar_thermal_probe, 2401e426ffdSKuninori Morimoto .remove = rcar_thermal_remove, 2411e426ffdSKuninori Morimoto }; 2421e426ffdSKuninori Morimoto module_platform_driver(rcar_thermal_driver); 2431e426ffdSKuninori Morimoto 2441e426ffdSKuninori Morimoto MODULE_LICENSE("GPL"); 2451e426ffdSKuninori Morimoto MODULE_DESCRIPTION("R-Car THS/TSC thermal sensor driver"); 2461e426ffdSKuninori Morimoto MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 247