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/sfp.h>
   7
   8#include "core.h"
   9#include "core_env.h"
  10#include "item.h"
  11#include "reg.h"
  12
  13static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
  14                                          bool *qsfp)
  15{
  16        char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
  17        char mcia_pl[MLXSW_REG_MCIA_LEN];
  18        u8 ident;
  19        int err;
  20
  21        mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
  22                            MLXSW_REG_MCIA_I2C_ADDR_LOW);
  23        err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
  24        if (err)
  25                return err;
  26        mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
  27        ident = eeprom_tmp[0];
  28        switch (ident) {
  29        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
  30                *qsfp = false;
  31                break;
  32        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: /* fall-through */
  33        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
  34        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28: /* fall-through */
  35        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
  36                *qsfp = true;
  37                break;
  38        default:
  39                return -EINVAL;
  40        }
  41
  42        return 0;
  43}
  44
  45static int
  46mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module,
  47                              u16 offset, u16 size, void *data,
  48                              unsigned int *p_read_size)
  49{
  50        char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
  51        char mcia_pl[MLXSW_REG_MCIA_LEN];
  52        u16 i2c_addr;
  53        int status;
  54        int err;
  55
  56        size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
  57
  58        if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
  59            offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
  60                /* Cross pages read, read until offset 256 in low page */
  61                size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
  62
  63        i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
  64        if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
  65                i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
  66                offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
  67        }
  68
  69        mlxsw_reg_mcia_pack(mcia_pl, module, 0, 0, offset, size, i2c_addr);
  70
  71        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
  72        if (err)
  73                return err;
  74
  75        status = mlxsw_reg_mcia_status_get(mcia_pl);
  76        if (status)
  77                return -EIO;
  78
  79        mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
  80        memcpy(data, eeprom_tmp, size);
  81        *p_read_size = size;
  82
  83        return 0;
  84}
  85
  86int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
  87                                         int off, int *temp)
  88{
  89        char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
  90        union {
  91                u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
  92                u16 temp;
  93        } temp_thresh;
  94        char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
  95        char mtmp_pl[MLXSW_REG_MTMP_LEN];
  96        unsigned int module_temp;
  97        bool qsfp;
  98        int err;
  99
 100        mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
 101                            false, false);
 102        err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
 103        if (err)
 104                return err;
 105        mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, NULL);
 106        if (!module_temp) {
 107                *temp = 0;
 108                return 0;
 109        }
 110
 111        /* Read Free Side Device Temperature Thresholds from page 03h
 112         * (MSB at lower byte address).
 113         * Bytes:
 114         * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
 115         * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
 116         * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
 117         * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
 118         */
 119
 120        /* Validate module identifier value. */
 121        err = mlxsw_env_validate_cable_ident(core, module, &qsfp);
 122        if (err)
 123                return err;
 124
 125        if (qsfp)
 126                mlxsw_reg_mcia_pack(mcia_pl, module, 0,
 127                                    MLXSW_REG_MCIA_TH_PAGE_NUM,
 128                                    MLXSW_REG_MCIA_TH_PAGE_OFF + off,
 129                                    MLXSW_REG_MCIA_TH_ITEM_SIZE,
 130                                    MLXSW_REG_MCIA_I2C_ADDR_LOW);
 131        else
 132                mlxsw_reg_mcia_pack(mcia_pl, module, 0,
 133                                    MLXSW_REG_MCIA_PAGE0_LO,
 134                                    off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
 135                                    MLXSW_REG_MCIA_I2C_ADDR_HIGH);
 136
 137        err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
 138        if (err)
 139                return err;
 140
 141        mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
 142        memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
 143        *temp = temp_thresh.temp * 1000;
 144
 145        return 0;
 146}
 147
 148int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
 149                              struct ethtool_modinfo *modinfo)
 150{
 151        u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
 152        u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
 153        u8 module_rev_id, module_id, diag_mon;
 154        unsigned int read_size;
 155        int err;
 156
 157        err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset,
 158                                            module_info, &read_size);
 159        if (err)
 160                return err;
 161
 162        if (read_size < offset)
 163                return -EIO;
 164
 165        module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
 166        module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
 167
 168        switch (module_id) {
 169        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
 170                modinfo->type       = ETH_MODULE_SFF_8436;
 171                modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
 172                break;
 173        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
 174        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
 175                if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
 176                    module_rev_id >=
 177                    MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
 178                        modinfo->type       = ETH_MODULE_SFF_8636;
 179                        modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
 180                } else {
 181                        modinfo->type       = ETH_MODULE_SFF_8436;
 182                        modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
 183                }
 184                break;
 185        case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
 186                /* Verify if transceiver provides diagnostic monitoring page */
 187                err = mlxsw_env_query_module_eeprom(mlxsw_core, module,
 188                                                    SFP_DIAGMON, 1, &diag_mon,
 189                                                    &read_size);
 190                if (err)
 191                        return err;
 192
 193                if (read_size < 1)
 194                        return -EIO;
 195
 196                modinfo->type       = ETH_MODULE_SFF_8472;
 197                if (diag_mon)
 198                        modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
 199                else
 200                        modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
 201                break;
 202        default:
 203                return -EINVAL;
 204        }
 205
 206        return 0;
 207}
 208EXPORT_SYMBOL(mlxsw_env_get_module_info);
 209
 210int mlxsw_env_get_module_eeprom(struct net_device *netdev,
 211                                struct mlxsw_core *mlxsw_core, int module,
 212                                struct ethtool_eeprom *ee, u8 *data)
 213{
 214        int offset = ee->offset;
 215        unsigned int read_size;
 216        int i = 0;
 217        int err;
 218
 219        if (!ee->len)
 220                return -EINVAL;
 221
 222        memset(data, 0, ee->len);
 223
 224        while (i < ee->len) {
 225                err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset,
 226                                                    ee->len - i, data + i,
 227                                                    &read_size);
 228                if (err) {
 229                        netdev_err(netdev, "Eeprom query failed\n");
 230                        return err;
 231                }
 232
 233                i += read_size;
 234                offset += read_size;
 235        }
 236
 237        return 0;
 238}
 239EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);
 240