linux/drivers/hwmon/ltq-cputemp.c
<<
>>
Prefs
   1/* Lantiq cpu temperature sensor driver
   2 *
   3 * Copyright (C) 2017 Florian Eckert <fe@dev.tdt.de>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License as published by
   7 * the Free Software Foundation; either version 2 of the License, or
   8 * (at your option) any later version
   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 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, see <http://www.gnu.org/licenses/>
  17 */
  18
  19#include <linux/bitops.h>
  20#include <linux/delay.h>
  21#include <linux/hwmon.h>
  22#include <linux/hwmon-sysfs.h>
  23#include <linux/init.h>
  24#include <linux/module.h>
  25#include <linux/of_device.h>
  26
  27#include <lantiq_soc.h>
  28
  29/* gphy1 configuration register contains cpu temperature */
  30#define CGU_GPHY1_CR   0x0040
  31#define CGU_TEMP_PD    BIT(19)
  32
  33static void ltq_cputemp_enable(void)
  34{
  35        ltq_cgu_w32(ltq_cgu_r32(CGU_GPHY1_CR) | CGU_TEMP_PD, CGU_GPHY1_CR);
  36}
  37
  38static void ltq_cputemp_disable(void *data)
  39{
  40        ltq_cgu_w32(ltq_cgu_r32(CGU_GPHY1_CR) & ~CGU_TEMP_PD, CGU_GPHY1_CR);
  41}
  42
  43static int ltq_read(struct device *dev, enum hwmon_sensor_types type,
  44                    u32 attr, int channel, long *temp)
  45{
  46        int value;
  47
  48        switch (attr) {
  49        case hwmon_temp_input:
  50                /* get the temperature including one decimal place */
  51                value = (ltq_cgu_r32(CGU_GPHY1_CR) >> 9) & 0x01FF;
  52                value = value * 5;
  53                /* range -38 to +154 °C, register value zero is -38.0 °C */
  54                value -= 380;
  55                /* scale temp to millidegree */
  56                value = value * 100;
  57                break;
  58        default:
  59                return -EOPNOTSUPP;
  60        }
  61
  62        *temp = value;
  63        return 0;
  64}
  65
  66static umode_t ltq_is_visible(const void *_data, enum hwmon_sensor_types type,
  67                              u32 attr, int channel)
  68{
  69        if (type != hwmon_temp)
  70                return 0;
  71
  72        switch (attr) {
  73        case hwmon_temp_input:
  74                return 0444;
  75        default:
  76                return 0;
  77        }
  78}
  79
  80static const u32 ltq_chip_config[] = {
  81        HWMON_C_REGISTER_TZ,
  82        0
  83};
  84
  85static const struct hwmon_channel_info ltq_chip = {
  86        .type = hwmon_chip,
  87        .config = ltq_chip_config,
  88};
  89
  90static const u32 ltq_temp_config[] = {
  91        HWMON_T_INPUT,
  92        0
  93};
  94
  95static const struct hwmon_channel_info ltq_temp = {
  96        .type = hwmon_temp,
  97        .config = ltq_temp_config,
  98};
  99
 100static const struct hwmon_channel_info *ltq_info[] = {
 101        &ltq_chip,
 102        &ltq_temp,
 103        NULL
 104};
 105
 106static const struct hwmon_ops ltq_hwmon_ops = {
 107        .is_visible = ltq_is_visible,
 108        .read = ltq_read,
 109};
 110
 111static const struct hwmon_chip_info ltq_chip_info = {
 112        .ops = &ltq_hwmon_ops,
 113        .info = ltq_info,
 114};
 115
 116static int ltq_cputemp_probe(struct platform_device *pdev)
 117{
 118        struct device *hwmon_dev;
 119        int err = 0;
 120
 121        /* available on vr9 v1.2 SoCs only */
 122        if (ltq_soc_type() != SOC_TYPE_VR9_2)
 123                return -ENODEV;
 124
 125        err = devm_add_action(&pdev->dev, ltq_cputemp_disable, NULL);
 126        if (err)
 127                return err;
 128
 129        ltq_cputemp_enable();
 130
 131        hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
 132                                                         "ltq_cputemp",
 133                                                         NULL,
 134                                                         &ltq_chip_info,
 135                                                         NULL);
 136
 137        if (IS_ERR(hwmon_dev)) {
 138                dev_err(&pdev->dev, "Failed to register as hwmon device");
 139                return PTR_ERR(hwmon_dev);
 140        }
 141
 142        return 0;
 143}
 144
 145const struct of_device_id ltq_cputemp_match[] = {
 146        { .compatible = "lantiq,cputemp" },
 147        {},
 148};
 149MODULE_DEVICE_TABLE(of, ltq_cputemp_match);
 150
 151static struct platform_driver ltq_cputemp_driver = {
 152        .probe = ltq_cputemp_probe,
 153        .driver = {
 154                .name = "ltq-cputemp",
 155                .of_match_table = ltq_cputemp_match,
 156        },
 157};
 158
 159module_platform_driver(ltq_cputemp_driver);
 160
 161MODULE_AUTHOR("Florian Eckert <fe@dev.tdt.de>");
 162MODULE_DESCRIPTION("Lantiq cpu temperature sensor driver");
 163MODULE_LICENSE("GPL");
 164