blob: 271a97ef5bf64ad76d7e0dd7f9f67c16349c8ae1 [file] [log] [blame]
Andrew Lunn83c0afa2016-06-04 21:17:07 +02001/*
2 * net/dsa/dsa2.c - Hardware switch handling, binding version 2
3 * Copyright (c) 2008-2009 Marvell Semiconductor
4 * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
5 * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13#include <linux/device.h>
14#include <linux/err.h>
15#include <linux/list.h>
Andrew Lunnc6e970a2017-03-28 23:45:06 +020016#include <linux/netdevice.h>
Andrew Lunn83c0afa2016-06-04 21:17:07 +020017#include <linux/slab.h>
18#include <linux/rtnetlink.h>
Andrew Lunn83c0afa2016-06-04 21:17:07 +020019#include <linux/of.h>
20#include <linux/of_net.h>
Vivien Didelotea5dd342017-05-17 15:46:03 -040021
Andrew Lunn83c0afa2016-06-04 21:17:07 +020022#include "dsa_priv.h"
23
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040024static LIST_HEAD(dsa_tree_list);
Andrew Lunn83c0afa2016-06-04 21:17:07 +020025static DEFINE_MUTEX(dsa2_mutex);
26
Andrew Lunn96567d52017-03-28 23:45:07 +020027static const struct devlink_ops dsa_devlink_ops = {
28};
29
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040030static struct dsa_switch_tree *dsa_tree_find(int index)
Andrew Lunn83c0afa2016-06-04 21:17:07 +020031{
32 struct dsa_switch_tree *dst;
33
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040034 list_for_each_entry(dst, &dsa_tree_list, list)
Vivien Didelot8e5bf972017-11-03 19:05:22 -040035 if (dst->index == index)
Andrew Lunn83c0afa2016-06-04 21:17:07 +020036 return dst;
Vivien Didelot8e5bf972017-11-03 19:05:22 -040037
Andrew Lunn83c0afa2016-06-04 21:17:07 +020038 return NULL;
39}
40
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040041static struct dsa_switch_tree *dsa_tree_alloc(int index)
Andrew Lunn83c0afa2016-06-04 21:17:07 +020042{
43 struct dsa_switch_tree *dst;
44
45 dst = kzalloc(sizeof(*dst), GFP_KERNEL);
46 if (!dst)
47 return NULL;
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040048
Vivien Didelot49463b72017-11-03 19:05:21 -040049 dst->index = index;
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040050
Andrew Lunn83c0afa2016-06-04 21:17:07 +020051 INIT_LIST_HEAD(&dst->list);
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040052 list_add_tail(&dsa_tree_list, &dst->list);
Vivien Didelot8e5bf972017-11-03 19:05:22 -040053
54 /* Initialize the reference counter to the number of switches, not 1 */
Andrew Lunn83c0afa2016-06-04 21:17:07 +020055 kref_init(&dst->refcount);
Vivien Didelot8e5bf972017-11-03 19:05:22 -040056 refcount_set(&dst->refcount.refcount, 0);
Andrew Lunn83c0afa2016-06-04 21:17:07 +020057
58 return dst;
59}
60
Vivien Didelot65254102017-11-03 19:05:23 -040061static void dsa_tree_free(struct dsa_switch_tree *dst)
62{
63 list_del(&dst->list);
64 kfree(dst);
65}
66
Vivien Didelot1ca28ec2017-11-03 19:05:24 -040067static struct dsa_switch_tree *dsa_tree_touch(int index)
68{
69 struct dsa_switch_tree *dst;
70
71 dst = dsa_tree_find(index);
72 if (!dst)
73 dst = dsa_tree_alloc(index);
74
75 return dst;
76}
77
Vivien Didelot65254102017-11-03 19:05:23 -040078static void dsa_tree_get(struct dsa_switch_tree *dst)
79{
80 kref_get(&dst->refcount);
81}
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{
94 kref_put(&dst->refcount, dsa_tree_release);
95}
96
Florian Fainelli71e0bbd2017-02-04 13:02:43 -080097/* For platform data configurations, we need to have a valid name argument to
98 * differentiate a disabled port from an enabled one
99 */
Florian Fainelli293784a2017-01-26 10:45:52 -0800100static bool dsa_port_is_valid(struct dsa_port *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200101{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400102 return port->type != DSA_PORT_TYPE_UNUSED;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200103}
104
Florian Fainelli293784a2017-01-26 10:45:52 -0800105static bool dsa_port_is_dsa(struct dsa_port *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200106{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400107 return port->type == DSA_PORT_TYPE_DSA;
Florian Fainelli293784a2017-01-26 10:45:52 -0800108}
109
110static bool dsa_port_is_cpu(struct dsa_port *port)
111{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400112 return port->type == DSA_PORT_TYPE_CPU;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200113}
114
Florian Fainelli3512a8e2017-01-26 10:45:53 -0800115static bool dsa_ds_find_port_dn(struct dsa_switch *ds,
116 struct device_node *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200117{
118 u32 index;
119
Vivien Didelot26895e22017-01-27 15:29:37 -0500120 for (index = 0; index < ds->num_ports; index++)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200121 if (ds->ports[index].dn == port)
122 return true;
123 return false;
124}
125
Florian Fainelli3512a8e2017-01-26 10:45:53 -0800126static struct dsa_switch *dsa_dst_find_port_dn(struct dsa_switch_tree *dst,
127 struct device_node *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200128{
129 struct dsa_switch *ds;
130 u32 index;
131
132 for (index = 0; index < DSA_MAX_SWITCHES; index++) {
133 ds = dst->ds[index];
134 if (!ds)
135 continue;
136
Florian Fainelli3512a8e2017-01-26 10:45:53 -0800137 if (dsa_ds_find_port_dn(ds, port))
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200138 return ds;
139 }
140
141 return NULL;
142}
143
144static int dsa_port_complete(struct dsa_switch_tree *dst,
145 struct dsa_switch *src_ds,
Florian Fainelli293784a2017-01-26 10:45:52 -0800146 struct dsa_port *port,
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200147 u32 src_port)
148{
149 struct device_node *link;
150 int index;
151 struct dsa_switch *dst_ds;
152
153 for (index = 0;; index++) {
Florian Fainelli293784a2017-01-26 10:45:52 -0800154 link = of_parse_phandle(port->dn, "link", index);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200155 if (!link)
156 break;
157
Florian Fainelli3512a8e2017-01-26 10:45:53 -0800158 dst_ds = dsa_dst_find_port_dn(dst, link);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200159 of_node_put(link);
160
161 if (!dst_ds)
162 return 1;
163
164 src_ds->rtable[dst_ds->index] = src_port;
165 }
166
167 return 0;
168}
169
170/* A switch is complete if all the DSA ports phandles point to ports
171 * known in the tree. A return value of 1 means the tree is not
172 * complete. This is not an error condition. A value of 0 is
173 * success.
174 */
175static int dsa_ds_complete(struct dsa_switch_tree *dst, struct dsa_switch *ds)
176{
Florian Fainelli293784a2017-01-26 10:45:52 -0800177 struct dsa_port *port;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200178 u32 index;
179 int err;
180
Vivien Didelot26895e22017-01-27 15:29:37 -0500181 for (index = 0; index < ds->num_ports; index++) {
Florian Fainelli293784a2017-01-26 10:45:52 -0800182 port = &ds->ports[index];
183 if (!dsa_port_is_valid(port))
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200184 continue;
185
186 if (!dsa_port_is_dsa(port))
187 continue;
188
189 err = dsa_port_complete(dst, ds, port, index);
190 if (err != 0)
191 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200192 }
193
194 return 0;
195}
196
197/* A tree is complete if all the DSA ports phandles point to ports
198 * known in the tree. A return value of 1 means the tree is not
199 * complete. This is not an error condition. A value of 0 is
200 * success.
201 */
202static int dsa_dst_complete(struct dsa_switch_tree *dst)
203{
204 struct dsa_switch *ds;
205 u32 index;
206 int err;
207
208 for (index = 0; index < DSA_MAX_SWITCHES; index++) {
209 ds = dst->ds[index];
210 if (!ds)
211 continue;
212
213 err = dsa_ds_complete(dst, ds);
214 if (err != 0)
215 return err;
216 }
217
218 return 0;
219}
220
Florian Fainellie41c1b52017-06-02 12:31:22 -0700221static int dsa_dsa_port_apply(struct dsa_port *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200222{
Florian Fainellie41c1b52017-06-02 12:31:22 -0700223 struct dsa_switch *ds = port->ds;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200224 int err;
225
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400226 err = dsa_port_fixed_link_register_of(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200227 if (err) {
228 dev_warn(ds->dev, "Failed to setup dsa port %d: %d\n",
Florian Fainellie41c1b52017-06-02 12:31:22 -0700229 port->index, err);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200230 return err;
231 }
232
Florian Fainellie41c1b52017-06-02 12:31:22 -0700233 memset(&port->devlink_port, 0, sizeof(port->devlink_port));
Andrew Lunn96567d52017-03-28 23:45:07 +0200234
Florian Fainellie41c1b52017-06-02 12:31:22 -0700235 return devlink_port_register(ds->devlink, &port->devlink_port,
236 port->index);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200237}
238
Florian Fainellie41c1b52017-06-02 12:31:22 -0700239static void dsa_dsa_port_unapply(struct dsa_port *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200240{
Florian Fainellie41c1b52017-06-02 12:31:22 -0700241 devlink_port_unregister(&port->devlink_port);
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400242 dsa_port_fixed_link_unregister_of(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200243}
244
Florian Fainellie41c1b52017-06-02 12:31:22 -0700245static int dsa_cpu_port_apply(struct dsa_port *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200246{
Florian Fainellie41c1b52017-06-02 12:31:22 -0700247 struct dsa_switch *ds = port->ds;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200248 int err;
249
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400250 err = dsa_port_fixed_link_register_of(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200251 if (err) {
252 dev_warn(ds->dev, "Failed to setup cpu port %d: %d\n",
Florian Fainellie41c1b52017-06-02 12:31:22 -0700253 port->index, err);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200254 return err;
255 }
256
Florian Fainellie41c1b52017-06-02 12:31:22 -0700257 memset(&port->devlink_port, 0, sizeof(port->devlink_port));
258 err = devlink_port_register(ds->devlink, &port->devlink_port,
259 port->index);
Andrew Lunn96567d52017-03-28 23:45:07 +0200260 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200261}
262
Florian Fainellie41c1b52017-06-02 12:31:22 -0700263static void dsa_cpu_port_unapply(struct dsa_port *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200264{
Florian Fainellie41c1b52017-06-02 12:31:22 -0700265 devlink_port_unregister(&port->devlink_port);
Vivien Didelot57ab1ca2017-10-26 10:50:07 -0400266 dsa_port_fixed_link_unregister_of(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200267}
268
Florian Fainellie41c1b52017-06-02 12:31:22 -0700269static int dsa_user_port_apply(struct dsa_port *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200270{
Florian Fainellie41c1b52017-06-02 12:31:22 -0700271 struct dsa_switch *ds = port->ds;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200272 int err;
273
Vivien Didelot951259aa2017-10-27 15:55:19 -0400274 err = dsa_slave_create(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200275 if (err) {
276 dev_warn(ds->dev, "Failed to create slave %d: %d\n",
Florian Fainellie41c1b52017-06-02 12:31:22 -0700277 port->index, err);
Vivien Didelotf8b8b1c2017-10-16 11:12:18 -0400278 port->slave = NULL;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200279 return err;
280 }
281
Florian Fainellie41c1b52017-06-02 12:31:22 -0700282 memset(&port->devlink_port, 0, sizeof(port->devlink_port));
283 err = devlink_port_register(ds->devlink, &port->devlink_port,
284 port->index);
Andrew Lunn96567d52017-03-28 23:45:07 +0200285 if (err)
286 return err;
287
Vivien Didelotf8b8b1c2017-10-16 11:12:18 -0400288 devlink_port_type_eth_set(&port->devlink_port, port->slave);
Andrew Lunn96567d52017-03-28 23:45:07 +0200289
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200290 return 0;
291}
292
Florian Fainellie41c1b52017-06-02 12:31:22 -0700293static void dsa_user_port_unapply(struct dsa_port *port)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200294{
Florian Fainellie41c1b52017-06-02 12:31:22 -0700295 devlink_port_unregister(&port->devlink_port);
Vivien Didelotf8b8b1c2017-10-16 11:12:18 -0400296 if (port->slave) {
297 dsa_slave_destroy(port->slave);
298 port->slave = NULL;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200299 }
300}
301
302static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds)
303{
Florian Fainelli293784a2017-01-26 10:45:52 -0800304 struct dsa_port *port;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200305 u32 index;
306 int err;
307
Florian Fainelli6e830d82016-06-07 16:32:39 -0700308 /* Initialize ds->phys_mii_mask before registering the slave MDIO bus
Vivien Didelot9d490b42016-08-23 12:38:56 -0400309 * driver and before ops->setup() has run, since the switch drivers and
Florian Fainelli6e830d82016-06-07 16:32:39 -0700310 * the slave MDIO bus driver rely on these values for probing PHY
311 * devices or not
312 */
Vivien Didelot02bc6e52017-10-26 11:22:56 -0400313 ds->phys_mii_mask |= dsa_user_ports(ds);
Florian Fainelli6e830d82016-06-07 16:32:39 -0700314
Andrew Lunn96567d52017-03-28 23:45:07 +0200315 /* Add the switch to devlink before calling setup, so that setup can
316 * add dpipe tables
317 */
318 ds->devlink = devlink_alloc(&dsa_devlink_ops, 0);
319 if (!ds->devlink)
320 return -ENOMEM;
321
322 err = devlink_register(ds->devlink, ds->dev);
323 if (err)
324 return err;
325
Vivien Didelot9d490b42016-08-23 12:38:56 -0400326 err = ds->ops->setup(ds);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200327 if (err < 0)
328 return err;
329
Vivien Didelotf515f192017-02-03 13:20:20 -0500330 err = dsa_switch_register_notifier(ds);
331 if (err)
332 return err;
333
Vivien Didelot9d490b42016-08-23 12:38:56 -0400334 if (!ds->slave_mii_bus && ds->ops->phy_read) {
Florian Fainelli1eb59442016-06-07 16:32:40 -0700335 ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev);
336 if (!ds->slave_mii_bus)
337 return -ENOMEM;
338
339 dsa_slave_mii_bus_init(ds);
340
341 err = mdiobus_register(ds->slave_mii_bus);
342 if (err < 0)
343 return err;
344 }
345
Vivien Didelot26895e22017-01-27 15:29:37 -0500346 for (index = 0; index < ds->num_ports; index++) {
Florian Fainelli293784a2017-01-26 10:45:52 -0800347 port = &ds->ports[index];
348 if (!dsa_port_is_valid(port))
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200349 continue;
350
351 if (dsa_port_is_dsa(port)) {
Florian Fainellie41c1b52017-06-02 12:31:22 -0700352 err = dsa_dsa_port_apply(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200353 if (err)
354 return err;
355 continue;
356 }
357
358 if (dsa_port_is_cpu(port)) {
Florian Fainellie41c1b52017-06-02 12:31:22 -0700359 err = dsa_cpu_port_apply(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200360 if (err)
361 return err;
362 continue;
363 }
364
Florian Fainellie41c1b52017-06-02 12:31:22 -0700365 err = dsa_user_port_apply(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200366 if (err)
367 continue;
368 }
369
370 return 0;
371}
372
373static void dsa_ds_unapply(struct dsa_switch_tree *dst, struct dsa_switch *ds)
374{
Florian Fainelli293784a2017-01-26 10:45:52 -0800375 struct dsa_port *port;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200376 u32 index;
377
Vivien Didelot26895e22017-01-27 15:29:37 -0500378 for (index = 0; index < ds->num_ports; index++) {
Florian Fainelli293784a2017-01-26 10:45:52 -0800379 port = &ds->ports[index];
380 if (!dsa_port_is_valid(port))
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200381 continue;
382
383 if (dsa_port_is_dsa(port)) {
Florian Fainellie41c1b52017-06-02 12:31:22 -0700384 dsa_dsa_port_unapply(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200385 continue;
386 }
387
388 if (dsa_port_is_cpu(port)) {
Florian Fainellie41c1b52017-06-02 12:31:22 -0700389 dsa_cpu_port_unapply(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200390 continue;
391 }
392
Florian Fainellie41c1b52017-06-02 12:31:22 -0700393 dsa_user_port_unapply(port);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200394 }
Florian Fainelli1eb59442016-06-07 16:32:40 -0700395
Vivien Didelot9d490b42016-08-23 12:38:56 -0400396 if (ds->slave_mii_bus && ds->ops->phy_read)
Florian Fainelli1eb59442016-06-07 16:32:40 -0700397 mdiobus_unregister(ds->slave_mii_bus);
Vivien Didelotf515f192017-02-03 13:20:20 -0500398
399 dsa_switch_unregister_notifier(ds);
Andrew Lunn96567d52017-03-28 23:45:07 +0200400
401 if (ds->devlink) {
402 devlink_unregister(ds->devlink);
403 devlink_free(ds->devlink);
404 ds->devlink = NULL;
405 }
406
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200407}
408
409static int dsa_dst_apply(struct dsa_switch_tree *dst)
410{
411 struct dsa_switch *ds;
412 u32 index;
413 int err;
414
415 for (index = 0; index < DSA_MAX_SWITCHES; index++) {
416 ds = dst->ds[index];
417 if (!ds)
418 continue;
419
420 err = dsa_ds_apply(dst, ds);
421 if (err)
422 return err;
423 }
424
425 /* If we use a tagging format that doesn't have an ethertype
426 * field, make sure that all packets from this point on get
427 * sent to the tag format's receive function.
428 */
429 wmb();
Vivien Didelotf8b8b1c2017-10-16 11:12:18 -0400430 dst->cpu_dp->master->dsa_ptr = dst->cpu_dp;
Vivien Didelot19435632017-09-19 11:56:59 -0400431
Vivien Didelotf8b8b1c2017-10-16 11:12:18 -0400432 err = dsa_master_ethtool_setup(dst->cpu_dp->master);
Vivien Didelot19435632017-09-19 11:56:59 -0400433 if (err)
434 return err;
435
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200436 dst->applied = true;
437
438 return 0;
439}
440
441static void dsa_dst_unapply(struct dsa_switch_tree *dst)
442{
443 struct dsa_switch *ds;
444 u32 index;
445
446 if (!dst->applied)
447 return;
448
Vivien Didelotf8b8b1c2017-10-16 11:12:18 -0400449 dsa_master_ethtool_restore(dst->cpu_dp->master);
Vivien Didelot19435632017-09-19 11:56:59 -0400450
Vivien Didelotf8b8b1c2017-10-16 11:12:18 -0400451 dst->cpu_dp->master->dsa_ptr = NULL;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200452
453 /* If we used a tagging format that doesn't have an ethertype
454 * field, make sure that all packets from this point get sent
455 * without the tag and go through the regular receive path.
456 */
457 wmb();
458
459 for (index = 0; index < DSA_MAX_SWITCHES; index++) {
460 ds = dst->ds[index];
461 if (!ds)
462 continue;
463
464 dsa_ds_unapply(dst, ds);
465 }
466
Vivien Didelotcd8d7dd2017-09-19 11:56:58 -0400467 dst->cpu_dp = NULL;
Florian Fainelli0c73c522016-06-07 16:32:42 -0700468
Vivien Didelot49463b72017-11-03 19:05:21 -0400469 pr_info("DSA: tree %d unapplied\n", dst->index);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200470 dst->applied = false;
471}
472
Vivien Didelot6da2a942017-11-03 19:05:25 -0400473static void dsa_tree_remove_switch(struct dsa_switch_tree *dst,
474 unsigned int index)
475{
476 dst->ds[index] = NULL;
477 dsa_tree_put(dst);
478}
479
480static int dsa_tree_add_switch(struct dsa_switch_tree *dst,
481 struct dsa_switch *ds)
482{
483 unsigned int index = ds->index;
484
485 if (dst->ds[index])
486 return -EBUSY;
487
488 dsa_tree_get(dst);
489 dst->ds[index] = ds;
490
491 return 0;
492}
493
Vivien Didelot06e24d02017-11-03 19:05:29 -0400494static int dsa_port_parse_user(struct dsa_port *dp, const char *name)
495{
496 if (!name)
497 name = "eth%d";
498
499 dp->type = DSA_PORT_TYPE_USER;
500 dp->name = name;
501
502 return 0;
503}
504
505static int dsa_port_parse_dsa(struct dsa_port *dp)
506{
507 dp->type = DSA_PORT_TYPE_DSA;
508
509 return 0;
510}
511
512static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)
513{
514 dp->type = DSA_PORT_TYPE_CPU;
515 dp->master = master;
516
517 return 0;
518}
519
Florian Fainelli293784a2017-01-26 10:45:52 -0800520static int dsa_cpu_parse(struct dsa_port *port, u32 index,
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200521 struct dsa_switch_tree *dst,
522 struct dsa_switch *ds)
523{
Vivien Didelot62fc9582017-09-29 17:19:17 -0400524 const struct dsa_device_ops *tag_ops;
Andrew Lunn7b314362016-08-22 16:01:01 +0200525 enum dsa_tag_protocol tag_protocol;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200526
Vivien Didelotcbabb0a2017-10-27 15:55:17 -0400527 if (!dst->cpu_dp)
Vivien Didelot8b0d3ea2017-05-16 14:10:33 -0400528 dst->cpu_dp = port;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200529
Florian Fainelli9f9e7722017-07-24 10:49:23 -0700530 tag_protocol = ds->ops->get_tag_protocol(ds);
Vivien Didelot62fc9582017-09-29 17:19:17 -0400531 tag_ops = dsa_resolve_tag_protocol(tag_protocol);
532 if (IS_ERR(tag_ops)) {
Florian Fainelli9f9e7722017-07-24 10:49:23 -0700533 dev_warn(ds->dev, "No tagger for this switch\n");
Vivien Didelot62fc9582017-09-29 17:19:17 -0400534 return PTR_ERR(tag_ops);
Florian Fainelli9f9e7722017-07-24 10:49:23 -0700535 }
536
Vivien Didelot15240242017-09-29 17:19:18 -0400537 dst->cpu_dp->tag_ops = tag_ops;
Vivien Didelot3e41f932017-09-29 17:19:19 -0400538
539 /* Make a few copies for faster access in master receive hot path */
540 dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv;
Vivien Didelot3e41f932017-09-29 17:19:19 -0400541 dst->cpu_dp->dst = dst;
Florian Fainelli9f9e7722017-07-24 10:49:23 -0700542
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200543 return 0;
544}
545
546static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds)
547{
Florian Fainelli293784a2017-01-26 10:45:52 -0800548 struct dsa_port *port;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200549 u32 index;
550 int err;
551
Vivien Didelot26895e22017-01-27 15:29:37 -0500552 for (index = 0; index < ds->num_ports; index++) {
Florian Fainelli293784a2017-01-26 10:45:52 -0800553 port = &ds->ports[index];
Florian Fainelli14be36c2017-06-02 12:31:23 -0700554 if (!dsa_port_is_valid(port) ||
555 dsa_port_is_dsa(port))
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200556 continue;
557
558 if (dsa_port_is_cpu(port)) {
559 err = dsa_cpu_parse(port, index, dst, ds);
560 if (err)
561 return err;
562 }
Florian Fainelli14be36c2017-06-02 12:31:23 -0700563
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200564 }
565
Vivien Didelot49463b72017-11-03 19:05:21 -0400566 pr_info("DSA: switch %d %d parsed\n", dst->index, ds->index);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200567
568 return 0;
569}
570
571static int dsa_dst_parse(struct dsa_switch_tree *dst)
572{
573 struct dsa_switch *ds;
Vivien Didelote4b77782017-06-15 15:06:54 -0400574 struct dsa_port *dp;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200575 u32 index;
Vivien Didelote4b77782017-06-15 15:06:54 -0400576 int port;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200577 int err;
578
579 for (index = 0; index < DSA_MAX_SWITCHES; index++) {
580 ds = dst->ds[index];
581 if (!ds)
582 continue;
583
584 err = dsa_ds_parse(dst, ds);
585 if (err)
586 return err;
587 }
588
Florian Fainellic7848392017-08-28 17:10:51 -0700589 if (!dst->cpu_dp) {
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200590 pr_warn("Tree has no master device\n");
591 return -EINVAL;
592 }
593
Vivien Didelote4b77782017-06-15 15:06:54 -0400594 /* Assign the default CPU port to all ports of the fabric */
595 for (index = 0; index < DSA_MAX_SWITCHES; index++) {
596 ds = dst->ds[index];
597 if (!ds)
598 continue;
599
600 for (port = 0; port < ds->num_ports; port++) {
601 dp = &ds->ports[port];
602 if (!dsa_port_is_valid(dp) ||
603 dsa_port_is_dsa(dp) ||
604 dsa_port_is_cpu(dp))
605 continue;
606
607 dp->cpu_dp = dst->cpu_dp;
608 }
609 }
610
Vivien Didelot49463b72017-11-03 19:05:21 -0400611 pr_info("DSA: tree %d parsed\n", dst->index);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200612
613 return 0;
614}
615
Vivien Didelotfd223e22017-10-27 15:55:14 -0400616static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn)
617{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400618 struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0);
Vivien Didelot1838fa82017-10-27 15:55:18 -0400619 const char *name = of_get_property(dn, "label", NULL);
Vivien Didelot54df6fa2017-11-03 19:05:28 -0400620 bool link = of_property_read_bool(dn, "link");
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400621
Vivien Didelot06e24d02017-11-03 19:05:29 -0400622 dp->dn = dn;
623
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400624 if (ethernet) {
Vivien Didelotcbabb0a2017-10-27 15:55:17 -0400625 struct net_device *master;
626
627 master = of_find_net_device_by_node(ethernet);
628 if (!master)
629 return -EPROBE_DEFER;
630
Vivien Didelot06e24d02017-11-03 19:05:29 -0400631 return dsa_port_parse_cpu(dp, master);
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400632 }
633
Vivien Didelot06e24d02017-11-03 19:05:29 -0400634 if (link)
635 return dsa_port_parse_dsa(dp);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400636
Vivien Didelot06e24d02017-11-03 19:05:29 -0400637 return dsa_port_parse_user(dp, name);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400638}
639
Vivien Didelot975e6e32017-11-03 19:05:27 -0400640static int dsa_switch_parse_ports_of(struct dsa_switch *ds,
641 struct device_node *dn)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200642{
Vivien Didelot5b32fe02017-10-27 15:55:13 -0400643 struct device_node *ports, *port;
Vivien Didelotfd223e22017-10-27 15:55:14 -0400644 struct dsa_port *dp;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200645 u32 reg;
Vivien Didelot5b32fe02017-10-27 15:55:13 -0400646 int err;
647
648 ports = of_get_child_by_name(dn, "ports");
649 if (!ports) {
650 dev_err(ds->dev, "no ports child node found\n");
651 return -EINVAL;
652 }
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200653
654 for_each_available_child_of_node(ports, port) {
655 err = of_property_read_u32(port, "reg", &reg);
656 if (err)
657 return err;
658
Vivien Didelot26895e22017-01-27 15:29:37 -0500659 if (reg >= ds->num_ports)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200660 return -EINVAL;
661
Vivien Didelotfd223e22017-10-27 15:55:14 -0400662 dp = &ds->ports[reg];
663
664 err = dsa_port_parse_of(dp, port);
665 if (err)
666 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200667 }
668
669 return 0;
670}
671
Vivien Didelot975e6e32017-11-03 19:05:27 -0400672static int dsa_switch_parse_member_of(struct dsa_switch *ds,
673 struct device_node *dn)
674{
675 u32 m[2] = { 0, 0 };
676 int sz;
677
678 /* Don't error out if this optional property isn't found */
679 sz = of_property_read_variable_u32_array(dn, "dsa,member", m, 2, 2);
680 if (sz < 0 && sz != -EINVAL)
681 return sz;
682
683 ds->index = m[1];
684 if (ds->index >= DSA_MAX_SWITCHES)
685 return -EINVAL;
686
687 ds->dst = dsa_tree_touch(m[0]);
688 if (!ds->dst)
689 return -ENOMEM;
690
691 return 0;
692}
693
694static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn)
695{
696 int err;
697
698 err = dsa_switch_parse_member_of(ds, dn);
699 if (err)
700 return err;
701
702 return dsa_switch_parse_ports_of(ds, dn);
703}
704
Vivien Didelotfd223e22017-10-27 15:55:14 -0400705static int dsa_port_parse(struct dsa_port *dp, const char *name,
706 struct device *dev)
707{
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400708 if (!strcmp(name, "cpu")) {
Vivien Didelotcbabb0a2017-10-27 15:55:17 -0400709 struct net_device *master;
710
711 master = dsa_dev_to_net_device(dev);
712 if (!master)
713 return -EPROBE_DEFER;
714
715 dev_put(master);
716
Vivien Didelot06e24d02017-11-03 19:05:29 -0400717 return dsa_port_parse_cpu(dp, master);
Vivien Didelot6d4e5c52017-10-27 15:55:15 -0400718 }
719
Vivien Didelot06e24d02017-11-03 19:05:29 -0400720 if (!strcmp(name, "dsa"))
721 return dsa_port_parse_dsa(dp);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400722
Vivien Didelot06e24d02017-11-03 19:05:29 -0400723 return dsa_port_parse_user(dp, name);
Vivien Didelotfd223e22017-10-27 15:55:14 -0400724}
725
Vivien Didelot975e6e32017-11-03 19:05:27 -0400726static int dsa_switch_parse_ports(struct dsa_switch *ds,
727 struct dsa_chip_data *cd)
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800728{
729 bool valid_name_found = false;
Vivien Didelotfd223e22017-10-27 15:55:14 -0400730 struct dsa_port *dp;
731 struct device *dev;
732 const char *name;
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800733 unsigned int i;
Vivien Didelotfd223e22017-10-27 15:55:14 -0400734 int err;
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800735
736 for (i = 0; i < DSA_MAX_PORTS; i++) {
Vivien Didelotfd223e22017-10-27 15:55:14 -0400737 name = cd->port_names[i];
738 dev = cd->netdev[i];
739 dp = &ds->ports[i];
740
741 if (!name)
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800742 continue;
743
Vivien Didelotfd223e22017-10-27 15:55:14 -0400744 err = dsa_port_parse(dp, name, dev);
745 if (err)
746 return err;
747
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800748 valid_name_found = true;
749 }
750
751 if (!valid_name_found && i == DSA_MAX_PORTS)
752 return -EINVAL;
753
754 return 0;
755}
756
Vivien Didelot975e6e32017-11-03 19:05:27 -0400757static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200758{
Vivien Didelot975e6e32017-11-03 19:05:27 -0400759 ds->cd = cd;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200760
Vivien Didelot975e6e32017-11-03 19:05:27 -0400761 /* We don't support interconnected switches nor multiple trees via
762 * platform data, so this is the unique switch of the tree.
763 */
764 ds->index = 0;
765 ds->dst = dsa_tree_touch(0);
766 if (!ds->dst)
767 return -ENOMEM;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200768
Vivien Didelot975e6e32017-11-03 19:05:27 -0400769 return dsa_switch_parse_ports(ds, cd);
Florian Fainelli71e0bbd2017-02-04 13:02:43 -0800770}
771
Vivien Didelot23c9ee42017-05-26 18:12:51 -0400772static int _dsa_register_switch(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200773{
Vivien Didelot23c9ee42017-05-26 18:12:51 -0400774 struct dsa_chip_data *pdata = ds->dev->platform_data;
775 struct device_node *np = ds->dev->of_node;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200776 struct dsa_switch_tree *dst;
Vivien Didelot975e6e32017-11-03 19:05:27 -0400777 unsigned int index;
Vivien Didelotd3902382016-07-06 20:03:54 -0400778 int i, err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200779
Vivien Didelot975e6e32017-11-03 19:05:27 -0400780 if (np)
781 err = dsa_switch_parse_of(ds, np);
782 else if (pdata)
783 err = dsa_switch_parse(ds, pdata);
784 else
785 err = -ENODEV;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200786
Vivien Didelot975e6e32017-11-03 19:05:27 -0400787 if (err)
788 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200789
Vivien Didelot975e6e32017-11-03 19:05:27 -0400790 index = ds->index;
791 dst = ds->dst;
Vivien Didelot0eefe2c2017-11-03 19:05:26 -0400792
Vivien Didelotd3902382016-07-06 20:03:54 -0400793 /* Initialize the routing table */
794 for (i = 0; i < DSA_MAX_SWITCHES; ++i)
795 ds->rtable[i] = DSA_RTABLE_NONE;
796
Vivien Didelot6da2a942017-11-03 19:05:25 -0400797 err = dsa_tree_add_switch(dst, ds);
798 if (err)
799 return err;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200800
801 err = dsa_dst_complete(dst);
802 if (err < 0)
803 goto out_del_dst;
804
Vivien Didelot8e5bf972017-11-03 19:05:22 -0400805 /* Not all switches registered yet */
806 if (err == 1)
807 return 0;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200808
809 if (dst->applied) {
810 pr_info("DSA: Disjoint trees?\n");
811 return -EINVAL;
812 }
813
814 err = dsa_dst_parse(dst);
Vivien Didelotcbabb0a2017-10-27 15:55:17 -0400815 if (err)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200816 goto out_del_dst;
817
818 err = dsa_dst_apply(dst);
819 if (err) {
820 dsa_dst_unapply(dst);
821 goto out_del_dst;
822 }
823
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200824 return 0;
825
826out_del_dst:
Vivien Didelot6da2a942017-11-03 19:05:25 -0400827 dsa_tree_remove_switch(dst, index);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200828
829 return err;
830}
831
Vivien Didelota0c02162017-01-27 15:29:36 -0500832struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n)
833{
834 size_t size = sizeof(struct dsa_switch) + n * sizeof(struct dsa_port);
835 struct dsa_switch *ds;
Vivien Didelot818be842017-01-27 15:29:38 -0500836 int i;
Vivien Didelota0c02162017-01-27 15:29:36 -0500837
838 ds = devm_kzalloc(dev, size, GFP_KERNEL);
839 if (!ds)
840 return NULL;
841
842 ds->dev = dev;
843 ds->num_ports = n;
844
Vivien Didelot818be842017-01-27 15:29:38 -0500845 for (i = 0; i < ds->num_ports; ++i) {
846 ds->ports[i].index = i;
847 ds->ports[i].ds = ds;
848 }
849
Vivien Didelota0c02162017-01-27 15:29:36 -0500850 return ds;
851}
852EXPORT_SYMBOL_GPL(dsa_switch_alloc);
853
Vivien Didelot23c9ee42017-05-26 18:12:51 -0400854int dsa_register_switch(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200855{
856 int err;
857
858 mutex_lock(&dsa2_mutex);
Vivien Didelot23c9ee42017-05-26 18:12:51 -0400859 err = _dsa_register_switch(ds);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200860 mutex_unlock(&dsa2_mutex);
861
862 return err;
863}
864EXPORT_SYMBOL_GPL(dsa_register_switch);
865
Wei Yongjun85c22ba2016-07-12 15:24:10 +0000866static void _dsa_unregister_switch(struct dsa_switch *ds)
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200867{
868 struct dsa_switch_tree *dst = ds->dst;
Vivien Didelot6da2a942017-11-03 19:05:25 -0400869 unsigned int index = ds->index;
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200870
871 dsa_dst_unapply(dst);
872
Vivien Didelot6da2a942017-11-03 19:05:25 -0400873 dsa_tree_remove_switch(dst, index);
Andrew Lunn83c0afa2016-06-04 21:17:07 +0200874}
875
876void dsa_unregister_switch(struct dsa_switch *ds)
877{
878 mutex_lock(&dsa2_mutex);
879 _dsa_unregister_switch(ds);
880 mutex_unlock(&dsa2_mutex);
881}
882EXPORT_SYMBOL_GPL(dsa_unregister_switch);