blob: c0ffc7a2b65f87bda5959423d7ae237f4edfc2dd [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
Andrew Lunn83c0afa2016-06-04 21:17:07 +020021static DEFINE_MUTEX(dsa2_mutex);
Vladimir Olteanbff33f72020-03-27 21:55:43 +020022LIST_HEAD(dsa_tree_list);
Andrew Lunn83c0afa2016-06-04 21:17:07 +020023
Andrew Lunn96567d52017-03-28 23:45:07 +020024static const struct devlink_ops dsa_devlink_ops = {
25};
26
Vladimir Oltean3b7bc1f2020-05-10 19:37:42 +030027struct dsa_switch *dsa_switch_find(int tree_index, int sw_index)
28{
29 struct dsa_switch_tree *dst;
30 struct dsa_port *dp;
31
32 list_for_each_entry(dst, &dsa_tree_list, list) {
33 if (dst->index != tree_index)
34 continue;
35
36 list_for_each_entry(dp, &dst->ports, list) {
37 if (dp->ds->index != sw_index)
38 continue;
39
40 return dp->ds;
41 }
42 }
43
44 return NULL;
45}
46EXPORT_SYMBOL_GPL(dsa_switch_find);
47
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040048static struct dsa_switch_tree *dsa_tree_find(int index)
Andrew Lunn83c0afa2016-06-04 21:17:07 +020049{
50 struct dsa_switch_tree *dst;
51
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040052 list_for_each_entry(dst, &dsa_tree_list, list)
Vivien Didelot8e5bf972017-11-03 19:05:22 -040053 if (dst->index == index)
Andrew Lunn83c0afa2016-06-04 21:17:07 +020054 return dst;
Vivien Didelot8e5bf972017-11-03 19:05:22 -040055
Andrew Lunn83c0afa2016-06-04 21:17:07 +020056 return NULL;
57}
58
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040059static struct dsa_switch_tree *dsa_tree_alloc(int index)
Andrew Lunn83c0afa2016-06-04 21:17:07 +020060{
61 struct dsa_switch_tree *dst;
62
63 dst = kzalloc(sizeof(*dst), GFP_KERNEL);
64 if (!dst)
65 return NULL;
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040066
Vivien Didelot49463b72017-11-03 19:05:21 -040067 dst->index = index;
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040068
Vivien Didelotc5f51762019-10-30 22:09:13 -040069 INIT_LIST_HEAD(&dst->rtable);
70
Vivien Didelotab8ccae2019-10-21 16:51:16 -040071 INIT_LIST_HEAD(&dst->ports);
72
Andrew Lunn83c0afa2016-06-04 21:17:07 +020073 INIT_LIST_HEAD(&dst->list);
Vivien Didelot50c7d2ba2019-10-18 17:02:46 -040074 list_add_tail(&dst->list, &dsa_tree_list);
Vivien Didelot8e5bf972017-11-03 19:05:22 -040075
Andrew Lunn83c0afa2016-06-04 21:17:07 +020076 kref_init(&dst->refcount);
77
78 return dst;
79}
80
Vivien Didelot65254102017-11-03 19:05:23 -040081static void dsa_tree_free(struct dsa_switch_tree *dst)
82{
83 list_del(&dst->list);
84 kfree(dst);
85}
86
Vivien Didelot9e741042017-11-24 11:36:06 -050087static struct dsa_switch_tree *dsa_tree_get(struct dsa_switch_tree *dst)
88{
89 if (dst)
90 kref_get(&dst->refcount);
91
92 return dst;
93}
94
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040095static struct dsa_switch_tree *dsa_tree_touch(int index)
96{
97 struct dsa_switch_tree *dst;
98
99 dst = dsa_tree_find(index);
Vivien Didelot9e741042017-11-24 11:36:06 -0500100 if (dst)
101 return dsa_tree_get(dst);
102 else
103 return dsa_tree_alloc(index);
Vivien Didelot65254102017-11-03 19:05:23 -0400104}
105
106static void dsa_tree_release(struct kref *ref)
107{
108 struct dsa_switch_tree *dst;
109
110 dst = container_of(ref, struct dsa_switch_tree, refcount);
111
112 dsa_tree_free(dst);
113}
114
115static void dsa_tree_put(struct dsa_switch_tree *dst)
116{
Vivien Didelot9e741042017-11-24 11:36:06 -0500117 if (dst)
118 kref_put(&dst->refcount, dsa_tree_release);
Vivien Didelot65254102017-11-03 19:05:23 -0400119}
120
Florian Fainelli293784a2017-01-26 10:45:52 -0800121static bool dsa_port_is_dsa(struct dsa_port *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200122{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400123 return port->type == DSA_PORT_TYPE_DSA;
Florian Fainelli293784a2017-01-26 10:45:52 -0800124}
125
126static bool dsa_port_is_cpu(struct dsa_port *port)
127{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400128 return port->type == DSA_PORT_TYPE_CPU;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200129}
130
Vivien Didelotf0704642017-11-06 16:11:44 -0500131static bool dsa_port_is_user(struct dsa_port *dp)
132{
133 return dp->type == DSA_PORT_TYPE_USER;
134}
135
Vivien Didelotf163da82017-11-06 16:11:49 -0500136static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst,
137 struct device_node *dn)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200138{
Vivien Didelotf163da82017-11-06 16:11:49 -0500139 struct dsa_port *dp;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200140
Vivien Didelot764b7e62019-10-21 16:51:21 -0400141 list_for_each_entry(dp, &dst->ports, list)
142 if (dp->dn == dn)
143 return dp;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200144
145 return NULL;
146}
147
Ben Dooks (Codethink)4e2ce6e2019-12-17 11:20:38 +0000148static struct dsa_link *dsa_link_touch(struct dsa_port *dp,
149 struct dsa_port *link_dp)
Vivien Didelotc5f51762019-10-30 22:09:13 -0400150{
151 struct dsa_switch *ds = dp->ds;
152 struct dsa_switch_tree *dst;
153 struct dsa_link *dl;
154
155 dst = ds->dst;
156
157 list_for_each_entry(dl, &dst->rtable, list)
158 if (dl->dp == dp && dl->link_dp == link_dp)
159 return dl;
160
161 dl = kzalloc(sizeof(*dl), GFP_KERNEL);
162 if (!dl)
163 return NULL;
164
165 dl->dp = dp;
166 dl->link_dp = link_dp;
167
168 INIT_LIST_HEAD(&dl->list);
169 list_add_tail(&dl->list, &dst->rtable);
170
171 return dl;
172}
173
Vivien Didelot34c09a82017-11-06 16:11:51 -0500174static bool dsa_port_setup_routing_table(struct dsa_port *dp)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200175{
Vivien Didelot34c09a82017-11-06 16:11:51 -0500176 struct dsa_switch *ds = dp->ds;
177 struct dsa_switch_tree *dst = ds->dst;
178 struct device_node *dn = dp->dn;
Vivien Didelotc5286662017-11-06 16:11:50 -0500179 struct of_phandle_iterator it;
Vivien Didelotf163da82017-11-06 16:11:49 -0500180 struct dsa_port *link_dp;
Vivien Didelotc5f51762019-10-30 22:09:13 -0400181 struct dsa_link *dl;
Vivien Didelotc5286662017-11-06 16:11:50 -0500182 int err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200183
Vivien Didelotc5286662017-11-06 16:11:50 -0500184 of_for_each_phandle(&it, err, dn, "link", NULL, 0) {
185 link_dp = dsa_tree_find_port_by_node(dst, it.node);
186 if (!link_dp) {
187 of_node_put(it.node);
Vivien Didelot34c09a82017-11-06 16:11:51 -0500188 return false;
Vivien Didelotc5286662017-11-06 16:11:50 -0500189 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200190
Vivien Didelotc5f51762019-10-30 22:09:13 -0400191 dl = dsa_link_touch(dp, link_dp);
192 if (!dl) {
193 of_node_put(it.node);
194 return false;
195 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200196 }
197
Vivien Didelot34c09a82017-11-06 16:11:51 -0500198 return true;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200199}
200
Vivien Didelot3774ecd2019-10-30 22:09:15 -0400201static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200202{
Vivien Didelot34c09a82017-11-06 16:11:51 -0500203 bool complete = true;
204 struct dsa_port *dp;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200205
Vivien Didelot86bfb2c2019-10-21 16:51:20 -0400206 list_for_each_entry(dp, &dst->ports, list) {
Vivien Didelot3774ecd2019-10-30 22:09:15 -0400207 if (dsa_port_is_dsa(dp)) {
Vivien Didelot34c09a82017-11-06 16:11:51 -0500208 complete = dsa_port_setup_routing_table(dp);
209 if (!complete)
210 break;
211 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200212 }
213
Vivien Didelot34c09a82017-11-06 16:11:51 -0500214 return complete;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200215}
216
Vivien Didelotf0704642017-11-06 16:11:44 -0500217static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst)
218{
Vivien Didelotf0704642017-11-06 16:11:44 -0500219 struct dsa_port *dp;
Vivien Didelotf0704642017-11-06 16:11:44 -0500220
Vivien Didelotc0b73622019-10-21 16:51:23 -0400221 list_for_each_entry(dp, &dst->ports, list)
222 if (dsa_port_is_cpu(dp))
223 return dp;
Vivien Didelotf0704642017-11-06 16:11:44 -0500224
225 return NULL;
226}
227
228static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst)
229{
Vivien Didelotda4561c2019-10-21 16:51:24 -0400230 struct dsa_port *cpu_dp, *dp;
Vivien Didelotf0704642017-11-06 16:11:44 -0500231
Vivien Didelotda4561c2019-10-21 16:51:24 -0400232 cpu_dp = dsa_tree_find_first_cpu(dst);
233 if (!cpu_dp) {
234 pr_err("DSA: tree %d has no CPU port\n", dst->index);
Vivien Didelotf0704642017-11-06 16:11:44 -0500235 return -EINVAL;
236 }
237
238 /* Assign the default CPU port to all ports of the fabric */
Vivien Didelotda4561c2019-10-21 16:51:24 -0400239 list_for_each_entry(dp, &dst->ports, list)
240 if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp))
241 dp->cpu_dp = cpu_dp;
Vivien Didelotf0704642017-11-06 16:11:44 -0500242
243 return 0;
244}
245
246static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst)
247{
Vivien Didelotda4561c2019-10-21 16:51:24 -0400248 struct dsa_port *dp;
249
250 list_for_each_entry(dp, &dst->ports, list)
251 if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp))
252 dp->cpu_dp = NULL;
Vivien Didelotf0704642017-11-06 16:11:44 -0500253}
254
Vivien Didelot1d277322017-11-06 16:11:48 -0500255static int dsa_port_setup(struct dsa_port *dp)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200256{
Vivien Didelot1d277322017-11-06 16:11:48 -0500257 struct dsa_switch *ds = dp->ds;
Jiri Pirko15b04ac2019-04-03 14:24:26 +0200258 struct dsa_switch_tree *dst = ds->dst;
Vivien Didelot955222c2019-08-19 16:00:48 -0400259 const unsigned char *id = (const unsigned char *)&dst->index;
260 const unsigned char len = sizeof(dst->index);
261 struct devlink_port *dlp = &dp->devlink_port;
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300262 bool dsa_port_link_registered = false;
263 bool devlink_port_registered = false;
Danielle Ratson71ad8d52020-07-09 16:18:16 +0300264 struct devlink_port_attrs attrs = {};
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
Danielle Ratson71ad8d52020-07-09 16:18:16 +0300269 attrs.phys.port_number = dp->index;
270 memcpy(attrs.switch_id.id, id, len);
271 attrs.switch_id.id_len = len;
272
Vivien Didelotfb35c602019-10-21 16:51:19 -0400273 if (dp->setup)
274 return 0;
275
Vivien Didelot1d277322017-11-06 16:11:48 -0500276 switch (dp->type) {
277 case DSA_PORT_TYPE_UNUSED:
Vivien Didelot0394a632019-08-19 16:00:50 -0400278 dsa_port_disable(dp);
Vivien Didelot1d277322017-11-06 16:11:48 -0500279 break;
280 case DSA_PORT_TYPE_CPU:
Vivien Didelot955222c2019-08-19 16:00:48 -0400281 memset(dlp, 0, sizeof(*dlp));
Danielle Ratson71ad8d52020-07-09 16:18:16 +0300282 attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
283 devlink_port_attrs_set(dlp, &attrs);
Vivien Didelot955222c2019-08-19 16:00:48 -0400284 err = devlink_port_register(dl, dlp, dp->index);
285 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300286 break;
287 devlink_port_registered = true;
Vivien Didelot955222c2019-08-19 16:00:48 -0400288
Jiri Pirkoda077392018-05-18 09:29:03 +0200289 err = dsa_port_link_register_of(dp);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300290 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300291 break;
292 dsa_port_link_registered = true;
Vivien Didelot0394a632019-08-19 16:00:50 -0400293
294 err = dsa_port_enable(dp, NULL);
295 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300296 break;
297 dsa_port_enabled = true;
298
Jiri Pirkoda077392018-05-18 09:29:03 +0200299 break;
Vivien Didelot1d277322017-11-06 16:11:48 -0500300 case DSA_PORT_TYPE_DSA:
Vivien Didelot955222c2019-08-19 16:00:48 -0400301 memset(dlp, 0, sizeof(*dlp));
Danielle Ratson71ad8d52020-07-09 16:18:16 +0300302 attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
303 devlink_port_attrs_set(dlp, &attrs);
Vivien Didelot955222c2019-08-19 16:00:48 -0400304 err = devlink_port_register(dl, dlp, dp->index);
305 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300306 break;
307 devlink_port_registered = true;
Vivien Didelot955222c2019-08-19 16:00:48 -0400308
Sebastian Reichel33615362018-01-23 16:03:46 +0100309 err = dsa_port_link_register_of(dp);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300310 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300311 break;
312 dsa_port_link_registered = true;
Vivien Didelot0394a632019-08-19 16:00:50 -0400313
314 err = dsa_port_enable(dp, NULL);
315 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300316 break;
317 dsa_port_enabled = true;
318
Vivien Didelot1d277322017-11-06 16:11:48 -0500319 break;
320 case DSA_PORT_TYPE_USER:
Vivien Didelot955222c2019-08-19 16:00:48 -0400321 memset(dlp, 0, sizeof(*dlp));
Danielle Ratson71ad8d52020-07-09 16:18:16 +0300322 attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
323 devlink_port_attrs_set(dlp, &attrs);
Vivien Didelot955222c2019-08-19 16:00:48 -0400324 err = devlink_port_register(dl, dlp, dp->index);
325 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300326 break;
327 devlink_port_registered = true;
Vivien Didelot955222c2019-08-19 16:00:48 -0400328
329 dp->mac = of_get_mac_address(dp->dn);
Vivien Didelot1d277322017-11-06 16:11:48 -0500330 err = dsa_slave_create(dp);
331 if (err)
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300332 break;
Vivien Didelot955222c2019-08-19 16:00:48 -0400333
334 devlink_port_type_eth_set(dlp, dp->slave);
Vivien Didelot1d277322017-11-06 16:11:48 -0500335 break;
336 }
Andrew Lunn96567d52017-03-28 23:45:07 +0200337
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300338 if (err && dsa_port_enabled)
339 dsa_port_disable(dp);
340 if (err && dsa_port_link_registered)
341 dsa_port_link_unregister_of(dp);
342 if (err && devlink_port_registered)
343 devlink_port_unregister(dlp);
Vivien Didelotfb35c602019-10-21 16:51:19 -0400344 if (err)
345 return err;
Vladimir Oltean4ba0ebb2019-08-31 15:46:19 +0300346
Vivien Didelotfb35c602019-10-21 16:51:19 -0400347 dp->setup = true;
348
349 return 0;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200350}
351
Vivien Didelot1d277322017-11-06 16:11:48 -0500352static void dsa_port_teardown(struct dsa_port *dp)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200353{
Vivien Didelot955222c2019-08-19 16:00:48 -0400354 struct devlink_port *dlp = &dp->devlink_port;
Vivien Didelot1d277322017-11-06 16:11:48 -0500355
Vivien Didelotfb35c602019-10-21 16:51:19 -0400356 if (!dp->setup)
357 return;
358
Vivien Didelot1d277322017-11-06 16:11:48 -0500359 switch (dp->type) {
360 case DSA_PORT_TYPE_UNUSED:
361 break;
362 case DSA_PORT_TYPE_CPU:
Vivien Didelot0394a632019-08-19 16:00:50 -0400363 dsa_port_disable(dp);
Andrew Lunn4dad81e2019-04-28 19:37:19 +0200364 dsa_tag_driver_put(dp->tag_ops);
Vivien Didelot955222c2019-08-19 16:00:48 -0400365 devlink_port_unregister(dlp);
366 dsa_port_link_unregister_of(dp);
367 break;
Vivien Didelot1d277322017-11-06 16:11:48 -0500368 case DSA_PORT_TYPE_DSA:
Vivien Didelot0394a632019-08-19 16:00:50 -0400369 dsa_port_disable(dp);
Vivien Didelot955222c2019-08-19 16:00:48 -0400370 devlink_port_unregister(dlp);
Sebastian Reichel33615362018-01-23 16:03:46 +0100371 dsa_port_link_unregister_of(dp);
Vivien Didelot1d277322017-11-06 16:11:48 -0500372 break;
373 case DSA_PORT_TYPE_USER:
Vivien Didelot955222c2019-08-19 16:00:48 -0400374 devlink_port_unregister(dlp);
Vivien Didelot1d277322017-11-06 16:11:48 -0500375 if (dp->slave) {
376 dsa_slave_destroy(dp->slave);
377 dp->slave = NULL;
378 }
379 break;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200380 }
Vivien Didelotfb35c602019-10-21 16:51:19 -0400381
382 dp->setup = false;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200383}
384
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500385static int dsa_switch_setup(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200386{
Andrew Lunn6b297522019-10-25 01:03:51 +0200387 struct dsa_devlink_priv *dl_priv;
Vivien Didelotfb35c602019-10-21 16:51:19 -0400388 int err;
389
390 if (ds->setup)
391 return 0;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200392
Florian Fainelli6e830d82016-06-07 16:32:39 -0700393 /* Initialize ds->phys_mii_mask before registering the slave MDIO bus
Vivien Didelot9d490b42016-08-23 12:38:56 -0400394 * driver and before ops->setup() has run, since the switch drivers and
Florian Fainelli6e830d82016-06-07 16:32:39 -0700395 * the slave MDIO bus driver rely on these values for probing PHY
396 * devices or not
397 */
Vivien Didelot02bc6e52017-10-26 11:22:56 -0400398 ds->phys_mii_mask |= dsa_user_ports(ds);
Florian Fainelli6e830d82016-06-07 16:32:39 -0700399
Andrew Lunn96567d52017-03-28 23:45:07 +0200400 /* Add the switch to devlink before calling setup, so that setup can
401 * add dpipe tables
402 */
Andrew Lunn6b297522019-10-25 01:03:51 +0200403 ds->devlink = devlink_alloc(&dsa_devlink_ops, sizeof(*dl_priv));
Andrew Lunn96567d52017-03-28 23:45:07 +0200404 if (!ds->devlink)
405 return -ENOMEM;
Andrew Lunn6b297522019-10-25 01:03:51 +0200406 dl_priv = devlink_priv(ds->devlink);
407 dl_priv->ds = ds;
Andrew Lunn96567d52017-03-28 23:45:07 +0200408
409 err = devlink_register(ds->devlink, ds->dev);
410 if (err)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300411 goto free_devlink;
Andrew Lunn96567d52017-03-28 23:45:07 +0200412
Vivien Didelotf515f192017-02-03 13:20:20 -0500413 err = dsa_switch_register_notifier(ds);
414 if (err)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300415 goto unregister_devlink;
Vivien Didelotf515f192017-02-03 13:20:20 -0500416
Vladimir Olteanb2243b32019-05-05 13:19:20 +0300417 err = ds->ops->setup(ds);
418 if (err < 0)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300419 goto unregister_notifier;
Vladimir Olteanb2243b32019-05-05 13:19:20 +0300420
Andrew Lunn6b297522019-10-25 01:03:51 +0200421 devlink_params_publish(ds->devlink);
422
Vivien Didelot9d490b42016-08-23 12:38:56 -0400423 if (!ds->slave_mii_bus && ds->ops->phy_read) {
Florian Fainelli1eb59442016-06-07 16:32:40 -0700424 ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300425 if (!ds->slave_mii_bus) {
426 err = -ENOMEM;
427 goto unregister_notifier;
428 }
Florian Fainelli1eb59442016-06-07 16:32:40 -0700429
430 dsa_slave_mii_bus_init(ds);
431
432 err = mdiobus_register(ds->slave_mii_bus);
433 if (err < 0)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300434 goto unregister_notifier;
Florian Fainelli1eb59442016-06-07 16:32:40 -0700435 }
436
Vivien Didelotfb35c602019-10-21 16:51:19 -0400437 ds->setup = true;
438
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200439 return 0;
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300440
441unregister_notifier:
442 dsa_switch_unregister_notifier(ds);
443unregister_devlink:
444 devlink_unregister(ds->devlink);
445free_devlink:
446 devlink_free(ds->devlink);
447 ds->devlink = NULL;
448
449 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200450}
451
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500452static void dsa_switch_teardown(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200453{
Vivien Didelotfb35c602019-10-21 16:51:19 -0400454 if (!ds->setup)
455 return;
456
Vivien Didelot9d490b42016-08-23 12:38:56 -0400457 if (ds->slave_mii_bus && ds->ops->phy_read)
Florian Fainelli1eb59442016-06-07 16:32:40 -0700458 mdiobus_unregister(ds->slave_mii_bus);
Vivien Didelotf515f192017-02-03 13:20:20 -0500459
460 dsa_switch_unregister_notifier(ds);
Andrew Lunn96567d52017-03-28 23:45:07 +0200461
Vladimir Oltean5e3f8472019-06-08 15:04:28 +0300462 if (ds->ops->teardown)
463 ds->ops->teardown(ds);
464
Andrew Lunn96567d52017-03-28 23:45:07 +0200465 if (ds->devlink) {
466 devlink_unregister(ds->devlink);
467 devlink_free(ds->devlink);
468 ds->devlink = NULL;
469 }
470
Vivien Didelotfb35c602019-10-21 16:51:19 -0400471 ds->setup = false;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200472}
473
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500474static int dsa_tree_setup_switches(struct dsa_switch_tree *dst)
475{
Vivien Didelot1d277322017-11-06 16:11:48 -0500476 struct dsa_port *dp;
Vivien Didelotfb35c602019-10-21 16:51:19 -0400477 int err;
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500478
Vivien Didelotfb35c602019-10-21 16:51:19 -0400479 list_for_each_entry(dp, &dst->ports, list) {
480 err = dsa_switch_setup(dp->ds);
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500481 if (err)
Vivien Didelotfb35c602019-10-21 16:51:19 -0400482 goto teardown;
483 }
Vivien Didelot1d277322017-11-06 16:11:48 -0500484
Vivien Didelotfb35c602019-10-21 16:51:19 -0400485 list_for_each_entry(dp, &dst->ports, list) {
486 err = dsa_port_setup(dp);
487 if (err)
Florian Fainelli86f8b1c2020-05-03 20:50:57 -0700488 continue;
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500489 }
490
491 return 0;
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300492
Vivien Didelotfb35c602019-10-21 16:51:19 -0400493teardown:
494 list_for_each_entry(dp, &dst->ports, list)
495 dsa_port_teardown(dp);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300496
Vivien Didelotfb35c602019-10-21 16:51:19 -0400497 list_for_each_entry(dp, &dst->ports, list)
498 dsa_switch_teardown(dp->ds);
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300499
500 return err;
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500501}
502
503static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst)
504{
Vivien Didelot1d277322017-11-06 16:11:48 -0500505 struct dsa_port *dp;
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500506
Vivien Didelotfb35c602019-10-21 16:51:19 -0400507 list_for_each_entry(dp, &dst->ports, list)
508 dsa_port_teardown(dp);
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500509
Vivien Didelotfb35c602019-10-21 16:51:19 -0400510 list_for_each_entry(dp, &dst->ports, list)
511 dsa_switch_teardown(dp->ds);
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500512}
513
Vivien Didelot17a22fc2017-11-06 16:11:45 -0500514static int dsa_tree_setup_master(struct dsa_switch_tree *dst)
515{
Vivien Didelot0cfec582019-10-21 16:51:22 -0400516 struct dsa_port *dp;
517 int err;
Vivien Didelot17a22fc2017-11-06 16:11:45 -0500518
Vivien Didelot0cfec582019-10-21 16:51:22 -0400519 list_for_each_entry(dp, &dst->ports, list) {
520 if (dsa_port_is_cpu(dp)) {
521 err = dsa_master_setup(dp->master, dp);
522 if (err)
523 return err;
524 }
525 }
526
527 return 0;
Vivien Didelot17a22fc2017-11-06 16:11:45 -0500528}
529
530static void dsa_tree_teardown_master(struct dsa_switch_tree *dst)
531{
Vivien Didelot0cfec582019-10-21 16:51:22 -0400532 struct dsa_port *dp;
Vivien Didelot17a22fc2017-11-06 16:11:45 -0500533
Vivien Didelot0cfec582019-10-21 16:51:22 -0400534 list_for_each_entry(dp, &dst->ports, list)
535 if (dsa_port_is_cpu(dp))
536 dsa_master_teardown(dp->master);
Vivien Didelot17a22fc2017-11-06 16:11:45 -0500537}
538
Vivien Didelotec15dd42017-11-06 16:11:46 -0500539static int dsa_tree_setup(struct dsa_switch_tree *dst)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200540{
Vivien Didelot34c09a82017-11-06 16:11:51 -0500541 bool complete;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200542 int err;
543
Vivien Didelotec15dd42017-11-06 16:11:46 -0500544 if (dst->setup) {
545 pr_err("DSA: tree %d already setup! Disjoint trees?\n",
546 dst->index);
547 return -EEXIST;
548 }
549
Vivien Didelot34c09a82017-11-06 16:11:51 -0500550 complete = dsa_tree_setup_routing_table(dst);
551 if (!complete)
552 return 0;
553
Vivien Didelotf0704642017-11-06 16:11:44 -0500554 err = dsa_tree_setup_default_cpu(dst);
555 if (err)
556 return err;
557
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500558 err = dsa_tree_setup_switches(dst);
559 if (err)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300560 goto teardown_default_cpu;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200561
Vivien Didelot17a22fc2017-11-06 16:11:45 -0500562 err = dsa_tree_setup_master(dst);
Vivien Didelot19435632017-09-19 11:56:59 -0400563 if (err)
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300564 goto teardown_switches;
Vivien Didelot19435632017-09-19 11:56:59 -0400565
Vivien Didelotec15dd42017-11-06 16:11:46 -0500566 dst->setup = true;
567
568 pr_info("DSA: tree %d setup\n", dst->index);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200569
570 return 0;
Ioana Ciorneie70c7aa2019-05-30 09:09:07 +0300571
572teardown_switches:
573 dsa_tree_teardown_switches(dst);
574teardown_default_cpu:
575 dsa_tree_teardown_default_cpu(dst);
576
577 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200578}
579
Vivien Didelotec15dd42017-11-06 16:11:46 -0500580static void dsa_tree_teardown(struct dsa_switch_tree *dst)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200581{
Vivien Didelotc5f51762019-10-30 22:09:13 -0400582 struct dsa_link *dl, *next;
583
Vivien Didelotec15dd42017-11-06 16:11:46 -0500584 if (!dst->setup)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200585 return;
586
Vivien Didelot17a22fc2017-11-06 16:11:45 -0500587 dsa_tree_teardown_master(dst);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200588
Vivien Didelot1f08f9e2017-11-06 16:11:47 -0500589 dsa_tree_teardown_switches(dst);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200590
Vivien Didelotf0704642017-11-06 16:11:44 -0500591 dsa_tree_teardown_default_cpu(dst);
Florian Fainelli0c73c522016-06-07 16:32:42 -0700592
Vivien Didelotc5f51762019-10-30 22:09:13 -0400593 list_for_each_entry_safe(dl, next, &dst->rtable, list) {
594 list_del(&dl->list);
595 kfree(dl);
596 }
597
Vivien Didelotec15dd42017-11-06 16:11:46 -0500598 pr_info("DSA: tree %d torn down\n", dst->index);
599
600 dst->setup = false;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200601}
602
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400603static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
604{
605 struct dsa_switch_tree *dst = ds->dst;
606 struct dsa_port *dp;
607
Vivien Didelot05f294a2019-10-21 16:51:29 -0400608 list_for_each_entry(dp, &dst->ports, list)
609 if (dp->ds == ds && dp->index == index)
610 return dp;
611
612 dp = kzalloc(sizeof(*dp), GFP_KERNEL);
613 if (!dp)
614 return NULL;
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400615
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
Florian Fainelli4d776482020-01-07 21:06:05 -0800643static enum dsa_tag_protocol dsa_get_tag_protocol(struct dsa_port *dp,
644 struct net_device *master)
645{
646 enum dsa_tag_protocol tag_protocol = DSA_TAG_PROTO_NONE;
647 struct dsa_switch *mds, *ds = dp->ds;
648 unsigned int mdp_upstream;
649 struct dsa_port *mdp;
650
651 /* It is possible to stack DSA switches onto one another when that
652 * happens the switch driver may want to know if its tagging protocol
653 * is going to work in such a configuration.
654 */
655 if (dsa_slave_dev_check(master)) {
656 mdp = dsa_slave_to_port(master);
657 mds = mdp->ds;
658 mdp_upstream = dsa_upstream_port(mds, mdp->index);
659 tag_protocol = mds->ops->get_tag_protocol(mds, mdp_upstream,
660 DSA_TAG_PROTO_NONE);
661 }
662
663 /* If the master device is not itself a DSA slave in a disjoint DSA
664 * tree, then return immediately.
665 */
666 return ds->ops->get_tag_protocol(ds, dp->index, tag_protocol);
667}
668
Vivien Didelot06e24d02017-11-03 19:05:29 -0400669static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)
670{
Vivien Didelot7354fcb2017-11-03 19:05:30 -0400671 struct dsa_switch *ds = dp->ds;
672 struct dsa_switch_tree *dst = ds->dst;
Vivien Didelot62fc9582017-09-29 17:19:17 -0400673 const struct dsa_device_ops *tag_ops;
Andrew Lunn7b314362016-08-22 16:01:01 +0200674 enum dsa_tag_protocol tag_protocol;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200675
Florian Fainelli4d776482020-01-07 21:06:05 -0800676 tag_protocol = dsa_get_tag_protocol(dp, master);
Andrew Lunnc39e2a12019-04-28 19:37:18 +0200677 tag_ops = dsa_tag_driver_get(tag_protocol);
Vivien Didelot62fc9582017-09-29 17:19:17 -0400678 if (IS_ERR(tag_ops)) {
Andrew Lunn23426a22019-09-12 15:16:45 +0200679 if (PTR_ERR(tag_ops) == -ENOPROTOOPT)
680 return -EPROBE_DEFER;
Florian Fainelli9f9e7722017-07-24 10:49:23 -0700681 dev_warn(ds->dev, "No tagger for this switch\n");
Florian Fainelli4d776482020-01-07 21:06:05 -0800682 dp->master = NULL;
Vivien Didelot62fc9582017-09-29 17:19:17 -0400683 return PTR_ERR(tag_ops);
Florian Fainelli9f9e7722017-07-24 10:49:23 -0700684 }
685
Florian Fainelli4d776482020-01-07 21:06:05 -0800686 dp->master = master;
Vivien Didelot7354fcb2017-11-03 19:05:30 -0400687 dp->type = DSA_PORT_TYPE_CPU;
Vladimir Olteancc1939e2019-05-05 13:19:23 +0300688 dp->filter = tag_ops->filter;
Vivien Didelot7354fcb2017-11-03 19:05:30 -0400689 dp->rcv = tag_ops->rcv;
690 dp->tag_ops = tag_ops;
Vivien Didelot7354fcb2017-11-03 19:05:30 -0400691 dp->dst = dst;
Vivien Didelot3e41f932017-09-29 17:19:19 -0400692
Vivien Didelot7354fcb2017-11-03 19:05:30 -0400693 return 0;
694}
695
Vivien Didelotfd223e22017-10-27 15:55:14 -0400696static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn)
697{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400698 struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0);
Vivien Didelot1838fa82017-10-27 15:55:18 -0400699 const char *name = of_get_property(dn, "label", NULL);
Vivien Didelot54df6fa2017-11-03 19:05:28 -0400700 bool link = of_property_read_bool(dn, "link");
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400701
Vivien Didelot06e24d02017-11-03 19:05:29 -0400702 dp->dn = dn;
703
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400704 if (ethernet) {
Vivien Didelotcbabb0a2017-10-27 15:55:17 -0400705 struct net_device *master;
706
707 master = of_find_net_device_by_node(ethernet);
708 if (!master)
709 return -EPROBE_DEFER;
710
Vivien Didelot06e24d02017-11-03 19:05:29 -0400711 return dsa_port_parse_cpu(dp, master);
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400712 }
713
Vivien Didelot06e24d02017-11-03 19:05:29 -0400714 if (link)
715 return dsa_port_parse_dsa(dp);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400716
Vivien Didelot06e24d02017-11-03 19:05:29 -0400717 return dsa_port_parse_user(dp, name);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400718}
719
Vivien Didelot975e6e32017-11-03 19:05:27 -0400720static int dsa_switch_parse_ports_of(struct dsa_switch *ds,
721 struct device_node *dn)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200722{
Vivien Didelot5b32fe02017-10-27 15:55:13 -0400723 struct device_node *ports, *port;
Vivien Didelotfd223e22017-10-27 15:55:14 -0400724 struct dsa_port *dp;
Wen Yang9919a362019-02-25 15:22:19 +0800725 int err = 0;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200726 u32 reg;
Vivien Didelot5b32fe02017-10-27 15:55:13 -0400727
728 ports = of_get_child_by_name(dn, "ports");
729 if (!ports) {
Kurt Kanzenbach85e05d22020-07-20 14:49:39 +0200730 /* The second possibility is "ethernet-ports" */
731 ports = of_get_child_by_name(dn, "ethernet-ports");
732 if (!ports) {
733 dev_err(ds->dev, "no ports child node found\n");
734 return -EINVAL;
735 }
Vivien Didelot5b32fe02017-10-27 15:55:13 -0400736 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200737
738 for_each_available_child_of_node(ports, port) {
739 err = of_property_read_u32(port, "reg", &reg);
740 if (err)
Wen Yang9919a362019-02-25 15:22:19 +0800741 goto out_put_node;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200742
Wen Yang9919a362019-02-25 15:22:19 +0800743 if (reg >= ds->num_ports) {
744 err = -EINVAL;
745 goto out_put_node;
746 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200747
Vivien Didelot68bb8ea2019-10-21 16:51:15 -0400748 dp = dsa_to_port(ds, reg);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400749
750 err = dsa_port_parse_of(dp, port);
751 if (err)
Wen Yang9919a362019-02-25 15:22:19 +0800752 goto out_put_node;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200753 }
754
Wen Yang9919a362019-02-25 15:22:19 +0800755out_put_node:
756 of_node_put(ports);
757 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200758}
759
Vivien Didelot975e6e32017-11-03 19:05:27 -0400760static int dsa_switch_parse_member_of(struct dsa_switch *ds,
761 struct device_node *dn)
762{
763 u32 m[2] = { 0, 0 };
764 int sz;
765
766 /* Don't error out if this optional property isn't found */
767 sz = of_property_read_variable_u32_array(dn, "dsa,member", m, 2, 2);
768 if (sz < 0 && sz != -EINVAL)
769 return sz;
770
771 ds->index = m[1];
Vivien Didelot975e6e32017-11-03 19:05:27 -0400772
773 ds->dst = dsa_tree_touch(m[0]);
774 if (!ds->dst)
775 return -ENOMEM;
776
777 return 0;
778}
779
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400780static int dsa_switch_touch_ports(struct dsa_switch *ds)
781{
782 struct dsa_port *dp;
783 int port;
784
785 for (port = 0; port < ds->num_ports; port++) {
786 dp = dsa_port_touch(ds, port);
787 if (!dp)
788 return -ENOMEM;
789 }
790
791 return 0;
792}
793
Vivien Didelot975e6e32017-11-03 19:05:27 -0400794static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn)
795{
796 int err;
797
798 err = dsa_switch_parse_member_of(ds, dn);
799 if (err)
800 return err;
801
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400802 err = dsa_switch_touch_ports(ds);
803 if (err)
804 return err;
805
Vivien Didelot975e6e32017-11-03 19:05:27 -0400806 return dsa_switch_parse_ports_of(ds, dn);
807}
808
Vivien Didelotfd223e22017-10-27 15:55:14 -0400809static int dsa_port_parse(struct dsa_port *dp, const char *name,
810 struct device *dev)
811{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400812 if (!strcmp(name, "cpu")) {
Vivien Didelotcbabb0a2017-10-27 15:55:17 -0400813 struct net_device *master;
814
815 master = dsa_dev_to_net_device(dev);
816 if (!master)
817 return -EPROBE_DEFER;
818
819 dev_put(master);
820
Vivien Didelot06e24d02017-11-03 19:05:29 -0400821 return dsa_port_parse_cpu(dp, master);
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400822 }
823
Vivien Didelot06e24d02017-11-03 19:05:29 -0400824 if (!strcmp(name, "dsa"))
825 return dsa_port_parse_dsa(dp);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400826
Vivien Didelot06e24d02017-11-03 19:05:29 -0400827 return dsa_port_parse_user(dp, name);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400828}
829
Vivien Didelot975e6e32017-11-03 19:05:27 -0400830static int dsa_switch_parse_ports(struct dsa_switch *ds,
831 struct dsa_chip_data *cd)
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800832{
833 bool valid_name_found = false;
Vivien Didelotfd223e22017-10-27 15:55:14 -0400834 struct dsa_port *dp;
835 struct device *dev;
836 const char *name;
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800837 unsigned int i;
Vivien Didelotfd223e22017-10-27 15:55:14 -0400838 int err;
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800839
840 for (i = 0; i < DSA_MAX_PORTS; i++) {
Vivien Didelotfd223e22017-10-27 15:55:14 -0400841 name = cd->port_names[i];
842 dev = cd->netdev[i];
Vivien Didelot68bb8ea2019-10-21 16:51:15 -0400843 dp = dsa_to_port(ds, i);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400844
845 if (!name)
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800846 continue;
847
Vivien Didelotfd223e22017-10-27 15:55:14 -0400848 err = dsa_port_parse(dp, name, dev);
849 if (err)
850 return err;
851
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800852 valid_name_found = true;
853 }
854
855 if (!valid_name_found && i == DSA_MAX_PORTS)
856 return -EINVAL;
857
858 return 0;
859}
860
Vivien Didelot975e6e32017-11-03 19:05:27 -0400861static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200862{
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400863 int err;
864
Vivien Didelot975e6e32017-11-03 19:05:27 -0400865 ds->cd = cd;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200866
Vivien Didelot975e6e32017-11-03 19:05:27 -0400867 /* We don't support interconnected switches nor multiple trees via
868 * platform data, so this is the unique switch of the tree.
869 */
870 ds->index = 0;
871 ds->dst = dsa_tree_touch(0);
872 if (!ds->dst)
873 return -ENOMEM;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200874
Vivien Didelotab8ccae2019-10-21 16:51:16 -0400875 err = dsa_switch_touch_ports(ds);
876 if (err)
877 return err;
878
Vivien Didelot975e6e32017-11-03 19:05:27 -0400879 return dsa_switch_parse_ports(ds, cd);
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800880}
881
Vladimir Oltean6dc43cd2020-01-25 23:01:11 +0200882static void dsa_switch_release_ports(struct dsa_switch *ds)
883{
884 struct dsa_switch_tree *dst = ds->dst;
885 struct dsa_port *dp, *next;
886
887 list_for_each_entry_safe(dp, next, &dst->ports, list) {
888 if (dp->ds != ds)
889 continue;
890 list_del(&dp->list);
891 kfree(dp);
892 }
893}
894
Vivien Didelotb4fbb342017-11-06 16:11:53 -0500895static int dsa_switch_probe(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200896{
Vivien Didelot8e5cb842019-10-30 22:09:17 -0400897 struct dsa_switch_tree *dst;
Colin Ian King556f1242019-10-24 11:32:18 +0100898 struct dsa_chip_data *pdata;
899 struct device_node *np;
Vivien Didelot34c09a82017-11-06 16:11:51 -0500900 int err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200901
Vivien Didelot7e99e342019-10-21 16:51:30 -0400902 if (!ds->dev)
903 return -ENODEV;
904
Colin Ian King556f1242019-10-24 11:32:18 +0100905 pdata = ds->dev->platform_data;
906 np = ds->dev->of_node;
907
Vivien Didelot7e99e342019-10-21 16:51:30 -0400908 if (!ds->num_ports)
909 return -EINVAL;
910
Vladimir Oltean6dc43cd2020-01-25 23:01:11 +0200911 if (np) {
Vivien Didelot975e6e32017-11-03 19:05:27 -0400912 err = dsa_switch_parse_of(ds, np);
Vladimir Oltean6dc43cd2020-01-25 23:01:11 +0200913 if (err)
914 dsa_switch_release_ports(ds);
915 } else if (pdata) {
Vivien Didelot975e6e32017-11-03 19:05:27 -0400916 err = dsa_switch_parse(ds, pdata);
Vladimir Oltean6dc43cd2020-01-25 23:01:11 +0200917 if (err)
918 dsa_switch_release_ports(ds);
919 } else {
Vivien Didelot975e6e32017-11-03 19:05:27 -0400920 err = -ENODEV;
Vladimir Oltean6dc43cd2020-01-25 23:01:11 +0200921 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200922
Vivien Didelot975e6e32017-11-03 19:05:27 -0400923 if (err)
924 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200925
Vivien Didelot8e5cb842019-10-30 22:09:17 -0400926 dst = ds->dst;
927 dsa_tree_get(dst);
928 err = dsa_tree_setup(dst);
Vladimir Oltean6dc43cd2020-01-25 23:01:11 +0200929 if (err) {
930 dsa_switch_release_ports(ds);
Vivien Didelot8e5cb842019-10-30 22:09:17 -0400931 dsa_tree_put(dst);
Vladimir Oltean6dc43cd2020-01-25 23:01:11 +0200932 }
Vivien Didelot8e5cb842019-10-30 22:09:17 -0400933
934 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200935}
936
Vivien Didelot23c9ee42017-05-26 18:12:51 -0400937int dsa_register_switch(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200938{
939 int err;
940
941 mutex_lock(&dsa2_mutex);
Vivien Didelotb4fbb342017-11-06 16:11:53 -0500942 err = dsa_switch_probe(ds);
Vivien Didelot9e741042017-11-24 11:36:06 -0500943 dsa_tree_put(ds->dst);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200944 mutex_unlock(&dsa2_mutex);
945
946 return err;
947}
948EXPORT_SYMBOL_GPL(dsa_register_switch);
949
Vivien Didelotb4fbb342017-11-06 16:11:53 -0500950static void dsa_switch_remove(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200951{
952 struct dsa_switch_tree *dst = ds->dst;
Vivien Didelot05f294a2019-10-21 16:51:29 -0400953
Florian Fainellic058f6d2019-11-02 20:13:26 -0700954 dsa_tree_teardown(dst);
Vladimir Oltean6dc43cd2020-01-25 23:01:11 +0200955 dsa_switch_release_ports(ds);
Vivien Didelot8e5cb842019-10-30 22:09:17 -0400956 dsa_tree_put(dst);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200957}
958
959void dsa_unregister_switch(struct dsa_switch *ds)
960{
961 mutex_lock(&dsa2_mutex);
Vivien Didelotb4fbb342017-11-06 16:11:53 -0500962 dsa_switch_remove(ds);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200963 mutex_unlock(&dsa2_mutex);
964}
965EXPORT_SYMBOL_GPL(dsa_unregister_switch);