linux/drivers/hwmon/sch5627.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/***************************************************************************
   3 *   Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com>           *
   4 *                                                                         *
   5 ***************************************************************************/
   6
   7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   8
   9#include <linux/module.h>
  10#include <linux/init.h>
  11#include <linux/slab.h>
  12#include <linux/jiffies.h>
  13#include <linux/platform_device.h>
  14#include <linux/hwmon.h>
  15#include <linux/err.h>
  16#include <linux/mutex.h>
  17#include "sch56xx-common.h"
  18
  19#define DRVNAME "sch5627"
  20#define DEVNAME DRVNAME /* We only support one model */
  21
  22#define SCH5627_HWMON_ID                0xa5
  23#define SCH5627_COMPANY_ID              0x5c
  24#define SCH5627_PRIMARY_ID              0xa0
  25
  26#define SCH5627_REG_BUILD_CODE          0x39
  27#define SCH5627_REG_BUILD_ID            0x3a
  28#define SCH5627_REG_HWMON_ID            0x3c
  29#define SCH5627_REG_HWMON_REV           0x3d
  30#define SCH5627_REG_COMPANY_ID          0x3e
  31#define SCH5627_REG_PRIMARY_ID          0x3f
  32#define SCH5627_REG_CTRL                0x40
  33
  34#define SCH5627_NO_TEMPS                8
  35#define SCH5627_NO_FANS                 4
  36#define SCH5627_NO_IN                   5
  37
  38static const u16 SCH5627_REG_TEMP_MSB[SCH5627_NO_TEMPS] = {
  39        0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181 };
  40static const u16 SCH5627_REG_TEMP_LSN[SCH5627_NO_TEMPS] = {
  41        0xE2, 0xE1, 0xE1, 0xE5, 0xE5, 0xE6, 0x182, 0x182 };
  42static const u16 SCH5627_REG_TEMP_HIGH_NIBBLE[SCH5627_NO_TEMPS] = {
  43        0, 0, 1, 1, 0, 0, 0, 1 };
  44static const u16 SCH5627_REG_TEMP_HIGH[SCH5627_NO_TEMPS] = {
  45        0x61, 0x57, 0x59, 0x5B, 0x5D, 0x5F, 0x184, 0x186 };
  46static const u16 SCH5627_REG_TEMP_ABS[SCH5627_NO_TEMPS] = {
  47        0x9B, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x1A8, 0x1A9 };
  48
  49static const u16 SCH5627_REG_FAN[SCH5627_NO_FANS] = {
  50        0x2C, 0x2E, 0x30, 0x32 };
  51static const u16 SCH5627_REG_FAN_MIN[SCH5627_NO_FANS] = {
  52        0x62, 0x64, 0x66, 0x68 };
  53
  54static const u16 SCH5627_REG_IN_MSB[SCH5627_NO_IN] = {
  55        0x22, 0x23, 0x24, 0x25, 0x189 };
  56static const u16 SCH5627_REG_IN_LSN[SCH5627_NO_IN] = {
  57        0xE4, 0xE4, 0xE3, 0xE3, 0x18A };
  58static const u16 SCH5627_REG_IN_HIGH_NIBBLE[SCH5627_NO_IN] = {
  59        1, 0, 1, 0, 1 };
  60static const u16 SCH5627_REG_IN_FACTOR[SCH5627_NO_IN] = {
  61        10745, 3660, 9765, 10745, 3660 };
  62static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = {
  63        "VCC", "VTT", "VBAT", "VTR", "V_IN" };
  64
  65struct sch5627_data {
  66        unsigned short addr;
  67        u8 control;
  68        u8 temp_max[SCH5627_NO_TEMPS];
  69        u8 temp_crit[SCH5627_NO_TEMPS];
  70        u16 fan_min[SCH5627_NO_FANS];
  71
  72        struct mutex update_lock;
  73        unsigned long last_battery;     /* In jiffies */
  74        char temp_valid;                /* !=0 if following fields are valid */
  75        char fan_valid;
  76        char in_valid;
  77        unsigned long temp_last_updated;        /* In jiffies */
  78        unsigned long fan_last_updated;
  79        unsigned long in_last_updated;
  80        u16 temp[SCH5627_NO_TEMPS];
  81        u16 fan[SCH5627_NO_FANS];
  82        u16 in[SCH5627_NO_IN];
  83};
  84
  85static int sch5627_update_temp(struct sch5627_data *data)
  86{
  87        int ret = 0;
  88        int i, val;
  89
  90        mutex_lock(&data->update_lock);
  91
  92        /* Cache the values for 1 second */
  93        if (time_after(jiffies, data->temp_last_updated + HZ) || !data->temp_valid) {
  94                for (i = 0; i < SCH5627_NO_TEMPS; i++) {
  95                        val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_TEMP_MSB[i],
  96                                                         SCH5627_REG_TEMP_LSN[i],
  97                                                         SCH5627_REG_TEMP_HIGH_NIBBLE[i]);
  98                        if (unlikely(val < 0)) {
  99                                ret = val;
 100                                goto abort;
 101                        }
 102                        data->temp[i] = val;
 103                }
 104                data->temp_last_updated = jiffies;
 105                data->temp_valid = 1;
 106        }
 107abort:
 108        mutex_unlock(&data->update_lock);
 109        return ret;
 110}
 111
 112static int sch5627_update_fan(struct sch5627_data *data)
 113{
 114        int ret = 0;
 115        int i, val;
 116
 117        mutex_lock(&data->update_lock);
 118
 119        /* Cache the values for 1 second */
 120        if (time_after(jiffies, data->fan_last_updated + HZ) || !data->fan_valid) {
 121                for (i = 0; i < SCH5627_NO_FANS; i++) {
 122                        val = sch56xx_read_virtual_reg16(data->addr, SCH5627_REG_FAN[i]);
 123                        if (unlikely(val < 0)) {
 124                                ret = val;
 125                                goto abort;
 126                        }
 127                        data->fan[i] = val;
 128                }
 129                data->fan_last_updated = jiffies;
 130                data->fan_valid = 1;
 131        }
 132abort:
 133        mutex_unlock(&data->update_lock);
 134        return ret;
 135}
 136
 137static int sch5627_update_in(struct sch5627_data *data)
 138{
 139        int ret = 0;
 140        int i, val;
 141
 142        mutex_lock(&data->update_lock);
 143
 144        /* Trigger a Vbat voltage measurement every 5 minutes */
 145        if (time_after(jiffies, data->last_battery + 300 * HZ)) {
 146                sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | 0x10);
 147                data->last_battery = jiffies;
 148        }
 149
 150        /* Cache the values for 1 second */
 151        if (time_after(jiffies, data->in_last_updated + HZ) || !data->in_valid) {
 152                for (i = 0; i < SCH5627_NO_IN; i++) {
 153                        val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_IN_MSB[i],
 154                                                         SCH5627_REG_IN_LSN[i],
 155                                                         SCH5627_REG_IN_HIGH_NIBBLE[i]);
 156                        if (unlikely(val < 0)) {
 157                                ret = val;
 158                                goto abort;
 159                        }
 160                        data->in[i] = val;
 161                }
 162                data->in_last_updated = jiffies;
 163                data->in_valid = 1;
 164        }
 165abort:
 166        mutex_unlock(&data->update_lock);
 167        return ret;
 168}
 169
 170static int sch5627_read_limits(struct sch5627_data *data)
 171{
 172        int i, val;
 173
 174        for (i = 0; i < SCH5627_NO_TEMPS; i++) {
 175                /*
 176                 * Note what SMSC calls ABS, is what lm_sensors calls max
 177                 * (aka high), and HIGH is what lm_sensors calls crit.
 178                 */
 179                val = sch56xx_read_virtual_reg(data->addr,
 180                                               SCH5627_REG_TEMP_ABS[i]);
 181                if (val < 0)
 182                        return val;
 183                data->temp_max[i] = val;
 184
 185                val = sch56xx_read_virtual_reg(data->addr,
 186                                               SCH5627_REG_TEMP_HIGH[i]);
 187                if (val < 0)
 188                        return val;
 189                data->temp_crit[i] = val;
 190        }
 191        for (i = 0; i < SCH5627_NO_FANS; i++) {
 192                val = sch56xx_read_virtual_reg16(data->addr,
 193                                                 SCH5627_REG_FAN_MIN[i]);
 194                if (val < 0)
 195                        return val;
 196                data->fan_min[i] = val;
 197        }
 198
 199        return 0;
 200}
 201
 202static int reg_to_temp(u16 reg)
 203{
 204        return (reg * 625) / 10 - 64000;
 205}
 206
 207static int reg_to_temp_limit(u8 reg)
 208{
 209        return (reg - 64) * 1000;
 210}
 211
 212static int reg_to_rpm(u16 reg)
 213{
 214        if (reg == 0)
 215                return -EIO;
 216        if (reg == 0xffff)
 217                return 0;
 218
 219        return 5400540 / reg;
 220}
 221
 222static umode_t sch5627_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
 223                                  int channel)
 224{
 225        return 0444;
 226}
 227
 228static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
 229                        long *val)
 230{
 231        struct sch5627_data *data = dev_get_drvdata(dev);
 232        int ret;
 233
 234        switch (type) {
 235        case hwmon_temp:
 236                ret = sch5627_update_temp(data);
 237                if (ret < 0)
 238                        return ret;
 239                switch (attr) {
 240                case hwmon_temp_input:
 241                        *val = reg_to_temp(data->temp[channel]);
 242                        return 0;
 243                case hwmon_temp_max:
 244                        *val = reg_to_temp_limit(data->temp_max[channel]);
 245                        return 0;
 246                case hwmon_temp_crit:
 247                        *val = reg_to_temp_limit(data->temp_crit[channel]);
 248                        return 0;
 249                case hwmon_temp_fault:
 250                        *val = (data->temp[channel] == 0);
 251                        return 0;
 252                default:
 253                        break;
 254                }
 255                break;
 256        case hwmon_fan:
 257                ret = sch5627_update_fan(data);
 258                if (ret < 0)
 259                        return ret;
 260                switch (attr) {
 261                case hwmon_fan_input:
 262                        ret = reg_to_rpm(data->fan[channel]);
 263                        if (ret < 0)
 264                                return ret;
 265                        *val = ret;
 266                        return 0;
 267                case hwmon_fan_min:
 268                        ret = reg_to_rpm(data->fan_min[channel]);
 269                        if (ret < 0)
 270                                return ret;
 271                        *val = ret;
 272                        return 0;
 273                case hwmon_fan_fault:
 274                        *val = (data->fan[channel] == 0xffff);
 275                        return 0;
 276                default:
 277                        break;
 278                }
 279                break;
 280        case hwmon_in:
 281                ret = sch5627_update_in(data);
 282                if (ret < 0)
 283                        return ret;
 284                switch (attr) {
 285                case hwmon_in_input:
 286                        *val = DIV_ROUND_CLOSEST(data->in[channel] * SCH5627_REG_IN_FACTOR[channel],
 287                                                 10000);
 288                        return 0;
 289                default:
 290                        break;
 291                }
 292                break;
 293        default:
 294                break;
 295        }
 296
 297        return -EOPNOTSUPP;
 298}
 299
 300static int sch5627_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
 301                               int channel, const char **str)
 302{
 303        switch (type) {
 304        case hwmon_in:
 305                switch (attr) {
 306                case hwmon_in_label:
 307                        *str = SCH5627_IN_LABELS[channel];
 308                        return 0;
 309                default:
 310                        break;
 311                }
 312                break;
 313        default:
 314                break;
 315        }
 316
 317        return -EOPNOTSUPP;
 318}
 319
 320static const struct hwmon_ops sch5627_ops = {
 321        .is_visible = sch5627_is_visible,
 322        .read = sch5627_read,
 323        .read_string = sch5627_read_string,
 324};
 325
 326static const struct hwmon_channel_info *sch5627_info[] = {
 327        HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
 328        HWMON_CHANNEL_INFO(temp,
 329                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
 330                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
 331                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
 332                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
 333                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
 334                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
 335                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
 336                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT
 337                           ),
 338        HWMON_CHANNEL_INFO(fan,
 339                           HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT,
 340                           HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT,
 341                           HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT,
 342                           HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT
 343                           ),
 344        HWMON_CHANNEL_INFO(in,
 345                           HWMON_I_INPUT | HWMON_I_LABEL,
 346                           HWMON_I_INPUT | HWMON_I_LABEL,
 347                           HWMON_I_INPUT | HWMON_I_LABEL,
 348                           HWMON_I_INPUT | HWMON_I_LABEL,
 349                           HWMON_I_INPUT
 350                           ),
 351        NULL
 352};
 353
 354static const struct hwmon_chip_info sch5627_chip_info = {
 355        .ops = &sch5627_ops,
 356        .info = sch5627_info,
 357};
 358
 359static int sch5627_probe(struct platform_device *pdev)
 360{
 361        struct sch5627_data *data;
 362        struct device *hwmon_dev;
 363        int err, build_code, build_id, hwmon_rev, val;
 364
 365        data = devm_kzalloc(&pdev->dev, sizeof(struct sch5627_data),
 366                            GFP_KERNEL);
 367        if (!data)
 368                return -ENOMEM;
 369
 370        data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
 371        mutex_init(&data->update_lock);
 372        platform_set_drvdata(pdev, data);
 373
 374        val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID);
 375        if (val < 0)
 376                return val;
 377
 378        if (val != SCH5627_HWMON_ID) {
 379                pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "hwmon",
 380                       val, SCH5627_HWMON_ID);
 381                return -ENODEV;
 382        }
 383
 384        val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID);
 385        if (val < 0)
 386                return val;
 387
 388        if (val != SCH5627_COMPANY_ID) {
 389                pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "company",
 390                       val, SCH5627_COMPANY_ID);
 391                return -ENODEV;
 392        }
 393
 394        val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID);
 395        if (val < 0)
 396                return val;
 397
 398        if (val != SCH5627_PRIMARY_ID) {
 399                pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "primary",
 400                       val, SCH5627_PRIMARY_ID);
 401                return -ENODEV;
 402        }
 403
 404        build_code = sch56xx_read_virtual_reg(data->addr,
 405                                              SCH5627_REG_BUILD_CODE);
 406        if (build_code < 0)
 407                return build_code;
 408
 409        build_id = sch56xx_read_virtual_reg16(data->addr,
 410                                              SCH5627_REG_BUILD_ID);
 411        if (build_id < 0)
 412                return build_id;
 413
 414        hwmon_rev = sch56xx_read_virtual_reg(data->addr,
 415                                             SCH5627_REG_HWMON_REV);
 416        if (hwmon_rev < 0)
 417                return hwmon_rev;
 418
 419        val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL);
 420        if (val < 0)
 421                return val;
 422
 423        data->control = val;
 424        if (!(data->control & 0x01)) {
 425                pr_err("hardware monitoring not enabled\n");
 426                return -ENODEV;
 427        }
 428        /* Trigger a Vbat voltage measurement, so that we get a valid reading
 429           the first time we read Vbat */
 430        sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL,
 431                                  data->control | 0x10);
 432        data->last_battery = jiffies;
 433
 434        /*
 435         * Read limits, we do this only once as reading a register on
 436         * the sch5627 is quite expensive (and they don't change).
 437         */
 438        err = sch5627_read_limits(data);
 439        if (err)
 440                return err;
 441
 442        pr_info("found %s chip at %#hx\n", DEVNAME, data->addr);
 443        pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n",
 444                build_code, build_id, hwmon_rev);
 445
 446        hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, DEVNAME, data,
 447                                                         &sch5627_chip_info, NULL);
 448        if (IS_ERR(hwmon_dev))
 449                return PTR_ERR(hwmon_dev);
 450
 451        /* Note failing to register the watchdog is not a fatal error */
 452        sch56xx_watchdog_register(&pdev->dev, data->addr,
 453                                  (build_code << 24) | (build_id << 8) | hwmon_rev,
 454                                  &data->update_lock, 1);
 455
 456        return 0;
 457}
 458
 459static struct platform_driver sch5627_driver = {
 460        .driver = {
 461                .name   = DRVNAME,
 462        },
 463        .probe          = sch5627_probe,
 464};
 465
 466module_platform_driver(sch5627_driver);
 467
 468MODULE_DESCRIPTION("SMSC SCH5627 Hardware Monitoring Driver");
 469MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 470MODULE_LICENSE("GPL");
 471