blob: 623805ba8e1acb312b93117d6dc80301c2f47160 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Andrew Lunn83c0afa2016-06-04 21:17:07 +02002/*
3 * net/dsa/dsa2.c - Hardware switch handling, binding version 2
4 * Copyright (c) 2008-2009 Marvell Semiconductor
5 * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
6 * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
Andrew Lunn83c0afa2016-06-04 21:17:07 +02007 */
8
9#include <linux/device.h>
10#include <linux/err.h>
11#include <linux/list.h>
Andrew Lunnc6e970a2017-03-28 23:45:06 +020012#include <linux/netdevice.h>
Andrew Lunn83c0afa2016-06-04 21:17:07 +020013#include <linux/slab.h>
14#include <linux/rtnetlink.h>
Andrew Lunn83c0afa2016-06-04 21:17:07 +020015#include <linux/of.h>
16#include <linux/of_net.h>
Jiri Pirko402f99e52019-03-24 11:14:26 +010017#include <net/devlink.h>
Vivien Didelotea5dd342017-05-17 15:46:03 -040018
Andrew Lunn83c0afa2016-06-04 21:17:07 +020019#include "dsa_priv.h"
20
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040021static LIST_HEAD(dsa_tree_list);
Andrew Lunn83c0afa2016-06-04 21:17:07 +020022static DEFINE_MUTEX(dsa2_mutex);
23
Andrew Lunn96567d52017-03-28 23:45:07 +020024static const struct devlink_ops dsa_devlink_ops = {
25};
26
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040027static struct dsa_switch_tree *dsa_tree_find(int index)
Andrew Lunn83c0afa2016-06-04 21:17:07 +020028{
29 struct dsa_switch_tree *dst;
30
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040031 list_for_each_entry(dst, &dsa_tree_list, list)
Vivien Didelot8e5bf972017-11-03 19:05:22 -040032 if (dst->index == index)
Andrew Lunn83c0afa2016-06-04 21:17:07 +020033 return dst;
Vivien Didelot8e5bf972017-11-03 19:05:22 -040034
Andrew Lunn83c0afa2016-06-04 21:17:07 +020035 return NULL;
36}
37
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040038static struct dsa_switch_tree *dsa_tree_alloc(int index)
Andrew Lunn83c0afa2016-06-04 21:17:07 +020039{
40 struct dsa_switch_tree *dst;
41
42 dst = kzalloc(sizeof(*dst), GFP_KERNEL);
43 if (!dst)
44 return NULL;
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040045
Vivien Didelot49463b72017-11-03 19:05:21 -040046 dst->index = index;
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040047
Vivien Didelotab8ccae2019-10-21 16:51:16 -040048 INIT_LIST_HEAD(&dst->ports);
49
Andrew Lunn83c0afa2016-06-04 21:17:07 +020050 INIT_LIST_HEAD(&dst->list);
Vivien Didelot50c7d2ba2019-10-18 17:02:46 -040051 list_add_tail(&dst->list, &dsa_tree_list);
Vivien Didelot8e5bf972017-11-03 19:05:22 -040052
Andrew Lunn83c0afa2016-06-04 21:17:07 +020053 kref_init(&dst->refcount);
54
55 return dst;
56}
57
Vivien Didelot65254102017-11-03 19:05:23 -040058static void dsa_tree_free(struct dsa_switch_tree *dst)
59{
60 list_del(&dst->list);
61 kfree(dst);
62}
63
Vivien Didelot9e741042017-11-24 11:36:06 -050064static struct dsa_switch_tree *dsa_tree_get(struct dsa_switch_tree *dst)
65{
66 if (dst)
67 kref_get(&dst->refcount);
68
69 return dst;
70}
71
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040072static struct dsa_switch_tree *dsa_tree_touch(int index)
73{
74 struct dsa_switch_tree *dst;
75
76 dst = dsa_tree_find(index);
Vivien Didelot9e741042017-11-24 11:36:06 -050077 if (dst)
78 return dsa_tree_get(dst);
79 else
80 return dsa_tree_alloc(index);
Vivien Didelot65254102017-11-03 19:05:23 -040081}
82
83static void dsa_tree_release(struct kref *ref)
84{
85 struct dsa_switch_tree *dst;
86
87 dst = container_of(ref, struct dsa_switch_tree, refcount);
88
89 dsa_tree_free(dst);
90}
91
92static void dsa_tree_put(struct dsa_switch_tree *dst)
93{
Vivien Didelot9e741042017-11-24 11:36:06 -050094 if (dst)
95 kref_put(&dst->refcount, dsa_tree_release);
Vivien Didelot65254102017-11-03 19:05:23 -040096}
97
Florian Fainelli293784a2017-01-26 10:45:52 -080098static bool dsa_port_is_dsa(struct dsa_port *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +020099{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400100 return port->type == DSA_PORT_TYPE_DSA;
Florian Fainelli293784a2017-01-26 10:45:52 -0800101}
102
103static bool dsa_port_is_cpu(struct dsa_port *port)
104{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400105 return port->type == DSA_PORT_TYPE_CPU;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200106}
107
Vivien Didelotf0704642017-11-06 16:11:44 -0500108static bool dsa_port_is_user(struct dsa_port *dp)
109{
110 return dp->type == DSA_PORT_TYPE_USER;
111}
112
Vivien Didelotf163da82017-11-06 16:11:49 -0500113static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst,
114 struct device_node *dn)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200115{
116 struct dsa_switch *ds;
Vivien Didelotf163da82017-11-06 16:11:49 -0500117 struct dsa_port *dp;
118 int device, port;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200119
Vivien Didelotf163da82017-11-06 16:11:49 -0500120 for (device = 0; device < DSA_MAX_SWITCHES; device++) {
121 ds = dst->ds[device];
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200122 if (!ds)
123 continue;
124
Vivien Didelotf163da82017-11-06 16:11:49 -0500125 for (port = 0; port < ds->num_ports; port++) {
126 dp = &ds->ports[port];
127
128 if (dp->dn == dn)
129 return dp;
130 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200131 }
132
133 return NULL;
134}
135
Vivien Didelot34c09a82017-11-06 16:11:51 -0500136static bool dsa_port_setup_routing_table(struct dsa_port *dp)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200137{
Vivien Didelot34c09a82017-11-06 16:11:51 -0500138 struct dsa_switch *ds = dp->ds;
139 struct dsa_switch_tree *dst = ds->dst;
140 struct device_node *dn = dp->dn;
Vivien Didelotc5286662017-11-06 16:11:50 -0500141 struct of_phandle_iterator it;
Vivien Didelotf163da82017-11-06 16:11:49 -0500142 struct dsa_port *link_dp;
Vivien Didelotc5286662017-11-06 16:11:50 -0500143 int err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200144
Vivien Didelotc5286662017-11-06 16:11:50 -0500145 of_for_each_phandle(&it, err, dn, "link", NULL, 0) {
146 link_dp = dsa_tree_find_port_by_node(dst, it.node);
147 if (!link_dp) {
148 of_node_put(it.node);
Vivien Didelot34c09a82017-11-06 16:11:51 -0500149 return false;
Vivien Didelotc5286662017-11-06 16:11:50 -0500150 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200151
Vivien Didelot34c09a82017-11-06 16:11:51 -0500152 ds->rtable[link_dp->ds->index] = dp->index;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200153 }
154
Vivien Didelot34c09a82017-11-06 16:11:51 -0500155 return true;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200156}
157
Vivien Didelot34c09a82017-11-06 16:11:51 -0500158static bool dsa_switch_setup_routing_table(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200159{
Vivien Didelot86bfb2c2019-10-21 16:51:20 -0400160 struct dsa_switch_tree *dst = ds->dst;
Vivien Didelot34c09a82017-11-06 16:11:51 -0500161 bool complete = true;
162 struct dsa_port *dp;
163 int i;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200164
Vivien Didelot34c09a82017-11-06 16:11:51 -0500165 for (i = 0; i < DSA_MAX_SWITCHES; i++)
166 ds->rtable[i] = DSA_RTABLE_NONE;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200167
Vivien Didelot86bfb2c2019-10-21 16:51:20 -0400168 list_for_each_entry(dp, &dst->ports, list) {
169 if (dp->ds == ds && dsa_port_is_dsa(dp)) {
Vivien Didelot34c09a82017-11-06 16:11:51 -0500170 complete = dsa_port_setup_routing_table(dp);
171 if (!complete)
172 break;
173 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200174 }
175
Vivien Didelot34c09a82017-11-06 16:11:51 -0500176 return complete;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200177}
178
Vivien Didelot34c09a82017-11-06 16:11:51 -0500179static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200180{
181 struct dsa_switch *ds;
Vivien Didelot34c09a82017-11-06 16:11:51 -0500182 bool complete = true;
183 int device;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200184
Vivien Didelot34c09a82017-11-06 16:11:51 -0500185 for (device = 0; device < DSA_MAX_SWITCHES; device++) {
186 ds = dst->ds[device];
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200187 if (!ds)
188 continue;
189
Vivien Didelot34c09a82017-11-06 16:11:51 -0500190 complete = dsa_switch_setup_routing_table(ds);
191 if (!complete)
192 break;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200193 }
194
Vivien Didelot34c09a82017-11-06 16:11:51 -0500195 return complete;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200196}
197
Vivien Didelotf0704642017-11-06 16:11:44 -0500198static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst)
199{
200 struct dsa_switch *ds;
201 struct dsa_port *dp;
202 int device, port;
203
204 for (device = 0; device < DSA_MAX_SWITCHES; device++) {
205 ds = dst->ds[device];
206 if (!ds)
207 continue;
208
209 for (port = 0; port < ds->num_ports; port++) {
210 dp = &ds->ports[port];
211
212 if (dsa_port_is_cpu(dp))
213 return dp;
214 }
215 }
216
217 return NULL;
218}
219
220static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst)
221{
222 struct dsa_switch *ds;
223 struct dsa_port *dp;
224 int device, port;
225
226 /* DSA currently only supports a single CPU port */
227 dst->cpu_dp = dsa_tree_find_first_cpu(dst);
228 if (!dst->cpu_dp) {
229 pr_warn("Tree has no master device\n");
230 return -EINVAL;
231 }
232
233 /* Assign the default CPU port to all ports of the fabric */
234 for (device = 0; device < DSA_MAX_SWITCHES; device++) {
235 ds = dst->ds[device];
236 if (!ds)
237 continue;
238
239 for (port = 0; port < ds->num_ports; port++) {
240 dp = &ds->ports[port];
241
Vivien Didelot986d7cc2017-12-05 15:34:12 -0500242 if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp))
Vivien Didelotf0704642017-11-06 16:11:44 -0500243 dp->cpu_dp = dst->cpu_dp;
244 }
245 }
246
247 return 0;
248}
249
250static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst)
251{
252 /* DSA currently only supports a single CPU port */
253 dst->cpu_dp = NULL;
254}
255
Vivien Didelot1d277322017-11-06 16:11:48 -0500256static int dsa_port_setup(struct dsa_port *dp)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200257{
Vivien Didelot1d277322017-11-06 16:11:48 -0500258 struct dsa_switch *ds = dp->ds;
Jiri Pirko15b04ac2019-04-03 14:24:26 +0200259 struct dsa_switch_tree *dst = ds->dst;
Vivien Didelot955222c2019-08-19 16:00:48 -0400260 const unsigned char *id = (const unsigned char *)&dst->index;
261 const unsigned char len = sizeof(dst->index);
262 struct devlink_port *dlp = &dp->devlink_port;
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300263 bool dsa_port_link_registered = false;
264 bool devlink_port_registered = false;
Vivien Didelot955222c2019-08-19 16:00:48 -0400265 struct devlink *dl = ds->devlink;
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300266 bool dsa_port_enabled = false;
267 int err = 0;
Andrew Lunn96567d52017-03-28 23:45:07 +0200268
Vivien Didelotfb35c602019-10-21 16:51:19 -0400269 if (dp->setup)
270 return 0;
271
Vivien Didelot1d277322017-11-06 16:11:48 -0500272 switch (dp->type) {
273 case DSA_PORT_TYPE_UNUSED:
Vivien Didelot0394a632019-08-19 16:00:50 -0400274 dsa_port_disable(dp);
Vivien Didelot1d277322017-11-06 16:11:48 -0500275 break;
276 case DSA_PORT_TYPE_CPU:
Vivien Didelot955222c2019-08-19 16:00:48 -0400277 memset(dlp, 0, sizeof(*dlp));
278 devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_CPU,
279 dp->index, false, 0, id, len);
280 err = devlink_port_register(dl, dlp, dp->index);
281 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300282 break;
283 devlink_port_registered = true;
Vivien Didelot955222c2019-08-19 16:00:48 -0400284
Jiri Pirkoda077392018-05-18 09:29:03 +0200285 err = dsa_port_link_register_of(dp);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300286 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300287 break;
288 dsa_port_link_registered = true;
Vivien Didelot0394a632019-08-19 16:00:50 -0400289
290 err = dsa_port_enable(dp, NULL);
291 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300292 break;
293 dsa_port_enabled = true;
294
Jiri Pirkoda077392018-05-18 09:29:03 +0200295 break;
Vivien Didelot1d277322017-11-06 16:11:48 -0500296 case DSA_PORT_TYPE_DSA:
Vivien Didelot955222c2019-08-19 16:00:48 -0400297 memset(dlp, 0, sizeof(*dlp));
298 devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_DSA,
299 dp->index, false, 0, id, len);
300 err = devlink_port_register(dl, dlp, dp->index);
301 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300302 break;
303 devlink_port_registered = true;
Vivien Didelot955222c2019-08-19 16:00:48 -0400304
Sebastian Reichel33615362018-01-23 16:03:46 +0100305 err = dsa_port_link_register_of(dp);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300306 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300307 break;
308 dsa_port_link_registered = true;
Vivien Didelot0394a632019-08-19 16:00:50 -0400309
310 err = dsa_port_enable(dp, NULL);
311 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300312 break;
313 dsa_port_enabled = true;
314
Vivien Didelot1d277322017-11-06 16:11:48 -0500315 break;
316 case DSA_PORT_TYPE_USER:
Vivien Didelot955222c2019-08-19 16:00:48 -0400317 memset(dlp, 0, sizeof(*dlp));
318 devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_PHYSICAL,
319 dp->index, false, 0, id, len);
320 err = devlink_port_register(dl, dlp, dp->index);
321 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300322 break;
323 devlink_port_registered = true;
Vivien Didelot955222c2019-08-19 16:00:48 -0400324
325 dp->mac = of_get_mac_address(dp->dn);
Vivien Didelot1d277322017-11-06 16:11:48 -0500326 err = dsa_slave_create(dp);
327 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300328 break;
Vivien Didelot955222c2019-08-19 16:00:48 -0400329
330 devlink_port_type_eth_set(dlp, dp->slave);
Vivien Didelot1d277322017-11-06 16:11:48 -0500331 break;
332 }
Andrew Lunn96567d52017-03-28 23:45:07 +0200333
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300334 if (err && dsa_port_enabled)
335 dsa_port_disable(dp);
336 if (err && dsa_port_link_registered)
337 dsa_port_link_unregister_of(dp);
338 if (err && devlink_port_registered)
339 devlink_port_unregister(dlp);
Vivien Didelotfb35c602019-10-21 16:51:19 -0400340 if (err)
341 return err;
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300342
Vivien Didelotfb35c602019-10-21 16:51:19 -0400343 dp->setup = true;
344
345 return 0;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200346}
347
Vivien Didelot1d277322017-11-06 16:11:48 -0500348static void dsa_port_teardown(struct dsa_port *dp)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200349{
Vivien Didelot955222c2019-08-19 16:00:48 -0400350 struct devlink_port *dlp = &dp->devlink_port;
Vivien Didelot1d277322017-11-06 16:11:48 -0500351
Vivien Didelotfb35c602019-10-21 16:51:19 -0400352 if (!dp->setup)
353 return;
354
Vivien Didelot1d277322017-11-06 16:11:48 -0500355 switch (dp->type) {
356 case DSA_PORT_TYPE_UNUSED:
357 break;
358 case DSA_PORT_TYPE_CPU:
Vivien Didelot0394a632019-08-19 16:00:50 -0400359 dsa_port_disable(dp);
Andrew Lunn4dad81e2019-04-28 19:37:19 +0200360 dsa_tag_driver_put(dp->tag_ops);
Vivien Didelot955222c2019-08-19 16:00:48 -0400361 devlink_port_unregister(dlp);
362 dsa_port_link_unregister_of(dp);
363 break;
Vivien Didelot1d277322017-11-06 16:11:48 -0500364 case DSA_PORT_TYPE_DSA:
Vivien Didelot0394a632019-08-19 16:00:50 -0400365 dsa_port_disable(dp);
Vivien Didelot955222c2019-08-19 16:00:48 -0400366 devlink_port_unregister(dlp);
Sebastian Reichel33615362018-01-23 16:03:46 +0100367 dsa_port_link_unregister_of(dp);
Vivien Didelot1d277322017-11-06 16:11:48 -0500368 break;
369 case DSA_PORT_TYPE_USER:
Vivien Didelot955222c2019-08-19 16:00:48 -0400370 devlink_port_unregister(dlp);
Vivien Didelot1d277322017-11-06 16:11:48 -0500371 if (dp->slave) {
372 dsa_slave_destroy(dp->slave);
373 dp->slave = NULL;
374 }
375 break;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200376 }
Vivien Didelotfb35c602019-10-21 16:51:19 -0400377
378 dp->setup = false;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200379}
380
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500381static int dsa_switch_setup(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200382{
Vivien Didelotfb35c602019-10-21 16:51:19 -0400383 int err;
384
385 if (ds->setup)
386 return 0;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200387
Florian Fainelli6e830d82016-06-07 16:32:39 -0700388 /* Initialize ds->phys_mii_mask before registering the slave MDIO bus
Vivien Didelot9d490b42016-08-23 12:38:56 -0400389 * driver and before ops->setup() has run, since the switch drivers and
Florian Fainelli6e830d82016-06-07 16:32:39 -0700390 * the slave MDIO bus driver rely on these values for probing PHY
391 * devices or not
392 */
Vivien Didelot02bc6e52017-10-26 11:22:56 -0400393 ds->phys_mii_mask |= dsa_user_ports(ds);
Florian Fainelli6e830d82016-06-07 16:32:39 -0700394
Andrew Lunn96567d52017-03-28 23:45:07 +0200395 /* Add the switch to devlink before calling setup, so that setup can
396 * add dpipe tables
397 */
398 ds->devlink = devlink_alloc(&dsa_devlink_ops, 0);
399 if (!ds->devlink)
400 return -ENOMEM;
401
402 err = devlink_register(ds->devlink, ds->dev);
403 if (err)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300404 goto free_devlink;
Andrew Lunn96567d52017-03-28 23:45:07 +0200405
Vivien Didelotf515f192017-02-03 13:20:20 -0500406 err = dsa_switch_register_notifier(ds);
407 if (err)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300408 goto unregister_devlink;
Vivien Didelotf515f192017-02-03 13:20:20 -0500409
Vladimir Olteanb2243b32019-05-05 13:19:20 +0300410 err = ds->ops->setup(ds);
411 if (err < 0)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300412 goto unregister_notifier;
Vladimir Olteanb2243b32019-05-05 13:19:20 +0300413
Vivien Didelot9d490b42016-08-23 12:38:56 -0400414 if (!ds->slave_mii_bus && ds->ops->phy_read) {
Florian Fainelli1eb59442016-06-07 16:32:40 -0700415 ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300416 if (!ds->slave_mii_bus) {
417 err = -ENOMEM;
418 goto unregister_notifier;
419 }
Florian Fainelli1eb59442016-06-07 16:32:40 -0700420
421 dsa_slave_mii_bus_init(ds);
422
423 err = mdiobus_register(ds->slave_mii_bus);
424 if (err < 0)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300425 goto unregister_notifier;
Florian Fainelli1eb59442016-06-07 16:32:40 -0700426 }
427
Vivien Didelotfb35c602019-10-21 16:51:19 -0400428 ds->setup = true;
429
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200430 return 0;
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300431
432unregister_notifier:
433 dsa_switch_unregister_notifier(ds);
434unregister_devlink:
435 devlink_unregister(ds->devlink);
436free_devlink:
437 devlink_free(ds->devlink);
438 ds->devlink = NULL;
439
440 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200441}
442
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500443static void dsa_switch_teardown(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200444{
Vivien Didelotfb35c602019-10-21 16:51:19 -0400445 if (!ds->setup)
446 return;
447
Vivien Didelot9d490b42016-08-23 12:38:56 -0400448 if (ds->slave_mii_bus && ds->ops->phy_read)
Florian Fainelli1eb59442016-06-07 16:32:40 -0700449 mdiobus_unregister(ds->slave_mii_bus);
Vivien Didelotf515f192017-02-03 13:20:20 -0500450
451 dsa_switch_unregister_notifier(ds);
Andrew Lunn96567d52017-03-28 23:45:07 +0200452
Vladimir Oltean5e3f8472019-06-08 15:04:28 +0300453 if (ds->ops->teardown)
454 ds->ops->teardown(ds);
455
Andrew Lunn96567d52017-03-28 23:45:07 +0200456 if (ds->devlink) {
457 devlink_unregister(ds->devlink);
458 devlink_free(ds->devlink);
459 ds->devlink = NULL;
460 }
461
Vivien Didelotfb35c602019-10-21 16:51:19 -0400462 ds->setup = false;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200463}
464
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500465static int dsa_tree_setup_switches(struct dsa_switch_tree *dst)
466{
Vivien Didelot1d277322017-11-06 16:11:48 -0500467 struct dsa_port *dp;
Vivien Didelotfb35c602019-10-21 16:51:19 -0400468 int err;
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500469
Vivien Didelotfb35c602019-10-21 16:51:19 -0400470 list_for_each_entry(dp, &dst->ports, list) {
471 err = dsa_switch_setup(dp->ds);
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500472 if (err)
Vivien Didelotfb35c602019-10-21 16:51:19 -0400473 goto teardown;
474 }
Vivien Didelot1d277322017-11-06 16:11:48 -0500475
Vivien Didelotfb35c602019-10-21 16:51:19 -0400476 list_for_each_entry(dp, &dst->ports, list) {
477 err = dsa_port_setup(dp);
478 if (err)
479 goto teardown;
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500480 }
481
482 return 0;
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300483
Vivien Didelotfb35c602019-10-21 16:51:19 -0400484teardown:
485 list_for_each_entry(dp, &dst->ports, list)
486 dsa_port_teardown(dp);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300487
Vivien Didelotfb35c602019-10-21 16:51:19 -0400488 list_for_each_entry(dp, &dst->ports, list)
489 dsa_switch_teardown(dp->ds);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300490
491 return err;
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500492}
493
494static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst)
495{
Vivien Didelot1d277322017-11-06 16:11:48 -0500496 struct dsa_port *dp;
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500497
Vivien Didelotfb35c602019-10-21 16:51:19 -0400498 list_for_each_entry(dp, &dst->ports, list)
499 dsa_port_teardown(dp);
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500500
Vivien Didelotfb35c602019-10-21 16:51:19 -0400501 list_for_each_entry(dp, &dst->ports, list)
502 dsa_switch_teardown(dp->ds);
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500503}
504
Vivien Didelot17a22fc2017-11-06 16:11:45 -0500505static int dsa_tree_setup_master(struct dsa_switch_tree *dst)
506{
507 struct dsa_port *cpu_dp = dst->cpu_dp;
508 struct net_device *master = cpu_dp->master;
509
510 /* DSA currently supports a single pair of CPU port and master device */
511 return dsa_master_setup(master, cpu_dp);
512}
513
514static void dsa_tree_teardown_master(struct dsa_switch_tree *dst)
515{
516 struct dsa_port *cpu_dp = dst->cpu_dp;
517 struct net_device *master = cpu_dp->master;
518
519 return dsa_master_teardown(master);
520}
521
Vivien Didelotec15dd42017-11-06 16:11:46 -0500522static int dsa_tree_setup(struct dsa_switch_tree *dst)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200523{
Vivien Didelot34c09a82017-11-06 16:11:51 -0500524 bool complete;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200525 int err;
526
Vivien Didelotec15dd42017-11-06 16:11:46 -0500527 if (dst->setup) {
528 pr_err("DSA: tree %d already setup! Disjoint trees?\n",
529 dst->index);
530 return -EEXIST;
531 }
532
Vivien Didelot34c09a82017-11-06 16:11:51 -0500533 complete = dsa_tree_setup_routing_table(dst);
534 if (!complete)
535 return 0;
536
Vivien Didelotf0704642017-11-06 16:11:44 -0500537 err = dsa_tree_setup_default_cpu(dst);
538 if (err)
539 return err;
540
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500541 err = dsa_tree_setup_switches(dst);
542 if (err)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300543 goto teardown_default_cpu;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200544
Vivien Didelot17a22fc2017-11-06 16:11:45 -0500545 err = dsa_tree_setup_master(dst);
Vivien Didelot19435632017-09-19 11:56:59 -0400546 if (err)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300547 goto teardown_switches;
Vivien Didelot19435632017-09-19 11:56:59 -0400548
Vivien Didelotec15dd42017-11-06 16:11:46 -0500549 dst->setup = true;
550
551 pr_info("DSA: tree %d setup\n", dst->index);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200552
553 return 0;
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300554
555teardown_switches:
556 dsa_tree_teardown_switches(dst);
557teardown_default_cpu:
558 dsa_tree_teardown_default_cpu(dst);
559
560 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200561}
562
Vivien Didelotec15dd42017-11-06 16:11:46 -0500563static void dsa_tree_teardown(struct dsa_switch_tree *dst)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200564{
Vivien Didelotec15dd42017-11-06 16:11:46 -0500565 if (!dst->setup)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200566 return;
567
Vivien Didelot17a22fc2017-11-06 16:11:45 -0500568 dsa_tree_teardown_master(dst);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200569
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500570 dsa_tree_teardown_switches(dst);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200571
Vivien Didelotf0704642017-11-06 16:11:44 -0500572 dsa_tree_teardown_default_cpu(dst);
Florian Fainelli0c73c522016-06-07 16:32:42 -0700573
Vivien Didelotec15dd42017-11-06 16:11:46 -0500574 pr_info("DSA: tree %d torn down\n", dst->index);
575
576 dst->setup = false;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200577}
578
Vivien Didelot6da2a942017-11-03 19:05:25 -0400579static void dsa_tree_remove_switch(struct dsa_switch_tree *dst,
580 unsigned int index)
581{
Vivien Didelot30817352017-11-06 16:11:52 -0500582 dsa_tree_teardown(dst);
583
Vivien Didelot6da2a942017-11-03 19:05:25 -0400584 dst->ds[index] = NULL;
585 dsa_tree_put(dst);
586}
587
588static int dsa_tree_add_switch(struct dsa_switch_tree *dst,
589 struct dsa_switch *ds)
590{
591 unsigned int index = ds->index;
Vivien Didelot30817352017-11-06 16:11:52 -0500592 int err;
Vivien Didelot6da2a942017-11-03 19:05:25 -0400593
594 if (dst->ds[index])
595 return -EBUSY;
596
597 dsa_tree_get(dst);
598 dst->ds[index] = ds;
599
Vivien Didelot30817352017-11-06 16:11:52 -0500600 err = dsa_tree_setup(dst);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300601 if (err) {
602 dst->ds[index] = NULL;
603 dsa_tree_put(dst);
604 }
Vivien Didelot30817352017-11-06 16:11:52 -0500605
606 return err;
Vivien Didelot6da2a942017-11-03 19:05:25 -0400607}
608
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400609static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
610{
611 struct dsa_switch_tree *dst = ds->dst;
612 struct dsa_port *dp;
613
614 dp = &ds->ports[index];
615
616 dp->ds = ds;
617 dp->index = index;
618
619 INIT_LIST_HEAD(&dp->list);
620 list_add_tail(&dp->list, &dst->ports);
621
622 return dp;
623}
624
Vivien Didelot06e24d02017-11-03 19:05:29 -0400625static int dsa_port_parse_user(struct dsa_port *dp, const char *name)
626{
627 if (!name)
628 name = "eth%d";
629
630 dp->type = DSA_PORT_TYPE_USER;
631 dp->name = name;
632
633 return 0;
634}
635
636static int dsa_port_parse_dsa(struct dsa_port *dp)
637{
638 dp->type = DSA_PORT_TYPE_DSA;
639
640 return 0;
641}
642
643static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)
644{
Vivien Didelot7354fcb2017-11-03 19:05:30 -0400645 struct dsa_switch *ds = dp->ds;
646 struct dsa_switch_tree *dst = ds->dst;
Vivien Didelot62fc9582017-09-29 17:19:17 -0400647 const struct dsa_device_ops *tag_ops;
Andrew Lunn7b314362016-08-22 16:01:01 +0200648 enum dsa_tag_protocol tag_protocol;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200649
Florian Fainelli5ed4e3e2017-11-10 15:22:52 -0800650 tag_protocol = ds->ops->get_tag_protocol(ds, dp->index);
Andrew Lunnc39e2a12019-04-28 19:37:18 +0200651 tag_ops = dsa_tag_driver_get(tag_protocol);
Vivien Didelot62fc9582017-09-29 17:19:17 -0400652 if (IS_ERR(tag_ops)) {
Andrew Lunn23426a22019-09-12 15:16:45 +0200653 if (PTR_ERR(tag_ops) == -ENOPROTOOPT)
654 return -EPROBE_DEFER;
Florian Fainelli9f9e7722017-07-24 10:49:23 -0700655 dev_warn(ds->dev, "No tagger for this switch\n");
Vivien Didelot62fc9582017-09-29 17:19:17 -0400656 return PTR_ERR(tag_ops);
Florian Fainelli9f9e7722017-07-24 10:49:23 -0700657 }
658
Vivien Didelot7354fcb2017-11-03 19:05:30 -0400659 dp->type = DSA_PORT_TYPE_CPU;
Vladimir Olteancc1939e2019-05-05 13:19:23 +0300660 dp->filter = tag_ops->filter;
Vivien Didelot7354fcb2017-11-03 19:05:30 -0400661 dp->rcv = tag_ops->rcv;
662 dp->tag_ops = tag_ops;
663 dp->master = master;
664 dp->dst = dst;
Vivien Didelot3e41f932017-09-29 17:19:19 -0400665
Vivien Didelot7354fcb2017-11-03 19:05:30 -0400666 return 0;
667}
668
Vivien Didelotfd223e22017-10-27 15:55:14 -0400669static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn)
670{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400671 struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0);
Vivien Didelot1838fa82017-10-27 15:55:18 -0400672 const char *name = of_get_property(dn, "label", NULL);
Vivien Didelot54df6fa2017-11-03 19:05:28 -0400673 bool link = of_property_read_bool(dn, "link");
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400674
Vivien Didelot06e24d02017-11-03 19:05:29 -0400675 dp->dn = dn;
676
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400677 if (ethernet) {
Vivien Didelotcbabb0a2017-10-27 15:55:17 -0400678 struct net_device *master;
679
680 master = of_find_net_device_by_node(ethernet);
681 if (!master)
682 return -EPROBE_DEFER;
683
Vivien Didelot06e24d02017-11-03 19:05:29 -0400684 return dsa_port_parse_cpu(dp, master);
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400685 }
686
Vivien Didelot06e24d02017-11-03 19:05:29 -0400687 if (link)
688 return dsa_port_parse_dsa(dp);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400689
Vivien Didelot06e24d02017-11-03 19:05:29 -0400690 return dsa_port_parse_user(dp, name);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400691}
692
Vivien Didelot975e6e32017-11-03 19:05:27 -0400693static int dsa_switch_parse_ports_of(struct dsa_switch *ds,
694 struct device_node *dn)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200695{
Vivien Didelot5b32fe02017-10-27 15:55:13 -0400696 struct device_node *ports, *port;
Vivien Didelotfd223e22017-10-27 15:55:14 -0400697 struct dsa_port *dp;
Wen Yang9919a362019-02-25 15:22:19 +0800698 int err = 0;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200699 u32 reg;
Vivien Didelot5b32fe02017-10-27 15:55:13 -0400700
701 ports = of_get_child_by_name(dn, "ports");
702 if (!ports) {
703 dev_err(ds->dev, "no ports child node found\n");
704 return -EINVAL;
705 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200706
707 for_each_available_child_of_node(ports, port) {
708 err = of_property_read_u32(port, "reg", &reg);
709 if (err)
Wen Yang9919a362019-02-25 15:22:19 +0800710 goto out_put_node;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200711
Wen Yang9919a362019-02-25 15:22:19 +0800712 if (reg >= ds->num_ports) {
713 err = -EINVAL;
714 goto out_put_node;
715 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200716
Vivien Didelot68bb8ea2019-10-21 16:51:15 -0400717 dp = dsa_to_port(ds, reg);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400718
719 err = dsa_port_parse_of(dp, port);
720 if (err)
Wen Yang9919a362019-02-25 15:22:19 +0800721 goto out_put_node;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200722 }
723
Wen Yang9919a362019-02-25 15:22:19 +0800724out_put_node:
725 of_node_put(ports);
726 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200727}
728
Vivien Didelot975e6e32017-11-03 19:05:27 -0400729static int dsa_switch_parse_member_of(struct dsa_switch *ds,
730 struct device_node *dn)
731{
732 u32 m[2] = { 0, 0 };
733 int sz;
734
735 /* Don't error out if this optional property isn't found */
736 sz = of_property_read_variable_u32_array(dn, "dsa,member", m, 2, 2);
737 if (sz < 0 && sz != -EINVAL)
738 return sz;
739
740 ds->index = m[1];
741 if (ds->index >= DSA_MAX_SWITCHES)
742 return -EINVAL;
743
744 ds->dst = dsa_tree_touch(m[0]);
745 if (!ds->dst)
746 return -ENOMEM;
747
748 return 0;
749}
750
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400751static int dsa_switch_touch_ports(struct dsa_switch *ds)
752{
753 struct dsa_port *dp;
754 int port;
755
756 for (port = 0; port < ds->num_ports; port++) {
757 dp = dsa_port_touch(ds, port);
758 if (!dp)
759 return -ENOMEM;
760 }
761
762 return 0;
763}
764
Vivien Didelot975e6e32017-11-03 19:05:27 -0400765static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn)
766{
767 int err;
768
769 err = dsa_switch_parse_member_of(ds, dn);
770 if (err)
771 return err;
772
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400773 err = dsa_switch_touch_ports(ds);
774 if (err)
775 return err;
776
Vivien Didelot975e6e32017-11-03 19:05:27 -0400777 return dsa_switch_parse_ports_of(ds, dn);
778}
779
Vivien Didelotfd223e22017-10-27 15:55:14 -0400780static int dsa_port_parse(struct dsa_port *dp, const char *name,
781 struct device *dev)
782{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400783 if (!strcmp(name, "cpu")) {
Vivien Didelotcbabb0a2017-10-27 15:55:17 -0400784 struct net_device *master;
785
786 master = dsa_dev_to_net_device(dev);
787 if (!master)
788 return -EPROBE_DEFER;
789
790 dev_put(master);
791
Vivien Didelot06e24d02017-11-03 19:05:29 -0400792 return dsa_port_parse_cpu(dp, master);
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400793 }
794
Vivien Didelot06e24d02017-11-03 19:05:29 -0400795 if (!strcmp(name, "dsa"))
796 return dsa_port_parse_dsa(dp);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400797
Vivien Didelot06e24d02017-11-03 19:05:29 -0400798 return dsa_port_parse_user(dp, name);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400799}
800
Vivien Didelot975e6e32017-11-03 19:05:27 -0400801static int dsa_switch_parse_ports(struct dsa_switch *ds,
802 struct dsa_chip_data *cd)
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800803{
804 bool valid_name_found = false;
Vivien Didelotfd223e22017-10-27 15:55:14 -0400805 struct dsa_port *dp;
806 struct device *dev;
807 const char *name;
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800808 unsigned int i;
Vivien Didelotfd223e22017-10-27 15:55:14 -0400809 int err;
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800810
811 for (i = 0; i < DSA_MAX_PORTS; i++) {
Vivien Didelotfd223e22017-10-27 15:55:14 -0400812 name = cd->port_names[i];
813 dev = cd->netdev[i];
Vivien Didelot68bb8ea2019-10-21 16:51:15 -0400814 dp = dsa_to_port(ds, i);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400815
816 if (!name)
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800817 continue;
818
Vivien Didelotfd223e22017-10-27 15:55:14 -0400819 err = dsa_port_parse(dp, name, dev);
820 if (err)
821 return err;
822
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800823 valid_name_found = true;
824 }
825
826 if (!valid_name_found && i == DSA_MAX_PORTS)
827 return -EINVAL;
828
829 return 0;
830}
831
Vivien Didelot975e6e32017-11-03 19:05:27 -0400832static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200833{
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400834 int err;
835
Vivien Didelot975e6e32017-11-03 19:05:27 -0400836 ds->cd = cd;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200837
Vivien Didelot975e6e32017-11-03 19:05:27 -0400838 /* We don't support interconnected switches nor multiple trees via
839 * platform data, so this is the unique switch of the tree.
840 */
841 ds->index = 0;
842 ds->dst = dsa_tree_touch(0);
843 if (!ds->dst)
844 return -ENOMEM;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200845
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400846 err = dsa_switch_touch_ports(ds);
847 if (err)
848 return err;
849
Vivien Didelot975e6e32017-11-03 19:05:27 -0400850 return dsa_switch_parse_ports(ds, cd);
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800851}
852
Vivien Didelot30817352017-11-06 16:11:52 -0500853static int dsa_switch_add(struct dsa_switch *ds)
854{
855 struct dsa_switch_tree *dst = ds->dst;
856
857 return dsa_tree_add_switch(dst, ds);
858}
859
Vivien Didelotb4fbb342017-11-06 16:11:53 -0500860static int dsa_switch_probe(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200861{
Vivien Didelot23c9ee42017-05-26 18:12:51 -0400862 struct dsa_chip_data *pdata = ds->dev->platform_data;
863 struct device_node *np = ds->dev->of_node;
Vivien Didelot34c09a82017-11-06 16:11:51 -0500864 int err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200865
Vivien Didelot975e6e32017-11-03 19:05:27 -0400866 if (np)
867 err = dsa_switch_parse_of(ds, np);
868 else if (pdata)
869 err = dsa_switch_parse(ds, pdata);
870 else
871 err = -ENODEV;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200872
Vivien Didelot975e6e32017-11-03 19:05:27 -0400873 if (err)
874 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200875
Vivien Didelot30817352017-11-06 16:11:52 -0500876 return dsa_switch_add(ds);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200877}
878
Vivien Didelota0c02162017-01-27 15:29:36 -0500879struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n)
880{
Vivien Didelota0c02162017-01-27 15:29:36 -0500881 struct dsa_switch *ds;
882
Gustavo A. R. Silva33b363e2019-02-07 19:16:03 -0600883 ds = devm_kzalloc(dev, struct_size(ds, ports, n), GFP_KERNEL);
Vivien Didelota0c02162017-01-27 15:29:36 -0500884 if (!ds)
885 return NULL;
886
887 ds->dev = dev;
888 ds->num_ports = n;
889
890 return ds;
891}
892EXPORT_SYMBOL_GPL(dsa_switch_alloc);
893
Vivien Didelot23c9ee42017-05-26 18:12:51 -0400894int dsa_register_switch(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200895{
896 int err;
897
898 mutex_lock(&dsa2_mutex);
Vivien Didelotb4fbb342017-11-06 16:11:53 -0500899 err = dsa_switch_probe(ds);
Vivien Didelot9e741042017-11-24 11:36:06 -0500900 dsa_tree_put(ds->dst);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200901 mutex_unlock(&dsa2_mutex);
902
903 return err;
904}
905EXPORT_SYMBOL_GPL(dsa_register_switch);
906
Vivien Didelotb4fbb342017-11-06 16:11:53 -0500907static void dsa_switch_remove(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200908{
909 struct dsa_switch_tree *dst = ds->dst;
Vivien Didelot6da2a942017-11-03 19:05:25 -0400910 unsigned int index = ds->index;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200911
Vivien Didelot6da2a942017-11-03 19:05:25 -0400912 dsa_tree_remove_switch(dst, index);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200913}
914
915void dsa_unregister_switch(struct dsa_switch *ds)
916{
917 mutex_lock(&dsa2_mutex);
Vivien Didelotb4fbb342017-11-06 16:11:53 -0500918 dsa_switch_remove(ds);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200919 mutex_unlock(&dsa2_mutex);
920}
921EXPORT_SYMBOL_GPL(dsa_unregister_switch);