linux/drivers/hwmon/intel-m10-bmc-hwmon.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Intel MAX 10 BMC HWMON Driver
   4 *
   5 * Copyright (C) 2018-2020 Intel Corporation. All rights reserved.
   6 *
   7 */
   8#include <linux/device.h>
   9#include <linux/hwmon.h>
  10#include <linux/mfd/intel-m10-bmc.h>
  11#include <linux/module.h>
  12#include <linux/mod_devicetable.h>
  13#include <linux/platform_device.h>
  14
  15struct m10bmc_sdata {
  16        unsigned int reg_input;
  17        unsigned int reg_max;
  18        unsigned int reg_crit;
  19        unsigned int reg_hyst;
  20        unsigned int reg_min;
  21        unsigned int multiplier;
  22        const char *label;
  23};
  24
  25struct m10bmc_hwmon_board_data {
  26        const struct m10bmc_sdata *tables[hwmon_max];
  27        const struct hwmon_channel_info **hinfo;
  28};
  29
  30struct m10bmc_hwmon {
  31        struct device *dev;
  32        struct hwmon_chip_info chip;
  33        char *hw_name;
  34        struct intel_m10bmc *m10bmc;
  35        const struct m10bmc_hwmon_board_data *bdata;
  36};
  37
  38static const struct m10bmc_sdata n3000bmc_temp_tbl[] = {
  39        { 0x100, 0x104, 0x108, 0x10c, 0x0, 500, "Board Temperature" },
  40        { 0x110, 0x114, 0x118, 0x0, 0x0, 500, "FPGA Die Temperature" },
  41        { 0x11c, 0x124, 0x120, 0x0, 0x0, 500, "QSFP0 Temperature" },
  42        { 0x12c, 0x134, 0x130, 0x0, 0x0, 500, "QSFP1 Temperature" },
  43        { 0x168, 0x0, 0x0, 0x0, 0x0, 500, "Retimer A Temperature" },
  44        { 0x16c, 0x0, 0x0, 0x0, 0x0, 500, "Retimer A SerDes Temperature" },
  45        { 0x170, 0x0, 0x0, 0x0, 0x0, 500, "Retimer B Temperature" },
  46        { 0x174, 0x0, 0x0, 0x0, 0x0, 500, "Retimer B SerDes Temperature" },
  47};
  48
  49static const struct m10bmc_sdata n3000bmc_in_tbl[] = {
  50        { 0x128, 0x0, 0x0, 0x0, 0x0, 1, "QSFP0 Supply Voltage" },
  51        { 0x138, 0x0, 0x0, 0x0, 0x0, 1, "QSFP1 Supply Voltage" },
  52        { 0x13c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage" },
  53        { 0x144, 0x0, 0x0, 0x0, 0x0, 1, "12V Backplane Voltage" },
  54        { 0x14c, 0x0, 0x0, 0x0, 0x0, 1, "1.2V Voltage" },
  55        { 0x150, 0x0, 0x0, 0x0, 0x0, 1, "12V AUX Voltage" },
  56        { 0x158, 0x0, 0x0, 0x0, 0x0, 1, "1.8V Voltage" },
  57        { 0x15c, 0x0, 0x0, 0x0, 0x0, 1, "3.3V Voltage" },
  58};
  59
  60static const struct m10bmc_sdata n3000bmc_curr_tbl[] = {
  61        { 0x140, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Current" },
  62        { 0x148, 0x0, 0x0, 0x0, 0x0, 1, "12V Backplane Current" },
  63        { 0x154, 0x0, 0x0, 0x0, 0x0, 1, "12V AUX Current" },
  64};
  65
  66static const struct m10bmc_sdata n3000bmc_power_tbl[] = {
  67        { 0x160, 0x0, 0x0, 0x0, 0x0, 1000, "Board Power" },
  68};
  69
  70static const struct hwmon_channel_info *n3000bmc_hinfo[] = {
  71        HWMON_CHANNEL_INFO(temp,
  72                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
  73                           HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
  74                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
  75                           HWMON_T_LABEL,
  76                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
  77                           HWMON_T_LABEL,
  78                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
  79                           HWMON_T_LABEL,
  80                           HWMON_T_INPUT | HWMON_T_LABEL,
  81                           HWMON_T_INPUT | HWMON_T_LABEL,
  82                           HWMON_T_INPUT | HWMON_T_LABEL,
  83                           HWMON_T_INPUT | HWMON_T_LABEL),
  84        HWMON_CHANNEL_INFO(in,
  85                           HWMON_I_INPUT | HWMON_I_LABEL,
  86                           HWMON_I_INPUT | HWMON_I_LABEL,
  87                           HWMON_I_INPUT | HWMON_I_LABEL,
  88                           HWMON_I_INPUT | HWMON_I_LABEL,
  89                           HWMON_I_INPUT | HWMON_I_LABEL,
  90                           HWMON_I_INPUT | HWMON_I_LABEL,
  91                           HWMON_I_INPUT | HWMON_I_LABEL,
  92                           HWMON_I_INPUT | HWMON_I_LABEL),
  93        HWMON_CHANNEL_INFO(curr,
  94                           HWMON_C_INPUT | HWMON_C_LABEL,
  95                           HWMON_C_INPUT | HWMON_C_LABEL,
  96                           HWMON_C_INPUT | HWMON_C_LABEL),
  97        HWMON_CHANNEL_INFO(power,
  98                           HWMON_P_INPUT | HWMON_P_LABEL),
  99        NULL
 100};
 101
 102static const struct m10bmc_sdata d5005bmc_temp_tbl[] = {
 103        { 0x100, 0x104, 0x108, 0x10c, 0x0, 500, "Board Inlet Air Temperature" },
 104        { 0x110, 0x114, 0x118, 0x0, 0x0, 500, "FPGA Core Temperature" },
 105        { 0x11c, 0x120, 0x124, 0x128, 0x0, 500, "Board Exhaust Air Temperature" },
 106        { 0x12c, 0x130, 0x134, 0x0, 0x0, 500, "FPGA Transceiver Temperature" },
 107        { 0x138, 0x13c, 0x140, 0x144, 0x0, 500, "RDIMM0 Temperature" },
 108        { 0x148, 0x14c, 0x150, 0x154, 0x0, 500, "RDIMM1 Temperature" },
 109        { 0x158, 0x15c, 0x160, 0x164, 0x0, 500, "RDIMM2 Temperature" },
 110        { 0x168, 0x16c, 0x170, 0x174, 0x0, 500, "RDIMM3 Temperature" },
 111        { 0x178, 0x17c, 0x180, 0x0, 0x0, 500, "QSFP0 Temperature" },
 112        { 0x188, 0x18c, 0x190, 0x0, 0x0, 500, "QSFP1 Temperature" },
 113        { 0x1a0, 0x1a4, 0x1a8, 0x0, 0x0, 500, "3.3v Temperature" },
 114        { 0x1bc, 0x1c0, 0x1c4, 0x0, 0x0, 500, "VCCERAM Temperature" },
 115        { 0x1d8, 0x1dc, 0x1e0, 0x0, 0x0, 500, "VCCR Temperature" },
 116        { 0x1f4, 0x1f8, 0x1fc, 0x0, 0x0, 500, "VCCT Temperature" },
 117        { 0x210, 0x214, 0x218, 0x0, 0x0, 500, "1.8v Temperature" },
 118        { 0x22c, 0x230, 0x234, 0x0, 0x0, 500, "12v Backplane Temperature" },
 119        { 0x248, 0x24c, 0x250, 0x0, 0x0, 500, "12v AUX Temperature" },
 120};
 121
 122static const struct m10bmc_sdata d5005bmc_in_tbl[] = {
 123        { 0x184, 0x0, 0x0, 0x0, 0x0, 1, "QSFP0 Supply Voltage" },
 124        { 0x194, 0x0, 0x0, 0x0, 0x0, 1, "QSFP1 Supply Voltage" },
 125        { 0x198, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage" },
 126        { 0x1ac, 0x1b0, 0x1b4, 0x0, 0x0, 1, "3.3v Voltage" },
 127        { 0x1c8, 0x1cc, 0x1d0, 0x0, 0x0, 1, "VCCERAM Voltage" },
 128        { 0x1e4, 0x1e8, 0x1ec, 0x0, 0x0, 1, "VCCR Voltage" },
 129        { 0x200, 0x204, 0x208, 0x0, 0x0, 1, "VCCT Voltage" },
 130        { 0x21c, 0x220, 0x224, 0x0, 0x0, 1, "1.8v Voltage" },
 131        { 0x238, 0x0, 0x0, 0x0, 0x23c, 1, "12v Backplane Voltage" },
 132        { 0x254, 0x0, 0x0, 0x0, 0x258, 1, "12v AUX Voltage" },
 133};
 134
 135static const struct m10bmc_sdata d5005bmc_curr_tbl[] = {
 136        { 0x19c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Current" },
 137        { 0x1b8, 0x0, 0x0, 0x0, 0x0, 1, "3.3v Current" },
 138        { 0x1d4, 0x0, 0x0, 0x0, 0x0, 1, "VCCERAM Current" },
 139        { 0x1f0, 0x0, 0x0, 0x0, 0x0, 1, "VCCR Current" },
 140        { 0x20c, 0x0, 0x0, 0x0, 0x0, 1, "VCCT Current" },
 141        { 0x228, 0x0, 0x0, 0x0, 0x0, 1, "1.8v Current" },
 142        { 0x240, 0x244, 0x0, 0x0, 0x0, 1, "12v Backplane Current" },
 143        { 0x25c, 0x260, 0x0, 0x0, 0x0, 1, "12v AUX Current" },
 144};
 145
 146static const struct m10bmc_hwmon_board_data n3000bmc_hwmon_bdata = {
 147        .tables = {
 148                [hwmon_temp] = n3000bmc_temp_tbl,
 149                [hwmon_in] = n3000bmc_in_tbl,
 150                [hwmon_curr] = n3000bmc_curr_tbl,
 151                [hwmon_power] = n3000bmc_power_tbl,
 152        },
 153
 154        .hinfo = n3000bmc_hinfo,
 155};
 156
 157static const struct hwmon_channel_info *d5005bmc_hinfo[] = {
 158        HWMON_CHANNEL_INFO(temp,
 159                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
 160                           HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
 161                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 162                           HWMON_T_LABEL,
 163                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
 164                           HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
 165                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 166                           HWMON_T_LABEL,
 167                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
 168                           HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
 169                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
 170                           HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
 171                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
 172                           HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
 173                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
 174                           HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
 175                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 176                           HWMON_T_LABEL,
 177                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 178                           HWMON_T_LABEL,
 179                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 180                           HWMON_T_LABEL,
 181                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 182                           HWMON_T_LABEL,
 183                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 184                           HWMON_T_LABEL,
 185                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 186                           HWMON_T_LABEL,
 187                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 188                           HWMON_T_LABEL,
 189                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 190                           HWMON_T_LABEL,
 191                           HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
 192                           HWMON_T_LABEL),
 193        HWMON_CHANNEL_INFO(in,
 194                           HWMON_I_INPUT | HWMON_I_LABEL,
 195                           HWMON_I_INPUT | HWMON_I_LABEL,
 196                           HWMON_I_INPUT | HWMON_I_LABEL,
 197                           HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
 198                           HWMON_I_LABEL,
 199                           HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
 200                           HWMON_I_LABEL,
 201                           HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
 202                           HWMON_I_LABEL,
 203                           HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
 204                           HWMON_I_LABEL,
 205                           HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
 206                           HWMON_I_LABEL,
 207                           HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_LABEL,
 208                           HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_LABEL),
 209        HWMON_CHANNEL_INFO(curr,
 210                           HWMON_C_INPUT | HWMON_C_LABEL,
 211                           HWMON_C_INPUT | HWMON_C_LABEL,
 212                           HWMON_C_INPUT | HWMON_C_LABEL,
 213                           HWMON_C_INPUT | HWMON_C_LABEL,
 214                           HWMON_C_INPUT | HWMON_C_LABEL,
 215                           HWMON_C_INPUT | HWMON_C_LABEL,
 216                           HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_LABEL,
 217                           HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_LABEL),
 218        NULL
 219};
 220
 221static const struct m10bmc_hwmon_board_data d5005bmc_hwmon_bdata = {
 222        .tables = {
 223                [hwmon_temp] = d5005bmc_temp_tbl,
 224                [hwmon_in] = d5005bmc_in_tbl,
 225                [hwmon_curr] = d5005bmc_curr_tbl,
 226        },
 227
 228        .hinfo = d5005bmc_hinfo,
 229};
 230
 231static umode_t
 232m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
 233                        u32 attr, int channel)
 234{
 235        return 0444;
 236}
 237
 238static const struct m10bmc_sdata *
 239find_sensor_data(struct m10bmc_hwmon *hw, enum hwmon_sensor_types type,
 240                 int channel)
 241{
 242        const struct m10bmc_sdata *tbl;
 243
 244        tbl = hw->bdata->tables[type];
 245        if (!tbl)
 246                return ERR_PTR(-EOPNOTSUPP);
 247
 248        return &tbl[channel];
 249}
 250
 251static int do_sensor_read(struct m10bmc_hwmon *hw,
 252                          const struct m10bmc_sdata *data,
 253                          unsigned int regoff, long *val)
 254{
 255        unsigned int regval;
 256        int ret;
 257
 258        ret = m10bmc_sys_read(hw->m10bmc, regoff, &regval);
 259        if (ret)
 260                return ret;
 261
 262        /*
 263         * BMC Firmware will return 0xdeadbeef if the sensor value is invalid
 264         * at that time. This usually happens on sensor channels which connect
 265         * to external pluggable modules, e.g. QSFP temperature and voltage.
 266         * When the QSFP is unplugged from cage, driver will get 0xdeadbeef
 267         * from their registers.
 268         */
 269        if (regval == 0xdeadbeef)
 270                return -ENODATA;
 271
 272        *val = regval * data->multiplier;
 273
 274        return 0;
 275}
 276
 277static int m10bmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
 278                             u32 attr, int channel, long *val)
 279{
 280        struct m10bmc_hwmon *hw = dev_get_drvdata(dev);
 281        unsigned int reg = 0, reg_hyst = 0;
 282        const struct m10bmc_sdata *data;
 283        long hyst, value;
 284        int ret;
 285
 286        data = find_sensor_data(hw, type, channel);
 287        if (IS_ERR(data))
 288                return PTR_ERR(data);
 289
 290        switch (type) {
 291        case hwmon_temp:
 292                switch (attr) {
 293                case hwmon_temp_input:
 294                        reg = data->reg_input;
 295                        break;
 296                case hwmon_temp_max_hyst:
 297                        reg_hyst = data->reg_hyst;
 298                        fallthrough;
 299                case hwmon_temp_max:
 300                        reg = data->reg_max;
 301                        break;
 302                case hwmon_temp_crit_hyst:
 303                        reg_hyst = data->reg_hyst;
 304                        fallthrough;
 305                case hwmon_temp_crit:
 306                        reg = data->reg_crit;
 307                        break;
 308                default:
 309                        return -EOPNOTSUPP;
 310                }
 311                break;
 312        case hwmon_in:
 313                switch (attr) {
 314                case hwmon_in_input:
 315                        reg = data->reg_input;
 316                        break;
 317                case hwmon_in_max:
 318                        reg = data->reg_max;
 319                        break;
 320                case hwmon_in_crit:
 321                        reg = data->reg_crit;
 322                        break;
 323                case hwmon_in_min:
 324                        reg = data->reg_min;
 325                        break;
 326                default:
 327                        return -EOPNOTSUPP;
 328                }
 329                break;
 330        case hwmon_curr:
 331                switch (attr) {
 332                case hwmon_curr_input:
 333                        reg = data->reg_input;
 334                        break;
 335                case hwmon_curr_max:
 336                        reg = data->reg_max;
 337                        break;
 338                case hwmon_curr_crit:
 339                        reg = data->reg_crit;
 340                        break;
 341                default:
 342                        return -EOPNOTSUPP;
 343                }
 344                break;
 345        case hwmon_power:
 346                switch (attr) {
 347                case hwmon_power_input:
 348                        reg = data->reg_input;
 349                        break;
 350                default:
 351                        return -EOPNOTSUPP;
 352                }
 353                break;
 354        default:
 355                return -EOPNOTSUPP;
 356        }
 357
 358        if (!reg)
 359                return -EOPNOTSUPP;
 360
 361        ret = do_sensor_read(hw, data, reg, &value);
 362        if (ret)
 363                return ret;
 364
 365        if (reg_hyst) {
 366                ret = do_sensor_read(hw, data, reg_hyst, &hyst);
 367                if (ret)
 368                        return ret;
 369
 370                value -= hyst;
 371        }
 372
 373        *val = value;
 374
 375        return 0;
 376}
 377
 378static int m10bmc_hwmon_read_string(struct device *dev,
 379                                    enum hwmon_sensor_types type,
 380                                    u32 attr, int channel, const char **str)
 381{
 382        struct m10bmc_hwmon *hw = dev_get_drvdata(dev);
 383        const struct m10bmc_sdata *data;
 384
 385        data = find_sensor_data(hw, type, channel);
 386        if (IS_ERR(data))
 387                return PTR_ERR(data);
 388
 389        *str = data->label;
 390
 391        return 0;
 392}
 393
 394static const struct hwmon_ops m10bmc_hwmon_ops = {
 395        .is_visible = m10bmc_hwmon_is_visible,
 396        .read = m10bmc_hwmon_read,
 397        .read_string = m10bmc_hwmon_read_string,
 398};
 399
 400static int m10bmc_hwmon_probe(struct platform_device *pdev)
 401{
 402        const struct platform_device_id *id = platform_get_device_id(pdev);
 403        struct intel_m10bmc *m10bmc = dev_get_drvdata(pdev->dev.parent);
 404        struct device *hwmon_dev, *dev = &pdev->dev;
 405        struct m10bmc_hwmon *hw;
 406        int i;
 407
 408        hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
 409        if (!hw)
 410                return -ENOMEM;
 411
 412        hw->dev = dev;
 413        hw->m10bmc = m10bmc;
 414        hw->bdata = (const struct m10bmc_hwmon_board_data *)id->driver_data;
 415
 416        hw->chip.info = hw->bdata->hinfo;
 417        hw->chip.ops = &m10bmc_hwmon_ops;
 418
 419        hw->hw_name = devm_kstrdup(dev, id->name, GFP_KERNEL);
 420        if (!hw->hw_name)
 421                return -ENOMEM;
 422
 423        for (i = 0; hw->hw_name[i]; i++)
 424                if (hwmon_is_bad_char(hw->hw_name[i]))
 425                        hw->hw_name[i] = '_';
 426
 427        hwmon_dev = devm_hwmon_device_register_with_info(dev, hw->hw_name,
 428                                                         hw, &hw->chip, NULL);
 429        return PTR_ERR_OR_ZERO(hwmon_dev);
 430}
 431
 432static const struct platform_device_id intel_m10bmc_hwmon_ids[] = {
 433        {
 434                .name = "n3000bmc-hwmon",
 435                .driver_data = (unsigned long)&n3000bmc_hwmon_bdata,
 436        },
 437        {
 438                .name = "d5005bmc-hwmon",
 439                .driver_data = (unsigned long)&d5005bmc_hwmon_bdata,
 440        },
 441        { }
 442};
 443
 444static struct platform_driver intel_m10bmc_hwmon_driver = {
 445        .probe = m10bmc_hwmon_probe,
 446        .driver = {
 447                .name = "intel-m10-bmc-hwmon",
 448        },
 449        .id_table = intel_m10bmc_hwmon_ids,
 450};
 451module_platform_driver(intel_m10bmc_hwmon_driver);
 452
 453MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids);
 454MODULE_AUTHOR("Intel Corporation");
 455MODULE_DESCRIPTION("Intel MAX 10 BMC hardware monitor");
 456MODULE_LICENSE("GPL");
 457