linux/drivers/net/ethernet/mellanox/mlxsw/core_env.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
   3
   4#include <linux/kernel.h>
   5#include <linux/err.h>
   6#include <linux/ethtool.h>
   7#include <linux/sfp.h>
   8
   9#include "core.h"
  10#include "core_env.h"
  11#include "item.h"
  12#include "reg.h"
  13
  14struct mlxsw_env_module_info {
  15        u64 module_overheat_counter;
  16        bool is_overheat;
  17};
  18
  19struct mlxsw_env {
  20        struct mlxsw_core *core;
  21        u8 module_count;
  22        spinlock_t module_info_lock; /* Protects 'module_info'. */
  23        struct mlxsw_env_module_info module_info[];
  24};
  25
  26static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
  27                                          bool *qsfp, bool *cmis)
  28{
  29        char mcia_pl[MLXSW_REG_MCIA_LEN];
  30        char *eeprom_tmp;
  31        u8 ident;
  32        int err;
  33
  34        mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
  35                            MLXSW_REG_MCIA_I2C_ADDR_LOW);
  36        err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
  37        if (err)
  38                return err;
  39        eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
  40        ident = eeprom_tmp[0];
  41        *cmis = false;
  42        switch (ident) {
  43        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
  44                *qsfp = false;
  45                break;
  46        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
  47        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
  48        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
  49                *qsfp = true;
  50                break;
  51        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
  52                *qsfp = true;
  53                *cmis = true;
  54                break;
  55        default:
  56                return -EINVAL;
  57        }
  58
  59        return 0;
  60}
  61
  62static int
  63mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module,
  64                              u16 offset, u16 size, void *data,
  65                              bool qsfp, unsigned int *p_read_size)
  66{
  67        char mcia_pl[MLXSW_REG_MCIA_LEN];
  68        char *eeprom_tmp;
  69        u16 i2c_addr;
  70        u8 page = 0;
  71        int status;
  72        int err;
  73
  74        /* MCIA register accepts buffer size <= 48. Page of size 128 should be
  75         * read by chunks of size 48, 48, 32. Align the size of the last chunk
  76         * to avoid reading after the end of the page.
  77         */
  78        size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
  79
  80        if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
  81            offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
  82                /* Cross pages read, read until offset 256 in low page */
  83                size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
  84
  85        i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
  86        if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
  87                if (qsfp) {
  88                        /* When reading upper pages 1, 2 and 3 the offset
  89                         * starts at 128. Please refer to "QSFP+ Memory Map"
  90                         * figure in SFF-8436 specification and to "CMIS Module
  91                         * Memory Map" figure in CMIS specification for
  92                         * graphical depiction.
  93                         */
  94                        page = MLXSW_REG_MCIA_PAGE_GET(offset);
  95                        offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;
  96                        if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
  97                                size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
  98                } else {
  99                        /* When reading upper pages 1, 2 and 3 the offset
 100                         * starts at 0 and I2C high address is used. Please refer
 101                         * refer to "Memory Organization" figure in SFF-8472
 102                         * specification for graphical depiction.
 103                         */
 104                        i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
 105                        offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
 106                }
 107        }
 108
 109        mlxsw_reg_mcia_pack(mcia_pl, module, 0, page, offset, size, i2c_addr);
 110
 111        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
 112        if (err)
 113                return err;
 114
 115        status = mlxsw_reg_mcia_status_get(mcia_pl);
 116        if (status)
 117                return -EIO;
 118
 119        eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
 120        memcpy(data, eeprom_tmp, size);
 121        *p_read_size = size;
 122
 123        return 0;
 124}
 125
 126int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
 127                                         int off, int *temp)
 128{
 129        unsigned int module_temp, module_crit, module_emerg;
 130        union {
 131                u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
 132                u16 temp;
 133        } temp_thresh;
 134        char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
 135        char mtmp_pl[MLXSW_REG_MTMP_LEN];
 136        char *eeprom_tmp;
 137        bool qsfp, cmis;
 138        int page;
 139        int err;
 140
 141        mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
 142                            false, false);
 143        err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
 144        if (err)
 145                return err;
 146        mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, &module_crit,
 147                              &module_emerg, NULL);
 148        if (!module_temp) {
 149                *temp = 0;
 150                return 0;
 151        }
 152
 153        /* Validate if threshold reading is available through MTMP register,
 154         * otherwise fallback to read through MCIA.
 155         */
 156        if (module_emerg) {
 157                *temp = off == SFP_TEMP_HIGH_WARN ? module_crit : module_emerg;
 158                return 0;
 159        }
 160
 161        /* Read Free Side Device Temperature Thresholds from page 03h
 162         * (MSB at lower byte address).
 163         * Bytes:
 164         * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
 165         * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
 166         * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
 167         * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
 168         */
 169
 170        /* Validate module identifier value. */
 171        err = mlxsw_env_validate_cable_ident(core, module, &qsfp, &cmis);
 172        if (err)
 173                return err;
 174
 175        if (qsfp) {
 176                /* For QSFP/CMIS module-defined thresholds are located in page
 177                 * 02h, otherwise in page 03h.
 178                 */
 179                if (cmis)
 180                        page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;
 181                else
 182                        page = MLXSW_REG_MCIA_TH_PAGE_NUM;
 183                mlxsw_reg_mcia_pack(mcia_pl, module, 0, page,
 184                                    MLXSW_REG_MCIA_TH_PAGE_OFF + off,
 185                                    MLXSW_REG_MCIA_TH_ITEM_SIZE,
 186                                    MLXSW_REG_MCIA_I2C_ADDR_LOW);
 187        } else {
 188                mlxsw_reg_mcia_pack(mcia_pl, module, 0,
 189                                    MLXSW_REG_MCIA_PAGE0_LO,
 190                                    off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
 191                                    MLXSW_REG_MCIA_I2C_ADDR_HIGH);
 192        }
 193
 194        err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
 195        if (err)
 196                return err;
 197
 198        eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
 199        memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
 200        *temp = temp_thresh.temp * 1000;
 201
 202        return 0;
 203}
 204
 205int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
 206                              struct ethtool_modinfo *modinfo)
 207{
 208        u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
 209        u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
 210        u8 module_rev_id, module_id, diag_mon;
 211        unsigned int read_size;
 212        int err;
 213
 214        err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset,
 215                                            module_info, false, &read_size);
 216        if (err)
 217                return err;
 218
 219        if (read_size < offset)
 220                return -EIO;
 221
 222        module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
 223        module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
 224
 225        switch (module_id) {
 226        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
 227                modinfo->type       = ETH_MODULE_SFF_8436;
 228                modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
 229                break;
 230        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
 231        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
 232                if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
 233                    module_rev_id >=
 234                    MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
 235                        modinfo->type       = ETH_MODULE_SFF_8636;
 236                        modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
 237                } else {
 238                        modinfo->type       = ETH_MODULE_SFF_8436;
 239                        modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
 240                }
 241                break;
 242        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
 243                /* Verify if transceiver provides diagnostic monitoring page */
 244                err = mlxsw_env_query_module_eeprom(mlxsw_core, module,
 245                                                    SFP_DIAGMON, 1, &diag_mon,
 246                                                    false, &read_size);
 247                if (err)
 248                        return err;
 249
 250                if (read_size < 1)
 251                        return -EIO;
 252
 253                modinfo->type       = ETH_MODULE_SFF_8472;
 254                if (diag_mon)
 255                        modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
 256                else
 257                        modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
 258                break;
 259        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
 260                /* Use SFF_8636 as base type. ethtool should recognize specific
 261                 * type through the identifier value.
 262                 */
 263                modinfo->type       = ETH_MODULE_SFF_8636;
 264                /* Verify if module EEPROM is a flat memory. In case of flat
 265                 * memory only page 00h (0-255 bytes) can be read. Otherwise
 266                 * upper pages 01h and 02h can also be read. Upper pages 10h
 267                 * and 11h are currently not supported by the driver.
 268                 */
 269                if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] &
 270                    MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY)
 271                        modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
 272                else
 273                        modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
 274                break;
 275        default:
 276                return -EINVAL;
 277        }
 278
 279        return 0;
 280}
 281EXPORT_SYMBOL(mlxsw_env_get_module_info);
 282
 283int mlxsw_env_get_module_eeprom(struct net_device *netdev,
 284                                struct mlxsw_core *mlxsw_core, int module,
 285                                struct ethtool_eeprom *ee, u8 *data)
 286{
 287        int offset = ee->offset;
 288        unsigned int read_size;
 289        bool qsfp, cmis;
 290        int i = 0;
 291        int err;
 292
 293        if (!ee->len)
 294                return -EINVAL;
 295
 296        memset(data, 0, ee->len);
 297        /* Validate module identifier value. */
 298        err = mlxsw_env_validate_cable_ident(mlxsw_core, module, &qsfp, &cmis);
 299        if (err)
 300                return err;
 301
 302        while (i < ee->len) {
 303                err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset,
 304                                                    ee->len - i, data + i,
 305                                                    qsfp, &read_size);
 306                if (err) {
 307                        netdev_err(netdev, "Eeprom query failed\n");
 308                        return err;
 309                }
 310
 311                i += read_size;
 312                offset += read_size;
 313        }
 314
 315        return 0;
 316}
 317EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);
 318
 319static int mlxsw_env_mcia_status_process(const char *mcia_pl,
 320                                         struct netlink_ext_ack *extack)
 321{
 322        u8 status = mlxsw_reg_mcia_status_get(mcia_pl);
 323
 324        switch (status) {
 325        case MLXSW_REG_MCIA_STATUS_GOOD:
 326                return 0;
 327        case MLXSW_REG_MCIA_STATUS_NO_EEPROM_MODULE:
 328                NL_SET_ERR_MSG_MOD(extack, "No response from module's EEPROM");
 329                return -EIO;
 330        case MLXSW_REG_MCIA_STATUS_MODULE_NOT_SUPPORTED:
 331                NL_SET_ERR_MSG_MOD(extack, "Module type not supported by the device");
 332                return -EOPNOTSUPP;
 333        case MLXSW_REG_MCIA_STATUS_MODULE_NOT_CONNECTED:
 334                NL_SET_ERR_MSG_MOD(extack, "No module present indication");
 335                return -EIO;
 336        case MLXSW_REG_MCIA_STATUS_I2C_ERROR:
 337                NL_SET_ERR_MSG_MOD(extack, "Error occurred while trying to access module's EEPROM using I2C");
 338                return -EIO;
 339        case MLXSW_REG_MCIA_STATUS_MODULE_DISABLED:
 340                NL_SET_ERR_MSG_MOD(extack, "Module is disabled");
 341                return -EIO;
 342        default:
 343                NL_SET_ERR_MSG_MOD(extack, "Unknown error");
 344                return -EIO;
 345        }
 346}
 347
 348int
 349mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module,
 350                                    const struct ethtool_module_eeprom *page,
 351                                    struct netlink_ext_ack *extack)
 352{
 353        u32 bytes_read = 0;
 354        u16 device_addr;
 355
 356        /* Offset cannot be larger than 2 * ETH_MODULE_EEPROM_PAGE_LEN */
 357        device_addr = page->offset;
 358
 359        while (bytes_read < page->length) {
 360                char mcia_pl[MLXSW_REG_MCIA_LEN];
 361                char *eeprom_tmp;
 362                u8 size;
 363                int err;
 364
 365                size = min_t(u8, page->length - bytes_read,
 366                             MLXSW_REG_MCIA_EEPROM_SIZE);
 367
 368                mlxsw_reg_mcia_pack(mcia_pl, module, 0, page->page,
 369                                    device_addr + bytes_read, size,
 370                                    page->i2c_address);
 371                mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank);
 372
 373                err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
 374                if (err) {
 375                        NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM");
 376                        return err;
 377                }
 378
 379                err = mlxsw_env_mcia_status_process(mcia_pl, extack);
 380                if (err)
 381                        return err;
 382
 383                eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
 384                memcpy(page->data + bytes_read, eeprom_tmp, size);
 385                bytes_read += size;
 386        }
 387
 388        return bytes_read;
 389}
 390EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page);
 391
 392static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core,
 393                                            u8 module,
 394                                            bool *p_has_temp_sensor)
 395{
 396        char mtbr_pl[MLXSW_REG_MTBR_LEN];
 397        u16 temp;
 398        int err;
 399
 400        mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
 401                            1);
 402        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), mtbr_pl);
 403        if (err)
 404                return err;
 405
 406        mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
 407
 408        switch (temp) {
 409        case MLXSW_REG_MTBR_BAD_SENS_INFO:
 410        case MLXSW_REG_MTBR_NO_CONN:
 411        case MLXSW_REG_MTBR_NO_TEMP_SENS:
 412        case MLXSW_REG_MTBR_INDEX_NA:
 413                *p_has_temp_sensor = false;
 414                break;
 415        default:
 416                *p_has_temp_sensor = temp ? true : false;
 417        }
 418        return 0;
 419}
 420
 421static int mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core,
 422                                    u16 sensor_index, bool enable)
 423{
 424        char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};
 425        enum mlxsw_reg_mtmp_tee tee;
 426        int err, threshold_hi;
 427
 428        mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, sensor_index);
 429        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
 430        if (err)
 431                return err;
 432
 433        if (enable) {
 434                err = mlxsw_env_module_temp_thresholds_get(mlxsw_core,
 435                                                           sensor_index -
 436                                                           MLXSW_REG_MTMP_MODULE_INDEX_MIN,
 437                                                           SFP_TEMP_HIGH_WARN,
 438                                                           &threshold_hi);
 439                /* In case it is not possible to query the module's threshold,
 440                 * use the default value.
 441                 */
 442                if (err)
 443                        threshold_hi = MLXSW_REG_MTMP_THRESH_HI;
 444                else
 445                        /* mlxsw_env_module_temp_thresholds_get() multiplies
 446                         * Celsius degrees by 1000 whereas MTMP expects
 447                         * temperature in 0.125 Celsius degrees units.
 448                         * Convert threshold_hi to correct units.
 449                         */
 450                        threshold_hi = threshold_hi / 1000 * 8;
 451
 452                mlxsw_reg_mtmp_temperature_threshold_hi_set(mtmp_pl, threshold_hi);
 453                mlxsw_reg_mtmp_temperature_threshold_lo_set(mtmp_pl, threshold_hi -
 454                                                            MLXSW_REG_MTMP_HYSTERESIS_TEMP);
 455        }
 456        tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT;
 457        mlxsw_reg_mtmp_tee_set(mtmp_pl, tee);
 458        return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
 459}
 460
 461static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,
 462                                              u8 module_count)
 463{
 464        int i, err, sensor_index;
 465        bool has_temp_sensor;
 466
 467        for (i = 0; i < module_count; i++) {
 468                err = mlxsw_env_module_has_temp_sensor(mlxsw_core, i,
 469                                                       &has_temp_sensor);
 470                if (err)
 471                        return err;
 472
 473                if (!has_temp_sensor)
 474                        continue;
 475
 476                sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
 477                err = mlxsw_env_temp_event_set(mlxsw_core, sensor_index, true);
 478                if (err)
 479                        return err;
 480        }
 481
 482        return 0;
 483}
 484
 485static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg,
 486                                      char *mtwe_pl, void *priv)
 487{
 488        struct mlxsw_env *mlxsw_env = priv;
 489        int i, sensor_warning;
 490        bool is_overheat;
 491
 492        for (i = 0; i < mlxsw_env->module_count; i++) {
 493                /* 64-127 of sensor_index are mapped to the port modules
 494                 * sequentially (module 0 is mapped to sensor_index 64,
 495                 * module 1 to sensor_index 65 and so on)
 496                 */
 497                sensor_warning =
 498                        mlxsw_reg_mtwe_sensor_warning_get(mtwe_pl,
 499                                                          i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);
 500                spin_lock(&mlxsw_env->module_info_lock);
 501                is_overheat =
 502                        mlxsw_env->module_info[i].is_overheat;
 503
 504                if ((is_overheat && sensor_warning) ||
 505                    (!is_overheat && !sensor_warning)) {
 506                        /* Current state is "warning" and MTWE still reports
 507                         * warning OR current state in "no warning" and MTWE
 508                         * does not report warning.
 509                         */
 510                        spin_unlock(&mlxsw_env->module_info_lock);
 511                        continue;
 512                } else if (is_overheat && !sensor_warning) {
 513                        /* MTWE reports "no warning", turn is_overheat off.
 514                         */
 515                        mlxsw_env->module_info[i].is_overheat = false;
 516                        spin_unlock(&mlxsw_env->module_info_lock);
 517                } else {
 518                        /* Current state is "no warning" and MTWE reports
 519                         * "warning", increase the counter and turn is_overheat
 520                         * on.
 521                         */
 522                        mlxsw_env->module_info[i].is_overheat = true;
 523                        mlxsw_env->module_info[i].module_overheat_counter++;
 524                        spin_unlock(&mlxsw_env->module_info_lock);
 525                }
 526        }
 527}
 528
 529static const struct mlxsw_listener mlxsw_env_temp_warn_listener =
 530        MLXSW_EVENTL(mlxsw_env_mtwe_event_func, MTWE, MTWE);
 531
 532static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)
 533{
 534        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 535
 536        if (!mlxsw_core_temp_warn_enabled(mlxsw_core))
 537                return 0;
 538
 539        return mlxsw_core_trap_register(mlxsw_core,
 540                                        &mlxsw_env_temp_warn_listener,
 541                                        mlxsw_env);
 542}
 543
 544static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env)
 545{
 546        if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core))
 547                return;
 548
 549        mlxsw_core_trap_unregister(mlxsw_env->core,
 550                                   &mlxsw_env_temp_warn_listener, mlxsw_env);
 551}
 552
 553struct mlxsw_env_module_plug_unplug_event {
 554        struct mlxsw_env *mlxsw_env;
 555        u8 module;
 556        struct work_struct work;
 557};
 558
 559static void mlxsw_env_pmpe_event_work(struct work_struct *work)
 560{
 561        struct mlxsw_env_module_plug_unplug_event *event;
 562        struct mlxsw_env *mlxsw_env;
 563        bool has_temp_sensor;
 564        u16 sensor_index;
 565        int err;
 566
 567        event = container_of(work, struct mlxsw_env_module_plug_unplug_event,
 568                             work);
 569        mlxsw_env = event->mlxsw_env;
 570
 571        spin_lock_bh(&mlxsw_env->module_info_lock);
 572        mlxsw_env->module_info[event->module].is_overheat = false;
 573        spin_unlock_bh(&mlxsw_env->module_info_lock);
 574
 575        err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core, event->module,
 576                                               &has_temp_sensor);
 577        /* Do not disable events on modules without sensors or faulty sensors
 578         * because FW returns errors.
 579         */
 580        if (err)
 581                goto out;
 582
 583        if (!has_temp_sensor)
 584                goto out;
 585
 586        sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
 587        mlxsw_env_temp_event_set(mlxsw_env->core, sensor_index, true);
 588
 589out:
 590        kfree(event);
 591}
 592
 593static void
 594mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,
 595                             void *priv)
 596{
 597        struct mlxsw_env_module_plug_unplug_event *event;
 598        enum mlxsw_reg_pmpe_module_status module_status;
 599        u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl);
 600        struct mlxsw_env *mlxsw_env = priv;
 601
 602        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 603                return;
 604
 605        module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl);
 606        if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED)
 607                return;
 608
 609        event = kmalloc(sizeof(*event), GFP_ATOMIC);
 610        if (!event)
 611                return;
 612
 613        event->mlxsw_env = mlxsw_env;
 614        event->module = module;
 615        INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);
 616        mlxsw_core_schedule_work(&event->work);
 617}
 618
 619static const struct mlxsw_listener mlxsw_env_module_plug_listener =
 620        MLXSW_EVENTL(mlxsw_env_pmpe_listener_func, PMPE, PMPE);
 621
 622static int
 623mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core)
 624{
 625        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 626
 627        if (!mlxsw_core_temp_warn_enabled(mlxsw_core))
 628                return 0;
 629
 630        return mlxsw_core_trap_register(mlxsw_core,
 631                                        &mlxsw_env_module_plug_listener,
 632                                        mlxsw_env);
 633}
 634
 635static void
 636mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env)
 637{
 638        if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core))
 639                return;
 640
 641        mlxsw_core_trap_unregister(mlxsw_env->core,
 642                                   &mlxsw_env_module_plug_listener,
 643                                   mlxsw_env);
 644}
 645
 646static int
 647mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,
 648                                         u8 module_count)
 649{
 650        int i, err;
 651
 652        for (i = 0; i < module_count; i++) {
 653                char pmaos_pl[MLXSW_REG_PMAOS_LEN];
 654
 655                mlxsw_reg_pmaos_pack(pmaos_pl, i,
 656                                     MLXSW_REG_PMAOS_E_GENERATE_EVENT);
 657                err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
 658                if (err)
 659                        return err;
 660        }
 661        return 0;
 662}
 663
 664int
 665mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module,
 666                                      u64 *p_counter)
 667{
 668        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 669
 670        /* Prevent switch driver from accessing uninitialized data. */
 671        if (!mlxsw_core_is_initialized(mlxsw_core)) {
 672                *p_counter = 0;
 673                return 0;
 674        }
 675
 676        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 677                return -EINVAL;
 678
 679        spin_lock_bh(&mlxsw_env->module_info_lock);
 680        *p_counter = mlxsw_env->module_info[module].module_overheat_counter;
 681        spin_unlock_bh(&mlxsw_env->module_info_lock);
 682
 683        return 0;
 684}
 685EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);
 686
 687int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env)
 688{
 689        char mgpir_pl[MLXSW_REG_MGPIR_LEN];
 690        struct mlxsw_env *env;
 691        u8 module_count;
 692        int err;
 693
 694        mlxsw_reg_mgpir_pack(mgpir_pl);
 695        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
 696        if (err)
 697                return err;
 698
 699        mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count);
 700
 701        env = kzalloc(struct_size(env, module_info, module_count), GFP_KERNEL);
 702        if (!env)
 703                return -ENOMEM;
 704
 705        spin_lock_init(&env->module_info_lock);
 706        env->core = mlxsw_core;
 707        env->module_count = module_count;
 708        *p_env = env;
 709
 710        err = mlxsw_env_temp_warn_event_register(mlxsw_core);
 711        if (err)
 712                goto err_temp_warn_event_register;
 713
 714        err = mlxsw_env_module_plug_event_register(mlxsw_core);
 715        if (err)
 716                goto err_module_plug_event_register;
 717
 718        err = mlxsw_env_module_oper_state_event_enable(mlxsw_core,
 719                                                       env->module_count);
 720        if (err)
 721                goto err_oper_state_event_enable;
 722
 723        err = mlxsw_env_module_temp_event_enable(mlxsw_core, env->module_count);
 724        if (err)
 725                goto err_temp_event_enable;
 726
 727        return 0;
 728
 729err_temp_event_enable:
 730err_oper_state_event_enable:
 731        mlxsw_env_module_plug_event_unregister(env);
 732err_module_plug_event_register:
 733        mlxsw_env_temp_warn_event_unregister(env);
 734err_temp_warn_event_register:
 735        kfree(env);
 736        return err;
 737}
 738
 739void mlxsw_env_fini(struct mlxsw_env *env)
 740{
 741        mlxsw_env_module_plug_event_unregister(env);
 742        /* Make sure there is no more event work scheduled. */
 743        mlxsw_core_flush_owq();
 744        mlxsw_env_temp_warn_event_unregister(env);
 745        kfree(env);
 746}
 747