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 const struct m10bmc_sdata n5010bmc_temp_tbl[] = {
 232        { 0x100, 0x0, 0x104, 0x0, 0x0, 1000, "Board Local Temperature" },
 233        { 0x108, 0x0, 0x10c, 0x0, 0x0, 1000, "FPGA 1 Temperature" },
 234        { 0x110, 0x0, 0x114, 0x0, 0x0, 1000, "FPGA 2 Temperature" },
 235        { 0x118, 0x0, 0x0, 0x0, 0x0, 1000, "Card Top Temperature" },
 236        { 0x11c, 0x0, 0x0, 0x0, 0x0, 1000, "Card Bottom Temperature" },
 237        { 0x128, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 1.2V Temperature" },
 238        { 0x134, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 5V Temperature" },
 239        { 0x140, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 0.9V Temperature" },
 240        { 0x14c, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 0.85V Temperature" },
 241        { 0x158, 0x0, 0x0, 0x0, 0x0, 1000, "AUX 12V Temperature" },
 242        { 0x164, 0x0, 0x0, 0x0, 0x0, 1000, "Backplane 12V Temperature" },
 243        { 0x1a8, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-1 Temperature" },
 244        { 0x1ac, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-2 Temperature" },
 245        { 0x1b0, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-3 Temperature" },
 246        { 0x1b4, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-4 Temperature" },
 247        { 0x1b8, 0x0, 0x0, 0x0, 0x0, 1000, "CVL1 Internal Temperature" },
 248        { 0x1bc, 0x0, 0x0, 0x0, 0x0, 1000, "CVL2 Internal Temperature" },
 249};
 250
 251static const struct m10bmc_sdata n5010bmc_in_tbl[] = {
 252        { 0x120, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.2V Voltage" },
 253        { 0x12c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 5V Voltage" },
 254        { 0x138, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.9V Voltage" },
 255        { 0x144, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.85V Voltage" },
 256        { 0x150, 0x0, 0x0, 0x0, 0x0, 1, "AUX 12V Voltage" },
 257        { 0x15c, 0x0, 0x0, 0x0, 0x0, 1, "Backplane 12V Voltage" },
 258        { 0x16c, 0x0, 0x0, 0x0, 0x0, 1, "DDR4 1.2V Voltage" },
 259        { 0x17c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.8V Voltage" },
 260        { 0x184, 0x0, 0x0, 0x0, 0x0, 1, "QDR 1.3V Voltage" },
 261        { 0x18c, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 0.8V Voltage" },
 262        { 0x194, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 1.05V Voltage" },
 263        { 0x19c, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 1.05V Voltage" },
 264        { 0x1a4, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 0.8V Voltage" },
 265};
 266
 267static const struct m10bmc_sdata n5010bmc_curr_tbl[] = {
 268        { 0x124, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.2V Current" },
 269        { 0x130, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 5V Current" },
 270        { 0x13c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.9V Current" },
 271        { 0x148, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.85V Current" },
 272        { 0x154, 0x0, 0x0, 0x0, 0x0, 1, "AUX 12V Current" },
 273        { 0x160, 0x0, 0x0, 0x0, 0x0, 1, "Backplane 12V Current" },
 274        { 0x168, 0x0, 0x0, 0x0, 0x0, 1, "DDR4 1.2V Current" },
 275        { 0x178, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.8V Current" },
 276        { 0x180, 0x0, 0x0, 0x0, 0x0, 1, "QDR 1.3V Current" },
 277        { 0x188, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 0.8V Current" },
 278        { 0x190, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 1.05V Current" },
 279        { 0x198, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 1.05V Current" },
 280        { 0x1a0, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 0.8V Current" },
 281};
 282
 283static const struct hwmon_channel_info *n5010bmc_hinfo[] = {
 284        HWMON_CHANNEL_INFO(temp,
 285                           HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL,
 286                           HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL,
 287                           HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL,
 288                           HWMON_T_INPUT | HWMON_T_LABEL,
 289                           HWMON_T_INPUT | HWMON_T_LABEL,
 290                           HWMON_T_INPUT | HWMON_T_LABEL,
 291                           HWMON_T_INPUT | HWMON_T_LABEL,
 292                           HWMON_T_INPUT | HWMON_T_LABEL,
 293                           HWMON_T_INPUT | HWMON_T_LABEL,
 294                           HWMON_T_INPUT | HWMON_T_LABEL,
 295                           HWMON_T_INPUT | HWMON_T_LABEL,
 296                           HWMON_T_INPUT | HWMON_T_LABEL,
 297                           HWMON_T_INPUT | HWMON_T_LABEL,
 298                           HWMON_T_INPUT | HWMON_T_LABEL,
 299                           HWMON_T_INPUT | HWMON_T_LABEL,
 300                           HWMON_T_INPUT | HWMON_T_LABEL,
 301                           HWMON_T_INPUT | HWMON_T_LABEL),
 302        HWMON_CHANNEL_INFO(in,
 303                           HWMON_I_INPUT | HWMON_I_LABEL,
 304                           HWMON_I_INPUT | HWMON_I_LABEL,
 305                           HWMON_I_INPUT | HWMON_I_LABEL,
 306                           HWMON_I_INPUT | HWMON_I_LABEL,
 307                           HWMON_I_INPUT | HWMON_I_LABEL,
 308                           HWMON_I_INPUT | HWMON_I_LABEL,
 309                           HWMON_I_INPUT | HWMON_I_LABEL,
 310                           HWMON_I_INPUT | HWMON_I_LABEL,
 311                           HWMON_I_INPUT | HWMON_I_LABEL,
 312                           HWMON_I_INPUT | HWMON_I_LABEL,
 313                           HWMON_I_INPUT | HWMON_I_LABEL,
 314                           HWMON_I_INPUT | HWMON_I_LABEL,
 315                           HWMON_I_INPUT | HWMON_I_LABEL),
 316        HWMON_CHANNEL_INFO(curr,
 317                           HWMON_C_INPUT | HWMON_C_LABEL,
 318                           HWMON_C_INPUT | HWMON_C_LABEL,
 319                           HWMON_C_INPUT | HWMON_C_LABEL,
 320                           HWMON_C_INPUT | HWMON_C_LABEL,
 321                           HWMON_C_INPUT | HWMON_C_LABEL,
 322                           HWMON_C_INPUT | HWMON_C_LABEL,
 323                           HWMON_C_INPUT | HWMON_C_LABEL,
 324                           HWMON_C_INPUT | HWMON_C_LABEL,
 325                           HWMON_C_INPUT | HWMON_C_LABEL,
 326                           HWMON_C_INPUT | HWMON_C_LABEL,
 327                           HWMON_C_INPUT | HWMON_C_LABEL,
 328                           HWMON_C_INPUT | HWMON_C_LABEL,
 329                           HWMON_C_INPUT | HWMON_C_LABEL),
 330        NULL
 331};
 332
 333static const struct m10bmc_hwmon_board_data n5010bmc_hwmon_bdata = {
 334        .tables = {
 335                [hwmon_temp] = n5010bmc_temp_tbl,
 336                [hwmon_in] = n5010bmc_in_tbl,
 337                [hwmon_curr] = n5010bmc_curr_tbl,
 338        },
 339
 340        .hinfo = n5010bmc_hinfo,
 341};
 342
 343static umode_t
 344m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
 345                        u32 attr, int channel)
 346{
 347        return 0444;
 348}
 349
 350static const struct m10bmc_sdata *
 351find_sensor_data(struct m10bmc_hwmon *hw, enum hwmon_sensor_types type,
 352                 int channel)
 353{
 354        const struct m10bmc_sdata *tbl;
 355
 356        tbl = hw->bdata->tables[type];
 357        if (!tbl)
 358                return ERR_PTR(-EOPNOTSUPP);
 359
 360        return &tbl[channel];
 361}
 362
 363static int do_sensor_read(struct m10bmc_hwmon *hw,
 364                          const struct m10bmc_sdata *data,
 365                          unsigned int regoff, long *val)
 366{
 367        unsigned int regval;
 368        int ret;
 369
 370        ret = m10bmc_sys_read(hw->m10bmc, regoff, &regval);
 371        if (ret)
 372                return ret;
 373
 374        /*
 375         * BMC Firmware will return 0xdeadbeef if the sensor value is invalid
 376         * at that time. This usually happens on sensor channels which connect
 377         * to external pluggable modules, e.g. QSFP temperature and voltage.
 378         * When the QSFP is unplugged from cage, driver will get 0xdeadbeef
 379         * from their registers.
 380         */
 381        if (regval == 0xdeadbeef)
 382                return -ENODATA;
 383
 384        *val = regval * data->multiplier;
 385
 386        return 0;
 387}
 388
 389static int m10bmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
 390                             u32 attr, int channel, long *val)
 391{
 392        struct m10bmc_hwmon *hw = dev_get_drvdata(dev);
 393        unsigned int reg = 0, reg_hyst = 0;
 394        const struct m10bmc_sdata *data;
 395        long hyst, value;
 396        int ret;
 397
 398        data = find_sensor_data(hw, type, channel);
 399        if (IS_ERR(data))
 400                return PTR_ERR(data);
 401
 402        switch (type) {
 403        case hwmon_temp:
 404                switch (attr) {
 405                case hwmon_temp_input:
 406                        reg = data->reg_input;
 407                        break;
 408                case hwmon_temp_max_hyst:
 409                        reg_hyst = data->reg_hyst;
 410                        fallthrough;
 411                case hwmon_temp_max:
 412                        reg = data->reg_max;
 413                        break;
 414                case hwmon_temp_crit_hyst:
 415                        reg_hyst = data->reg_hyst;
 416                        fallthrough;
 417                case hwmon_temp_crit:
 418                        reg = data->reg_crit;
 419                        break;
 420                default:
 421                        return -EOPNOTSUPP;
 422                }
 423                break;
 424        case hwmon_in:
 425                switch (attr) {
 426                case hwmon_in_input:
 427                        reg = data->reg_input;
 428                        break;
 429                case hwmon_in_max:
 430                        reg = data->reg_max;
 431                        break;
 432                case hwmon_in_crit:
 433                        reg = data->reg_crit;
 434                        break;
 435                case hwmon_in_min:
 436                        reg = data->reg_min;
 437                        break;
 438                default:
 439                        return -EOPNOTSUPP;
 440                }
 441                break;
 442        case hwmon_curr:
 443                switch (attr) {
 444                case hwmon_curr_input:
 445                        reg = data->reg_input;
 446                        break;
 447                case hwmon_curr_max:
 448                        reg = data->reg_max;
 449                        break;
 450                case hwmon_curr_crit:
 451                        reg = data->reg_crit;
 452                        break;
 453                default:
 454                        return -EOPNOTSUPP;
 455                }
 456                break;
 457        case hwmon_power:
 458                switch (attr) {
 459                case hwmon_power_input:
 460                        reg = data->reg_input;
 461                        break;
 462                default:
 463                        return -EOPNOTSUPP;
 464                }
 465                break;
 466        default:
 467                return -EOPNOTSUPP;
 468        }
 469
 470        if (!reg)
 471                return -EOPNOTSUPP;
 472
 473        ret = do_sensor_read(hw, data, reg, &value);
 474        if (ret)
 475                return ret;
 476
 477        if (reg_hyst) {
 478                ret = do_sensor_read(hw, data, reg_hyst, &hyst);
 479                if (ret)
 480                        return ret;
 481
 482                value -= hyst;
 483        }
 484
 485        *val = value;
 486
 487        return 0;
 488}
 489
 490static int m10bmc_hwmon_read_string(struct device *dev,
 491                                    enum hwmon_sensor_types type,
 492                                    u32 attr, int channel, const char **str)
 493{
 494        struct m10bmc_hwmon *hw = dev_get_drvdata(dev);
 495        const struct m10bmc_sdata *data;
 496
 497        data = find_sensor_data(hw, type, channel);
 498        if (IS_ERR(data))
 499                return PTR_ERR(data);
 500
 501        *str = data->label;
 502
 503        return 0;
 504}
 505
 506static const struct hwmon_ops m10bmc_hwmon_ops = {
 507        .is_visible = m10bmc_hwmon_is_visible,
 508        .read = m10bmc_hwmon_read,
 509        .read_string = m10bmc_hwmon_read_string,
 510};
 511
 512static int m10bmc_hwmon_probe(struct platform_device *pdev)
 513{
 514        const struct platform_device_id *id = platform_get_device_id(pdev);
 515        struct intel_m10bmc *m10bmc = dev_get_drvdata(pdev->dev.parent);
 516        struct device *hwmon_dev, *dev = &pdev->dev;
 517        struct m10bmc_hwmon *hw;
 518        int i;
 519
 520        hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
 521        if (!hw)
 522                return -ENOMEM;
 523
 524        hw->dev = dev;
 525        hw->m10bmc = m10bmc;
 526        hw->bdata = (const struct m10bmc_hwmon_board_data *)id->driver_data;
 527
 528        hw->chip.info = hw->bdata->hinfo;
 529        hw->chip.ops = &m10bmc_hwmon_ops;
 530
 531        hw->hw_name = devm_kstrdup(dev, id->name, GFP_KERNEL);
 532        if (!hw->hw_name)
 533                return -ENOMEM;
 534
 535        for (i = 0; hw->hw_name[i]; i++)
 536                if (hwmon_is_bad_char(hw->hw_name[i]))
 537                        hw->hw_name[i] = '_';
 538
 539        hwmon_dev = devm_hwmon_device_register_with_info(dev, hw->hw_name,
 540                                                         hw, &hw->chip, NULL);
 541        return PTR_ERR_OR_ZERO(hwmon_dev);
 542}
 543
 544static const struct platform_device_id intel_m10bmc_hwmon_ids[] = {
 545        {
 546                .name = "n3000bmc-hwmon",
 547                .driver_data = (unsigned long)&n3000bmc_hwmon_bdata,
 548        },
 549        {
 550                .name = "d5005bmc-hwmon",
 551                .driver_data = (unsigned long)&d5005bmc_hwmon_bdata,
 552        },
 553        {
 554                .name = "n5010bmc-hwmon",
 555                .driver_data = (unsigned long)&n5010bmc_hwmon_bdata,
 556        },
 557        { }
 558};
 559
 560static struct platform_driver intel_m10bmc_hwmon_driver = {
 561        .probe = m10bmc_hwmon_probe,
 562        .driver = {
 563                .name = "intel-m10-bmc-hwmon",
 564        },
 565        .id_table = intel_m10bmc_hwmon_ids,
 566};
 567module_platform_driver(intel_m10bmc_hwmon_driver);
 568
 569MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids);
 570MODULE_AUTHOR("Intel Corporation");
 571MODULE_DESCRIPTION("Intel MAX 10 BMC hardware monitor");
 572MODULE_LICENSE("GPL");
 573