1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2016-2019 Mellanox Technologies. All rights reserved */ |
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 | |
17 | static const char mlxsw_m_driver_name[] = "mlxsw_minimal" ; |
18 | |
19 | #define MLXSW_M_FWREV_MINOR 2000 |
20 | #define MLXSW_M_FWREV_SUBMINOR 1886 |
21 | |
22 | static const struct mlxsw_fw_rev mlxsw_m_fw_rev = { |
23 | .minor = MLXSW_M_FWREV_MINOR, |
24 | .subminor = MLXSW_M_FWREV_SUBMINOR, |
25 | }; |
26 | |
27 | struct mlxsw_m_port; |
28 | |
29 | struct mlxsw_m_line_card { |
30 | bool active; |
31 | int module_to_port[]; |
32 | }; |
33 | |
34 | struct mlxsw_m { |
35 | struct mlxsw_m_port **ports; |
36 | struct mlxsw_core *core; |
37 | const struct mlxsw_bus_info *bus_info; |
38 | u8 base_mac[ETH_ALEN]; |
39 | u8 max_ports; |
40 | u8 max_modules_per_slot; /* Maximum number of modules per-slot. */ |
41 | u8 num_of_slots; /* Including the main board. */ |
42 | struct mlxsw_m_line_card **line_cards; |
43 | }; |
44 | |
45 | struct mlxsw_m_port { |
46 | struct net_device *dev; |
47 | struct mlxsw_m *mlxsw_m; |
48 | u16 local_port; |
49 | u8 slot_index; |
50 | u8 module; |
51 | u8 module_offset; |
52 | }; |
53 | |
54 | static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m) |
55 | { |
56 | char spad_pl[MLXSW_REG_SPAD_LEN] = {0}; |
57 | int err; |
58 | |
59 | err = mlxsw_reg_query(mlxsw_core: mlxsw_m->core, MLXSW_REG(spad), payload: spad_pl); |
60 | if (err) |
61 | return err; |
62 | mlxsw_reg_spad_base_mac_memcpy_from(buf: spad_pl, dst: mlxsw_m->base_mac); |
63 | return 0; |
64 | } |
65 | |
66 | static int mlxsw_m_port_open(struct net_device *dev) |
67 | { |
68 | struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); |
69 | struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; |
70 | |
71 | return mlxsw_env_module_port_up(mlxsw_core: mlxsw_m->core, slot_index: 0, |
72 | module: mlxsw_m_port->module); |
73 | } |
74 | |
75 | static int mlxsw_m_port_stop(struct net_device *dev) |
76 | { |
77 | struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); |
78 | struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; |
79 | |
80 | mlxsw_env_module_port_down(mlxsw_core: mlxsw_m->core, slot_index: 0, module: mlxsw_m_port->module); |
81 | return 0; |
82 | } |
83 | |
84 | static const struct net_device_ops mlxsw_m_port_netdev_ops = { |
85 | .ndo_open = mlxsw_m_port_open, |
86 | .ndo_stop = mlxsw_m_port_stop, |
87 | }; |
88 | |
89 | static void mlxsw_m_module_get_drvinfo(struct net_device *dev, |
90 | struct ethtool_drvinfo *drvinfo) |
91 | { |
92 | struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); |
93 | struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; |
94 | |
95 | strscpy(p: drvinfo->driver, q: mlxsw_m->bus_info->device_kind, |
96 | size: sizeof(drvinfo->driver)); |
97 | snprintf(buf: drvinfo->fw_version, size: sizeof(drvinfo->fw_version), |
98 | fmt: "%d.%d.%d" , |
99 | mlxsw_m->bus_info->fw_rev.major, |
100 | mlxsw_m->bus_info->fw_rev.minor, |
101 | mlxsw_m->bus_info->fw_rev.subminor); |
102 | strscpy(p: drvinfo->bus_info, q: mlxsw_m->bus_info->device_name, |
103 | size: sizeof(drvinfo->bus_info)); |
104 | } |
105 | |
106 | static int mlxsw_m_get_module_info(struct net_device *netdev, |
107 | struct ethtool_modinfo *modinfo) |
108 | { |
109 | struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev: netdev); |
110 | struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; |
111 | |
112 | return mlxsw_env_get_module_info(netdev, mlxsw_core: core, |
113 | slot_index: mlxsw_m_port->slot_index, |
114 | module: mlxsw_m_port->module, modinfo); |
115 | } |
116 | |
117 | static int |
118 | mlxsw_m_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee, |
119 | u8 *data) |
120 | { |
121 | struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev: netdev); |
122 | struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; |
123 | |
124 | return mlxsw_env_get_module_eeprom(netdev, mlxsw_core: core, |
125 | slot_index: mlxsw_m_port->slot_index, |
126 | module: mlxsw_m_port->module, ee, data); |
127 | } |
128 | |
129 | static int |
130 | mlxsw_m_get_module_eeprom_by_page(struct net_device *netdev, |
131 | const struct ethtool_module_eeprom *page, |
132 | struct netlink_ext_ack *extack) |
133 | { |
134 | struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev: netdev); |
135 | struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; |
136 | |
137 | return mlxsw_env_get_module_eeprom_by_page(mlxsw_core: core, |
138 | slot_index: mlxsw_m_port->slot_index, |
139 | module: mlxsw_m_port->module, |
140 | page, extack); |
141 | } |
142 | |
143 | static int mlxsw_m_reset(struct net_device *netdev, u32 *flags) |
144 | { |
145 | struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev: netdev); |
146 | struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; |
147 | |
148 | return mlxsw_env_reset_module(netdev, mlxsw_core: core, slot_index: mlxsw_m_port->slot_index, |
149 | module: mlxsw_m_port->module, |
150 | flags); |
151 | } |
152 | |
153 | static int |
154 | mlxsw_m_get_module_power_mode(struct net_device *netdev, |
155 | struct ethtool_module_power_mode_params *params, |
156 | struct netlink_ext_ack *extack) |
157 | { |
158 | struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev: netdev); |
159 | struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; |
160 | |
161 | return mlxsw_env_get_module_power_mode(mlxsw_core: core, slot_index: mlxsw_m_port->slot_index, |
162 | module: mlxsw_m_port->module, |
163 | params, extack); |
164 | } |
165 | |
166 | static int |
167 | mlxsw_m_set_module_power_mode(struct net_device *netdev, |
168 | const struct ethtool_module_power_mode_params *params, |
169 | struct netlink_ext_ack *extack) |
170 | { |
171 | struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev: netdev); |
172 | struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; |
173 | |
174 | return mlxsw_env_set_module_power_mode(mlxsw_core: core, slot_index: mlxsw_m_port->slot_index, |
175 | module: mlxsw_m_port->module, |
176 | policy: params->policy, extack); |
177 | } |
178 | |
179 | static const struct ethtool_ops mlxsw_m_port_ethtool_ops = { |
180 | .get_drvinfo = mlxsw_m_module_get_drvinfo, |
181 | .get_module_info = mlxsw_m_get_module_info, |
182 | .get_module_eeprom = mlxsw_m_get_module_eeprom, |
183 | .get_module_eeprom_by_page = mlxsw_m_get_module_eeprom_by_page, |
184 | .reset = mlxsw_m_reset, |
185 | .get_module_power_mode = mlxsw_m_get_module_power_mode, |
186 | .set_module_power_mode = mlxsw_m_set_module_power_mode, |
187 | }; |
188 | |
189 | static int |
190 | mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u16 local_port, |
191 | u8 *p_module, u8 *p_width, u8 *p_slot_index) |
192 | { |
193 | char pmlp_pl[MLXSW_REG_PMLP_LEN]; |
194 | int err; |
195 | |
196 | mlxsw_reg_pmlp_pack(payload: pmlp_pl, local_port); |
197 | err = mlxsw_reg_query(mlxsw_core: mlxsw_m->core, MLXSW_REG(pmlp), payload: pmlp_pl); |
198 | if (err) |
199 | return err; |
200 | *p_module = mlxsw_reg_pmlp_module_get(buf: pmlp_pl, index: 0); |
201 | *p_width = mlxsw_reg_pmlp_width_get(buf: pmlp_pl); |
202 | *p_slot_index = mlxsw_reg_pmlp_slot_index_get(buf: pmlp_pl, index: 0); |
203 | |
204 | return 0; |
205 | } |
206 | |
207 | static int |
208 | mlxsw_m_port_dev_addr_get(struct mlxsw_m_port *mlxsw_m_port) |
209 | { |
210 | struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; |
211 | char ppad_pl[MLXSW_REG_PPAD_LEN]; |
212 | u8 addr[ETH_ALEN]; |
213 | int err; |
214 | |
215 | mlxsw_reg_ppad_pack(payload: ppad_pl, single_base_mac: false, local_port: 0); |
216 | err = mlxsw_reg_query(mlxsw_core: mlxsw_m->core, MLXSW_REG(ppad), payload: ppad_pl); |
217 | if (err) |
218 | return err; |
219 | mlxsw_reg_ppad_mac_memcpy_from(buf: ppad_pl, dst: addr); |
220 | eth_hw_addr_gen(dev: mlxsw_m_port->dev, base_addr: addr, id: mlxsw_m_port->module + 1 + |
221 | mlxsw_m_port->module_offset); |
222 | return 0; |
223 | } |
224 | |
225 | static bool mlxsw_m_port_created(struct mlxsw_m *mlxsw_m, u16 local_port) |
226 | { |
227 | return mlxsw_m->ports[local_port]; |
228 | } |
229 | |
230 | static int |
231 | mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u16 local_port, u8 slot_index, |
232 | u8 module) |
233 | { |
234 | struct mlxsw_m_port *mlxsw_m_port; |
235 | struct net_device *dev; |
236 | int err; |
237 | |
238 | err = mlxsw_core_port_init(mlxsw_core: mlxsw_m->core, local_port, slot_index, |
239 | port_number: module + 1, split: false, split_port_subnumber: 0, splittable: false, |
240 | lanes: 0, switch_id: mlxsw_m->base_mac, |
241 | switch_id_len: sizeof(mlxsw_m->base_mac)); |
242 | if (err) { |
243 | dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to init core port\n" , |
244 | local_port); |
245 | return err; |
246 | } |
247 | |
248 | dev = alloc_etherdev(sizeof(struct mlxsw_m_port)); |
249 | if (!dev) { |
250 | err = -ENOMEM; |
251 | goto err_alloc_etherdev; |
252 | } |
253 | |
254 | SET_NETDEV_DEV(dev, mlxsw_m->bus_info->dev); |
255 | dev_net_set(dev, net: mlxsw_core_net(mlxsw_core: mlxsw_m->core)); |
256 | mlxsw_m_port = netdev_priv(dev); |
257 | mlxsw_core_port_netdev_link(mlxsw_core: mlxsw_m->core, local_port, |
258 | port_driver_priv: mlxsw_m_port, dev); |
259 | mlxsw_m_port->dev = dev; |
260 | mlxsw_m_port->mlxsw_m = mlxsw_m; |
261 | mlxsw_m_port->local_port = local_port; |
262 | mlxsw_m_port->module = module; |
263 | mlxsw_m_port->slot_index = slot_index; |
264 | /* Add module offset for line card. Offset for main board iz zero. |
265 | * For line card in slot #n offset is calculated as (#n - 1) |
266 | * multiplied by maximum modules number, which could be found on a line |
267 | * card. |
268 | */ |
269 | mlxsw_m_port->module_offset = mlxsw_m_port->slot_index ? |
270 | (mlxsw_m_port->slot_index - 1) * |
271 | mlxsw_m->max_modules_per_slot : 0; |
272 | |
273 | dev->netdev_ops = &mlxsw_m_port_netdev_ops; |
274 | dev->ethtool_ops = &mlxsw_m_port_ethtool_ops; |
275 | |
276 | err = mlxsw_m_port_dev_addr_get(mlxsw_m_port); |
277 | if (err) { |
278 | dev_err(mlxsw_m->bus_info->dev, "Port %d: Unable to get port mac address\n" , |
279 | mlxsw_m_port->local_port); |
280 | goto err_dev_addr_get; |
281 | } |
282 | |
283 | netif_carrier_off(dev); |
284 | mlxsw_m->ports[local_port] = mlxsw_m_port; |
285 | err = register_netdev(dev); |
286 | if (err) { |
287 | dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to register netdev\n" , |
288 | mlxsw_m_port->local_port); |
289 | goto err_register_netdev; |
290 | } |
291 | |
292 | return 0; |
293 | |
294 | err_register_netdev: |
295 | mlxsw_m->ports[local_port] = NULL; |
296 | err_dev_addr_get: |
297 | free_netdev(dev); |
298 | err_alloc_etherdev: |
299 | mlxsw_core_port_fini(mlxsw_core: mlxsw_m->core, local_port); |
300 | return err; |
301 | } |
302 | |
303 | static void mlxsw_m_port_remove(struct mlxsw_m *mlxsw_m, u16 local_port) |
304 | { |
305 | struct mlxsw_m_port *mlxsw_m_port = mlxsw_m->ports[local_port]; |
306 | |
307 | unregister_netdev(dev: mlxsw_m_port->dev); /* This calls ndo_stop */ |
308 | mlxsw_m->ports[local_port] = NULL; |
309 | free_netdev(dev: mlxsw_m_port->dev); |
310 | mlxsw_core_port_fini(mlxsw_core: mlxsw_m->core, local_port); |
311 | } |
312 | |
313 | static int* |
314 | mlxsw_m_port_mapping_get(struct mlxsw_m *mlxsw_m, u8 slot_index, u8 module) |
315 | { |
316 | return &mlxsw_m->line_cards[slot_index]->module_to_port[module]; |
317 | } |
318 | |
319 | static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u16 local_port, |
320 | u8 *last_module) |
321 | { |
322 | unsigned int max_ports = mlxsw_core_max_ports(mlxsw_core: mlxsw_m->core); |
323 | u8 module, width, slot_index; |
324 | int *module_to_port; |
325 | int err; |
326 | |
327 | /* Fill out to local port mapping array */ |
328 | err = mlxsw_m_port_module_info_get(mlxsw_m, local_port, p_module: &module, |
329 | p_width: &width, p_slot_index: &slot_index); |
330 | if (err) |
331 | return err; |
332 | |
333 | /* Skip if line card has been already configured */ |
334 | if (mlxsw_m->line_cards[slot_index]->active) |
335 | return 0; |
336 | if (!width) |
337 | return 0; |
338 | /* Skip, if port belongs to the cluster */ |
339 | if (module == *last_module) |
340 | return 0; |
341 | *last_module = module; |
342 | |
343 | if (WARN_ON_ONCE(module >= max_ports)) |
344 | return -EINVAL; |
345 | mlxsw_env_module_port_map(mlxsw_core: mlxsw_m->core, slot_index, module); |
346 | module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, module); |
347 | *module_to_port = local_port; |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | static void |
353 | mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 slot_index, u8 module) |
354 | { |
355 | int *module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, |
356 | module); |
357 | *module_to_port = -1; |
358 | mlxsw_env_module_port_unmap(mlxsw_core: mlxsw_m->core, slot_index, module); |
359 | } |
360 | |
361 | static int mlxsw_m_linecards_init(struct mlxsw_m *mlxsw_m) |
362 | { |
363 | unsigned int max_ports = mlxsw_core_max_ports(mlxsw_core: mlxsw_m->core); |
364 | char mgpir_pl[MLXSW_REG_MGPIR_LEN]; |
365 | u8 num_of_modules; |
366 | int i, j, err; |
367 | |
368 | mlxsw_reg_mgpir_pack(payload: mgpir_pl, slot_index: 0); |
369 | err = mlxsw_reg_query(mlxsw_core: mlxsw_m->core, MLXSW_REG(mgpir), payload: mgpir_pl); |
370 | if (err) |
371 | return err; |
372 | |
373 | mlxsw_reg_mgpir_unpack(payload: mgpir_pl, NULL, NULL, NULL, num_of_modules: &num_of_modules, |
374 | num_of_slots: &mlxsw_m->num_of_slots); |
375 | /* If the system is modular, get the maximum number of modules per-slot. |
376 | * Otherwise, get the maximum number of modules on the main board. |
377 | */ |
378 | if (mlxsw_m->num_of_slots) |
379 | mlxsw_m->max_modules_per_slot = |
380 | mlxsw_reg_mgpir_max_modules_per_slot_get(buf: mgpir_pl); |
381 | else |
382 | mlxsw_m->max_modules_per_slot = num_of_modules; |
383 | /* Add slot for main board. */ |
384 | mlxsw_m->num_of_slots += 1; |
385 | |
386 | mlxsw_m->ports = kcalloc(n: max_ports, size: sizeof(*mlxsw_m->ports), |
387 | GFP_KERNEL); |
388 | if (!mlxsw_m->ports) |
389 | return -ENOMEM; |
390 | |
391 | mlxsw_m->line_cards = kcalloc(n: mlxsw_m->num_of_slots, |
392 | size: sizeof(*mlxsw_m->line_cards), |
393 | GFP_KERNEL); |
394 | if (!mlxsw_m->line_cards) { |
395 | err = -ENOMEM; |
396 | goto err_kcalloc; |
397 | } |
398 | |
399 | for (i = 0; i < mlxsw_m->num_of_slots; i++) { |
400 | mlxsw_m->line_cards[i] = |
401 | kzalloc(struct_size(mlxsw_m->line_cards[i], |
402 | module_to_port, |
403 | mlxsw_m->max_modules_per_slot), |
404 | GFP_KERNEL); |
405 | if (!mlxsw_m->line_cards[i]) { |
406 | err = -ENOMEM; |
407 | goto err_kmalloc_array; |
408 | } |
409 | |
410 | /* Invalidate the entries of module to local port mapping array. */ |
411 | for (j = 0; j < mlxsw_m->max_modules_per_slot; j++) |
412 | mlxsw_m->line_cards[i]->module_to_port[j] = -1; |
413 | } |
414 | |
415 | return 0; |
416 | |
417 | err_kmalloc_array: |
418 | for (i--; i >= 0; i--) |
419 | kfree(objp: mlxsw_m->line_cards[i]); |
420 | kfree(objp: mlxsw_m->line_cards); |
421 | err_kcalloc: |
422 | kfree(objp: mlxsw_m->ports); |
423 | return err; |
424 | } |
425 | |
426 | static void mlxsw_m_linecards_fini(struct mlxsw_m *mlxsw_m) |
427 | { |
428 | int i = mlxsw_m->num_of_slots; |
429 | |
430 | for (i--; i >= 0; i--) |
431 | kfree(objp: mlxsw_m->line_cards[i]); |
432 | kfree(objp: mlxsw_m->line_cards); |
433 | kfree(objp: mlxsw_m->ports); |
434 | } |
435 | |
436 | static void |
437 | mlxsw_m_linecard_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 slot_index) |
438 | { |
439 | int i; |
440 | |
441 | for (i = mlxsw_m->max_modules_per_slot - 1; i >= 0; i--) { |
442 | int *module_to_port; |
443 | |
444 | module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, module: i); |
445 | if (*module_to_port > 0) |
446 | mlxsw_m_port_module_unmap(mlxsw_m, slot_index, module: i); |
447 | } |
448 | } |
449 | |
450 | static int |
451 | mlxsw_m_linecard_ports_create(struct mlxsw_m *mlxsw_m, u8 slot_index) |
452 | { |
453 | int *module_to_port; |
454 | int i, err; |
455 | |
456 | for (i = 0; i < mlxsw_m->max_modules_per_slot; i++) { |
457 | module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, module: i); |
458 | if (*module_to_port > 0) { |
459 | err = mlxsw_m_port_create(mlxsw_m, local_port: *module_to_port, |
460 | slot_index, module: i); |
461 | if (err) |
462 | goto err_port_create; |
463 | /* Mark slot as active */ |
464 | if (!mlxsw_m->line_cards[slot_index]->active) |
465 | mlxsw_m->line_cards[slot_index]->active = true; |
466 | } |
467 | } |
468 | return 0; |
469 | |
470 | err_port_create: |
471 | for (i--; i >= 0; i--) { |
472 | module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, module: i); |
473 | if (*module_to_port > 0 && |
474 | mlxsw_m_port_created(mlxsw_m, local_port: *module_to_port)) { |
475 | mlxsw_m_port_remove(mlxsw_m, local_port: *module_to_port); |
476 | /* Mark slot as inactive */ |
477 | if (mlxsw_m->line_cards[slot_index]->active) |
478 | mlxsw_m->line_cards[slot_index]->active = false; |
479 | } |
480 | } |
481 | return err; |
482 | } |
483 | |
484 | static void |
485 | mlxsw_m_linecard_ports_remove(struct mlxsw_m *mlxsw_m, u8 slot_index) |
486 | { |
487 | int i; |
488 | |
489 | for (i = 0; i < mlxsw_m->max_modules_per_slot; i++) { |
490 | int *module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, |
491 | slot_index, module: i); |
492 | |
493 | if (*module_to_port > 0 && |
494 | mlxsw_m_port_created(mlxsw_m, local_port: *module_to_port)) { |
495 | mlxsw_m_port_remove(mlxsw_m, local_port: *module_to_port); |
496 | mlxsw_m_port_module_unmap(mlxsw_m, slot_index, module: i); |
497 | } |
498 | } |
499 | } |
500 | |
501 | static int mlxsw_m_ports_module_map(struct mlxsw_m *mlxsw_m) |
502 | { |
503 | unsigned int max_ports = mlxsw_core_max_ports(mlxsw_core: mlxsw_m->core); |
504 | u8 last_module = max_ports; |
505 | int i, err; |
506 | |
507 | for (i = 1; i < max_ports; i++) { |
508 | err = mlxsw_m_port_module_map(mlxsw_m, local_port: i, last_module: &last_module); |
509 | if (err) |
510 | return err; |
511 | } |
512 | |
513 | return 0; |
514 | } |
515 | |
516 | static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) |
517 | { |
518 | int err; |
519 | |
520 | /* Fill out module to local port mapping array */ |
521 | err = mlxsw_m_ports_module_map(mlxsw_m); |
522 | if (err) |
523 | goto err_ports_module_map; |
524 | |
525 | /* Create port objects for each valid entry */ |
526 | err = mlxsw_m_linecard_ports_create(mlxsw_m, slot_index: 0); |
527 | if (err) |
528 | goto err_linecard_ports_create; |
529 | |
530 | return 0; |
531 | |
532 | err_linecard_ports_create: |
533 | err_ports_module_map: |
534 | mlxsw_m_linecard_port_module_unmap(mlxsw_m, slot_index: 0); |
535 | |
536 | return err; |
537 | } |
538 | |
539 | static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) |
540 | { |
541 | mlxsw_m_linecard_ports_remove(mlxsw_m, slot_index: 0); |
542 | } |
543 | |
544 | static void |
545 | mlxsw_m_ports_remove_selected(struct mlxsw_core *mlxsw_core, |
546 | bool (*selector)(void *priv, u16 local_port), |
547 | void *priv) |
548 | { |
549 | struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); |
550 | struct mlxsw_linecard *linecard_priv = priv; |
551 | struct mlxsw_m_line_card *linecard; |
552 | |
553 | linecard = mlxsw_m->line_cards[linecard_priv->slot_index]; |
554 | |
555 | if (WARN_ON(!linecard->active)) |
556 | return; |
557 | |
558 | mlxsw_m_linecard_ports_remove(mlxsw_m, slot_index: linecard_priv->slot_index); |
559 | linecard->active = false; |
560 | } |
561 | |
562 | static int mlxsw_m_fw_rev_validate(struct mlxsw_m *mlxsw_m) |
563 | { |
564 | const struct mlxsw_fw_rev *rev = &mlxsw_m->bus_info->fw_rev; |
565 | |
566 | /* Validate driver and FW are compatible. |
567 | * Do not check major version, since it defines chip type, while |
568 | * driver is supposed to support any type. |
569 | */ |
570 | if (mlxsw_core_fw_rev_minor_subminor_validate(rev, req_rev: &mlxsw_m_fw_rev)) |
571 | return 0; |
572 | |
573 | dev_err(mlxsw_m->bus_info->dev, "The firmware version %d.%d.%d is incompatible with the driver (required >= %d.%d.%d)\n" , |
574 | rev->major, rev->minor, rev->subminor, rev->major, |
575 | mlxsw_m_fw_rev.minor, mlxsw_m_fw_rev.subminor); |
576 | |
577 | return -EINVAL; |
578 | } |
579 | |
580 | static void |
581 | mlxsw_m_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv) |
582 | { |
583 | struct mlxsw_m_line_card *linecard; |
584 | struct mlxsw_m *mlxsw_m = priv; |
585 | int err; |
586 | |
587 | linecard = mlxsw_m->line_cards[slot_index]; |
588 | /* Skip if line card has been already configured during init */ |
589 | if (linecard->active) |
590 | return; |
591 | |
592 | /* Fill out module to local port mapping array */ |
593 | err = mlxsw_m_ports_module_map(mlxsw_m); |
594 | if (err) |
595 | goto err_ports_module_map; |
596 | |
597 | /* Create port objects for each valid entry */ |
598 | err = mlxsw_m_linecard_ports_create(mlxsw_m, slot_index); |
599 | if (err) { |
600 | dev_err(mlxsw_m->bus_info->dev, "Failed to create port for line card at slot %d\n" , |
601 | slot_index); |
602 | goto err_linecard_ports_create; |
603 | } |
604 | |
605 | linecard->active = true; |
606 | |
607 | return; |
608 | |
609 | err_linecard_ports_create: |
610 | err_ports_module_map: |
611 | mlxsw_m_linecard_port_module_unmap(mlxsw_m, slot_index); |
612 | } |
613 | |
614 | static void |
615 | mlxsw_m_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv) |
616 | { |
617 | struct mlxsw_m_line_card *linecard; |
618 | struct mlxsw_m *mlxsw_m = priv; |
619 | |
620 | linecard = mlxsw_m->line_cards[slot_index]; |
621 | |
622 | if (WARN_ON(!linecard->active)) |
623 | return; |
624 | |
625 | mlxsw_m_linecard_ports_remove(mlxsw_m, slot_index); |
626 | linecard->active = false; |
627 | } |
628 | |
629 | static struct mlxsw_linecards_event_ops mlxsw_m_event_ops = { |
630 | .got_active = mlxsw_m_got_active, |
631 | .got_inactive = mlxsw_m_got_inactive, |
632 | }; |
633 | |
634 | static int mlxsw_m_init(struct mlxsw_core *mlxsw_core, |
635 | const struct mlxsw_bus_info *mlxsw_bus_info, |
636 | struct netlink_ext_ack *extack) |
637 | { |
638 | struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); |
639 | int err; |
640 | |
641 | mlxsw_m->core = mlxsw_core; |
642 | mlxsw_m->bus_info = mlxsw_bus_info; |
643 | |
644 | err = mlxsw_m_fw_rev_validate(mlxsw_m); |
645 | if (err) |
646 | return err; |
647 | |
648 | err = mlxsw_m_base_mac_get(mlxsw_m); |
649 | if (err) { |
650 | dev_err(mlxsw_m->bus_info->dev, "Failed to get base mac\n" ); |
651 | return err; |
652 | } |
653 | |
654 | err = mlxsw_m_linecards_init(mlxsw_m); |
655 | if (err) { |
656 | dev_err(mlxsw_m->bus_info->dev, "Failed to create line cards\n" ); |
657 | return err; |
658 | } |
659 | |
660 | err = mlxsw_linecards_event_ops_register(mlxsw_core, |
661 | ops: &mlxsw_m_event_ops, priv: mlxsw_m); |
662 | if (err) { |
663 | dev_err(mlxsw_m->bus_info->dev, "Failed to register line cards operations\n" ); |
664 | goto linecards_event_ops_register; |
665 | } |
666 | |
667 | err = mlxsw_m_ports_create(mlxsw_m); |
668 | if (err) { |
669 | dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n" ); |
670 | goto err_ports_create; |
671 | } |
672 | |
673 | return 0; |
674 | |
675 | err_ports_create: |
676 | mlxsw_linecards_event_ops_unregister(mlxsw_core, |
677 | ops: &mlxsw_m_event_ops, priv: mlxsw_m); |
678 | linecards_event_ops_register: |
679 | mlxsw_m_linecards_fini(mlxsw_m); |
680 | return err; |
681 | } |
682 | |
683 | static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core) |
684 | { |
685 | struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); |
686 | |
687 | mlxsw_m_ports_remove(mlxsw_m); |
688 | mlxsw_linecards_event_ops_unregister(mlxsw_core, |
689 | ops: &mlxsw_m_event_ops, priv: mlxsw_m); |
690 | mlxsw_m_linecards_fini(mlxsw_m); |
691 | } |
692 | |
693 | static const struct mlxsw_config_profile mlxsw_m_config_profile; |
694 | |
695 | static struct mlxsw_driver mlxsw_m_driver = { |
696 | .kind = mlxsw_m_driver_name, |
697 | .priv_size = sizeof(struct mlxsw_m), |
698 | .init = mlxsw_m_init, |
699 | .fini = mlxsw_m_fini, |
700 | .ports_remove_selected = mlxsw_m_ports_remove_selected, |
701 | .profile = &mlxsw_m_config_profile, |
702 | }; |
703 | |
704 | static const struct i2c_device_id mlxsw_m_i2c_id[] = { |
705 | { "mlxsw_minimal" , 0}, |
706 | { }, |
707 | }; |
708 | |
709 | static struct i2c_driver mlxsw_m_i2c_driver = { |
710 | .driver.name = "mlxsw_minimal" , |
711 | .class = I2C_CLASS_HWMON, |
712 | .id_table = mlxsw_m_i2c_id, |
713 | }; |
714 | |
715 | static int __init mlxsw_m_module_init(void) |
716 | { |
717 | int err; |
718 | |
719 | err = mlxsw_core_driver_register(mlxsw_driver: &mlxsw_m_driver); |
720 | if (err) |
721 | return err; |
722 | |
723 | err = mlxsw_i2c_driver_register(i2c_driver: &mlxsw_m_i2c_driver); |
724 | if (err) |
725 | goto err_i2c_driver_register; |
726 | |
727 | return 0; |
728 | |
729 | err_i2c_driver_register: |
730 | mlxsw_core_driver_unregister(mlxsw_driver: &mlxsw_m_driver); |
731 | |
732 | return err; |
733 | } |
734 | |
735 | static void __exit mlxsw_m_module_exit(void) |
736 | { |
737 | mlxsw_i2c_driver_unregister(i2c_driver: &mlxsw_m_i2c_driver); |
738 | mlxsw_core_driver_unregister(mlxsw_driver: &mlxsw_m_driver); |
739 | } |
740 | |
741 | module_init(mlxsw_m_module_init); |
742 | module_exit(mlxsw_m_module_exit); |
743 | |
744 | MODULE_LICENSE("Dual BSD/GPL" ); |
745 | MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>" ); |
746 | MODULE_DESCRIPTION("Mellanox minimal driver" ); |
747 | MODULE_DEVICE_TABLE(i2c, mlxsw_m_i2c_id); |
748 | |