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