diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 48e62a3..55ffd67 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -13,6 +13,15 @@ config NET_DSA_MV88E6060
 	  This enables support for the Marvell 88E6060 ethernet switch
 	  chip.
 
+config NET_DSA_MV88E6065
+	tristate "Marvell 88E6065 ethernet switch chip support"
+	select NET_DSA
+	select NET_DSA_TAG_TRAILER
+	select NET_DSA_MV88E6XXX
+	---help---
+	  This enables support for the Marvell 88E6065 ethernet switch
+	  chip.
+
 config NET_DSA_MV88E6XXX_NEED_PPU
 	bool
 	default n
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index e2d51c4..0c5f423 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
+obj-$(CONFIG_NET_DSA_MV88E6065) += mv88e6065.o
 obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx_drv.o
 mv88e6xxx_drv-y += mv88e6xxx.o
 ifdef CONFIG_NET_DSA_MV88E6123_61_65
diff --git a/drivers/net/dsa/mv88e6065.c b/drivers/net/dsa/mv88e6065.c
new file mode 100644
index 0000000..0403bb4
--- /dev/null
+++ b/drivers/net/dsa/mv88e6065.c
@@ -0,0 +1,601 @@
+/*
+ * net/dsa/mv88e6060.c - Driver for Marvell 88e6060 switch chips
+ * Copyright (c) 2008-2009 Marvell Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <net/dsa.h>
+
+#include "mv88e6xxx.h"
+
+//netlink
+#include <linux/module.h>
+#include <net/sock.h>
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+
+#define NETLINK_USER 0x42
+
+struct sock *nl_sk = NULL;
+
+
+static void nl_recv_msg(struct sk_buff *skb)
+{
+    struct nlmsghdr *nlh;
+    int pid;
+    struct sk_buff *skb_out;
+    int msg_size;
+    char *msg = "Hello from kernel";
+    int res;
+
+    printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
+
+    msg_size = strlen(msg);
+
+    nlh = (struct nlmsghdr *)skb->data;
+    printk(KERN_INFO "Netlink received msg payload: %s\n", (char *)nlmsg_data(nlh));
+    pid = nlh->nlmsg_pid; /*pid of sending process */
+
+    skb_out = nlmsg_new(msg_size, 0);
+
+    if (!skb_out) {
+        printk(KERN_ERR "Failed to allocate new skb\n");
+        return;
+    }
+
+    nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
+    NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */
+    strncpy(nlmsg_data(nlh), msg, msg_size);
+
+    res = nlmsg_unicast(nl_sk, skb_out, pid);
+
+    if (res < 0)
+        printk(KERN_INFO "Error while sending bak to user\n");
+}
+
+#define MV88E6065_GLOBAL_SW_MAC_DIFF_MAC_MASK	0xef
+
+/*Register Address*/
+#define MV88E6065_REG_SW_PORT(p)		(0x8+(p))
+#define MV88E6065_REG_PHY(p)		(0x0 + (p))
+#define MV88E6065_REG_GLOBAL		0xe
+#define MV88E6065_REG_GLOBAL2		0xf
+
+static int reg_read(struct dsa_switch *ds, int addr, int reg)
+{
+	struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev);
+
+	if (bus == NULL)
+		return -EINVAL;
+
+	return mdiobus_read(bus, ds->pd->sw_addr + addr, reg);
+}
+
+#define MV88E6065_REG_READ(addr, reg)					\
+	({							\
+		int __ret;					\
+								\
+		__ret = reg_read(ds, addr, reg);		\
+		if (__ret < 0)					\
+			return __ret;				\
+		__ret;						\
+	})
+
+
+static int reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
+{
+	struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev);
+
+	if (bus == NULL)
+		return -EINVAL;
+
+	return mdiobus_write(bus, ds->pd->sw_addr + addr, reg, val);
+}
+
+#define MV88E6065_REG_WRITE(addr, reg, val)				\
+	({							\
+		int __ret;					\
+								\
+		__ret = reg_write(ds, addr, reg, val);		\
+		if (__ret < 0)					\
+			return __ret;				\
+	})
+
+static char *mv88e6065_probe(struct device *host_dev, int sw_addr)
+{
+	struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
+	int ret;
+
+	if (bus == NULL)
+		return NULL;
+	ret = mdiobus_read(bus, sw_addr + MV88E6065_REG_SW_PORT(0), 0x03);
+
+	if (ret >= 0) {
+		if ((ret & 0xfff0) == 0x0650)
+			return "Marvell 88E6065";
+	}
+
+	return NULL;
+}
+
+static int mv88e6065_switch_reset(struct dsa_switch *ds)
+{
+	int i;
+	int ret;
+	unsigned long timeout;
+
+	/* Set all ports to the disabled state. */
+	for (i = 0; i < 6; i++) {
+		ret = MV88E6065_REG_READ(MV88E6065_REG_SW_PORT(i), 0x04);
+		MV88E6065_REG_WRITE(MV88E6065_REG_SW_PORT(i), 0x04, ret & 0xfffc);
+	}
+
+	/* Wait for transmit queues to drain. */
+	usleep_range(2000, 4000);
+
+	/* Reset the switch. */
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x0a, 0xa130);
+
+	/* Wait up to one second for reset to complete. */
+	timeout = jiffies + 1 * HZ;
+	while (time_before(jiffies, timeout)) {
+		ret = MV88E6065_REG_READ(MV88E6065_REG_GLOBAL, 0x00);
+		if ((ret & 0x8000) == 0x0000)
+			break;
+
+		usleep_range(1000, 2000);
+	}
+	if (time_after(jiffies, timeout))
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+
+static void mv88e6065_setup_priv(struct dsa_switch *ds)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+	mutex_init(&ps->smi_mutex);
+	mutex_init(&ps->stats_mutex);
+	mutex_init(&ps->phy_mutex);
+}
+
+
+
+//TODO nastaveni pro firmu jinak OK.
+static int mv88e6065_setup_global(struct dsa_switch *ds)
+{
+	/* Disable discarding of frames with excessive collisions,
+	 * set the maximum frame size to 1536 bytes, and mask all
+	 * interrupt sources. Counting all frames.
+	 */
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x04, 0x000);
+
+	/* Enable automatic address learning, set the address
+	 * database size to 1024 entries, and set the default aging
+	 * time to 5 minutes.
+	 */
+
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x0a, 0x2130);
+
+
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x07, 0x0003);
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x08, 0x0003);
+
+
+	return 0;
+}
+
+static int mv88e6065_setup_port(struct dsa_switch *ds, int p)
+{
+	int addr = MV88E6065_REG_SW_PORT(p);
+
+	/* MAC Forcing register: don't force link, speed, duplex
+	 * or flow control state to any particular values on physical
+	 * ports, but force the CPU port and all DSA ports to 100Mb/s and full duplex.
+	 */
+//	MV88E6065_REG_WRITE(addr, 0x01, 0x003d);
+//	MV88E6065_REG_WRITE(addr, 0x01, 0x000d);
+	MV88E6065_REG_WRITE(addr, 0x01, 0x0000);
+	/* Do not force flow control, disable Ingress and Egress
+	 * Header tagging, disable VLAN tunneling, and set the port
+	 * state to Forwarding.  Additionally, if this is the CPU
+	 * port, enable Ingress and Egress Trailer tagging mode.
+	 */
+//	MV88E6065_REG_WRITE(addr, 0x04, 0x0003);
+	MV88E6065_REG_WRITE(addr, 0x04, 0x100f);
+
+	return 0;
+}
+
+static int mv88e6065_setup(struct dsa_switch *ds)
+{
+	int i;
+	int ret;
+
+
+	mv88e6065_setup_priv(ds);
+
+	ret = mv88e6065_switch_reset(ds);
+	if (ret < 0)
+		return ret;
+
+	/* @@@ initialise atu */
+
+	ret = mv88e6065_setup_global(ds);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < 6; i++) {
+		ret = mv88e6065_setup_port(ds, i);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Set MAC address on all ports. All these ports have same MAC address.
+ */
+static int mv88e6065_set_addr(struct dsa_switch *ds, u8 *addr)
+{
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x01, ((addr[0] << 8) & MV88E6065_GLOBAL_SW_MAC_DIFF_MAC_MASK) | addr[1]); //
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]);
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]);
+
+	return 0;
+}
+
+/**
+ * Check port address.
+ */
+static int mv88e6065_port_to_phy_addr(int port)
+{
+	if (port >= 0 && port <= 5)
+		return port;
+	return -1;
+}
+
+static int mv88e6065_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+	int addr;
+
+	addr = mv88e6065_port_to_phy_addr(port);
+	if (addr == -1)
+		return 0xffff;
+
+	return reg_read(ds, addr, regnum);
+}
+
+static int
+mv88e6065_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
+{
+	int addr;
+
+	addr = mv88e6065_port_to_phy_addr(port);
+	if (addr == -1)
+		return 0xffff;
+
+	return reg_write(ds, addr, regnum, val);
+}
+
+static void mv88e6065_poll_link(struct dsa_switch *ds)
+{
+	int i;
+	for (i = 0; i < DSA_MAX_PORTS; i++) {
+		struct net_device *dev;
+		int uninitialized_var(port_status);
+		int link;
+		int speed;
+		int duplex;
+		int fc;
+
+		dev = ds->ports[i];
+		if (dev == NULL)
+			continue;
+
+		link = 0;
+		if (dev->flags & IFF_UP) {
+			port_status = reg_read(ds, i + 0x8, 0x00);
+
+			if (port_status < 0)
+				continue;
+
+			link = !!(port_status & 0x1000);
+		}
+
+		if (!link) {
+			if (netif_carrier_ok(dev)) {
+				netdev_info(dev, "link down\n");
+				netif_carrier_off(dev);
+			}
+			continue;
+		}
+
+		speed = (port_status & 0x0100) ? 10 : 100; //obracene ??? TODO 
+		duplex = (port_status & 0x0200) ? 1 : 0;
+		fc = ((port_status & 0x0010) == 0x0010) ? 1 : 0;
+
+		if (!netif_carrier_ok(dev)) {
+			netdev_info(dev,
+				    "link up, %d Mb/s, %s duplex, flow control %sabled\n",
+				    speed,
+				    duplex ? "full" : "half",
+				    fc ? "en" : "dis");
+			netif_carrier_on(dev);
+		}
+	}
+}
+
+static int mv88e6065_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy)
+{
+	/*PHY reg 100MB full, port normal aneg unchange untagged frames*/
+	MV88E6065_REG_WRITE(port, 0x04, 0x00e1); 
+	MV88E6065_REG_WRITE(port, 0x00, 0x3300); //PHY reg 100MB full, port normal aneg
+	return 0;
+}
+
+static int mv88e6065_port_disable(struct dsa_switch *ds, int port, struct phy_device *phy)
+{
+	MV88E6065_REG_WRITE(port, 0x00, 0x0800); //port down
+	return 0;
+}
+
+
+static struct mv88e6xxx_hw_stat mv88e6065_hw_stats[] = {
+	{ "in_good_octets", 4, 0x00, },
+	{ "in_bad_octets", 4, 0x02, },
+	{ "in_unicast", 4, 0x04, },
+	{ "in_broadcasts", 4, 0x06, },
+	{ "in_multicasts", 4, 0x07, },
+	{ "in_pause", 4, 0x16, },
+	{ "in_undersize", 4, 0x18, },
+	{ "in_fragments", 4, 0x19, },
+	{ "in_oversize", 4, 0x1a, },
+	{ "in_jabber", 4, 0x1b, },
+	{ "in_rx_error", 4, 0x1c, },
+	{ "in_fcs_error", 4, 0x1d, },
+	{ "out_octets", 4, 0x0e, },
+	{ "out_unicast", 4, 0x10, },
+	{ "out_multicasts", 4, 0x12, },
+	{ "out_broadcasts", 4, 0x13, },
+	{ "out_pause", 4, 0x15, },
+	{ "excessive", 4, 0x11, },
+	{ "deferred", 4, 0x05, },
+	{ "single", 4, 0x14, },
+	{ "multiple", 4, 0x17, },
+	{ "late", 4, 0x1f, },
+	{ "hist_64bytes", 4, 0x08, },
+	{ "hist_65_127bytes", 4, 0x09, },
+	{ "hist_128_255bytes", 4, 0x0a, },
+	{ "hist_256_511bytes", 4, 0x0b, },
+	{ "hist_512_1023bytes", 4, 0x0c, },
+	{ "hist_1024_max_bytes", 4, 0x0d, },
+};
+
+
+static void mv88e6065_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
+{
+	mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6065_hw_stats), mv88e6065_hw_stats, port, data);
+}
+
+
+static int mv88e6065_wait(struct dsa_switch *ds, int addr)
+{
+	int ret;
+	int i;
+
+	/*check if stats is not busy*/
+	for (i = 0; i < 10; i++) {
+		ret = reg_read(ds, MV88E6065_REG_GLOBAL, addr);
+		if ((ret & 0x8000) == 0)
+			return 0;
+	}
+
+	return -EBUSY;
+}
+
+
+static int mv88e6065_vtu_read(struct dsa_switch *ds, uint16_t *vlan){
+	int ret, i;
+
+	while(true) {
+		MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x05, 0x4);
+		ret = mv88e6065_wait(ds, 0x05);
+		if (ret < 0)
+			return ret;
+
+		ret = reg_read(ds, MV88E6065_REG_GLOBAL, 0x06);
+		vlan[i++] = ret & 0x0fff;
+		if ((ret & 0x1000) == 0x1000)
+			break;
+	}
+
+	return 0;
+}
+
+static int mv88e6065_vtu_load(struct dsa_switch *ds, int port, int vlan){
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x06, vlan | 0x1000);
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x05, 0x3000 | port);
+	return 0;
+}
+
+
+static int mv88e6065_vtu_set_port_forwarding(struct dsa_switch *ds, int port){
+	int ret;
+	/*set port to learnig state and to tagging frames*/
+	if (port < 4) {
+		ret = reg_read(ds, MV88E6065_REG_GLOBAL, 0x07);
+		MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x07, (ret & !(0xf << (port*4))) & (0xa << (port*4)) );
+	}else{
+		ret = reg_read(ds, MV88E6065_REG_GLOBAL, 0x08);
+		MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x08, (ret & !(0xf << ((port-4)*4))) & (0xa << ((port-4)*4)) );
+	}
+	return 0;
+}
+
+static int mv88e6065_vtu_set_all_ports_forwarding(struct dsa_switch *ds, int port){
+	/*set all ports to learnig state and to tagging frames*/
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x07, 0xaaaa );
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x08, 0x00aa );
+	return 0;
+}
+
+static int mv88e6065_stats_snapshot(struct dsa_switch *ds, int port)
+{
+	int ret;
+
+	/* Snapshot the hardware statistics counters for this port. */
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x1d, 0xdb00 | (port << 5) );
+
+	/* Wait for the snapshotting to complete. */
+	ret = mv88e6065_wait(ds, 0x1d);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+
+static int mv88e6065_stats_read(struct dsa_switch *ds, int stat, u32 *val)
+{
+	u32 _val;
+	int ret;
+
+	*val = 0;
+
+	MV88E6065_REG_WRITE(MV88E6065_REG_GLOBAL, 0x1d, 0xcc00 | stat);
+
+	ret = mv88e6065_wait(ds, 0x1d);
+	if (ret < 0)
+		return ret;
+
+	ret = reg_read(ds, MV88E6065_REG_GLOBAL, 0x1e);
+	if (ret < 0)
+		return ret;
+
+	_val = ret << 16;
+
+	ret = reg_read(ds, MV88E6065_REG_GLOBAL, 0x1f);
+	if (ret < 0)
+		return ret;
+
+	*val = _val | ret;
+
+	return 0;
+}
+
+
+
+static void mv88e6065_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
+{
+	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+	int ret;
+	int i;
+
+	mutex_lock(&ps->stats_mutex);
+
+	ret = mv88e6065_stats_snapshot(ds, port);
+	if (ret < 0) {
+		mutex_unlock(&ps->stats_mutex);
+		return;
+	}
+
+	/* Read each of the counters. */
+	for (i = 0; i < ARRAY_SIZE(mv88e6065_hw_stats); i++) {
+		struct mv88e6xxx_hw_stat *s = mv88e6065_hw_stats + i;
+		u32 low;
+		u32 high = 0;
+
+		mv88e6065_stats_read(ds, s->reg, &low);
+		if (s->sizeof_stat == 4)
+			mv88e6065_stats_read(ds, s->reg - 1, &high);
+
+		data[i] = (((u64)high) << 16) | low;
+	}
+
+	mutex_unlock(&ps->stats_mutex);
+
+}
+
+static int mv88e6065_get_sset_count(struct dsa_switch *ds)
+{
+	return ARRAY_SIZE(mv88e6065_hw_stats);
+}
+static int mv88e6065_set_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom, u8 *data)
+{
+	return 0;
+}
+
+static int mv88e6065_get_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom, u8 *data)
+{
+	return 0;
+}
+
+struct dsa_switch_driver mv88e6065_switch_driver = {
+	.priv_size		= sizeof(struct mv88e6xxx_priv_state),
+	.tag_protocol	= DSA_TAG_PROTO_TRAILER,
+	.probe		= mv88e6065_probe,
+	.setup		= mv88e6065_setup,
+	.set_addr	= mv88e6065_set_addr,
+	.phy_read	= mv88e6065_phy_read,
+	.phy_write	= mv88e6065_phy_write,
+	.poll_link	= mv88e6065_poll_link,
+	/*port setting*/
+	.port_enable	 = mv88e6065_port_enable,
+	.port_disable	 = mv88e6065_port_disable,
+	/*stats*/
+	.get_strings			= mv88e6065_get_strings,
+	.get_ethtool_stats	= mv88e6065_get_ethtool_stats,
+	.get_sset_count		= mv88e6065_get_sset_count,
+	/* Register access.*/
+	.get_regs_len	= mv88e6xxx_get_regs_len,
+	.get_regs	= mv88e6xxx_get_regs,
+	/*eeprom ops*/
+	.get_eeprom             = mv88e6065_get_eeprom,
+	.set_eeprom             = mv88e6065_set_eeprom,
+};
+
+static struct netlink_kernel_cfg cfg = {
+	.groups	= 1,
+	.input	= nl_recv_msg,
+};
+
+static int __init mv88e6065_init(void)
+{
+	register_switch_driver(&mv88e6065_switch_driver);
+
+	/*Register netlink*/
+   nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
+	if (!nl_sk) {
+		printk(KERN_ALERT "Error creating socket.\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+module_init(mv88e6065_init);
+
+static void __exit mv88e6065_cleanup(void)
+{
+	unregister_switch_driver(&mv88e6065_switch_driver);
+	/*unregister netlink*/
+	netlink_kernel_release(nl_sk);
+}
+module_exit(mv88e6065_cleanup);
+
+MODULE_DESCRIPTION("Driver for Marvell 88E6065 ethernet switch chip");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mv88e6065");
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 03e397e..05dd359 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -87,6 +87,7 @@ extern struct dsa_switch_driver mv88e6131_switch_driver;
 extern struct dsa_switch_driver mv88e6123_61_65_switch_driver;
 extern struct dsa_switch_driver mv88e6352_switch_driver;
 extern struct dsa_switch_driver mv88e6171_switch_driver;
+extern struct dsa_switch_driver mv88e6065_switch_driver;
 
 #define REG_READ(addr, reg)						\
 	({								\
diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h
index 5d049d0..10b064d 100644
--- a/drivers/net/usb/asix.h
+++ b/drivers/net/usb/asix.h
@@ -172,8 +172,22 @@ struct asix_rx_fixup_info {
 	bool split_head;
 };
 
+#ifdef CONFIG_NET_DSA
+
+#include <net/dsa.h>
+
+#endif
+
 struct asix_common_private {
 	struct asix_rx_fixup_info rx_fixup_info;
+#ifdef CONFIG_NET_DSA
+	struct kobject kobj;
+	struct mii_bus *mdio;
+	int use_embphy;
+	bool dsa_up;
+	struct usbnet *dev;
+	struct platform_device *pdev;
+#endif
 };
 
 extern const struct driver_info ax88172a_info;
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index bf49792..8ee192e 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -35,6 +35,85 @@
 
 #define	PHY_MODE_RTL8211CL	0x000C
 
+#ifdef CONFIG_NET_DSA
+
+#define AX88772_RMII 0x02
+#define AX88772_MAX_PORTS 0x20
+#define MV88e6065_ID  0x0c89
+
+#include <linux/phy.h>
+
+#define to_asix_obj(x) container_of(x, struct asix_common_private, kobj)
+#define to_asix_attr(x) container_of(x, struct asix_attribute, attr)
+
+static int mii_asix_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+	int ret = asix_mdio_read(((struct usbnet *)bus->priv)->net, phy_id, regnum);
+	return ret;
+}
+
+static int mii_asix_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
+{
+	asix_mdio_write(((struct usbnet *)bus->priv)->net, phy_id, regnum, val);
+	return 0;
+}
+
+static int ax88772_init_mdio(struct usbnet *dev){
+	struct asix_common_private *priv = dev->driver_priv;
+	int ret, i;
+
+	priv->mdio = mdiobus_alloc();
+	if (!priv->mdio) {
+		netdev_err(dev->net, "Could not allocate mdio bus\n");
+		return -ENOMEM;
+	}
+
+	priv->mdio->priv = (void *)dev;
+	priv->mdio->read = &mii_asix_read;
+	priv->mdio->write = &mii_asix_write;
+	priv->mdio->name = "ax88772 mdio bus";
+	priv->mdio->parent = &dev->udev->dev;
+
+	/* mii bus name is usb-<usb bus number>-<usb device number> */
+	snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",dev->udev->bus->busnum, dev->udev->devnum);
+
+	priv->mdio->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+	if (!priv->mdio->irq) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	for (i = 0; i < PHY_MAX_ADDR; i++)
+		priv->mdio->irq[i] = PHY_POLL;
+
+	ret = mdiobus_register(priv->mdio);
+	if (ret) {
+		netdev_err(dev->net, "Could not register MDIO bus\n");
+		goto free_irq;
+	}
+
+	netdev_info(dev->net, "registered mdio bus %s\n", priv->mdio->id);
+	return 0;
+
+free_irq:
+	kfree(priv->mdio->irq);
+free:
+	mdiobus_free(priv->mdio);
+	return ret;
+}
+
+static void ax88772_remove_mdio(struct usbnet *dev)
+{
+	struct asix_common_private *priv = dev->driver_priv;
+
+	netdev_info(dev->net, "deregistering mdio bus %s\n", priv->mdio->id);
+	mdiobus_unregister(priv->mdio);
+	kfree(priv->mdio->irq);
+	mdiobus_free(priv->mdio);
+}
+
+#endif
+
 struct ax88172_int_data {
 	__le16 res1;
 	u8 link;
@@ -298,9 +377,30 @@ static int ax88772_link_reset(struct usbnet *dev)
 static int ax88772_reset(struct usbnet *dev)
 {
 	struct asix_data *data = (struct asix_data *)&dev->data;
-	int ret, embd_phy;
+	int ret, embd_phy = 0;
 	u16 rx_ctl;
 
+#ifdef CONFIG_NET_DSA
+	int temp = AX88772_RMII;
+	struct asix_common_private *priv = dev->driver_priv;
+
+	if (priv->use_embphy == 1) {
+		data->phymode = PHY_MODE_MARVELL;
+		data->ledmode = 0;
+
+		/* Set AX88772 to enable RMII interface for external PHY */
+		asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL);
+		asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 1, &temp);
+
+		asix_sw_reset(dev, 0);
+		msleep(150);
+
+		asix_write_rx_ctl(dev, 0);
+		msleep(60);
+   }else{
+
+#endif
+
 	ret = asix_write_gpio(dev,
 			AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5);
 	if (ret < 0)
@@ -314,6 +414,9 @@ static int ax88772_reset(struct usbnet *dev)
 		goto out;
 	}
 
+#ifdef CONFIG_NET_DSA
+	}
+#endif
 	ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL);
 	if (ret < 0)
 		goto out;
@@ -415,11 +518,230 @@ static const struct net_device_ops ax88772_netdev_ops = {
 	.ndo_set_rx_mode        = asix_set_multicast,
 };
 
+
+#ifdef CONFIG_NET_DSA
+struct asix_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct asix_common_private *priv, struct asix_attribute *attr, char *buf);
+	ssize_t (*store)(struct asix_common_private *priv, struct asix_attribute *attr, const char *buf, size_t count);
+};
+
+static int fill_platform_datax(struct dsa_platform_data *pd,
+		struct mii_bus *bus, struct device * parent){
+	struct dsa_chip_data * cd;
+	int i;
+
+	static struct device_node dn = {
+		.name = "name",
+		.type = "type",
+		.phandle = 0,
+		.full_name = "fullname",
+		.fwnode =  {1},
+		.properties = NULL,
+		.deadprops = NULL,
+		.parent = NULL,
+		.child = NULL,
+		.sibling = NULL,
+		.kobj = {NULL},
+		._flags = 0,
+		.data = NULL
+	};
+	static struct device_node dnc = {
+		.name = "name",
+		.type = "type",
+		.phandle = 0,
+		.full_name = "fullname",
+		.fwnode =  {1},
+		.properties = NULL,
+		.deadprops = NULL,
+		.parent = &dn,
+		.child = NULL,
+		.sibling = NULL,
+		.kobj = {NULL},
+		._flags = 0,
+		.data = NULL
+	};
+	static char *port_names[12] = {"0", "1", "2", "3", "4",
+		"5", "6", "7", "8", "9", "10", "11"};
+
+	pd->nr_chips = 1;
+	pd->netdev = parent;
+
+	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+	if (cd == NULL)
+		return -ENOMEM;
+
+	pd->chip = cd;
+
+	cd->host_dev = parent;
+	cd->sw_addr = 0x10;
+	cd->eeprom_len = 256;
+	cd->of_node = &dn;
+	cd->rtable = 0;
+
+//	cd->of_node = kzalloc(sizeof(*cd->of_node), GFP_KERNEL);
+//	if(cd->of_node == NULL)
+//		goto free;
+
+	for (i = 0; i < DSA_MAX_PORTS; i++) {
+		cd->port_names[i] = port_names[i];
+		cd->port_dn[i] = &dnc;
+	}
+
+	return 0;
+
+//free:
+//	kfree(cd);
+//	return -ENOMEM;
+}
+
+static struct platform_device *dsa_create_pdev(struct mii_bus *bus, struct net_device *dev){
+	struct dsa_platform_data *pd;
+	int ret;
+	struct platform_device *pdev = platform_device_register_simple("MYTEMP", 0x42, NULL, 0);
+
+	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+	if (pd == NULL)
+		goto freex;
+	ret = fill_platform_datax(pd, bus, bus->parent);
+	if (ret != 0)
+		goto freepx;
+
+	pdev->dev.of_node = NULL;
+//	pd->netdev = &dev->dev;
+	pd->netdev = bus->parent;
+	pdev->dev.platform_data = pd;
+	return pdev;
+
+freepx:
+	kfree(pd);
+freex:
+	platform_device_unregister(pdev);
+	return NULL;
+}
+
+static int ax88772_set_bind_dsa(struct asix_common_private *priv)
+{
+	struct usbnet *dev = priv->dev;
+	int i, ret, embd_phy;
+	u32 phyid;
+	int temp = AX88772_RMII;
+
+	if (priv->dsa_up == 1)
+		return -EINVAL;
+	priv->dsa_up = 1;
+
+	/* Enable RMII interface for external PHY */
+	asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 1, &temp);
+
+	for (i = 0; i < AX88772_MAX_PORTS; i++){
+		phyid = asix_mdio_read(dev->net, i, 0x3);
+		if (phyid == MV88e6065_ID)
+			break;
+	}
+
+	if (phyid == MV88e6065_ID) {
+		ret = ax88772_init_mdio(dev);
+		if (ret)
+			return ret;
+
+		priv->use_embphy = 1;
+
+		priv->pdev = dsa_create_pdev(priv->mdio, dev->net);
+		if (priv->pdev == NULL)
+			return -ENOMEM;
+
+		ret = dsa_probe2(priv->pdev, dev->net);
+		if (ret)
+			return ret;
+
+		dev->mii.phy_id = 0x11; //TODO load addr of mii reg
+	}else{
+		/* Revert to previous settings */
+		embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
+		ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL);
+		if (ret)
+			return ret;
+
+		priv->use_embphy = 0;
+	}
+
+	return 0;
+}
+
+static ssize_t usb_dsa_store(struct asix_common_private *priv,
+		struct asix_attribute *attr, const char *buf, size_t count)
+{
+	ax88772_set_bind_dsa(priv);
+	return count;
+}
+
+static ssize_t usb_dsa_show(struct asix_common_private *priv,
+		struct asix_attribute *attr, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "usb_dsa_binding.\n");
+}
+
+static void driver_release(struct kobject *kobj)
+{
+	struct asix_common_private *priv = to_asix_obj(kobj);
+	kfree(priv);
+}
+
+static ssize_t usb_dsa_attr_show(struct kobject *kobj,
+	struct attribute *attr,
+	char *buf)
+{
+	struct asix_attribute *attribute;
+	struct asix_common_private *priv;
+
+	attribute = to_asix_attr(attr);
+	priv = to_asix_obj(kobj);
+
+	if (!attribute->show)
+		return -EINVAL;
+
+	return attribute->show(priv, attribute, buf);
+}
+static ssize_t usb_dsa_attr_store(struct kobject *kobj,
+		struct attribute *attr,
+		const char *buf, size_t len)
+{
+	struct asix_attribute *attribute;
+	struct asix_common_private *priv;
+
+	attribute = to_asix_attr(attr);
+	priv = to_asix_obj(kobj);
+
+	if (!attribute->store)
+		return -EINVAL;
+	return attribute->store(priv, attribute, buf, len);
+}
+
+static struct asix_attribute asix_attribute =  __ATTR(usb_dsa_bind, 0664, usb_dsa_show, usb_dsa_store);
+static struct attribute *asix_default_attrs[] = {
+	&asix_attribute.attr,
+	NULL,
+};
+static const struct sysfs_ops dsa_bind_sysfs_ops = {
+	.show   = usb_dsa_attr_show,
+	.store  = usb_dsa_attr_store,
+};
+static struct kobj_type dsa_bind_ktype = {
+	.sysfs_ops      = &dsa_bind_sysfs_ops,
+	.release        = driver_release,
+	.default_attrs  = asix_default_attrs,
+};
+#endif
+
 static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
 {
 	int ret, embd_phy, i;
 	u8 buf[ETH_ALEN];
 	u32 phyid;
+#ifdef CONFIG_NET_DSA
+	struct asix_common_private *priv;
+#endif
 
 	usbnet_get_endpoints(dev,intf);
 
@@ -465,6 +787,25 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
 		return ret;
 	}
 
