linux/drivers/thermal/armada_thermal.c
<<
>>
Prefs
   1/*
   2 * Marvell Armada 370/XP thermal sensor driver
   3 *
   4 * Copyright (C) 2013 Marvell
   5 *
   6 * This software is licensed under the terms of the GNU General Public
   7 * License version 2, as published by the Free Software Foundation, and
   8 * may be copied, distributed, and modified under those terms.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 * GNU General Public License for more details.
  14 *
  15 */
  16#include <linux/device.h>
  17#include <linux/err.h>
  18#include <linux/io.h>
  19#include <linux/kernel.h>
  20#include <linux/of.h>
  21#include <linux/module.h>
  22#include <linux/delay.h>
  23#include <linux/platform_device.h>
  24#include <linux/of_device.h>
  25#include <linux/thermal.h>
  26
  27#define THERMAL_VALID_OFFSET            9
  28#define THERMAL_VALID_MASK              0x1
  29#define THERMAL_TEMP_OFFSET             10
  30#define THERMAL_TEMP_MASK               0x1ff
  31
  32/* Thermal Manager Control and Status Register */
  33#define PMU_TDC0_SW_RST_MASK            (0x1 << 1)
  34#define PMU_TM_DISABLE_OFFS             0
  35#define PMU_TM_DISABLE_MASK             (0x1 << PMU_TM_DISABLE_OFFS)
  36#define PMU_TDC0_REF_CAL_CNT_OFFS       11
  37#define PMU_TDC0_REF_CAL_CNT_MASK       (0x1ff << PMU_TDC0_REF_CAL_CNT_OFFS)
  38#define PMU_TDC0_OTF_CAL_MASK           (0x1 << 30)
  39#define PMU_TDC0_START_CAL_MASK         (0x1 << 25)
  40
  41struct armada_thermal_ops;
  42
  43/* Marvell EBU Thermal Sensor Dev Structure */
  44struct armada_thermal_priv {
  45        void __iomem *sensor;
  46        void __iomem *control;
  47        struct armada_thermal_ops *ops;
  48};
  49
  50struct armada_thermal_ops {
  51        /* Initialize the sensor */
  52        void (*init_sensor)(struct armada_thermal_priv *);
  53
  54        /* Test for a valid sensor value (optional) */
  55        bool (*is_valid)(struct armada_thermal_priv *);
  56};
  57
  58static void armadaxp_init_sensor(struct armada_thermal_priv *priv)
  59{
  60        unsigned long reg;
  61
  62        reg = readl_relaxed(priv->control);
  63        reg |= PMU_TDC0_OTF_CAL_MASK;
  64        writel(reg, priv->control);
  65
  66        /* Reference calibration value */
  67        reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
  68        reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
  69        writel(reg, priv->control);
  70
  71        /* Reset the sensor */
  72        reg = readl_relaxed(priv->control);
  73        writel((reg | PMU_TDC0_SW_RST_MASK), priv->control);
  74
  75        writel(reg, priv->control);
  76
  77        /* Enable the sensor */
  78        reg = readl_relaxed(priv->sensor);
  79        reg &= ~PMU_TM_DISABLE_MASK;
  80        writel(reg, priv->sensor);
  81}
  82
  83static void armada370_init_sensor(struct armada_thermal_priv *priv)
  84{
  85        unsigned long reg;
  86
  87        reg = readl_relaxed(priv->control);
  88        reg |= PMU_TDC0_OTF_CAL_MASK;
  89        writel(reg, priv->control);
  90
  91        /* Reference calibration value */
  92        reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
  93        reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
  94        writel(reg, priv->control);
  95
  96        reg &= ~PMU_TDC0_START_CAL_MASK;
  97        writel(reg, priv->control);
  98
  99        mdelay(10);
 100}
 101
 102static bool armada_is_valid(struct armada_thermal_priv *priv)
 103{
 104        unsigned long reg = readl_relaxed(priv->sensor);
 105
 106        return (reg >> THERMAL_VALID_OFFSET) & THERMAL_VALID_MASK;
 107}
 108
 109static int armada_get_temp(struct thermal_zone_device *thermal,
 110                          unsigned long *temp)
 111{
 112        struct armada_thermal_priv *priv = thermal->devdata;
 113        unsigned long reg;
 114
 115        /* Valid check */
 116        if (priv->ops->is_valid && !priv->ops->is_valid(priv)) {
 117                dev_err(&thermal->device,
 118                        "Temperature sensor reading not valid\n");
 119                return -EIO;
 120        }
 121
 122        reg = readl_relaxed(priv->sensor);
 123        reg = (reg >> THERMAL_TEMP_OFFSET) & THERMAL_TEMP_MASK;
 124        *temp = (3153000000UL - (10000000UL*reg)) / 13825;
 125        return 0;
 126}
 127
 128static struct thermal_zone_device_ops ops = {
 129        .get_temp = armada_get_temp,
 130};
 131
 132static const struct armada_thermal_ops armadaxp_ops = {
 133        .init_sensor = armadaxp_init_sensor,
 134};
 135
 136static const struct armada_thermal_ops armada370_ops = {
 137        .is_valid = armada_is_valid,
 138        .init_sensor = armada370_init_sensor,
 139};
 140
 141static const struct of_device_id armada_thermal_id_table[] = {
 142        {
 143                .compatible = "marvell,armadaxp-thermal",
 144                .data       = &armadaxp_ops,
 145        },
 146        {
 147                .compatible = "marvell,armada370-thermal",
 148                .data       = &armada370_ops,
 149        },
 150        {
 151                /* sentinel */
 152        },
 153};
 154MODULE_DEVICE_TABLE(of, armada_thermal_id_table);
 155
 156static int armada_thermal_probe(struct platform_device *pdev)
 157{
 158        struct thermal_zone_device *thermal;
 159        const struct of_device_id *match;
 160        struct armada_thermal_priv *priv;
 161        struct resource *res;
 162
 163        match = of_match_device(armada_thermal_id_table, &pdev->dev);
 164        if (!match)
 165                return -ENODEV;
 166
 167        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 168        if (!priv)
 169                return -ENOMEM;
 170
 171        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 172        priv->sensor = devm_ioremap_resource(&pdev->dev, res);
 173        if (IS_ERR(priv->sensor))
 174                return PTR_ERR(priv->sensor);
 175
 176        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 177        priv->control = devm_ioremap_resource(&pdev->dev, res);
 178        if (IS_ERR(priv->control))
 179                return PTR_ERR(priv->control);
 180
 181        priv->ops = (struct armada_thermal_ops *)match->data;
 182        priv->ops->init_sensor(priv);
 183
 184        thermal = thermal_zone_device_register("armada_thermal", 0, 0,
 185                                               priv, &ops, NULL, 0, 0);
 186        if (IS_ERR(thermal)) {
 187                dev_err(&pdev->dev,
 188                        "Failed to register thermal zone device\n");
 189                return PTR_ERR(thermal);
 190        }
 191
 192        platform_set_drvdata(pdev, thermal);
 193
 194        return 0;
 195}
 196
 197static int armada_thermal_exit(struct platform_device *pdev)
 198{
 199        struct thermal_zone_device *armada_thermal =
 200                platform_get_drvdata(pdev);
 201
 202        thermal_zone_device_unregister(armada_thermal);
 203        platform_set_drvdata(pdev, NULL);
 204
 205        return 0;
 206}
 207
 208static struct platform_driver armada_thermal_driver = {
 209        .probe = armada_thermal_probe,
 210        .remove = armada_thermal_exit,
 211        .driver = {
 212                .name = "armada_thermal",
 213                .owner = THIS_MODULE,
 214                .of_match_table = of_match_ptr(armada_thermal_id_table),
 215        },
 216};
 217
 218module_platform_driver(armada_thermal_driver);
 219
 220MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");
 221MODULE_DESCRIPTION("Armada 370/XP thermal driver");
 222MODULE_LICENSE("GPL v2");
 223
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.