Merge tag 'amd-drm-fixes-5.9-2020-08-12' of git://people.freedesktop.org/~agd5f/linux...
[linux-2.6-microblaze.git] / drivers / net / ethernet / mellanox / mlxsw / core_env.c
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
13 static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
14                                           bool *qsfp, bool *cmis)
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         *cmis = false;
29         switch (ident) {
30         case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
31                 *qsfp = false;
32                 break;
33         case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: /* fall-through */
34         case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
35         case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
36                 *qsfp = true;
37                 break;
38         case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
39                 *qsfp = true;
40                 *cmis = true;
41                 break;
42         default:
43                 return -EINVAL;
44         }
45
46         return 0;
47 }
48
49 static int
50 mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module,
51                               u16 offset, u16 size, void *data,
52                               bool qsfp, unsigned int *p_read_size)
53 {
54         char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
55         char mcia_pl[MLXSW_REG_MCIA_LEN];
56         u16 i2c_addr;
57         u8 page = 0;
58         int status;
59         int err;
60
61         /* MCIA register accepts buffer size <= 48. Page of size 128 should be
62          * read by chunks of size 48, 48, 32. Align the size of the last chunk
63          * to avoid reading after the end of the page.
64          */
65         size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
66
67         if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
68             offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
69                 /* Cross pages read, read until offset 256 in low page */
70                 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
71
72         i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
73         if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
74                 if (qsfp) {
75                         /* When reading upper pages 1, 2 and 3 the offset
76                          * starts at 128. Please refer to "QSFP+ Memory Map"
77                          * figure in SFF-8436 specification and to "CMIS Module
78                          * Memory Map" figure in CMIS specification for
79                          * graphical depiction.
80                          */
81                         page = MLXSW_REG_MCIA_PAGE_GET(offset);
82                         offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;
83                         if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
84                                 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
85                 } else {
86                         /* When reading upper pages 1, 2 and 3 the offset
87                          * starts at 0 and I2C high address is used. Please refer
88                          * refer to "Memory Organization" figure in SFF-8472
89                          * specification for graphical depiction.
90                          */
91                         i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
92                         offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
93                 }
94         }
95
96         mlxsw_reg_mcia_pack(mcia_pl, module, 0, page, offset, size, i2c_addr);
97
98         err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
99         if (err)
100                 return err;
101
102         status = mlxsw_reg_mcia_status_get(mcia_pl);
103         if (status)
104                 return -EIO;
105
106         mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
107         memcpy(data, eeprom_tmp, size);
108         *p_read_size = size;
109
110         return 0;
111 }
112
113 int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
114                                          int off, int *temp)
115 {
116         char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
117         union {
118                 u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
119                 u16 temp;
120         } temp_thresh;
121         char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
122         char mtmp_pl[MLXSW_REG_MTMP_LEN];
123         unsigned int module_temp;
124         bool qsfp, cmis;
125         int page;
126         int err;
127
128         mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
129                             false, false);
130         err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
131         if (err)
132                 return err;
133         mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, NULL);
134         if (!module_temp) {
135                 *temp = 0;
136                 return 0;
137         }
138
139         /* Read Free Side Device Temperature Thresholds from page 03h
140          * (MSB at lower byte address).
141          * Bytes:
142          * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
143          * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
144          * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
145          * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
146          */
147
148         /* Validate module identifier value. */
149         err = mlxsw_env_validate_cable_ident(core, module, &qsfp, &cmis);
150         if (err)
151                 return err;
152
153         if (qsfp) {
154                 /* For QSFP/CMIS module-defined thresholds are located in page
155                  * 02h, otherwise in page 03h.
156                  */
157                 if (cmis)
158                         page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;
159                 else
160                         page = MLXSW_REG_MCIA_TH_PAGE_NUM;
161                 mlxsw_reg_mcia_pack(mcia_pl, module, 0, page,
162                                     MLXSW_REG_MCIA_TH_PAGE_OFF + off,
163                                     MLXSW_REG_MCIA_TH_ITEM_SIZE,
164                                     MLXSW_REG_MCIA_I2C_ADDR_LOW);
165         } else {
166                 mlxsw_reg_mcia_pack(mcia_pl, module, 0,
167                                     MLXSW_REG_MCIA_PAGE0_LO,
168                                     off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
169                                     MLXSW_REG_MCIA_I2C_ADDR_HIGH);
170         }
171
172         err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
173         if (err)
174                 return err;
175
176         mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
177         memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
178         *temp = temp_thresh.temp * 1000;
179
180         return 0;
181 }
182
183 int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
184                               struct ethtool_modinfo *modinfo)
185 {
186         u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
187         u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
188         u8 module_rev_id, module_id, diag_mon;
189         unsigned int read_size;
190         int err;
191
192         err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset,
193                                             module_info, false, &read_size);
194         if (err)
195                 return err;
196
197         if (read_size < offset)
198                 return -EIO;
199
200         module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
201         module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
202
203         switch (module_id) {
204         case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
205                 modinfo->type       = ETH_MODULE_SFF_8436;
206                 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
207                 break;
208         case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
209         case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
210                 if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
211                     module_rev_id >=
212                     MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
213                         modinfo->type       = ETH_MODULE_SFF_8636;
214                         modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
215                 } else {
216                         modinfo->type       = ETH_MODULE_SFF_8436;
217                         modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
218                 }
219                 break;
220         case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
221                 /* Verify if transceiver provides diagnostic monitoring page */
222                 err = mlxsw_env_query_module_eeprom(mlxsw_core, module,
223                                                     SFP_DIAGMON, 1, &diag_mon,
224                                                     false, &read_size);
225                 if (err)
226                         return err;
227
228                 if (read_size < 1)
229                         return -EIO;
230
231                 modinfo->type       = ETH_MODULE_SFF_8472;
232                 if (diag_mon)
233                         modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
234                 else
235                         modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
236                 break;
237         case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
238                 /* Use SFF_8636 as base type. ethtool should recognize specific
239                  * type through the identifier value.
240                  */
241                 modinfo->type       = ETH_MODULE_SFF_8636;
242                 /* Verify if module EEPROM is a flat memory. In case of flat
243                  * memory only page 00h (0-255 bytes) can be read. Otherwise
244                  * upper pages 01h and 02h can also be read. Upper pages 10h
245                  * and 11h are currently not supported by the driver.
246                  */
247                 if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] &
248                     MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY)
249                         modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
250                 else
251                         modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
252                 break;
253         default:
254                 return -EINVAL;
255         }
256
257         return 0;
258 }
259 EXPORT_SYMBOL(mlxsw_env_get_module_info);
260
261 int mlxsw_env_get_module_eeprom(struct net_device *netdev,
262                                 struct mlxsw_core *mlxsw_core, int module,
263                                 struct ethtool_eeprom *ee, u8 *data)
264 {
265         int offset = ee->offset;
266         unsigned int read_size;
267         bool qsfp, cmis;
268         int i = 0;
269         int err;
270
271         if (!ee->len)
272                 return -EINVAL;
273
274         memset(data, 0, ee->len);
275         /* Validate module identifier value. */
276         err = mlxsw_env_validate_cable_ident(mlxsw_core, module, &qsfp, &cmis);
277         if (err)
278                 return err;
279
280         while (i < ee->len) {
281                 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset,
282                                                     ee->len - i, data + i,
283                                                     qsfp, &read_size);
284                 if (err) {
285                         netdev_err(netdev, "Eeprom query failed\n");
286                         return err;
287                 }
288
289                 i += read_size;
290                 offset += read_size;
291         }
292
293         return 0;
294 }
295 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);