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