Merge branch 'x86-pti-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / drivers / net / ethernet / mellanox / mlxsw / core_env.c
CommitLineData
d93c19a1
VP
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>
f1436c80 6#include <linux/sfp.h>
d93c19a1
VP
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
1b1c6c1a
VP
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;
a45bfb5a 53 u8 page = 0;
1b1c6c1a
VP
54 int status;
55 int err;
56
57 size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
58
59 if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
60 offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
61 /* Cross pages read, read until offset 256 in low page */
62 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
63
64 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
65 if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
a45bfb5a
VP
66 page = MLXSW_REG_MCIA_PAGE_GET(offset);
67 offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;
68 /* When reading upper pages 1, 2 and 3 the offset starts at
69 * 128. Please refer to "QSFP+ Memory Map" figure in SFF-8436
70 * specification for graphical depiction.
71 * MCIA register accepts buffer size <= 48. Page of size 128
72 * should be read by chunks of size 48, 48, 32. Align the size
73 * of the last chunk to avoid reading after the end of the
74 * page.
75 */
76 if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
77 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
1b1c6c1a
VP
78 }
79
a45bfb5a 80 mlxsw_reg_mcia_pack(mcia_pl, module, 0, page, offset, size, i2c_addr);
1b1c6c1a
VP
81
82 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
83 if (err)
84 return err;
85
86 status = mlxsw_reg_mcia_status_get(mcia_pl);
87 if (status)
88 return -EIO;
89
90 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
91 memcpy(data, eeprom_tmp, size);
92 *p_read_size = size;
93
94 return 0;
95}
96
d93c19a1
VP
97int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
98 int off, int *temp)
99{
100 char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
101 union {
102 u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
103 u16 temp;
104 } temp_thresh;
105 char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
e4e93d6d
VP
106 char mtmp_pl[MLXSW_REG_MTMP_LEN];
107 unsigned int module_temp;
d93c19a1
VP
108 bool qsfp;
109 int err;
110
e4e93d6d
VP
111 mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
112 false, false);
113 err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
d93c19a1
VP
114 if (err)
115 return err;
e4e93d6d
VP
116 mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, NULL);
117 if (!module_temp) {
d93c19a1
VP
118 *temp = 0;
119 return 0;
d93c19a1
VP
120 }
121
122 /* Read Free Side Device Temperature Thresholds from page 03h
123 * (MSB at lower byte address).
124 * Bytes:
125 * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
126 * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
127 * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
128 * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
129 */
130
131 /* Validate module identifier value. */
132 err = mlxsw_env_validate_cable_ident(core, module, &qsfp);
133 if (err)
134 return err;
135
136 if (qsfp)
137 mlxsw_reg_mcia_pack(mcia_pl, module, 0,
138 MLXSW_REG_MCIA_TH_PAGE_NUM,
139 MLXSW_REG_MCIA_TH_PAGE_OFF + off,
140 MLXSW_REG_MCIA_TH_ITEM_SIZE,
141 MLXSW_REG_MCIA_I2C_ADDR_LOW);
142 else
143 mlxsw_reg_mcia_pack(mcia_pl, module, 0,
144 MLXSW_REG_MCIA_PAGE0_LO,
145 off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
146 MLXSW_REG_MCIA_I2C_ADDR_HIGH);
147
148 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
149 if (err)
150 return err;
151
152 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
153 memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
154 *temp = temp_thresh.temp * 1000;
155
156 return 0;
157}
1b1c6c1a
VP
158
159int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
160 struct ethtool_modinfo *modinfo)
161{
162 u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
163 u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
f1436c80 164 u8 module_rev_id, module_id, diag_mon;
1b1c6c1a
VP
165 unsigned int read_size;
166 int err;
167
168 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset,
169 module_info, &read_size);
170 if (err)
171 return err;
172
173 if (read_size < offset)
174 return -EIO;
175
176 module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
177 module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
178
179 switch (module_id) {
180 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
181 modinfo->type = ETH_MODULE_SFF_8436;
a45bfb5a 182 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
1b1c6c1a
VP
183 break;
184 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
185 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
186 if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
187 module_rev_id >=
188 MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
189 modinfo->type = ETH_MODULE_SFF_8636;
a45bfb5a 190 modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
1b1c6c1a
VP
191 } else {
192 modinfo->type = ETH_MODULE_SFF_8436;
a45bfb5a 193 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
1b1c6c1a
VP
194 }
195 break;
196 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
f1436c80
VP
197 /* Verify if transceiver provides diagnostic monitoring page */
198 err = mlxsw_env_query_module_eeprom(mlxsw_core, module,
199 SFP_DIAGMON, 1, &diag_mon,
200 &read_size);
201 if (err)
202 return err;
203
204 if (read_size < 1)
205 return -EIO;
206
1b1c6c1a 207 modinfo->type = ETH_MODULE_SFF_8472;
f1436c80
VP
208 if (diag_mon)
209 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
210 else
211 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
1b1c6c1a
VP
212 break;
213 default:
214 return -EINVAL;
215 }
216
217 return 0;
218}
219EXPORT_SYMBOL(mlxsw_env_get_module_info);
220
221int mlxsw_env_get_module_eeprom(struct net_device *netdev,
222 struct mlxsw_core *mlxsw_core, int module,
223 struct ethtool_eeprom *ee, u8 *data)
224{
225 int offset = ee->offset;
226 unsigned int read_size;
227 int i = 0;
228 int err;
229
230 if (!ee->len)
231 return -EINVAL;
232
233 memset(data, 0, ee->len);
234
235 while (i < ee->len) {
236 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset,
237 ee->len - i, data + i,
238 &read_size);
239 if (err) {
240 netdev_err(netdev, "Eeprom query failed\n");
241 return err;
242 }
243
244 i += read_size;
245 offset += read_size;
246 }
247
248 return 0;
249}
250EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);