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#include <linux/mutex.h>
   9
  10#include "core.h"
  11#include "core_env.h"
  12#include "item.h"
  13#include "reg.h"
  14
  15struct mlxsw_env_module_info {
  16        u64 module_overheat_counter;
  17        bool is_overheat;
  18        int num_ports_mapped;
  19        int num_ports_up;
  20        enum ethtool_module_power_mode_policy power_mode_policy;
  21};
  22
  23struct mlxsw_env {
  24        struct mlxsw_core *core;
  25        u8 module_count;
  26        struct mutex module_info_lock; /* Protects 'module_info'. */
  27        struct mlxsw_env_module_info module_info[];
  28};
  29
  30static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
  31                                          bool *qsfp, bool *cmis)
  32{
  33        char mcia_pl[MLXSW_REG_MCIA_LEN];
  34        char *eeprom_tmp;
  35        u8 ident;
  36        int err;
  37
  38        mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
  39                            MLXSW_REG_MCIA_I2C_ADDR_LOW);
  40        err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
  41        if (err)
  42                return err;
  43        eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
  44        ident = eeprom_tmp[0];
  45        *cmis = false;
  46        switch (ident) {
  47        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
  48                *qsfp = false;
  49                break;
  50        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
  51        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
  52        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
  53                *qsfp = true;
  54                break;
  55        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
  56                *qsfp = true;
  57                *cmis = true;
  58                break;
  59        default:
  60                return -EINVAL;
  61        }
  62
  63        return 0;
  64}
  65
  66static int
  67mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module,
  68                              u16 offset, u16 size, void *data,
  69                              bool qsfp, unsigned int *p_read_size)
  70{
  71        char mcia_pl[MLXSW_REG_MCIA_LEN];
  72        char *eeprom_tmp;
  73        u16 i2c_addr;
  74        u8 page = 0;
  75        int status;
  76        int err;
  77
  78        /* MCIA register accepts buffer size <= 48. Page of size 128 should be
  79         * read by chunks of size 48, 48, 32. Align the size of the last chunk
  80         * to avoid reading after the end of the page.
  81         */
  82        size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
  83
  84        if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
  85            offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
  86                /* Cross pages read, read until offset 256 in low page */
  87                size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
  88
  89        i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
  90        if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
  91                if (qsfp) {
  92                        /* When reading upper pages 1, 2 and 3 the offset
  93                         * starts at 128. Please refer to "QSFP+ Memory Map"
  94                         * figure in SFF-8436 specification and to "CMIS Module
  95                         * Memory Map" figure in CMIS specification for
  96                         * graphical depiction.
  97                         */
  98                        page = MLXSW_REG_MCIA_PAGE_GET(offset);
  99                        offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;
 100                        if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
 101                                size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
 102                } else {
 103                        /* When reading upper pages 1, 2 and 3 the offset
 104                         * starts at 0 and I2C high address is used. Please refer
 105                         * refer to "Memory Organization" figure in SFF-8472
 106                         * specification for graphical depiction.
 107                         */
 108                        i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
 109                        offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
 110                }
 111        }
 112
 113        mlxsw_reg_mcia_pack(mcia_pl, module, 0, page, offset, size, i2c_addr);
 114
 115        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
 116        if (err)
 117                return err;
 118
 119        status = mlxsw_reg_mcia_status_get(mcia_pl);
 120        if (status)
 121                return -EIO;
 122
 123        eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
 124        memcpy(data, eeprom_tmp, size);
 125        *p_read_size = size;
 126
 127        return 0;
 128}
 129
 130int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
 131                                         int off, int *temp)
 132{
 133        unsigned int module_temp, module_crit, module_emerg;
 134        union {
 135                u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
 136                u16 temp;
 137        } temp_thresh;
 138        char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
 139        char mtmp_pl[MLXSW_REG_MTMP_LEN];
 140        char *eeprom_tmp;
 141        bool qsfp, cmis;
 142        int page;
 143        int err;
 144
 145        mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
 146                            false, false);
 147        err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
 148        if (err)
 149                return err;
 150        mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, &module_crit,
 151                              &module_emerg, NULL);
 152        if (!module_temp) {
 153                *temp = 0;
 154                return 0;
 155        }
 156
 157        /* Validate if threshold reading is available through MTMP register,
 158         * otherwise fallback to read through MCIA.
 159         */
 160        if (module_emerg) {
 161                *temp = off == SFP_TEMP_HIGH_WARN ? module_crit : module_emerg;
 162                return 0;
 163        }
 164
 165        /* Read Free Side Device Temperature Thresholds from page 03h
 166         * (MSB at lower byte address).
 167         * Bytes:
 168         * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
 169         * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
 170         * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
 171         * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
 172         */
 173
 174        /* Validate module identifier value. */
 175        err = mlxsw_env_validate_cable_ident(core, module, &qsfp, &cmis);
 176        if (err)
 177                return err;
 178
 179        if (qsfp) {
 180                /* For QSFP/CMIS module-defined thresholds are located in page
 181                 * 02h, otherwise in page 03h.
 182                 */
 183                if (cmis)
 184                        page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;
 185                else
 186                        page = MLXSW_REG_MCIA_TH_PAGE_NUM;
 187                mlxsw_reg_mcia_pack(mcia_pl, module, 0, page,
 188                                    MLXSW_REG_MCIA_TH_PAGE_OFF + off,
 189                                    MLXSW_REG_MCIA_TH_ITEM_SIZE,
 190                                    MLXSW_REG_MCIA_I2C_ADDR_LOW);
 191        } else {
 192                mlxsw_reg_mcia_pack(mcia_pl, module, 0,
 193                                    MLXSW_REG_MCIA_PAGE0_LO,
 194                                    off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
 195                                    MLXSW_REG_MCIA_I2C_ADDR_HIGH);
 196        }
 197
 198        err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
 199        if (err)
 200                return err;
 201
 202        eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
 203        memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
 204        *temp = temp_thresh.temp * 1000;
 205
 206        return 0;
 207}
 208
 209int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
 210                              struct ethtool_modinfo *modinfo)
 211{
 212        u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
 213        u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
 214        u8 module_rev_id, module_id, diag_mon;
 215        unsigned int read_size;
 216        int err;
 217
 218        err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset,
 219                                            module_info, false, &read_size);
 220        if (err)
 221                return err;
 222
 223        if (read_size < offset)
 224                return -EIO;
 225
 226        module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
 227        module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
 228
 229        switch (module_id) {
 230        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
 231                modinfo->type       = ETH_MODULE_SFF_8436;
 232                modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
 233                break;
 234        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
 235        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
 236                if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
 237                    module_rev_id >=
 238                    MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
 239                        modinfo->type       = ETH_MODULE_SFF_8636;
 240                        modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
 241                } else {
 242                        modinfo->type       = ETH_MODULE_SFF_8436;
 243                        modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
 244                }
 245                break;
 246        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
 247                /* Verify if transceiver provides diagnostic monitoring page */
 248                err = mlxsw_env_query_module_eeprom(mlxsw_core, module,
 249                                                    SFP_DIAGMON, 1, &diag_mon,
 250                                                    false, &read_size);
 251                if (err)
 252                        return err;
 253
 254                if (read_size < 1)
 255                        return -EIO;
 256
 257                modinfo->type       = ETH_MODULE_SFF_8472;
 258                if (diag_mon)
 259                        modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
 260                else
 261                        modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
 262                break;
 263        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
 264                /* Use SFF_8636 as base type. ethtool should recognize specific
 265                 * type through the identifier value.
 266                 */
 267                modinfo->type       = ETH_MODULE_SFF_8636;
 268                /* Verify if module EEPROM is a flat memory. In case of flat
 269                 * memory only page 00h (0-255 bytes) can be read. Otherwise
 270                 * upper pages 01h and 02h can also be read. Upper pages 10h
 271                 * and 11h are currently not supported by the driver.
 272                 */
 273                if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] &
 274                    MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY)
 275                        modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
 276                else
 277                        modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
 278                break;
 279        default:
 280                return -EINVAL;
 281        }
 282
 283        return 0;
 284}
 285EXPORT_SYMBOL(mlxsw_env_get_module_info);
 286
 287int mlxsw_env_get_module_eeprom(struct net_device *netdev,
 288                                struct mlxsw_core *mlxsw_core, int module,
 289                                struct ethtool_eeprom *ee, u8 *data)
 290{
 291        int offset = ee->offset;
 292        unsigned int read_size;
 293        bool qsfp, cmis;
 294        int i = 0;
 295        int err;
 296
 297        if (!ee->len)
 298                return -EINVAL;
 299
 300        memset(data, 0, ee->len);
 301        /* Validate module identifier value. */
 302        err = mlxsw_env_validate_cable_ident(mlxsw_core, module, &qsfp, &cmis);
 303        if (err)
 304                return err;
 305
 306        while (i < ee->len) {
 307                err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset,
 308                                                    ee->len - i, data + i,
 309                                                    qsfp, &read_size);
 310                if (err) {
 311                        netdev_err(netdev, "Eeprom query failed\n");
 312                        return err;
 313                }
 314
 315                i += read_size;
 316                offset += read_size;
 317        }
 318
 319        return 0;
 320}
 321EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);
 322
 323static int mlxsw_env_mcia_status_process(const char *mcia_pl,
 324                                         struct netlink_ext_ack *extack)
 325{
 326        u8 status = mlxsw_reg_mcia_status_get(mcia_pl);
 327
 328        switch (status) {
 329        case MLXSW_REG_MCIA_STATUS_GOOD:
 330                return 0;
 331        case MLXSW_REG_MCIA_STATUS_NO_EEPROM_MODULE:
 332                NL_SET_ERR_MSG_MOD(extack, "No response from module's EEPROM");
 333                return -EIO;
 334        case MLXSW_REG_MCIA_STATUS_MODULE_NOT_SUPPORTED:
 335                NL_SET_ERR_MSG_MOD(extack, "Module type not supported by the device");
 336                return -EOPNOTSUPP;
 337        case MLXSW_REG_MCIA_STATUS_MODULE_NOT_CONNECTED:
 338                NL_SET_ERR_MSG_MOD(extack, "No module present indication");
 339                return -EIO;
 340        case MLXSW_REG_MCIA_STATUS_I2C_ERROR:
 341                NL_SET_ERR_MSG_MOD(extack, "Error occurred while trying to access module's EEPROM using I2C");
 342                return -EIO;
 343        case MLXSW_REG_MCIA_STATUS_MODULE_DISABLED:
 344                NL_SET_ERR_MSG_MOD(extack, "Module is disabled");
 345                return -EIO;
 346        default:
 347                NL_SET_ERR_MSG_MOD(extack, "Unknown error");
 348                return -EIO;
 349        }
 350}
 351
 352int
 353mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module,
 354                                    const struct ethtool_module_eeprom *page,
 355                                    struct netlink_ext_ack *extack)
 356{
 357        u32 bytes_read = 0;
 358        u16 device_addr;
 359
 360        /* Offset cannot be larger than 2 * ETH_MODULE_EEPROM_PAGE_LEN */
 361        device_addr = page->offset;
 362
 363        while (bytes_read < page->length) {
 364                char mcia_pl[MLXSW_REG_MCIA_LEN];
 365                char *eeprom_tmp;
 366                u8 size;
 367                int err;
 368
 369                size = min_t(u8, page->length - bytes_read,
 370                             MLXSW_REG_MCIA_EEPROM_SIZE);
 371
 372                mlxsw_reg_mcia_pack(mcia_pl, module, 0, page->page,
 373                                    device_addr + bytes_read, size,
 374                                    page->i2c_address);
 375                mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank);
 376
 377                err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
 378                if (err) {
 379                        NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM");
 380                        return err;
 381                }
 382
 383                err = mlxsw_env_mcia_status_process(mcia_pl, extack);
 384                if (err)
 385                        return err;
 386
 387                eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
 388                memcpy(page->data + bytes_read, eeprom_tmp, size);
 389                bytes_read += size;
 390        }
 391
 392        return bytes_read;
 393}
 394EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page);
 395
 396static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 module)
 397{
 398        char pmaos_pl[MLXSW_REG_PMAOS_LEN];
 399
 400        mlxsw_reg_pmaos_pack(pmaos_pl, module);
 401        mlxsw_reg_pmaos_rst_set(pmaos_pl, true);
 402
 403        return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
 404}
 405
 406int mlxsw_env_reset_module(struct net_device *netdev,
 407                           struct mlxsw_core *mlxsw_core, u8 module, u32 *flags)
 408{
 409        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 410        u32 req = *flags;
 411        int err;
 412
 413        if (!(req & ETH_RESET_PHY) &&
 414            !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)))
 415                return 0;
 416
 417        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 418                return -EINVAL;
 419
 420        mutex_lock(&mlxsw_env->module_info_lock);
 421
 422        if (mlxsw_env->module_info[module].num_ports_up) {
 423                netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n");
 424                err = -EINVAL;
 425                goto out;
 426        }
 427
 428        if (mlxsw_env->module_info[module].num_ports_mapped > 1 &&
 429            !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) {
 430                netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n");
 431                err = -EINVAL;
 432                goto out;
 433        }
 434
 435        err = mlxsw_env_module_reset(mlxsw_core, module);
 436        if (err) {
 437                netdev_err(netdev, "Failed to reset module\n");
 438                goto out;
 439        }
 440
 441        *flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT));
 442
 443out:
 444        mutex_unlock(&mlxsw_env->module_info_lock);
 445        return err;
 446}
 447EXPORT_SYMBOL(mlxsw_env_reset_module);
 448
 449int
 450mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module,
 451                                struct ethtool_module_power_mode_params *params,
 452                                struct netlink_ext_ack *extack)
 453{
 454        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 455        char mcion_pl[MLXSW_REG_MCION_LEN];
 456        u32 status_bits;
 457        int err;
 458
 459        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 460                return -EINVAL;
 461
 462        mutex_lock(&mlxsw_env->module_info_lock);
 463
 464        params->policy = mlxsw_env->module_info[module].power_mode_policy;
 465
 466        mlxsw_reg_mcion_pack(mcion_pl, module);
 467        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcion), mcion_pl);
 468        if (err) {
 469                NL_SET_ERR_MSG_MOD(extack, "Failed to retrieve module's power mode");
 470                goto out;
 471        }
 472
 473        status_bits = mlxsw_reg_mcion_module_status_bits_get(mcion_pl);
 474        if (!(status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_PRESENT_MASK))
 475                goto out;
 476
 477        if (status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_LOW_POWER_MASK)
 478                params->mode = ETHTOOL_MODULE_POWER_MODE_LOW;
 479        else
 480                params->mode = ETHTOOL_MODULE_POWER_MODE_HIGH;
 481
 482out:
 483        mutex_unlock(&mlxsw_env->module_info_lock);
 484        return err;
 485}
 486EXPORT_SYMBOL(mlxsw_env_get_module_power_mode);
 487
 488static int mlxsw_env_module_enable_set(struct mlxsw_core *mlxsw_core,
 489                                       u8 module, bool enable)
 490{
 491        enum mlxsw_reg_pmaos_admin_status admin_status;
 492        char pmaos_pl[MLXSW_REG_PMAOS_LEN];
 493
 494        mlxsw_reg_pmaos_pack(pmaos_pl, module);
 495        admin_status = enable ? MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED :
 496                                MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED;
 497        mlxsw_reg_pmaos_admin_status_set(pmaos_pl, admin_status);
 498        mlxsw_reg_pmaos_ase_set(pmaos_pl, true);
 499
 500        return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
 501}
 502
 503static int mlxsw_env_module_low_power_set(struct mlxsw_core *mlxsw_core,
 504                                          u8 module, bool low_power)
 505{
 506        u16 eeprom_override_mask, eeprom_override;
 507        char pmmp_pl[MLXSW_REG_PMMP_LEN];
 508
 509        mlxsw_reg_pmmp_pack(pmmp_pl, module);
 510        mlxsw_reg_pmmp_sticky_set(pmmp_pl, true);
 511        /* Mask all the bits except low power mode. */
 512        eeprom_override_mask = ~MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK;
 513        mlxsw_reg_pmmp_eeprom_override_mask_set(pmmp_pl, eeprom_override_mask);
 514        eeprom_override = low_power ? MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK :
 515                                      0;
 516        mlxsw_reg_pmmp_eeprom_override_set(pmmp_pl, eeprom_override);
 517
 518        return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmmp), pmmp_pl);
 519}
 520
 521static int __mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core,
 522                                             u8 module, bool low_power,
 523                                             struct netlink_ext_ack *extack)
 524{
 525        int err;
 526
 527        err = mlxsw_env_module_enable_set(mlxsw_core, module, false);
 528        if (err) {
 529                NL_SET_ERR_MSG_MOD(extack, "Failed to disable module");
 530                return err;
 531        }
 532
 533        err = mlxsw_env_module_low_power_set(mlxsw_core, module, low_power);
 534        if (err) {
 535                NL_SET_ERR_MSG_MOD(extack, "Failed to set module's power mode");
 536                goto err_module_low_power_set;
 537        }
 538
 539        err = mlxsw_env_module_enable_set(mlxsw_core, module, true);
 540        if (err) {
 541                NL_SET_ERR_MSG_MOD(extack, "Failed to enable module");
 542                goto err_module_enable_set;
 543        }
 544
 545        return 0;
 546
 547err_module_enable_set:
 548        mlxsw_env_module_low_power_set(mlxsw_core, module, !low_power);
 549err_module_low_power_set:
 550        mlxsw_env_module_enable_set(mlxsw_core, module, true);
 551        return err;
 552}
 553
 554int
 555mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module,
 556                                enum ethtool_module_power_mode_policy policy,
 557                                struct netlink_ext_ack *extack)
 558{
 559        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 560        bool low_power;
 561        int err = 0;
 562
 563        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 564                return -EINVAL;
 565
 566        if (policy != ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH &&
 567            policy != ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) {
 568                NL_SET_ERR_MSG_MOD(extack, "Unsupported power mode policy");
 569                return -EOPNOTSUPP;
 570        }
 571
 572        mutex_lock(&mlxsw_env->module_info_lock);
 573
 574        if (mlxsw_env->module_info[module].power_mode_policy == policy)
 575                goto out;
 576
 577        /* If any ports are up, we are already in high power mode. */
 578        if (mlxsw_env->module_info[module].num_ports_up)
 579                goto out_set_policy;
 580
 581        low_power = policy == ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO;
 582        err = __mlxsw_env_set_module_power_mode(mlxsw_core, module, low_power,
 583                                                extack);
 584        if (err)
 585                goto out;
 586
 587out_set_policy:
 588        mlxsw_env->module_info[module].power_mode_policy = policy;
 589out:
 590        mutex_unlock(&mlxsw_env->module_info_lock);
 591        return err;
 592}
 593EXPORT_SYMBOL(mlxsw_env_set_module_power_mode);
 594
 595static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core,
 596                                            u8 module,
 597                                            bool *p_has_temp_sensor)
 598{
 599        char mtbr_pl[MLXSW_REG_MTBR_LEN];
 600        u16 temp;
 601        int err;
 602
 603        mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
 604                            1);
 605        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), mtbr_pl);
 606        if (err)
 607                return err;
 608
 609        mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
 610
 611        switch (temp) {
 612        case MLXSW_REG_MTBR_BAD_SENS_INFO:
 613        case MLXSW_REG_MTBR_NO_CONN:
 614        case MLXSW_REG_MTBR_NO_TEMP_SENS:
 615        case MLXSW_REG_MTBR_INDEX_NA:
 616                *p_has_temp_sensor = false;
 617                break;
 618        default:
 619                *p_has_temp_sensor = temp ? true : false;
 620        }
 621        return 0;
 622}
 623
 624static int mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core,
 625                                    u16 sensor_index, bool enable)
 626{
 627        char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};
 628        enum mlxsw_reg_mtmp_tee tee;
 629        int err, threshold_hi;
 630
 631        mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, sensor_index);
 632        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
 633        if (err)
 634                return err;
 635
 636        if (enable) {
 637                err = mlxsw_env_module_temp_thresholds_get(mlxsw_core,
 638                                                           sensor_index -
 639                                                           MLXSW_REG_MTMP_MODULE_INDEX_MIN,
 640                                                           SFP_TEMP_HIGH_WARN,
 641                                                           &threshold_hi);
 642                /* In case it is not possible to query the module's threshold,
 643                 * use the default value.
 644                 */
 645                if (err)
 646                        threshold_hi = MLXSW_REG_MTMP_THRESH_HI;
 647                else
 648                        /* mlxsw_env_module_temp_thresholds_get() multiplies
 649                         * Celsius degrees by 1000 whereas MTMP expects
 650                         * temperature in 0.125 Celsius degrees units.
 651                         * Convert threshold_hi to correct units.
 652                         */
 653                        threshold_hi = threshold_hi / 1000 * 8;
 654
 655                mlxsw_reg_mtmp_temperature_threshold_hi_set(mtmp_pl, threshold_hi);
 656                mlxsw_reg_mtmp_temperature_threshold_lo_set(mtmp_pl, threshold_hi -
 657                                                            MLXSW_REG_MTMP_HYSTERESIS_TEMP);
 658        }
 659        tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT;
 660        mlxsw_reg_mtmp_tee_set(mtmp_pl, tee);
 661        return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
 662}
 663
 664static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,
 665                                              u8 module_count)
 666{
 667        int i, err, sensor_index;
 668        bool has_temp_sensor;
 669
 670        for (i = 0; i < module_count; i++) {
 671                err = mlxsw_env_module_has_temp_sensor(mlxsw_core, i,
 672                                                       &has_temp_sensor);
 673                if (err)
 674                        return err;
 675
 676                if (!has_temp_sensor)
 677                        continue;
 678
 679                sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
 680                err = mlxsw_env_temp_event_set(mlxsw_core, sensor_index, true);
 681                if (err)
 682                        return err;
 683        }
 684
 685        return 0;
 686}
 687
 688struct mlxsw_env_module_temp_warn_event {
 689        struct mlxsw_env *mlxsw_env;
 690        char mtwe_pl[MLXSW_REG_MTWE_LEN];
 691        struct work_struct work;
 692};
 693
 694static void mlxsw_env_mtwe_event_work(struct work_struct *work)
 695{
 696        struct mlxsw_env_module_temp_warn_event *event;
 697        struct mlxsw_env *mlxsw_env;
 698        int i, sensor_warning;
 699        bool is_overheat;
 700
 701        event = container_of(work, struct mlxsw_env_module_temp_warn_event,
 702                             work);
 703        mlxsw_env = event->mlxsw_env;
 704
 705        for (i = 0; i < mlxsw_env->module_count; i++) {
 706                /* 64-127 of sensor_index are mapped to the port modules
 707                 * sequentially (module 0 is mapped to sensor_index 64,
 708                 * module 1 to sensor_index 65 and so on)
 709                 */
 710                sensor_warning =
 711                        mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl,
 712                                                          i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);
 713                mutex_lock(&mlxsw_env->module_info_lock);
 714                is_overheat =
 715                        mlxsw_env->module_info[i].is_overheat;
 716
 717                if ((is_overheat && sensor_warning) ||
 718                    (!is_overheat && !sensor_warning)) {
 719                        /* Current state is "warning" and MTWE still reports
 720                         * warning OR current state in "no warning" and MTWE
 721                         * does not report warning.
 722                         */
 723                        mutex_unlock(&mlxsw_env->module_info_lock);
 724                        continue;
 725                } else if (is_overheat && !sensor_warning) {
 726                        /* MTWE reports "no warning", turn is_overheat off.
 727                         */
 728                        mlxsw_env->module_info[i].is_overheat = false;
 729                        mutex_unlock(&mlxsw_env->module_info_lock);
 730                } else {
 731                        /* Current state is "no warning" and MTWE reports
 732                         * "warning", increase the counter and turn is_overheat
 733                         * on.
 734                         */
 735                        mlxsw_env->module_info[i].is_overheat = true;
 736                        mlxsw_env->module_info[i].module_overheat_counter++;
 737                        mutex_unlock(&mlxsw_env->module_info_lock);
 738                }
 739        }
 740
 741        kfree(event);
 742}
 743
 744static void
 745mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl,
 746                             void *priv)
 747{
 748        struct mlxsw_env_module_temp_warn_event *event;
 749        struct mlxsw_env *mlxsw_env = priv;
 750
 751        event = kmalloc(sizeof(*event), GFP_ATOMIC);
 752        if (!event)
 753                return;
 754
 755        event->mlxsw_env = mlxsw_env;
 756        memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN);
 757        INIT_WORK(&event->work, mlxsw_env_mtwe_event_work);
 758        mlxsw_core_schedule_work(&event->work);
 759}
 760
 761static const struct mlxsw_listener mlxsw_env_temp_warn_listener =
 762        MLXSW_EVENTL(mlxsw_env_mtwe_listener_func, MTWE, MTWE);
 763
 764static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)
 765{
 766        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 767
 768        if (!mlxsw_core_temp_warn_enabled(mlxsw_core))
 769                return 0;
 770
 771        return mlxsw_core_trap_register(mlxsw_core,
 772                                        &mlxsw_env_temp_warn_listener,
 773                                        mlxsw_env);
 774}
 775
 776static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env)
 777{
 778        if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core))
 779                return;
 780
 781        mlxsw_core_trap_unregister(mlxsw_env->core,
 782                                   &mlxsw_env_temp_warn_listener, mlxsw_env);
 783}
 784
 785struct mlxsw_env_module_plug_unplug_event {
 786        struct mlxsw_env *mlxsw_env;
 787        u8 module;
 788        struct work_struct work;
 789};
 790
 791static void mlxsw_env_pmpe_event_work(struct work_struct *work)
 792{
 793        struct mlxsw_env_module_plug_unplug_event *event;
 794        struct mlxsw_env *mlxsw_env;
 795        bool has_temp_sensor;
 796        u16 sensor_index;
 797        int err;
 798
 799        event = container_of(work, struct mlxsw_env_module_plug_unplug_event,
 800                             work);
 801        mlxsw_env = event->mlxsw_env;
 802
 803        mutex_lock(&mlxsw_env->module_info_lock);
 804        mlxsw_env->module_info[event->module].is_overheat = false;
 805        mutex_unlock(&mlxsw_env->module_info_lock);
 806
 807        err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core, event->module,
 808                                               &has_temp_sensor);
 809        /* Do not disable events on modules without sensors or faulty sensors
 810         * because FW returns errors.
 811         */
 812        if (err)
 813                goto out;
 814
 815        if (!has_temp_sensor)
 816                goto out;
 817
 818        sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
 819        mlxsw_env_temp_event_set(mlxsw_env->core, sensor_index, true);
 820
 821out:
 822        kfree(event);
 823}
 824
 825static void
 826mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,
 827                             void *priv)
 828{
 829        struct mlxsw_env_module_plug_unplug_event *event;
 830        enum mlxsw_reg_pmpe_module_status module_status;
 831        u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl);
 832        struct mlxsw_env *mlxsw_env = priv;
 833
 834        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 835                return;
 836
 837        module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl);
 838        if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED)
 839                return;
 840
 841        event = kmalloc(sizeof(*event), GFP_ATOMIC);
 842        if (!event)
 843                return;
 844
 845        event->mlxsw_env = mlxsw_env;
 846        event->module = module;
 847        INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);
 848        mlxsw_core_schedule_work(&event->work);
 849}
 850
 851static const struct mlxsw_listener mlxsw_env_module_plug_listener =
 852        MLXSW_EVENTL(mlxsw_env_pmpe_listener_func, PMPE, PMPE);
 853
 854static int
 855mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core)
 856{
 857        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 858
 859        if (!mlxsw_core_temp_warn_enabled(mlxsw_core))
 860                return 0;
 861
 862        return mlxsw_core_trap_register(mlxsw_core,
 863                                        &mlxsw_env_module_plug_listener,
 864                                        mlxsw_env);
 865}
 866
 867static void
 868mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env)
 869{
 870        if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core))
 871                return;
 872
 873        mlxsw_core_trap_unregister(mlxsw_env->core,
 874                                   &mlxsw_env_module_plug_listener,
 875                                   mlxsw_env);
 876}
 877
 878static int
 879mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,
 880                                         u8 module_count)
 881{
 882        int i, err;
 883
 884        for (i = 0; i < module_count; i++) {
 885                char pmaos_pl[MLXSW_REG_PMAOS_LEN];
 886
 887                mlxsw_reg_pmaos_pack(pmaos_pl, i);
 888                mlxsw_reg_pmaos_e_set(pmaos_pl,
 889                                      MLXSW_REG_PMAOS_E_GENERATE_EVENT);
 890                mlxsw_reg_pmaos_ee_set(pmaos_pl, true);
 891                err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
 892                if (err)
 893                        return err;
 894        }
 895        return 0;
 896}
 897
 898int
 899mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module,
 900                                      u64 *p_counter)
 901{
 902        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 903
 904        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 905                return -EINVAL;
 906
 907        mutex_lock(&mlxsw_env->module_info_lock);
 908        *p_counter = mlxsw_env->module_info[module].module_overheat_counter;
 909        mutex_unlock(&mlxsw_env->module_info_lock);
 910
 911        return 0;
 912}
 913EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);
 914
 915void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 module)
 916{
 917        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 918
 919        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 920                return;
 921
 922        mutex_lock(&mlxsw_env->module_info_lock);
 923        mlxsw_env->module_info[module].num_ports_mapped++;
 924        mutex_unlock(&mlxsw_env->module_info_lock);
 925}
 926EXPORT_SYMBOL(mlxsw_env_module_port_map);
 927
 928void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 module)
 929{
 930        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 931
 932        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 933                return;
 934
 935        mutex_lock(&mlxsw_env->module_info_lock);
 936        mlxsw_env->module_info[module].num_ports_mapped--;
 937        mutex_unlock(&mlxsw_env->module_info_lock);
 938}
 939EXPORT_SYMBOL(mlxsw_env_module_port_unmap);
 940
 941int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 module)
 942{
 943        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 944        int err = 0;
 945
 946        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 947                return -EINVAL;
 948
 949        mutex_lock(&mlxsw_env->module_info_lock);
 950
 951        if (mlxsw_env->module_info[module].power_mode_policy !=
 952            ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
 953                goto out_inc;
 954
 955        if (mlxsw_env->module_info[module].num_ports_up != 0)
 956                goto out_inc;
 957
 958        /* Transition to high power mode following first port using the module
 959         * being put administratively up.
 960         */
 961        err = __mlxsw_env_set_module_power_mode(mlxsw_core, module, false,
 962                                                NULL);
 963        if (err)
 964                goto out_unlock;
 965
 966out_inc:
 967        mlxsw_env->module_info[module].num_ports_up++;
 968out_unlock:
 969        mutex_unlock(&mlxsw_env->module_info_lock);
 970        return err;
 971}
 972EXPORT_SYMBOL(mlxsw_env_module_port_up);
 973
 974void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 module)
 975{
 976        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
 977
 978        if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
 979                return;
 980
 981        mutex_lock(&mlxsw_env->module_info_lock);
 982
 983        mlxsw_env->module_info[module].num_ports_up--;
 984
 985        if (mlxsw_env->module_info[module].power_mode_policy !=
 986            ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
 987                goto out_unlock;
 988
 989        if (mlxsw_env->module_info[module].num_ports_up != 0)
 990                goto out_unlock;
 991
 992        /* Transition to low power mode following last port using the module
 993         * being put administratively down.
 994         */
 995        __mlxsw_env_set_module_power_mode(mlxsw_core, module, true, NULL);
 996
 997out_unlock:
 998        mutex_unlock(&mlxsw_env->module_info_lock);
 999}
1000EXPORT_SYMBOL(mlxsw_env_module_port_down);
1001
1002int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env)
1003{
1004        char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1005        struct mlxsw_env *env;
1006        u8 module_count;
1007        int i, err;
1008
1009        mlxsw_reg_mgpir_pack(mgpir_pl);
1010        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1011        if (err)
1012                return err;
1013
1014        mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count);
1015
1016        env = kzalloc(struct_size(env, module_info, module_count), GFP_KERNEL);
1017        if (!env)
1018                return -ENOMEM;
1019
1020        /* Firmware defaults to high power mode policy where modules are
1021         * transitioned to high power mode following plug-in.
1022         */
1023        for (i = 0; i < module_count; i++)
1024                env->module_info[i].power_mode_policy =
1025                        ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH;
1026
1027        mutex_init(&env->module_info_lock);
1028        env->core = mlxsw_core;
1029        env->module_count = module_count;
1030        *p_env = env;
1031
1032        err = mlxsw_env_temp_warn_event_register(mlxsw_core);
1033        if (err)
1034                goto err_temp_warn_event_register;
1035
1036        err = mlxsw_env_module_plug_event_register(mlxsw_core);
1037        if (err)
1038                goto err_module_plug_event_register;
1039
1040        err = mlxsw_env_module_oper_state_event_enable(mlxsw_core,
1041                                                       env->module_count);
1042        if (err)
1043                goto err_oper_state_event_enable;
1044
1045        err = mlxsw_env_module_temp_event_enable(mlxsw_core, env->module_count);
1046        if (err)
1047                goto err_temp_event_enable;
1048
1049        return 0;
1050
1051err_temp_event_enable:
1052err_oper_state_event_enable:
1053        mlxsw_env_module_plug_event_unregister(env);
1054err_module_plug_event_register:
1055        mlxsw_env_temp_warn_event_unregister(env);
1056err_temp_warn_event_register:
1057        mutex_destroy(&env->module_info_lock);
1058        kfree(env);
1059        return err;
1060}
1061
1062void mlxsw_env_fini(struct mlxsw_env *env)
1063{
1064        mlxsw_env_module_plug_event_unregister(env);
1065        /* Make sure there is no more event work scheduled. */
1066        mlxsw_core_flush_owq();
1067        mlxsw_env_temp_warn_event_unregister(env);
1068        mutex_destroy(&env->module_info_lock);
1069        kfree(env);
1070}
1071