linux/drivers/thermal/zx2967_thermal.c
<<
>>
Prefs
   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
  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 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
 222static 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
 242static SIMPLE_DEV_PM_OPS(zx2967_thermal_pm_ops,
 243                         zx2967_thermal_suspend, zx2967_thermal_resume);
 244
 245static 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};
 254module_platform_driver(zx2967_thermal_driver);
 255
 256MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
 257MODULE_DESCRIPTION("ZTE zx2967 thermal driver");
 258MODULE_LICENSE("GPL v2");
 259