1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * ZTE's zx2967 family thermal sensor driver 4 * 5 * Copyright (C) 2017 ZTE Ltd. 6 * 7 * Author: Baoyou Xie <baoyou.xie@linaro.org> 8 */ 9 10#include <linux/clk.h> 11#include <linux/device.h> 12#include <linux/err.h> 13#include <linux/iopoll.h> 14#include <linux/module.h> 15#include <linux/platform_device.h> 16#include <linux/thermal.h> 17 18/* Power Mode: 0->low 1->high */ 19#define ZX2967_THERMAL_POWER_MODE 0 20#define ZX2967_POWER_MODE_LOW 0 21#define ZX2967_POWER_MODE_HIGH 1 22 23/* DCF Control Register */ 24#define ZX2967_THERMAL_DCF 0x4 25#define ZX2967_DCF_EN BIT(1) 26#define ZX2967_DCF_FREEZE BIT(0) 27 28/* Selection Register */ 29#define ZX2967_THERMAL_SEL 0x8 30 31/* Control Register */ 32#define ZX2967_THERMAL_CTRL 0x10 33 34#define ZX2967_THERMAL_READY BIT(12) 35#define ZX2967_THERMAL_TEMP_MASK GENMASK(11, 0) 36#define ZX2967_THERMAL_ID_MASK 0x18 37#define ZX2967_THERMAL_ID 0x10 38 39#define ZX2967_GET_TEMP_TIMEOUT_US (100 * 1024) 40 41/** 42 * struct zx2967_thermal_priv - zx2967 thermal sensor private structure 43 * @tzd: struct thermal_zone_device where the sensor is registered 44 * @lock: prevents read sensor in parallel 45 * @clk_topcrm: topcrm clk structure 46 * @clk_apb: apb clk structure 47 * @regs: pointer to base address of the thermal sensor 48 * @dev: struct device pointer 49 */ 50 51struct zx2967_thermal_priv { 52 struct thermal_zone_device *tzd; 53 struct mutex lock; 54 struct clk *clk_topcrm; 55 struct clk *clk_apb; 56 void __iomem *regs; 57 struct device *dev; 58}; 59 60static int zx2967_thermal_get_temp(void *data, int *temp) 61{ 62 void __iomem *regs; 63 struct zx2967_thermal_priv *priv = data; 64 u32 val; 65 int ret; 66 67 if (!priv->tzd) 68 return -EAGAIN; 69 70 regs = priv->regs; 71 mutex_lock(&priv->lock); 72 writel_relaxed(ZX2967_POWER_MODE_LOW, 73 regs + ZX2967_THERMAL_POWER_MODE); 74 writel_relaxed(ZX2967_DCF_EN, regs + ZX2967_THERMAL_DCF); 75 76 val = readl_relaxed(regs + ZX2967_THERMAL_SEL); 77 val &= ~ZX2967_THERMAL_ID_MASK; 78 val |= ZX2967_THERMAL_ID; 79 writel_relaxed(val, regs + ZX2967_THERMAL_SEL); 80 81 /* 82 * Must wait for a while, surely it's a bit odd. 83 * otherwise temperature value we got has a few deviation, even if 84 * the THERMAL_READY bit is set. 85 */ 86 usleep_range(100, 300); 87 ret = readx_poll_timeout(readl, regs + ZX2967_THERMAL_CTRL, 88 val, val & ZX2967_THERMAL_READY, 300, 89 ZX2967_GET_TEMP_TIMEOUT_US); 90 if (ret) { 91 dev_err(priv->dev, "Thermal sensor data timeout\n"); 92 goto unlock; 93 } 94 95 writel_relaxed(ZX2967_DCF_FREEZE | ZX2967_DCF_EN, 96 regs + ZX2967_THERMAL_DCF); 97 val = readl_relaxed(regs + ZX2967_THERMAL_CTRL) 98 & ZX2967_THERMAL_TEMP_MASK; 99 writel_relaxed(ZX2967_POWER_MODE_HIGH, 100 regs + ZX2967_THERMAL_POWER_MODE); 101 102 /* 103 * Calculate temperature 104 * In dts, slope is multiplied by 1000. 105 */ 106 *temp = DIV_ROUND_CLOSEST(((s32)val + priv->tzd->tzp->offset) * 1000, 107 priv->tzd->tzp->slope); 108 109unlock: 110 mutex_unlock(&priv->lock); 111 return ret; 112} 113 114static const struct thermal_zone_of_device_ops zx2967_of_thermal_ops = { 115 .get_temp = zx2967_thermal_get_temp, 116}; 117 118static int zx2967_thermal_probe(struct platform_device *pdev) 119{ 120 struct zx2967_thermal_priv *priv; 121 struct resource *res; 122 int ret; 123 124 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 125 if (!priv) 126 return -ENOMEM; 127 128 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 129 priv->regs = devm_ioremap_resource(&pdev->dev, res); 130 if (IS_ERR(priv->regs)) 131 return PTR_ERR(priv->regs); 132 133 priv->clk_topcrm = devm_clk_get(&pdev->dev, "topcrm"); 134 if (IS_ERR(priv->clk_topcrm)) { 135 ret = PTR_ERR(priv->clk_topcrm); 136 dev_err(&pdev->dev, "failed to get topcrm clock: %d\n", ret); 137 return ret; 138 } 139 140 ret = clk_prepare_enable(priv->clk_topcrm); 141 if (ret) { 142 dev_err(&pdev->dev, "failed to enable topcrm clock: %d\n", 143 ret); 144 return ret; 145 } 146 147 priv->clk_apb = devm_clk_get(&pdev->dev, "apb"); 148 if (IS_ERR(priv->clk_apb)) { 149 ret = PTR_ERR(priv->clk_apb); 150 dev_err(&pdev->dev, "failed to get apb clock: %d\n", ret); 151 goto disable_clk_topcrm; 152 } 153 154 ret = clk_prepare_enable(priv->clk_apb); 155 if (ret) { 156 dev_err(&pdev->dev, "failed to enable apb clock: %d\n", 157 ret); 158 goto disable_clk_topcrm; 159 } 160 161 mutex_init(&priv->lock); 162 priv->tzd = thermal_zone_of_sensor_register(&pdev->dev, 163 0, priv, &zx2967_of_thermal_ops); 164 165 if (IS_ERR(priv->tzd)) { 166 ret = PTR_ERR(priv->tzd); 167 dev_err(&pdev->dev, "failed to register sensor: %d\n", ret); 168 goto disable_clk_all; 169 } 170 171 if (priv->tzd->tzp->slope == 0) { 172 thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd); 173 dev_err(&pdev->dev, "coefficients of sensor is invalid\n"); 174 ret = -EINVAL; 175 goto disable_clk_all; 176 } 177 178 priv->dev = &pdev->dev; 179 platform_set_drvdata(pdev, priv); 180 181 return 0; 182 183disable_clk_all: 184 clk_disable_unprepare(priv->clk_apb); 185disable_clk_topcrm: 186 clk_disable_unprepare(priv->clk_topcrm); 187 return ret; 188} 189 190static int zx2967_thermal_exit(struct platform_device *pdev) 191{ 192 struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev); 193 194 thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd); 195 clk_disable_unprepare(priv->clk_topcrm); 196 clk_disable_unprepare(priv->clk_apb); 197 198 return 0; 199} 200 201static const struct of_device_id zx2967_thermal_id_table[] = { 202 { .compatible = "zte,zx296718-thermal" }, 203 {} 204}; 205MODULE_DEVICE_TABLE(of, zx2967_thermal_id_table); 206 207#ifdef CONFIG_PM_SLEEP 208static int zx2967_thermal_suspend(struct device *dev) 209{ 210 struct zx2967_thermal_priv *priv = dev_get_drvdata(dev); 211 212 if (priv && priv->clk_topcrm) 213 clk_disable_unprepare(priv->clk_topcrm); 214 215 if (priv && priv->clk_apb) 216 clk_disable_unprepare(priv->clk_apb); 217 218 return 0; 219} 220 221static int zx2967_thermal_resume(struct device *dev) 222{ 223 struct zx2967_thermal_priv *priv = dev_get_drvdata(dev); 224 int error; 225 226 error = clk_prepare_enable(priv->clk_topcrm); 227 if (error) 228 return error; 229 230 error = clk_prepare_enable(priv->clk_apb); 231 if (error) { 232 clk_disable_unprepare(priv->clk_topcrm); 233 return error; 234 } 235 236 return 0; 237} 238#endif 239 240static SIMPLE_DEV_PM_OPS(zx2967_thermal_pm_ops, 241 zx2967_thermal_suspend, zx2967_thermal_resume); 242 243static struct platform_driver zx2967_thermal_driver = { 244 .probe = zx2967_thermal_probe, 245 .remove = zx2967_thermal_exit, 246 .driver = { 247 .name = "zx2967_thermal", 248 .of_match_table = zx2967_thermal_id_table, 249 .pm = &zx2967_thermal_pm_ops, 250 }, 251}; 252module_platform_driver(zx2967_thermal_driver); 253 254MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); 255MODULE_DESCRIPTION("ZTE zx2967 thermal driver"); 256MODULE_LICENSE("GPL v2"); 257