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