+	dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL);
+	if (!dev->driver_priv)
+		return -ENOMEM;
+
+#ifdef CONFIG_NET_DSA
+	priv = dev->driver_priv;
+	priv->dev = dev;
+	priv->dsa_up = 0;
+	priv->kobj.kset = kset_create_and_add("DSA_BIND", NULL, kobject_get(&dev->udev->dev.kobj));
+	if (!priv->kobj.kset){
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	ret = kobject_init_and_add(&priv->kobj, &dsa_bind_ktype, NULL, "dsa_bind");
+	if (ret)
+		goto free_kobj;
+#endif
+
 	ax88772_reset(dev);
 
 	/* Read PHYID register *AFTER* the PHY was reset properly */
@@ -478,15 +819,30 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
 		dev->rx_urb_size = 2048;
 	}
 
-	dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL);
-	if (!dev->driver_priv)
-		return -ENOMEM;
-
+	ax88772_set_bind_dsa(priv);
 	return 0;
+
+#ifdef CONFIG_NET_DSA
+free_kobj:
+	kobject_put(&priv->kobj);
+free:
+	kfree(priv);
+	return ret;
+#endif
 }
 
 static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)
 {
+	struct asix_common_private *priv = (struct asix_common_private *)dev->driver_priv;
+#ifdef CONFIG_NET_DSA
+	if (priv->dsa_up == 1){
+		dsa_remove(priv->pdev);
+	   ax88772_remove_mdio(dev);
+		platform_device_unregister(priv->pdev);
+		kset_unregister(priv->kobj.kset);
+		kobject_put(&priv->kobj);
+	}else
+#endif
 	kfree(dev->driver_priv);
 }
 
