xref: /linux/drivers/net/ethernet/mellanox/mlxsw/minimal.c (revision 1f2367a39f17bd553a75e179a747f9b257bc9478)
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 struct mlxsw_m_port;
20 
21 struct mlxsw_m {
22 	struct mlxsw_m_port **ports;
23 	int *module_to_port;
24 	struct mlxsw_core *core;
25 	const struct mlxsw_bus_info *bus_info;
26 	u8 base_mac[ETH_ALEN];
27 	u8 max_ports;
28 };
29 
30 struct mlxsw_m_port {
31 	struct net_device *dev;
32 	struct mlxsw_m *mlxsw_m;
33 	u8 local_port;
34 	u8 module;
35 };
36 
37 static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m)
38 {
39 	char spad_pl[MLXSW_REG_SPAD_LEN] = {0};
40 	int err;
41 
42 	err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(spad), spad_pl);
43 	if (err)
44 		return err;
45 	mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_m->base_mac);
46 	return 0;
47 }
48 
49 static int mlxsw_m_port_dummy_open_stop(struct net_device *dev)
50 {
51 	return 0;
52 }
53 
54 static int
55 mlxsw_m_port_get_phys_port_name(struct net_device *dev, char *name, size_t len)
56 {
57 	struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
58 	struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
59 	u8 local_port = mlxsw_m_port->local_port;
60 
61 	return mlxsw_core_port_get_phys_port_name(core, local_port, name, len);
62 }
63 
64 static int mlxsw_m_port_get_port_parent_id(struct net_device *dev,
65 					   struct netdev_phys_item_id *ppid)
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 	ppid->id_len = sizeof(mlxsw_m->base_mac);
71 	memcpy(&ppid->id, &mlxsw_m->base_mac, ppid->id_len);
72 
73 	return 0;
74 }
75 
76 static const struct net_device_ops mlxsw_m_port_netdev_ops = {
77 	.ndo_open		= mlxsw_m_port_dummy_open_stop,
78 	.ndo_stop		= mlxsw_m_port_dummy_open_stop,
79 	.ndo_get_phys_port_name	= mlxsw_m_port_get_phys_port_name,
80 	.ndo_get_port_parent_id	= mlxsw_m_port_get_port_parent_id,
81 };
82 
83 static int mlxsw_m_get_module_info(struct net_device *netdev,
84 				   struct ethtool_modinfo *modinfo)
85 {
86 	struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
87 	struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
88 
89 	return mlxsw_env_get_module_info(core, mlxsw_m_port->module, modinfo);
90 }
91 
92 static int
93 mlxsw_m_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee,
94 			  u8 *data)
95 {
96 	struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
97 	struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
98 
99 	return mlxsw_env_get_module_eeprom(netdev, core, mlxsw_m_port->module,
100 					   ee, data);
101 }
102 
103 static const struct ethtool_ops mlxsw_m_port_ethtool_ops = {
104 	.get_module_info	= mlxsw_m_get_module_info,
105 	.get_module_eeprom	= mlxsw_m_get_module_eeprom,
106 };
107 
108 static int
109 mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u8 local_port,
110 			     u8 *p_module, u8 *p_width)
111 {
112 	char pmlp_pl[MLXSW_REG_PMLP_LEN];
113 	int err;
114 
115 	mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
116 	err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(pmlp), pmlp_pl);
117 	if (err)
118 		return err;
119 	*p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
120 	*p_width = mlxsw_reg_pmlp_width_get(pmlp_pl);
121 
122 	return 0;
123 }
124 
125 static int
126 mlxsw_m_port_dev_addr_get(struct mlxsw_m_port *mlxsw_m_port)
127 {
128 	struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
129 	struct net_device *dev = mlxsw_m_port->dev;
130 	char ppad_pl[MLXSW_REG_PPAD_LEN];
131 	int err;
132 
133 	mlxsw_reg_ppad_pack(ppad_pl, false, 0);
134 	err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(ppad), ppad_pl);
135 	if (err)
136 		return err;
137 	mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, dev->dev_addr);
138 	/* The last byte value in base mac address is guaranteed
139 	 * to be such it does not overflow when adding local_port
140 	 * value.
141 	 */
142 	dev->dev_addr[ETH_ALEN - 1] += mlxsw_m_port->module + 1;
143 	return 0;
144 }
145 
146 static int
147 mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u8 local_port, u8 module)
148 {
149 	struct mlxsw_m_port *mlxsw_m_port;
150 	struct net_device *dev;
151 	int err;
152 
153 	err = mlxsw_core_port_init(mlxsw_m->core, local_port);
154 	if (err) {
155 		dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to init core port\n",
156 			local_port);
157 		return err;
158 	}
159 
160 	dev = alloc_etherdev(sizeof(struct mlxsw_m_port));
161 	if (!dev) {
162 		err = -ENOMEM;
163 		goto err_alloc_etherdev;
164 	}
165 
166 	SET_NETDEV_DEV(dev, mlxsw_m->bus_info->dev);
167 	mlxsw_m_port = netdev_priv(dev);
168 	mlxsw_m_port->dev = dev;
169 	mlxsw_m_port->mlxsw_m = mlxsw_m;
170 	mlxsw_m_port->local_port = local_port;
171 	mlxsw_m_port->module = module;
172 
173 	dev->netdev_ops = &mlxsw_m_port_netdev_ops;
174 	dev->ethtool_ops = &mlxsw_m_port_ethtool_ops;
175 
176 	err = mlxsw_m_port_dev_addr_get(mlxsw_m_port);
177 	if (err) {
178 		dev_err(mlxsw_m->bus_info->dev, "Port %d: Unable to get port mac address\n",
179 			mlxsw_m_port->local_port);
180 		goto err_dev_addr_get;
181 	}
182 
183 	netif_carrier_off(dev);
184 	mlxsw_m->ports[local_port] = mlxsw_m_port;
185 	err = register_netdev(dev);
186 	if (err) {
187 		dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to register netdev\n",
188 			mlxsw_m_port->local_port);
189 		goto err_register_netdev;
190 	}
191 
192 	mlxsw_core_port_eth_set(mlxsw_m->core, mlxsw_m_port->local_port,
193 				mlxsw_m_port, dev, module + 1, false, 0);
194 
195 	return 0;
196 
197 err_register_netdev:
198 	mlxsw_m->ports[local_port] = NULL;
199 	free_netdev(dev);
200 err_dev_addr_get:
201 err_alloc_etherdev:
202 	mlxsw_core_port_fini(mlxsw_m->core, local_port);
203 	return err;
204 }
205 
206 static void mlxsw_m_port_remove(struct mlxsw_m *mlxsw_m, u8 local_port)
207 {
208 	struct mlxsw_m_port *mlxsw_m_port = mlxsw_m->ports[local_port];
209 
210 	mlxsw_core_port_clear(mlxsw_m->core, local_port, mlxsw_m);
211 	unregister_netdev(mlxsw_m_port->dev); /* This calls ndo_stop */
212 	mlxsw_m->ports[local_port] = NULL;
213 	free_netdev(mlxsw_m_port->dev);
214 	mlxsw_core_port_fini(mlxsw_m->core, local_port);
215 }
216 
217 static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port,
218 				   u8 *last_module)
219 {
220 	u8 module, width;
221 	int err;
222 
223 	/* Fill out to local port mapping array */
224 	err = mlxsw_m_port_module_info_get(mlxsw_m, local_port, &module,
225 					   &width);
226 	if (err)
227 		return err;
228 
229 	if (!width)
230 		return 0;
231 	/* Skip, if port belongs to the cluster */
232 	if (module == *last_module)
233 		return 0;
234 	*last_module = module;
235 	mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports;
236 
237 	return 0;
238 }
239 
240 static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module)
241 {
242 	mlxsw_m->module_to_port[module] = -1;
243 }
244 
245 static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m)
246 {
247 	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);
248 	u8 last_module = max_ports;
249 	int i;
250 	int err;
251 
252 	mlxsw_m->ports = kcalloc(max_ports, sizeof(*mlxsw_m->ports),
253 				 GFP_KERNEL);
254 	if (!mlxsw_m->ports)
255 		return -ENOMEM;
256 
257 	mlxsw_m->module_to_port = kmalloc_array(max_ports, sizeof(int),
258 						GFP_KERNEL);
259 	if (!mlxsw_m->module_to_port) {
260 		err = -ENOMEM;
261 		goto err_module_to_port_alloc;
262 	}
263 
264 	/* Invalidate the entries of module to local port mapping array */
265 	for (i = 0; i < max_ports; i++)
266 		mlxsw_m->module_to_port[i] = -1;
267 
268 	/* Fill out module to local port mapping array */
269 	for (i = 1; i < max_ports; i++) {
270 		err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module);
271 		if (err)
272 			goto err_module_to_port_map;
273 	}
274 
275 	/* Create port objects for each valid entry */
276 	for (i = 0; i < mlxsw_m->max_ports; i++) {
277 		if (mlxsw_m->module_to_port[i] > 0) {
278 			err = mlxsw_m_port_create(mlxsw_m,
279 						  mlxsw_m->module_to_port[i],
280 						  i);
281 			if (err)
282 				goto err_module_to_port_create;
283 		}
284 	}
285 
286 	return 0;
287 
288 err_module_to_port_create:
289 	for (i--; i >= 0; i--) {
290 		if (mlxsw_m->module_to_port[i] > 0)
291 			mlxsw_m_port_remove(mlxsw_m,
292 					    mlxsw_m->module_to_port[i]);
293 	}
294 	i = max_ports;
295 err_module_to_port_map:
296 	for (i--; i > 0; i--)
297 		mlxsw_m_port_module_unmap(mlxsw_m, i);
298 	kfree(mlxsw_m->module_to_port);
299 err_module_to_port_alloc:
300 	kfree(mlxsw_m->ports);
301 	return err;
302 }
303 
304 static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m)
305 {
306 	int i;
307 
308 	for (i = 0; i < mlxsw_m->max_ports; i++) {
309 		if (mlxsw_m->module_to_port[i] > 0) {
310 			mlxsw_m_port_remove(mlxsw_m,
311 					    mlxsw_m->module_to_port[i]);
312 			mlxsw_m_port_module_unmap(mlxsw_m, i);
313 		}
314 	}
315 
316 	kfree(mlxsw_m->module_to_port);
317 	kfree(mlxsw_m->ports);
318 }
319 
320 static int mlxsw_m_init(struct mlxsw_core *mlxsw_core,
321 			const struct mlxsw_bus_info *mlxsw_bus_info)
322 {
323 	struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);
324 	int err;
325 
326 	mlxsw_m->core = mlxsw_core;
327 	mlxsw_m->bus_info = mlxsw_bus_info;
328 
329 	err = mlxsw_m_base_mac_get(mlxsw_m);
330 	if (err) {
331 		dev_err(mlxsw_m->bus_info->dev, "Failed to get base mac\n");
332 		return err;
333 	}
334 
335 	err = mlxsw_m_ports_create(mlxsw_m);
336 	if (err) {
337 		dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n");
338 		return err;
339 	}
340 
341 	return 0;
342 }
343 
344 static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core)
345 {
346 	struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);
347 
348 	mlxsw_m_ports_remove(mlxsw_m);
349 }
350 
351 static const struct mlxsw_config_profile mlxsw_m_config_profile;
352 
353 static struct mlxsw_driver mlxsw_m_driver = {
354 	.kind			= mlxsw_m_driver_name,
355 	.priv_size		= sizeof(struct mlxsw_m),
356 	.init			= mlxsw_m_init,
357 	.fini			= mlxsw_m_fini,
358 	.profile		= &mlxsw_m_config_profile,
359 	.res_query_enabled	= true,
360 };
361 
362 static const struct i2c_device_id mlxsw_m_i2c_id[] = {
363 	{ "mlxsw_minimal", 0},
364 	{ },
365 };
366 
367 static struct i2c_driver mlxsw_m_i2c_driver = {
368 	.driver.name = "mlxsw_minimal",
369 	.class = I2C_CLASS_HWMON,
370 	.id_table = mlxsw_m_i2c_id,
371 };
372 
373 static int __init mlxsw_m_module_init(void)
374 {
375 	int err;
376 
377 	err = mlxsw_core_driver_register(&mlxsw_m_driver);
378 	if (err)
379 		return err;
380 
381 	err = mlxsw_i2c_driver_register(&mlxsw_m_i2c_driver);
382 	if (err)
383 		goto err_i2c_driver_register;
384 
385 	return 0;
386 
387 err_i2c_driver_register:
388 	mlxsw_core_driver_unregister(&mlxsw_m_driver);
389 
390 	return err;
391 }
392 
393 static void __exit mlxsw_m_module_exit(void)
394 {
395 	mlxsw_i2c_driver_unregister(&mlxsw_m_i2c_driver);
396 	mlxsw_core_driver_unregister(&mlxsw_m_driver);
397 }
398 
399 module_init(mlxsw_m_module_init);
400 module_exit(mlxsw_m_module_exit);
401 
402 MODULE_LICENSE("Dual BSD/GPL");
403 MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
404 MODULE_DESCRIPTION("Mellanox minimal driver");
405 MODULE_DEVICE_TABLE(i2c, mlxsw_m_i2c_id);
406