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

thermal: zx2967: add thermal driver for ZTE's zx2967 family

This patch adds thermal driver for ZTE's zx2967 family.

Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
Reviewed-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>

authored by

Baoyou Xie and committed by
Eduardo Valentin
50fdd36f e0fa5648

+267
+8
drivers/thermal/Kconfig
··· 445 445 source "drivers/thermal/qcom/Kconfig" 446 446 endmenu 447 447 448 + config ZX2967_THERMAL 449 + tristate "Thermal sensors on zx2967 SoC" 450 + depends on ARCH_ZX || COMPILE_TEST 451 + help 452 + Enable the zx2967 thermal sensors driver, which supports 453 + the primitive temperature sensor embedded in zx2967 SoCs. 454 + This sensor generates the real time die temperature. 455 + 448 456 endif
+1
drivers/thermal/Makefile
··· 57 57 obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o 58 58 obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o 59 59 obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o 60 + obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
+258
drivers/thermal/zx2967_thermal.c
··· 1 + /* 2 + * ZTE's zx2967 family thermal sensor driver 3 + * 4 + * Copyright (C) 2017 ZTE Ltd. 5 + * 6 + * Author: Baoyou Xie <baoyou.xie@linaro.org> 7 + * 8 + * License terms: GNU General Public License (GPL) version 2 9 + */ 10 + 11 + #include <linux/clk.h> 12 + #include <linux/device.h> 13 + #include <linux/err.h> 14 + #include <linux/iopoll.h> 15 + #include <linux/module.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/thermal.h> 18 + 19 + /* Power Mode: 0->low 1->high */ 20 + #define ZX2967_THERMAL_POWER_MODE 0 21 + #define ZX2967_POWER_MODE_LOW 0 22 + #define ZX2967_POWER_MODE_HIGH 1 23 + 24 + /* DCF Control Register */ 25 + #define ZX2967_THERMAL_DCF 0x4 26 + #define ZX2967_DCF_EN BIT(1) 27 + #define ZX2967_DCF_FREEZE BIT(0) 28 + 29 + /* Selection Register */ 30 + #define ZX2967_THERMAL_SEL 0x8 31 + 32 + /* Control Register */ 33 + #define ZX2967_THERMAL_CTRL 0x10 34 + 35 + #define ZX2967_THERMAL_READY BIT(12) 36 + #define ZX2967_THERMAL_TEMP_MASK GENMASK(11, 0) 37 + #define ZX2967_THERMAL_ID_MASK 0x18 38 + #define ZX2967_THERMAL_ID 0x10 39 + 40 + #define ZX2967_GET_TEMP_TIMEOUT_US (100 * 1024) 41 + 42 + /** 43 + * struct zx2967_thermal_priv - zx2967 thermal sensor private structure 44 + * @tzd: struct thermal_zone_device where the sensor is registered 45 + * @lock: prevents read sensor in parallel 46 + * @clk_topcrm: topcrm clk structure 47 + * @clk_apb: apb clk structure 48 + * @regs: pointer to base address of the thermal sensor 49 + */ 50 + 51 + struct 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 + 60 + static 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 + 109 + unlock: 110 + mutex_unlock(&priv->lock); 111 + return ret; 112 + } 113 + 114 + static struct thermal_zone_of_device_ops zx2967_of_thermal_ops = { 115 + .get_temp = zx2967_thermal_get_temp, 116 + }; 117 + 118 + static 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 + 183 + disable_clk_all: 184 + clk_disable_unprepare(priv->clk_apb); 185 + disable_clk_topcrm: 186 + clk_disable_unprepare(priv->clk_topcrm); 187 + return ret; 188 + } 189 + 190 + static 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 + 201 + static const struct of_device_id zx2967_thermal_id_table[] = { 202 + { .compatible = "zte,zx296718-thermal" }, 203 + {} 204 + }; 205 + MODULE_DEVICE_TABLE(of, zx2967_thermal_id_table); 206 + 207 + #ifdef CONFIG_PM_SLEEP 208 + static int zx2967_thermal_suspend(struct device *dev) 209 + { 210 + struct platform_device *pdev = to_platform_device(dev); 211 + struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev); 212 + 213 + if (priv && priv->clk_topcrm) 214 + clk_disable_unprepare(priv->clk_topcrm); 215 + 216 + if (priv && priv->clk_apb) 217 + clk_disable_unprepare(priv->clk_apb); 218 + 219 + return 0; 220 + } 221 + 222 + static int zx2967_thermal_resume(struct device *dev) 223 + { 224 + struct platform_device *pdev = to_platform_device(dev); 225 + struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev); 226 + int error; 227 + 228 + error = clk_prepare_enable(priv->clk_topcrm); 229 + if (error) 230 + return error; 231 + 232 + error = clk_prepare_enable(priv->clk_apb); 233 + if (error) { 234 + clk_disable_unprepare(priv->clk_topcrm); 235 + return error; 236 + } 237 + 238 + return 0; 239 + } 240 + #endif 241 + 242 + static SIMPLE_DEV_PM_OPS(zx2967_thermal_pm_ops, 243 + zx2967_thermal_suspend, zx2967_thermal_resume); 244 + 245 + static struct platform_driver zx2967_thermal_driver = { 246 + .probe = zx2967_thermal_probe, 247 + .remove = zx2967_thermal_exit, 248 + .driver = { 249 + .name = "zx2967_thermal", 250 + .of_match_table = zx2967_thermal_id_table, 251 + .pm = &zx2967_thermal_pm_ops, 252 + }, 253 + }; 254 + module_platform_driver(zx2967_thermal_driver); 255 + 256 + MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); 257 + MODULE_DESCRIPTION("ZTE zx2967 thermal driver"); 258 + MODULE_LICENSE("GPL v2");