1
2
3
4#include <linux/netdevice.h>
5#include <linux/etherdevice.h>
6#include <linux/ethtool.h>
7#include <linux/i2c.h>
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/mod_devicetable.h>
11#include <linux/types.h>
12
13#include "core.h"
14#include "core_env.h"
15#include "i2c.h"
16
17static const char mlxsw_m_driver_name[] = "mlxsw_minimal";
18
19#define MLXSW_M_FWREV_MINOR 2000
20#define MLXSW_M_FWREV_SUBMINOR 1886
21
22static const struct mlxsw_fw_rev mlxsw_m_fw_rev = {
23 .minor = MLXSW_M_FWREV_MINOR,
24 .subminor = MLXSW_M_FWREV_SUBMINOR,
25};
26
27struct mlxsw_m_port;
28
29struct mlxsw_m {
30 struct mlxsw_m_port **ports;
31 int *module_to_port;
32 struct mlxsw_core *core;
33 const struct mlxsw_bus_info *bus_info;
34 u8 base_mac[ETH_ALEN];
35 u8 max_ports;
36};
37
38struct mlxsw_m_port {
39 struct net_device *dev;
40 struct mlxsw_m *mlxsw_m;
41 u8 local_port;
42 u8 module;
43};
44
45static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m)
46{
47 char spad_pl[MLXSW_REG_SPAD_LEN] = {0};
48 int err;
49
50 err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(spad), spad_pl);
51 if (err)
52 return err;
53 mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_m->base_mac);
54 return 0;
55}
56
57static int mlxsw_m_port_dummy_open_stop(struct net_device *dev)
58{
59 return 0;
60}
61
62static struct devlink_port *
63mlxsw_m_port_get_devlink_port(struct net_device *dev)
64{
65 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
66 struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
67
68 return mlxsw_core_port_devlink_port_get(mlxsw_m->core,
69 mlxsw_m_port->local_port);
70}
71
72static const struct net_device_ops mlxsw_m_port_netdev_ops = {
73 .ndo_open = mlxsw_m_port_dummy_open_stop,
74 .ndo_stop = mlxsw_m_port_dummy_open_stop,
75 .ndo_get_devlink_port = mlxsw_m_port_get_devlink_port,
76};
77
78static void mlxsw_m_module_get_drvinfo(struct net_device *dev,
79 struct ethtool_drvinfo *drvinfo)
80{
81 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
82 struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
83
84 strlcpy(drvinfo->driver, mlxsw_m->bus_info->device_kind,
85 sizeof(drvinfo->driver));
86 snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
87 "%d.%d.%d",
88 mlxsw_m->bus_info->fw_rev.major,
89 mlxsw_m->bus_info->fw_rev.minor,
90 mlxsw_m->bus_info->fw_rev.subminor);
91 strlcpy(drvinfo->bus_info, mlxsw_m->bus_info->device_name,
92 sizeof(drvinfo->bus_info));
93}
94
95static int mlxsw_m_get_module_info(struct net_device *netdev,
96 struct ethtool_modinfo *modinfo)
97{
98 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
99 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
100
101 return mlxsw_env_get_module_info(core, mlxsw_m_port->module, modinfo);
102}
103
104static int
105mlxsw_m_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee,
106 u8 *data)
107{
108 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
109 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
110
111 return mlxsw_env_get_module_eeprom(netdev, core, mlxsw_m_port->module,
112 ee, data);
113}
114
115static int
116mlxsw_m_get_module_eeprom_by_page(struct net_device *netdev,
117 const struct ethtool_module_eeprom *page,
118 struct netlink_ext_ack *extack)
119{
120 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
121 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
122
123 return mlxsw_env_get_module_eeprom_by_page(core, mlxsw_m_port->module,
124 page, extack);
125}
126
127static const struct ethtool_ops mlxsw_m_port_ethtool_ops = {
128 .get_drvinfo = mlxsw_m_module_get_drvinfo,
129 .get_module_info = mlxsw_m_get_module_info,
130 .get_module_eeprom = mlxsw_m_get_module_eeprom,
131 .get_module_eeprom_by_page = mlxsw_m_get_module_eeprom_by_page,
132};
133
134static int
135mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u8 local_port,
136 u8 *p_module, u8 *p_width)
137{
138 char pmlp_pl[MLXSW_REG_PMLP_LEN];
139 int err;
140
141 mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
142 err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(pmlp), pmlp_pl);
143 if (err)
144 return err;
145 *p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
146 *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl);
147
148 return 0;
149}
150
151static int
152mlxsw_m_port_dev_addr_get(struct mlxsw_m_port *mlxsw_m_port)
153{
154 struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
155 struct net_device *dev = mlxsw_m_port->dev;
156 char ppad_pl[MLXSW_REG_PPAD_LEN];
157 int err;
158
159 mlxsw_reg_ppad_pack(ppad_pl, false, 0);
160 err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(ppad), ppad_pl);
161 if (err)
162 return err;
163 mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, dev->dev_addr);
164
165
166
167
168 dev->dev_addr[ETH_ALEN - 1] += mlxsw_m_port->module + 1;
169 return 0;
170}
171
172static int
173mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u8 local_port, u8 module)
174{
175 struct mlxsw_m_port *mlxsw_m_port;
176 struct net_device *dev;
177 int err;
178
179 err = mlxsw_core_port_init(mlxsw_m->core, local_port,
180 module + 1, false, 0, false,
181 0, mlxsw_m->base_mac,
182 sizeof(mlxsw_m->base_mac));
183 if (err) {
184 dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to init core port\n",
185 local_port);
186 return err;
187 }
188
189 dev = alloc_etherdev(sizeof(struct mlxsw_m_port));
190 if (!dev) {
191 err = -ENOMEM;
192 goto err_alloc_etherdev;
193 }
194
195 SET_NETDEV_DEV(dev, mlxsw_m->bus_info->dev);
196 dev_net_set(dev, mlxsw_core_net(mlxsw_m->core));
197 mlxsw_m_port = netdev_priv(dev);
198 mlxsw_m_port->dev = dev;
199 mlxsw_m_port->mlxsw_m = mlxsw_m;
200 mlxsw_m_port->local_port = local_port;
201 mlxsw_m_port->module = module;
202
203 dev->netdev_ops = &mlxsw_m_port_netdev_ops;
204 dev->ethtool_ops = &mlxsw_m_port_ethtool_ops;
205
206 err = mlxsw_m_port_dev_addr_get(mlxsw_m_port);
207 if (err) {
208 dev_err(mlxsw_m->bus_info->dev, "Port %d: Unable to get port mac address\n",
209 mlxsw_m_port->local_port);
210 goto err_dev_addr_get;
211 }
212
213 netif_carrier_off(dev);
214 mlxsw_m->ports[local_port] = mlxsw_m_port;
215 err = register_netdev(dev);
216 if (err) {
217 dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to register netdev\n",
218 mlxsw_m_port->local_port);
219 goto err_register_netdev;
220 }
221
222 mlxsw_core_port_eth_set(mlxsw_m->core, mlxsw_m_port->local_port,
223 mlxsw_m_port, dev);
224
225 return 0;
226
227err_register_netdev:
228 mlxsw_m->ports[local_port] = NULL;
229err_dev_addr_get:
230 free_netdev(dev);
231err_alloc_etherdev:
232 mlxsw_core_port_fini(mlxsw_m->core, local_port);
233 return err;
234}
235
236static void mlxsw_m_port_remove(struct mlxsw_m *mlxsw_m, u8 local_port)
237{
238 struct mlxsw_m_port *mlxsw_m_port = mlxsw_m->ports[local_port];
239
240 mlxsw_core_port_clear(mlxsw_m->core, local_port, mlxsw_m);
241 unregister_netdev(mlxsw_m_port->dev);
242 mlxsw_m->ports[local_port] = NULL;
243 free_netdev(mlxsw_m_port->dev);
244 mlxsw_core_port_fini(mlxsw_m->core, local_port);
245}
246
247static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port,
248 u8 *last_module)
249{
250 unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);
251 u8 module, width;
252 int err;
253
254
255 err = mlxsw_m_port_module_info_get(mlxsw_m, local_port, &module,
256 &width);
257 if (err)
258 return err;
259
260 if (!width)
261 return 0;
262
263 if (module == *last_module)
264 return 0;
265 *last_module = module;
266
267 if (WARN_ON_ONCE(module >= max_ports))
268 return -EINVAL;
269 mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports;
270
271 return 0;
272}
273
274static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module)
275{
276 mlxsw_m->module_to_port[module] = -1;
277}
278
279static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m)
280{
281 unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);
282 u8 last_module = max_ports;
283 int i;
284 int err;
285
286 mlxsw_m->ports = kcalloc(max_ports, sizeof(*mlxsw_m->ports),
287 GFP_KERNEL);
288 if (!mlxsw_m->ports)
289 return -ENOMEM;
290
291 mlxsw_m->module_to_port = kmalloc_array(max_ports, sizeof(int),
292 GFP_KERNEL);
293 if (!mlxsw_m->module_to_port) {
294 err = -ENOMEM;
295 goto err_module_to_port_alloc;
296 }
297
298
299 for (i = 0; i < max_ports; i++)
300 mlxsw_m->module_to_port[i] = -1;
301
302
303 for (i = 1; i < max_ports; i++) {
304 err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module);
305 if (err)
306 goto err_module_to_port_map;
307 }
308
309
310 for (i = 0; i < mlxsw_m->max_ports; i++) {
311 if (mlxsw_m->module_to_port[i] > 0 &&
312 !mlxsw_core_port_is_xm(mlxsw_m->core, i)) {
313 err = mlxsw_m_port_create(mlxsw_m,
314 mlxsw_m->module_to_port[i],
315 i);
316 if (err)
317 goto err_module_to_port_create;
318 }
319 }
320
321 return 0;
322
323err_module_to_port_create:
324 for (i--; i >= 0; i--) {
325 if (mlxsw_m->module_to_port[i] > 0)
326 mlxsw_m_port_remove(mlxsw_m,
327 mlxsw_m->module_to_port[i]);
328 }
329 i = max_ports;
330err_module_to_port_map:
331 for (i--; i > 0; i--)
332 mlxsw_m_port_module_unmap(mlxsw_m, i);
333 kfree(mlxsw_m->module_to_port);
334err_module_to_port_alloc:
335 kfree(mlxsw_m->ports);
336 return err;
337}
338
339static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m)
340{
341 int i;
342
343 for (i = 0; i < mlxsw_m->max_ports; i++) {
344 if (mlxsw_m->module_to_port[i] > 0) {
345 mlxsw_m_port_remove(mlxsw_m,
346 mlxsw_m->module_to_port[i]);
347 mlxsw_m_port_module_unmap(mlxsw_m, i);
348 }
349 }
350
351 kfree(mlxsw_m->module_to_port);
352 kfree(mlxsw_m->ports);
353}
354
355static int mlxsw_m_fw_rev_validate(struct mlxsw_m *mlxsw_m)
356{
357 const struct mlxsw_fw_rev *rev = &mlxsw_m->bus_info->fw_rev;
358
359
360
361
362
363 if (mlxsw_core_fw_rev_minor_subminor_validate(rev, &mlxsw_m_fw_rev))
364 return 0;
365
366 dev_err(mlxsw_m->bus_info->dev, "The firmware version %d.%d.%d is incompatible with the driver (required >= %d.%d.%d)\n",
367 rev->major, rev->minor, rev->subminor, rev->major,
368 mlxsw_m_fw_rev.minor, mlxsw_m_fw_rev.subminor);
369
370 return -EINVAL;
371}
372
373static int mlxsw_m_init(struct mlxsw_core *mlxsw_core,
374 const struct mlxsw_bus_info *mlxsw_bus_info,
375 struct netlink_ext_ack *extack)
376{
377 struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);
378 int err;
379
380 mlxsw_m->core = mlxsw_core;
381 mlxsw_m->bus_info = mlxsw_bus_info;
382
383 err = mlxsw_m_fw_rev_validate(mlxsw_m);
384 if (err)
385 return err;
386
387 err = mlxsw_m_base_mac_get(mlxsw_m);
388 if (err) {
389 dev_err(mlxsw_m->bus_info->dev, "Failed to get base mac\n");
390 return err;
391 }
392
393 err = mlxsw_m_ports_create(mlxsw_m);
394 if (err) {
395 dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n");
396 return err;
397 }
398
399 return 0;
400}
401
402static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core)
403{
404 struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);
405
406 mlxsw_m_ports_remove(mlxsw_m);
407}
408
409static const struct mlxsw_config_profile mlxsw_m_config_profile;
410
411static struct mlxsw_driver mlxsw_m_driver = {
412 .kind = mlxsw_m_driver_name,
413 .priv_size = sizeof(struct mlxsw_m),
414 .init = mlxsw_m_init,
415 .fini = mlxsw_m_fini,
416 .profile = &mlxsw_m_config_profile,
417 .res_query_enabled = true,
418};
419
420static const struct i2c_device_id mlxsw_m_i2c_id[] = {
421 { "mlxsw_minimal", 0},
422 { },
423};
424
425static struct i2c_driver mlxsw_m_i2c_driver = {
426 .driver.name = "mlxsw_minimal",
427 .class = I2C_CLASS_HWMON,
428 .id_table = mlxsw_m_i2c_id,
429};
430
431static int __init mlxsw_m_module_init(void)
432{
433 int err;
434
435 err = mlxsw_core_driver_register(&mlxsw_m_driver);
436 if (err)
437 return err;
438
439 err = mlxsw_i2c_driver_register(&mlxsw_m_i2c_driver);
440 if (err)
441 goto err_i2c_driver_register;
442
443 return 0;
444
445err_i2c_driver_register:
446 mlxsw_core_driver_unregister(&mlxsw_m_driver);
447
448 return err;
449}
450
451static void __exit mlxsw_m_module_exit(void)
452{
453 mlxsw_i2c_driver_unregister(&mlxsw_m_i2c_driver);
454 mlxsw_core_driver_unregister(&mlxsw_m_driver);
455}
456
457module_init(mlxsw_m_module_init);
458module_exit(mlxsw_m_module_exit);
459
460MODULE_LICENSE("Dual BSD/GPL");
461MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
462MODULE_DESCRIPTION("Mellanox minimal driver");
463MODULE_DEVICE_TABLE(i2c, mlxsw_m_i2c_id);
464