diff --git a/include/net/dsa.h b/include/net/dsa.h
index ed3c34b..e58a625 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -19,6 +19,7 @@
 #include <linux/phy.h>
 #include <linux/phy_fixed.h>
 #include <linux/ethtool.h>
+#include <linux/platform_device.h>
 
 enum dsa_tag_protocol {
 	DSA_TAG_PROTO_NONE = 0,
@@ -290,4 +291,8 @@ static inline bool dsa_uses_tagged_protocol(struct dsa_switch_tree *dst)
 {
 	return dst->rcv != NULL;
 }
+
+int dsa_probe2(struct platform_device *pdev, struct net_device *dev);
+int dsa_remove(struct platform_device *pdev);
+
 #endif
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 3731714..b4558ae 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -23,6 +23,8 @@
 #include <linux/sysfs.h>
 #include "dsa_priv.h"
 
+#include <linux/phy.h>
+
 char dsa_driver_version[] = "0.1";
 
 
@@ -365,6 +367,19 @@ out:
 
 static void dsa_switch_destroy(struct dsa_switch *ds)
 {
+	int i;
+//	for (i = 0; i < DSA_MAX_SWITCHES; i++) {
+//		if (ds->ports[i] == NULL)
+//			continue;
+	for (i = 0; i < 5; i++) {
+		struct dsa_slave_priv *p = netdev_priv(ds->ports[i]);
+		if (p->phy != NULL)
+			phy_disconnect(p->phy);
+		unregister_netdev(ds->ports[i]);
+	}
+	mdiobus_unregister(ds->slave_mii_bus);
+	kfree(ds->slave_mii_bus->irq);
+	mdiobus_free(ds->slave_mii_bus);
 #ifdef CONFIG_NET_DSA_HWMON
 	if (ds->hwmon_dev)
 		hwmon_device_unregister(ds->hwmon_dev);
@@ -423,6 +438,8 @@ static void dsa_link_poll_work(struct work_struct *ugly)
 	struct dsa_switch_tree *dst;
 	int i;
 
+	mutex_lock(&dsa_switch_drivers_mutex);
+
 	dst = container_of(ugly, struct dsa_switch_tree, link_poll_work);
 
 	for (i = 0; i < dst->pd->nr_chips; i++) {
@@ -433,6 +450,8 @@ static void dsa_link_poll_work(struct work_struct *ugly)
 	}
 
 	mod_timer(&dst->link_poll_timer, round_jiffies(jiffies + HZ));
+
+	mutex_unlock(&dsa_switch_drivers_mutex);
 }
 
 static void dsa_link_poll_timer(unsigned long _dst)
@@ -699,12 +718,104 @@ static inline void dsa_of_remove(struct platform_device *pdev)
 }
 #endif
 
