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