Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.2-rc5 255 lines 6.4 kB view raw
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 */ 49 50struct zx2967_thermal_priv { 51 struct thermal_zone_device *tzd; 52 struct mutex lock; 53 struct clk *clk_topcrm; 54 struct clk *clk_apb; 55 void __iomem *regs; 56 struct device *dev; 57}; 58 59static int zx2967_thermal_get_temp(void *data, int *temp) 60{ 61 void __iomem *regs; 62 struct zx2967_thermal_priv *priv = data; 63 u32 val; 64 int ret; 65 66 if (!priv->tzd) 67 return -EAGAIN; 68 69 regs = priv->regs; 70 mutex_lock(&priv->lock); 71 writel_relaxed(ZX2967_POWER_MODE_LOW, 72 regs + ZX2967_THERMAL_POWER_MODE); 73 writel_relaxed(ZX2967_DCF_EN, regs + ZX2967_THERMAL_DCF); 74 75 val = readl_relaxed(regs + ZX2967_THERMAL_SEL); 76 val &= ~ZX2967_THERMAL_ID_MASK; 77 val |= ZX2967_THERMAL_ID; 78 writel_relaxed(val, regs + ZX2967_THERMAL_SEL); 79 80 /* 81 * Must wait for a while, surely it's a bit odd. 82 * otherwise temperature value we got has a few deviation, even if 83 * the THERMAL_READY bit is set. 84 */ 85 usleep_range(100, 300); 86 ret = readx_poll_timeout(readl, regs + ZX2967_THERMAL_CTRL, 87 val, val & ZX2967_THERMAL_READY, 300, 88 ZX2967_GET_TEMP_TIMEOUT_US); 89 if (ret) { 90 dev_err(priv->dev, "Thermal sensor data timeout\n"); 91 goto unlock; 92 } 93 94 writel_relaxed(ZX2967_DCF_FREEZE | ZX2967_DCF_EN, 95 regs + ZX2967_THERMAL_DCF); 96 val = readl_relaxed(regs + ZX2967_THERMAL_CTRL) 97 & ZX2967_THERMAL_TEMP_MASK; 98 writel_relaxed(ZX2967_POWER_MODE_HIGH, 99 regs + ZX2967_THERMAL_POWER_MODE); 100 101 /* 102 * Calculate temperature 103 * In dts, slope is multiplied by 1000. 104 */ 105 *temp = DIV_ROUND_CLOSEST(((s32)val + priv->tzd->tzp->offset) * 1000, 106 priv->tzd->tzp->slope); 107 108unlock: 109 mutex_unlock(&priv->lock); 110 return ret; 111} 112 113static const struct thermal_zone_of_device_ops zx2967_of_thermal_ops = { 114 .get_temp = zx2967_thermal_get_temp, 115}; 116 117static int zx2967_thermal_probe(struct platform_device *pdev) 118{ 119 struct zx2967_thermal_priv *priv; 120 struct resource *res; 121 int ret; 122 123 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 124 if (!priv) 125 return -ENOMEM; 126 127 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 128 priv->regs = devm_ioremap_resource(&pdev->dev, res); 129 if (IS_ERR(priv->regs)) 130 return PTR_ERR(priv->regs); 131 132 priv->clk_topcrm = devm_clk_get(&pdev->dev, "topcrm"); 133 if (IS_ERR(priv->clk_topcrm)) { 134 ret = PTR_ERR(priv->clk_topcrm); 135 dev_err(&pdev->dev, "failed to get topcrm clock: %d\n", ret); 136 return ret; 137 } 138 139 ret = clk_prepare_enable(priv->clk_topcrm); 140 if (ret) { 141 dev_err(&pdev->dev, "failed to enable topcrm clock: %d\n", 142 ret); 143 return ret; 144 } 145 146 priv->clk_apb = devm_clk_get(&pdev->dev, "apb"); 147 if (IS_ERR(priv->clk_apb)) { 148 ret = PTR_ERR(priv->clk_apb); 149 dev_err(&pdev->dev, "failed to get apb clock: %d\n", ret); 150 goto disable_clk_topcrm; 151 } 152 153 ret = clk_prepare_enable(priv->clk_apb); 154 if (ret) { 155 dev_err(&pdev->dev, "failed to enable apb clock: %d\n", 156 ret); 157 goto disable_clk_topcrm; 158 } 159 160 mutex_init(&priv->lock); 161 priv->tzd = thermal_zone_of_sensor_register(&pdev->dev, 162 0, priv, &zx2967_of_thermal_ops); 163 164 if (IS_ERR(priv->tzd)) { 165 ret = PTR_ERR(priv->tzd); 166 dev_err(&pdev->dev, "failed to register sensor: %d\n", ret); 167 goto disable_clk_all; 168 } 169 170 if (priv->tzd->tzp->slope == 0) { 171 thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd); 172 dev_err(&pdev->dev, "coefficients of sensor is invalid\n"); 173 ret = -EINVAL; 174 goto disable_clk_all; 175 } 176 177 priv->dev = &pdev->dev; 178 platform_set_drvdata(pdev, priv); 179 180 return 0; 181 182disable_clk_all: 183 clk_disable_unprepare(priv->clk_apb); 184disable_clk_topcrm: 185 clk_disable_unprepare(priv->clk_topcrm); 186 return ret; 187} 188 189static int zx2967_thermal_exit(struct platform_device *pdev) 190{ 191 struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev); 192 193 thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd); 194 clk_disable_unprepare(priv->clk_topcrm); 195 clk_disable_unprepare(priv->clk_apb); 196 197 return 0; 198} 199 200static const struct of_device_id zx2967_thermal_id_table[] = { 201 { .compatible = "zte,zx296718-thermal" }, 202 {} 203}; 204MODULE_DEVICE_TABLE(of, zx2967_thermal_id_table); 205 206#ifdef CONFIG_PM_SLEEP 207static int zx2967_thermal_suspend(struct device *dev) 208{ 209 struct zx2967_thermal_priv *priv = dev_get_drvdata(dev); 210 211 if (priv && priv->clk_topcrm) 212 clk_disable_unprepare(priv->clk_topcrm); 213 214 if (priv && priv->clk_apb) 215 clk_disable_unprepare(priv->clk_apb); 216 217 return 0; 218} 219 220static int zx2967_thermal_resume(struct device *dev) 221{ 222 struct zx2967_thermal_priv *priv = dev_get_drvdata(dev); 223 int error; 224 225 error = clk_prepare_enable(priv->clk_topcrm); 226 if (error) 227 return error; 228 229 error = clk_prepare_enable(priv->clk_apb); 230 if (error) { 231 clk_disable_unprepare(priv->clk_topcrm); 232 return error; 233 } 234 235 return 0; 236} 237#endif 238 239static SIMPLE_DEV_PM_OPS(zx2967_thermal_pm_ops, 240 zx2967_thermal_suspend, zx2967_thermal_resume); 241 242static struct platform_driver zx2967_thermal_driver = { 243 .probe = zx2967_thermal_probe, 244 .remove = zx2967_thermal_exit, 245 .driver = { 246 .name = "zx2967_thermal", 247 .of_match_table = zx2967_thermal_id_table, 248 .pm = &zx2967_thermal_pm_ops, 249 }, 250}; 251module_platform_driver(zx2967_thermal_driver); 252 253MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); 254MODULE_DESCRIPTION("ZTE zx2967 thermal driver"); 255MODULE_LICENSE("GPL v2");