-static int dsa_probe(struct platform_device *pdev)
+static int dsa_probe_common(struct dsa_switch_tree *dst, struct device *parent)
 {
+	struct dsa_platform_data *pd = dst->pd;
+	struct net_device *dev = dst->master_netdev;
+	int i;
+
+	dst->cpu_switch = -1;
+	dst->cpu_port = -1;
+
+	for (i = 0; i < pd->nr_chips; i++) {
+		struct dsa_switch *ds;
+
+		ds = dsa_switch_setup(dst, i, parent, pd->chip[i].host_dev);
+		if (IS_ERR(ds)) {
+			netdev_err(dev, "[%d]: couldn't create dsa switch instance (error %ld)\n",
+				   i, PTR_ERR(ds));
+			continue;
+		}
+
+		dst->ds[i] = ds;
+		if (ds->drv->poll_link != NULL)
+			dst->link_poll_needed = 1;
+	}
+
+	/*
+	 * If we use a tagging format that doesn't have an ethertype
+	 * field, make sure that all packets from this point on get
+	 * sent to the tag format's receive function.
+	 */
+	wmb();
+	dev->dsa_ptr = (void *)dst;
+
+	if (dst->link_poll_needed) {
+		INIT_WORK(&dst->link_poll_work, dsa_link_poll_work);
+		init_timer(&dst->link_poll_timer);
+		dst->link_poll_timer.data = (unsigned long)dst;
+		dst->link_poll_timer.function = dsa_link_poll_timer;
+		dst->link_poll_timer.expires = round_jiffies(jiffies + HZ);
+		add_timer(&dst->link_poll_timer);
+	}
+
+	return 0;
+
+}
+
+int dsa_probe2(struct platform_device *pdev, struct net_device *dev){
+	struct dsa_platform_data *pd = pdev->dev.platform_data;
+	struct dsa_switch_tree *dst;
+	int ret;
+
+	pr_notice_once("V2 !!!!! Distributed Switch Architecture driver version %s\n",
+		       dsa_driver_version);
+
+	if (pdev->dev.of_node) {
+		ret = dsa_of_probe(pdev);
+		if (ret)
+			return ret;
+
+		pd = pdev->dev.platform_data;
+	}
+
+	if (pd == NULL || pd->netdev == NULL)
+		return -EINVAL;
+
+	if (dev->dsa_ptr != NULL) {
+		dev_put(dev);
+		ret = -EEXIST;
+		goto out;
+	}
+
+	dst = kzalloc(sizeof(*dst), GFP_KERNEL);
+	if (dst == NULL) {
+		dev_put(dev);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	platform_set_drvdata(pdev, dst);
+
+	dst->pd = pd;
+	dst->master_netdev = dev;
+
+	dsa_probe_common(dst, &pdev->dev);
+
+	return 0;
+
+out:
+	dsa_of_remove(pdev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dsa_probe2);
+
+int dsa_probe(struct platform_device *pdev){
 	struct dsa_platform_data *pd = pdev->dev.platform_data;
 	struct net_device *dev;
 	struct dsa_switch_tree *dst;
-	int i, ret;
+	int ret;
 
 	pr_notice_once("Distributed Switch Architecture driver version %s\n",
 		       dsa_driver_version);
@@ -743,40 +854,8 @@ static int dsa_probe(struct platform_device *pdev)
 
 	dst->pd = pd;
 	dst->master_netdev = dev;
-	dst->cpu_switch = -1;
-	dst->cpu_port = -1;
 
-	for (i = 0; i < pd->nr_chips; i++) {
-		struct dsa_switch *ds;
-
-		ds = dsa_switch_setup(dst, i, &pdev->dev, pd->chip[i].host_dev);
-		if (IS_ERR(ds)) {
-			netdev_err(dev, "[%d]: couldn't create dsa switch instance (error %ld)\n",
-				   i, PTR_ERR(ds));
-			continue;
-		}
-
-		dst->ds[i] = ds;
-		if (ds->drv->poll_link != NULL)
-			dst->link_poll_needed = 1;
-	}
-
-	/*
-	 * If we use a tagging format that doesn't have an ethertype
-	 * field, make sure that all packets from this point on get
-	 * sent to the tag format's receive function.
-	 */
-	wmb();
-	dev->dsa_ptr = (void *)dst;
-
-	if (dst->link_poll_needed) {
-		INIT_WORK(&dst->link_poll_work, dsa_link_poll_work);
-		init_timer(&dst->link_poll_timer);
-		dst->link_poll_timer.data = (unsigned long)dst;
-		dst->link_poll_timer.function = dsa_link_poll_timer;
-		dst->link_poll_timer.expires = round_jiffies(jiffies + HZ);
-		add_timer(&dst->link_poll_timer);
-	}
+	dsa_probe_common(dst, &pdev->dev);
 
 	return 0;
 
@@ -785,12 +864,16 @@ out:
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(dsa_probe);
+
 
-static int dsa_remove(struct platform_device *pdev)
+int dsa_remove(struct platform_device *pdev)
 {
 	struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
 	int i;
 
+	mutex_lock(&dsa_switch_drivers_mutex);
+
 	if (dst->link_poll_needed)
 		del_timer_sync(&dst->link_poll_timer);
 
@@ -805,8 +888,11 @@ static int dsa_remove(struct platform_device *pdev)
 
 	dsa_of_remove(pdev);
 
+	mutex_unlock(&dsa_switch_drivers_mutex);
+
 	return 0;
 }
+EXPORT_SYMBOL_GPL(dsa_remove);
 
 static void dsa_shutdown(struct platform_device *pdev)
 {
