191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek/*
291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek * net/dsa/dsa.c - Hardware switch handling
3e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek * Copyright (c) 2008-2009 Marvell Semiconductor
45e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek *
691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek * This program is free software; you can redistribute it and/or modify
791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek * it under the terms of the GNU General Public License as published by
891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek * the Free Software Foundation; either version 2 of the License, or
991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek * (at your option) any later version.
1091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek */
1191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
1291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek#include <linux/list.h>
1391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek#include <linux/platform_device.h>
145a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
153a9a231d977222eea36eae091df2c358e03ac839Paul Gortmaker#include <linux/module.h>
1691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek#include <net/dsa.h>
175e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli#include <linux/of.h>
185e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli#include <linux/of_mdio.h>
195e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli#include <linux/of_platform.h>
2091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek#include "dsa_priv.h"
2191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
2291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekchar dsa_driver_version[] = "0.1";
2391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
2491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
2591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek/* switch driver registration ***********************************************/
2691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic DEFINE_MUTEX(dsa_switch_drivers_mutex);
2791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic LIST_HEAD(dsa_switch_drivers);
2891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
2991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekvoid register_switch_driver(struct dsa_switch_driver *drv)
3091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
3191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	mutex_lock(&dsa_switch_drivers_mutex);
3291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	list_add_tail(&drv->list, &dsa_switch_drivers);
3391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	mutex_unlock(&dsa_switch_drivers_mutex);
3491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
35ad293b8a218ca13a9ee3e3c98137fa301987577cBen HutchingsEXPORT_SYMBOL_GPL(register_switch_driver);
3691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
3791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekvoid unregister_switch_driver(struct dsa_switch_driver *drv)
3891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
3991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	mutex_lock(&dsa_switch_drivers_mutex);
4091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	list_del_init(&drv->list);
4191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	mutex_unlock(&dsa_switch_drivers_mutex);
4291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
43ad293b8a218ca13a9ee3e3c98137fa301987577cBen HutchingsEXPORT_SYMBOL_GPL(unregister_switch_driver);
4491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
4591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic struct dsa_switch_driver *
46b4d2394d01bc642e95b2cba956d908423c1bef77Alexander Duyckdsa_switch_probe(struct device *host_dev, int sw_addr, char **_name)
4791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
4891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	struct dsa_switch_driver *ret;
4991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	struct list_head *list;
5091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	char *name;
5191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
5291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	ret = NULL;
5391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	name = NULL;
5491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
5591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	mutex_lock(&dsa_switch_drivers_mutex);
5691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	list_for_each(list, &dsa_switch_drivers) {
5791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		struct dsa_switch_driver *drv;
5891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
5991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		drv = list_entry(list, struct dsa_switch_driver, list);
6091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
61b4d2394d01bc642e95b2cba956d908423c1bef77Alexander Duyck		name = drv->probe(host_dev, sw_addr);
6291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		if (name != NULL) {
6391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek			ret = drv;
6491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek			break;
6591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		}
6691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
6791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	mutex_unlock(&dsa_switch_drivers_mutex);
6891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
6991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	*_name = name;
7091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
7191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	return ret;
7291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
7391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
7491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
7591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek/* basic switch operations **************************************************/
7691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic struct dsa_switch *
77e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhekdsa_switch_setup(struct dsa_switch_tree *dst, int index,
78b4d2394d01bc642e95b2cba956d908423c1bef77Alexander Duyck		 struct device *parent, struct device *host_dev)
7991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
80e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	struct dsa_chip_data *pd = dst->pd->chip + index;
81e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	struct dsa_switch_driver *drv;
8291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	struct dsa_switch *ds;
8391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	int ret;
8491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	char *name;
8591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	int i;
86f9bf5a2ca6cc331f32e3dd9cf16ced7215d0e6e8Florian Fainelli	bool valid_name_found = false;
8791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
8891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	/*
8991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 * Probe for switch model.
9091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 */
91b4d2394d01bc642e95b2cba956d908423c1bef77Alexander Duyck	drv = dsa_switch_probe(host_dev, pd->sw_addr, &name);
9291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (drv == NULL) {
93e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		printk(KERN_ERR "%s[%d]: could not detect attached switch\n",
94e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		       dst->master_netdev->name, index);
9591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		return ERR_PTR(-EINVAL);
9691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
97e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	printk(KERN_INFO "%s[%d]: detected a %s switch\n",
98e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		dst->master_netdev->name, index, name);
9991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
10091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
10191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	/*
10291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 * Allocate and initialise switch state.
10391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 */
10491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	ds = kzalloc(sizeof(*ds) + drv->priv_size, GFP_KERNEL);
10591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (ds == NULL)
10691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		return ERR_PTR(-ENOMEM);
10791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
108e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	ds->dst = dst;
109e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	ds->index = index;
110e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	ds->pd = dst->pd->chip + index;
11191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	ds->drv = drv;
112b4d2394d01bc642e95b2cba956d908423c1bef77Alexander Duyck	ds->master_dev = host_dev;
11391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
11491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	/*
11591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 * Validate supplied switch configuration.
11691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 */
11791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	for (i = 0; i < DSA_MAX_PORTS; i++) {
11891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		char *name;
11991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
12091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		name = pd->port_names[i];
12191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		if (name == NULL)
12291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek			continue;
12391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
12491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		if (!strcmp(name, "cpu")) {
125e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			if (dst->cpu_switch != -1) {
12691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek				printk(KERN_ERR "multiple cpu ports?!\n");
12791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek				ret = -EINVAL;
12891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek				goto out;
12991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek			}
130e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			dst->cpu_switch = index;
131e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			dst->cpu_port = i;
132e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		} else if (!strcmp(name, "dsa")) {
133e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			ds->dsa_port_mask |= 1 << i;
13491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		} else {
135e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			ds->phys_port_mask |= 1 << i;
13691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		}
137f9bf5a2ca6cc331f32e3dd9cf16ced7215d0e6e8Florian Fainelli		valid_name_found = true;
13891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
13991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
140f9bf5a2ca6cc331f32e3dd9cf16ced7215d0e6e8Florian Fainelli	if (!valid_name_found && i == DSA_MAX_PORTS) {
141f9bf5a2ca6cc331f32e3dd9cf16ced7215d0e6e8Florian Fainelli		ret = -EINVAL;
142f9bf5a2ca6cc331f32e3dd9cf16ced7215d0e6e8Florian Fainelli		goto out;
143f9bf5a2ca6cc331f32e3dd9cf16ced7215d0e6e8Florian Fainelli	}
14491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
1450d8bcdd383b8865e752a7e8edb4712c2e3902052Florian Fainelli	/* Make the built-in MII bus mask match the number of ports,
1460d8bcdd383b8865e752a7e8edb4712c2e3902052Florian Fainelli	 * switch drivers can override this later
1470d8bcdd383b8865e752a7e8edb4712c2e3902052Florian Fainelli	 */
1480d8bcdd383b8865e752a7e8edb4712c2e3902052Florian Fainelli	ds->phys_mii_mask = ds->phys_port_mask;
1490d8bcdd383b8865e752a7e8edb4712c2e3902052Florian Fainelli
15091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	/*
151e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	 * If the CPU connects to this switch, set the switch tree
152e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	 * tagging protocol to the preferred tagging format of this
153e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	 * switch.
15491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 */
1555075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck	if (dst->cpu_switch == index) {
1565075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck		switch (drv->tag_protocol) {
1575075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck#ifdef CONFIG_NET_DSA_TAG_DSA
1585075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck		case DSA_TAG_PROTO_DSA:
1595075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck			dst->rcv = dsa_netdev_ops.rcv;
1605075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck			break;
1615075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck#endif
1625075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck#ifdef CONFIG_NET_DSA_TAG_EDSA
1635075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck		case DSA_TAG_PROTO_EDSA:
1645075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck			dst->rcv = edsa_netdev_ops.rcv;
1655075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck			break;
1665075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck#endif
1675075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck#ifdef CONFIG_NET_DSA_TAG_TRAILER
1685075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck		case DSA_TAG_PROTO_TRAILER:
1695075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck			dst->rcv = trailer_netdev_ops.rcv;
1705075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck			break;
1715075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck#endif
1725075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck#ifdef CONFIG_NET_DSA_TAG_BRCM
1735075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck		case DSA_TAG_PROTO_BRCM:
1745075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck			dst->rcv = brcm_netdev_ops.rcv;
1755075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck			break;
1765075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck#endif
177ae439286a0dec99cc8029868243689b5b5f3ff75Andrew Lunn		case DSA_TAG_PROTO_NONE:
1785075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck			break;
179ae439286a0dec99cc8029868243689b5b5f3ff75Andrew Lunn		default:
180ae439286a0dec99cc8029868243689b5b5f3ff75Andrew Lunn			ret = -ENOPROTOOPT;
181ae439286a0dec99cc8029868243689b5b5f3ff75Andrew Lunn			goto out;
1825075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck		}
18391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
1845075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck		dst->tag_protocol = drv->tag_protocol;
1855075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck	}
18691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
18791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	/*
18891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 * Do basic register setup.
18991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 */
19091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	ret = drv->setup(ds);
19191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (ret < 0)
19291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		goto out;
19391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
194e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	ret = drv->set_addr(ds, dst->master_netdev->dev_addr);
19591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (ret < 0)
19691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		goto out;
19791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
19891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	ds->slave_mii_bus = mdiobus_alloc();
19991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (ds->slave_mii_bus == NULL) {
20091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		ret = -ENOMEM;
20191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		goto out;
20291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
20391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	dsa_slave_mii_bus_init(ds);
20491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
20591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	ret = mdiobus_register(ds->slave_mii_bus);
20691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (ret < 0)
20791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		goto out_free;
20891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
20991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
21091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	/*
21191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 * Create network devices for physical switch ports.
21291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	 */
21391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	for (i = 0; i < DSA_MAX_PORTS; i++) {
21491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		struct net_device *slave_dev;
21591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
216e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		if (!(ds->phys_port_mask & (1 << i)))
21791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek			continue;
21891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
21991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]);
22091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		if (slave_dev == NULL) {
221e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			printk(KERN_ERR "%s[%d]: can't create dsa "
222e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			       "slave device for port %d(%s)\n",
223e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			       dst->master_netdev->name,
224e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			       index, i, pd->port_names[i]);
22591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek			continue;
22691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		}
22791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
22891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		ds->ports[i] = slave_dev;
22991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
23091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
23191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	return ds;
23291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
23391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekout_free:
23491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	mdiobus_free(ds->slave_mii_bus);
23591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekout:
23691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	kfree(ds);
23791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	return ERR_PTR(ret);
23891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
23991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
24091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic void dsa_switch_destroy(struct dsa_switch *ds)
24191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
24291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
24391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
244e506d405ac7d34d03996c97ac68aa2ac010be64aThierry Reding#ifdef CONFIG_PM_SLEEP
2452446254915a7d6f08bba9a755a34cc0402880472Florian Fainellistatic int dsa_switch_suspend(struct dsa_switch *ds)
2462446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli{
2472446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	int i, ret = 0;
2482446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
2492446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	/* Suspend slave network devices */
2502446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	for (i = 0; i < DSA_MAX_PORTS; i++) {
2512446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		if (!(ds->phys_port_mask & (1 << i)))
2522446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli			continue;
2532446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
2542446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		ret = dsa_slave_suspend(ds->ports[i]);
2552446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		if (ret)
2562446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli			return ret;
2572446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	}
2582446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
2592446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	if (ds->drv->suspend)
2602446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		ret = ds->drv->suspend(ds);
2612446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
2622446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	return ret;
2632446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli}
2642446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
2652446254915a7d6f08bba9a755a34cc0402880472Florian Fainellistatic int dsa_switch_resume(struct dsa_switch *ds)
2662446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli{
2672446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	int i, ret = 0;
2682446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
2692446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	if (ds->drv->resume)
2702446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		ret = ds->drv->resume(ds);
2712446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
2722446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	if (ret)
2732446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		return ret;
2742446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
2752446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	/* Resume slave network devices */
2762446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	for (i = 0; i < DSA_MAX_PORTS; i++) {
2772446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		if (!(ds->phys_port_mask & (1 << i)))
2782446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli			continue;
2792446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
2802446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		ret = dsa_slave_resume(ds->ports[i]);
2812446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		if (ret)
2822446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli			return ret;
2832446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	}
2842446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
2852446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	return 0;
2862446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli}
287e506d405ac7d34d03996c97ac68aa2ac010be64aThierry Reding#endif
2882446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
28991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
29091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek/* link polling *************************************************************/
29191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic void dsa_link_poll_work(struct work_struct *ugly)
29291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
293e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	struct dsa_switch_tree *dst;
294e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	int i;
295e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek
296e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	dst = container_of(ugly, struct dsa_switch_tree, link_poll_work);
29791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
298e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	for (i = 0; i < dst->pd->nr_chips; i++) {
299e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		struct dsa_switch *ds = dst->ds[i];
30091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
301e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		if (ds != NULL && ds->drv->poll_link != NULL)
302e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			ds->drv->poll_link(ds);
303e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	}
304e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek
305e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	mod_timer(&dst->link_poll_timer, round_jiffies(jiffies + HZ));
30691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
30791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
308e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhekstatic void dsa_link_poll_timer(unsigned long _dst)
30991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
310e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	struct dsa_switch_tree *dst = (void *)_dst;
31191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
312e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	schedule_work(&dst->link_poll_work);
31391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
31491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
31591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
31691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek/* platform driver init and cleanup *****************************************/
31791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic int dev_is_class(struct device *dev, void *class)
31891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
31991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (dev->class != NULL && !strcmp(dev->class->name, class))
32091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		return 1;
32191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
32291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	return 0;
32391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
32491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
32591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic struct device *dev_find_class(struct device *parent, char *class)
32691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
32791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (dev_is_class(parent, class)) {
32891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		get_device(parent);
32991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		return parent;
33091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
33191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
33291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	return device_find_child(parent, class, dev_is_class);
33391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
33491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
335b4d2394d01bc642e95b2cba956d908423c1bef77Alexander Duyckstruct mii_bus *dsa_host_dev_to_mii_bus(struct device *dev)
33691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
33791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	struct device *d;
33891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
33991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	d = dev_find_class(dev, "mdio_bus");
34091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (d != NULL) {
34191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		struct mii_bus *bus;
34291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
34391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		bus = to_mii_bus(d);
34491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		put_device(d);
34591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
34691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		return bus;
34791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
34891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
34991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	return NULL;
35091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
351b4d2394d01bc642e95b2cba956d908423c1bef77Alexander DuyckEXPORT_SYMBOL_GPL(dsa_host_dev_to_mii_bus);
35291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
35391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic struct net_device *dev_to_net_device(struct device *dev)
35491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
35591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	struct device *d;
35691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
35791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	d = dev_find_class(dev, "net");
35891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (d != NULL) {
35991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		struct net_device *nd;
36091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
36191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		nd = to_net_dev(d);
36291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		dev_hold(nd);
36391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		put_device(d);
36491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
36591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		return nd;
36691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
36791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
36891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	return NULL;
36991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
37091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
3715e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli#ifdef CONFIG_OF
3725e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainellistatic int dsa_of_setup_routing_table(struct dsa_platform_data *pd,
3735e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli					struct dsa_chip_data *cd,
3745e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli					int chip_index,
3755e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli					struct device_node *link)
3765e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli{
3775e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	int ret;
3785e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	const __be32 *reg;
3795e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	int link_port_addr;
3805e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	int link_sw_addr;
3815e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	struct device_node *parent_sw;
3825e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	int len;
3835e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
3845e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	parent_sw = of_get_parent(link);
3855e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!parent_sw)
3865e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		return -EINVAL;
3875e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
3885e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	reg = of_get_property(parent_sw, "reg", &len);
3895e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!reg || (len != sizeof(*reg) * 2))
3905e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		return -EINVAL;
3915e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
3925e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	link_sw_addr = be32_to_cpup(reg + 1);
3935e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
3945e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (link_sw_addr >= pd->nr_chips)
3955e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		return -EINVAL;
3965e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
3975e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	/* First time routing table allocation */
3985e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!cd->rtable) {
3995e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		cd->rtable = kmalloc(pd->nr_chips * sizeof(s8), GFP_KERNEL);
4005e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		if (!cd->rtable)
4015e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			return -ENOMEM;
4025e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4035e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		/* default to no valid uplink/downlink */
4045e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		memset(cd->rtable, -1, pd->nr_chips * sizeof(s8));
4055e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	}
4065e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4075e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	reg = of_get_property(link, "reg", NULL);
4085e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!reg) {
4095e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		ret = -EINVAL;
4105e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		goto out;
4115e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	}
4125e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4135e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	link_port_addr = be32_to_cpup(reg);
4145e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4155e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	cd->rtable[link_sw_addr] = link_port_addr;
4165e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4175e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	return 0;
4185e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelliout:
4195e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	kfree(cd->rtable);
4205e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	return ret;
4215e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli}
4225e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
42321168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainellistatic void dsa_of_free_platform_data(struct dsa_platform_data *pd)
42421168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli{
42521168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli	int i;
42621168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli	int port_index;
42721168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli
42821168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli	for (i = 0; i < pd->nr_chips; i++) {
42921168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli		port_index = 0;
4305f64a7dbf593c2317f132c8252d04cdfe8d4b104Florian Fainelli		while (port_index < DSA_MAX_PORTS) {
4311f74714f1e34a84fe4ef2a6d9b76c20794fb5dcfFabian Frederick			kfree(pd->chip[i].port_names[port_index]);
4325f64a7dbf593c2317f132c8252d04cdfe8d4b104Florian Fainelli			port_index++;
4335f64a7dbf593c2317f132c8252d04cdfe8d4b104Florian Fainelli		}
43421168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli		kfree(pd->chip[i].rtable);
43521168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli	}
43621168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli	kfree(pd->chip);
43721168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli}
43821168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli
4395e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainellistatic int dsa_of_probe(struct platform_device *pdev)
4405e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli{
4415e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	struct device_node *np = pdev->dev.of_node;
4425e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	struct device_node *child, *mdio, *ethernet, *port, *link;
4435e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	struct mii_bus *mdio_bus;
4445e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	struct platform_device *ethernet_dev;
4455e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	struct dsa_platform_data *pd;
4465e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	struct dsa_chip_data *cd;
4475e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	const char *port_name;
4485e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	int chip_index, port_index;
4495e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	const unsigned int *sw_addr, *port_reg;
45021168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli	int ret;
4515e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4525e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	mdio = of_parse_phandle(np, "dsa,mii-bus", 0);
4535e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!mdio)
4545e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		return -EINVAL;
4555e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4565e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	mdio_bus = of_mdio_find_bus(mdio);
4575e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!mdio_bus)
4585e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		return -EINVAL;
4595e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4605e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	ethernet = of_parse_phandle(np, "dsa,ethernet", 0);
4615e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!ethernet)
4625e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		return -EINVAL;
4635e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4645e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	ethernet_dev = of_find_device_by_node(ethernet);
4655e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!ethernet_dev)
4665e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		return -ENODEV;
4675e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4685e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
4695e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!pd)
4705e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		return -ENOMEM;
4715e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4725e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	pdev->dev.platform_data = pd;
4735e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	pd->netdev = &ethernet_dev->dev;
4745e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	pd->nr_chips = of_get_child_count(np);
4755e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (pd->nr_chips > DSA_MAX_SWITCHES)
4765e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		pd->nr_chips = DSA_MAX_SWITCHES;
4775e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4785e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	pd->chip = kzalloc(pd->nr_chips * sizeof(struct dsa_chip_data),
4795e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			GFP_KERNEL);
4805e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!pd->chip) {
4815e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		ret = -ENOMEM;
4825e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		goto out_free;
4835e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	}
4845e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
485d1c0b471b340f43fa857d19150e029257d7bb475Fabian Godehardt	chip_index = -1;
4865e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	for_each_available_child_of_node(np, child) {
487d1c0b471b340f43fa857d19150e029257d7bb475Fabian Godehardt		chip_index++;
4885e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		cd = &pd->chip[chip_index];
4895e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
490fa981d9af82e08f316ed25ed43078f995cc4be0aFlorian Fainelli		cd->of_node = child;
491c1f570a6abc192f047550743f9957b617af605afFlorian Fainelli		cd->host_dev = &mdio_bus->dev;
4925e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4935e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		sw_addr = of_get_property(child, "reg", NULL);
4945e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		if (!sw_addr)
4955e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			continue;
4965e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
4975e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		cd->sw_addr = be32_to_cpup(sw_addr);
4985e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		if (cd->sw_addr > PHY_MAX_ADDR)
4995e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			continue;
5005e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5015e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		for_each_available_child_of_node(child, port) {
5025e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			port_reg = of_get_property(port, "reg", NULL);
5035e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			if (!port_reg)
5045e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli				continue;
5055e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5065e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			port_index = be32_to_cpup(port_reg);
5075e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5085e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			port_name = of_get_property(port, "label", NULL);
5095e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			if (!port_name)
5105e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli				continue;
5115e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
512bd47497a0171b96264927e3377254db13b9fe3e3Florian Fainelli			cd->port_dn[port_index] = port;
513bd47497a0171b96264927e3377254db13b9fe3e3Florian Fainelli
5145e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			cd->port_names[port_index] = kstrdup(port_name,
5155e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli					GFP_KERNEL);
5165e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			if (!cd->port_names[port_index]) {
5175e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli				ret = -ENOMEM;
5185e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli				goto out_free_chip;
5195e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			}
5205e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5215e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			link = of_parse_phandle(port, "link", 0);
5225e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5235e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			if (!strcmp(port_name, "dsa") && link &&
5245e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli					pd->nr_chips > 1) {
5255e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli				ret = dsa_of_setup_routing_table(pd, cd,
5265e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli						chip_index, link);
5275e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli				if (ret)
5285e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli					goto out_free_chip;
5295e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			}
5305e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5315e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			if (port_index == DSA_MAX_PORTS)
5325e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli				break;
5335e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		}
5345e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	}
5355e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5365e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	return 0;
5375e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5385e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelliout_free_chip:
53921168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli	dsa_of_free_platform_data(pd);
5405e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelliout_free:
5415e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	kfree(pd);
5425e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	pdev->dev.platform_data = NULL;
5435e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	return ret;
5445e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli}
5455e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5465e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainellistatic void dsa_of_remove(struct platform_device *pdev)
5475e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli{
5485e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	struct dsa_platform_data *pd = pdev->dev.platform_data;
5495e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5505e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (!pdev->dev.of_node)
5515e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		return;
5525e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
55321168245031062212c0b805d0bd466ee6dd4a16fFlorian Fainelli	dsa_of_free_platform_data(pd);
5545e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	kfree(pd);
5555e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli}
5565e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli#else
5575e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainellistatic inline int dsa_of_probe(struct platform_device *pdev)
5585e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli{
5595e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	return 0;
5605e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli}
5615e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5625e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainellistatic inline void dsa_of_remove(struct platform_device *pdev)
5635e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli{
5645e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli}
5655e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli#endif
5665e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
56791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic int dsa_probe(struct platform_device *pdev)
56891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
56991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	static int dsa_version_printed;
57091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	struct dsa_platform_data *pd = pdev->dev.platform_data;
57191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	struct net_device *dev;
572e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	struct dsa_switch_tree *dst;
5735e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	int i, ret;
57491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
57591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (!dsa_version_printed++)
57691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		printk(KERN_NOTICE "Distributed Switch Architecture "
57791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek			"driver version %s\n", dsa_driver_version);
57891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
5795e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (pdev->dev.of_node) {
5805e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		ret = dsa_of_probe(pdev);
5815e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		if (ret)
5825e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli			return ret;
5835e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
5845e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		pd = pdev->dev.platform_data;
5855e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	}
5865e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
587e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	if (pd == NULL || pd->netdev == NULL)
58891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		return -EINVAL;
58991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
59091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	dev = dev_to_net_device(pd->netdev);
5915e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	if (dev == NULL) {
5925e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		ret = -EINVAL;
5935e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		goto out;
5945e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	}
59591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
59691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	if (dev->dsa_ptr != NULL) {
59791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		dev_put(dev);
5985e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		ret = -EEXIST;
5995e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		goto out;
60091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
60191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
602e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	dst = kzalloc(sizeof(*dst), GFP_KERNEL);
603e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	if (dst == NULL) {
60491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		dev_put(dev);
6055e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		ret = -ENOMEM;
6065e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		goto out;
60791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
60891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
609e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	platform_set_drvdata(pdev, dst);
610e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek
611e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	dst->pd = pd;
612e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	dst->master_netdev = dev;
613e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	dst->cpu_switch = -1;
614e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	dst->cpu_port = -1;
615e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek
616e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	for (i = 0; i < pd->nr_chips; i++) {
617e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		struct dsa_switch *ds;
618e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek
619b4d2394d01bc642e95b2cba956d908423c1bef77Alexander Duyck		ds = dsa_switch_setup(dst, i, &pdev->dev, pd->chip[i].host_dev);
620e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		if (IS_ERR(ds)) {
621e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			printk(KERN_ERR "%s[%d]: couldn't create dsa switch "
622e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek				"instance (error %ld)\n", dev->name, i,
623e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek				PTR_ERR(ds));
624e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			continue;
625e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		}
626e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek
627e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		dst->ds[i] = ds;
628e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		if (ds->drv->poll_link != NULL)
629e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			dst->link_poll_needed = 1;
63091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	}
63191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
632e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	/*
633e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	 * If we use a tagging format that doesn't have an ethertype
634e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	 * field, make sure that all packets from this point on get
635e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	 * sent to the tag format's receive function.
636e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	 */
637e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	wmb();
638e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	dev->dsa_ptr = (void *)dst;
639e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek
640e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	if (dst->link_poll_needed) {
641e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		INIT_WORK(&dst->link_poll_work, dsa_link_poll_work);
642e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		init_timer(&dst->link_poll_timer);
643e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		dst->link_poll_timer.data = (unsigned long)dst;
644e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		dst->link_poll_timer.function = dsa_link_poll_timer;
645e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		dst->link_poll_timer.expires = round_jiffies(jiffies + HZ);
646e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		add_timer(&dst->link_poll_timer);
647e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	}
64891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
64991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	return 0;
6505e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
6515e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelliout:
6525e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	dsa_of_remove(pdev);
6535e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
6545e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	return ret;
65591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
65691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
65791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic int dsa_remove(struct platform_device *pdev)
65891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
659e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
660e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	int i;
66191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
662e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	if (dst->link_poll_needed)
663e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		del_timer_sync(&dst->link_poll_timer);
66491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
66543829731dd372d04d6706c51052b9dabab9ca356Tejun Heo	flush_work(&dst->link_poll_work);
66691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
667e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	for (i = 0; i < dst->pd->nr_chips; i++) {
668e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		struct dsa_switch *ds = dst->ds[i];
669e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek
670e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek		if (ds != NULL)
671e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek			dsa_switch_destroy(ds);
672e84665c9cb4db963393fafad6fefe5efdd7e4a09Lennert Buytenhek	}
67391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
6745e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	dsa_of_remove(pdev);
6755e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
67691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	return 0;
67791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
67891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
67991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic void dsa_shutdown(struct platform_device *pdev)
68091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
68191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
68291da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
6833e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainellistatic int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
6843e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli			  struct packet_type *pt, struct net_device *orig_dev)
6853e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli{
6863e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli	struct dsa_switch_tree *dst = dev->dsa_ptr;
6873e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli
6883e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli	if (unlikely(dst == NULL)) {
6893e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli		kfree_skb(skb);
6903e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli		return 0;
6913e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli	}
6923e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli
6935075314e4e4b559cc37675ad8a721a89bccd6284Alexander Duyck	return dst->rcv(skb, dev, pt, orig_dev);
6943e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli}
6953e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli
69661b7363ffa48b36e2ff086c2d2524e40d3766571Florian Fainellistatic struct packet_type dsa_pack_type __read_mostly = {
6973e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli	.type	= cpu_to_be16(ETH_P_XDSA),
6983e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli	.func	= dsa_switch_rcv,
6993e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli};
7003e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli
7012446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli#ifdef CONFIG_PM_SLEEP
7022446254915a7d6f08bba9a755a34cc0402880472Florian Fainellistatic int dsa_suspend(struct device *d)
7032446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli{
7042446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	struct platform_device *pdev = to_platform_device(d);
7052446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
7062446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	int i, ret = 0;
7072446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
7082446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	for (i = 0; i < dst->pd->nr_chips; i++) {
7092446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		struct dsa_switch *ds = dst->ds[i];
7102446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
7112446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		if (ds != NULL)
7122446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli			ret = dsa_switch_suspend(ds);
7132446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	}
7142446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
7152446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	return ret;
7162446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli}
7172446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
7182446254915a7d6f08bba9a755a34cc0402880472Florian Fainellistatic int dsa_resume(struct device *d)
7192446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli{
7202446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	struct platform_device *pdev = to_platform_device(d);
7212446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
7222446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	int i, ret = 0;
7232446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
7242446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	for (i = 0; i < dst->pd->nr_chips; i++) {
7252446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		struct dsa_switch *ds = dst->ds[i];
7262446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
7272446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		if (ds != NULL)
7282446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli			ret = dsa_switch_resume(ds);
7292446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	}
7302446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
7312446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli	return ret;
7322446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli}
7332446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli#endif
7342446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
7352446254915a7d6f08bba9a755a34cc0402880472Florian Fainellistatic SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa_suspend, dsa_resume);
7362446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli
7375e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainellistatic const struct of_device_id dsa_of_match_table[] = {
738246d7f773c13cac3e3ab1609fd4ffee520242c63Florian Fainelli	{ .compatible = "brcm,bcm7445-switch-v4.0" },
7395e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	{ .compatible = "marvell,dsa", },
7405e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli	{}
7415e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli};
7425e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian FainelliMODULE_DEVICE_TABLE(of, dsa_of_match_table);
7435e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli
74491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic struct platform_driver dsa_driver = {
74591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	.probe		= dsa_probe,
74691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	.remove		= dsa_remove,
74791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	.shutdown	= dsa_shutdown,
74891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	.driver = {
74991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		.name	= "dsa",
75091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek		.owner	= THIS_MODULE,
7515e95329b701c4edf6c4d72487ec0369fa148c0bdFlorian Fainelli		.of_match_table = dsa_of_match_table,
7522446254915a7d6f08bba9a755a34cc0402880472Florian Fainelli		.pm	= &dsa_pm_ops,
75391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	},
75491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek};
75591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
75691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic int __init dsa_init_module(void)
75791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
7587df899c36cf09678bdef1824ce591ef4ac0e9864Ben Hutchings	int rc;
7597df899c36cf09678bdef1824ce591ef4ac0e9864Ben Hutchings
7607df899c36cf09678bdef1824ce591ef4ac0e9864Ben Hutchings	rc = platform_driver_register(&dsa_driver);
7617df899c36cf09678bdef1824ce591ef4ac0e9864Ben Hutchings	if (rc)
7627df899c36cf09678bdef1824ce591ef4ac0e9864Ben Hutchings		return rc;
7637df899c36cf09678bdef1824ce591ef4ac0e9864Ben Hutchings
7643e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli	dev_add_pack(&dsa_pack_type);
7653e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli
7667df899c36cf09678bdef1824ce591ef4ac0e9864Ben Hutchings	return 0;
76791da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
76891da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekmodule_init(dsa_init_module);
76991da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
77091da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekstatic void __exit dsa_cleanup_module(void)
77191da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek{
7723e8a72d1dae374cf6fc1dba97cec663585845ff9Florian Fainelli	dev_remove_pack(&dsa_pack_type);
77391da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek	platform_driver_unregister(&dsa_driver);
77491da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek}
77591da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhekmodule_exit(dsa_cleanup_module);
77691da11f870f00a3322b81c73042291d7f0be5a17Lennert Buytenhek
777577d6a7c3a0e273e115c65a148b71be6c1950f69Rusty RussellMODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
77891da11f870f00a3322b81c73042291d7f0be5a17Lennert BuytenhekMODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips");
77991da11f870f00a3322b81c73042291d7f0be5a17Lennert BuytenhekMODULE_LICENSE("GPL");
78091da11f870f00a3322b81c73042291d7f0be5a17Lennert BuytenhekMODULE_ALIAS("platform:dsa");
781