diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c
index 48f5288..584aca8 100644
--- a/drivers/net/ethernet/calxeda/xgmac.c
+++ b/drivers/net/ethernet/calxeda/xgmac.c
@@ -360,6 +360,13 @@ struct xgmac_extra_stats {
 	unsigned long rx_process_stopped;
 	unsigned long tx_early;
 	unsigned long fatal_bus_error;
+	unsigned long learning_drop;
+};
+
+struct xgmac_mac {
+	char mac_addr[ETH_ALEN];
+	unsigned long time;
+	bool rm_pending;
 };
 
 struct xgmac_priv {
@@ -384,6 +391,8 @@ struct xgmac_priv {
 	struct napi_struct napi;
 
 	int max_macs;
+	struct xgmac_mac mac_list[32];
+
 	struct xgmac_extra_stats xstats;
 
 	spinlock_t stats_lock;
@@ -392,6 +401,7 @@ struct xgmac_priv {
 	char tx_pause;
 	int wolopts;
 	struct work_struct tx_timeout_work;
+	struct delayed_work mac_aging_work;
 };
 
 /* XGMAC Configuration Settings */
@@ -634,7 +644,7 @@ static void xgmac_set_mac_addr(void __iomem *ioaddr, unsigned char *addr,
 	}
 }
 
-static void xgmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
+static bool xgmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
 			       int num)
 {
 	u32 hi_addr, lo_addr;
@@ -650,6 +660,8 @@ static void xgmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
 	addr[3] = (lo_addr >> 24) & 0xff;
 	addr[4] = hi_addr & 0xff;
 	addr[5] = (hi_addr >> 8) & 0xff;
+
+	return (hi_addr & XGMAC_ADDR_AE) ? true : false;
 }
 
 static int xgmac_set_flow_ctrl(struct xgmac_priv *priv, int rx, int tx)
@@ -1047,6 +1059,8 @@ static int xgmac_open(struct net_device *dev)
 	writel(DMA_INTR_DEFAULT_MASK, ioaddr + XGMAC_DMA_STATUS);
 	writel(DMA_INTR_DEFAULT_MASK, ioaddr + XGMAC_DMA_INTR_ENA);
 
+	schedule_delayed_work(&priv->mac_aging_work, 5 * HZ);
+
 	return 0;
 }
 
@@ -1059,6 +1073,7 @@ static int xgmac_open(struct net_device *dev)
 static int xgmac_stop(struct net_device *dev)
 {
 	struct xgmac_priv *priv = netdev_priv(dev);
+	int i;
 
 	netif_stop_queue(dev);
 
@@ -1073,9 +1088,111 @@ static int xgmac_stop(struct net_device *dev)
 	/* Release and free the Rx/Tx resources */
 	xgmac_free_dma_desc_rings(priv);
 
+	cancel_delayed_work_sync(&priv->mac_aging_work);
+	for (i = 0; i < priv->max_macs; i++) {
+		if (!is_valid_ether_addr(priv->mac_list[i].mac_addr))
+			continue;
+		dev_uc_del(dev, priv->mac_list[i].mac_addr);
+		memset(&priv->mac_list[i], 0, sizeof(priv->mac_list[i]));
+	}
+
 	return 0;
 }
 
+static void xgmac_check_mac_addrs(struct xgmac_priv *priv)
+{
+	int i, j;
+	char addr[ETH_ALEN];
+
+	for (i = 1; i < priv->max_macs; i++) {
+		if (xgmac_get_mac_addr(priv->base, addr, i))
+			continue;
+
+		if (!is_valid_ether_addr(addr))
+			break;
+
+		for (j = 1; j < priv->max_macs; j++) {
+			if (!ether_addr_equal(addr, priv->mac_list[j].mac_addr))
+				continue;
+			priv->mac_list[j].rm_pending = true;
+			break;
+		}
+	}
+}
+
+static bool xgmac_mac_rm_pending(struct xgmac_priv *priv, const u8 *addr)
+{
+	int i;
+
+	for (i = 1; i < priv->max_macs; i++) {
+		if (ether_addr_equal(addr, priv->mac_list[i].mac_addr) &&
+		    priv->mac_list[i].rm_pending)
+			return true;
+	}
+	return false;
+}
+
+static void xgmac_mac_aging_work(struct work_struct *work)
+{
+	int i;
+	struct xgmac_priv *priv =
+		container_of(work, struct xgmac_priv, mac_aging_work.work);
+	struct net_device *dev = priv->dev;
+
+	xgmac_check_mac_addrs(priv);
+
+	for (i = 1; i < priv->max_macs; i++) {
+		if (!priv->mac_list[i].rm_pending)
+			continue;
+
+		dev_uc_del(dev, priv->mac_list[i].mac_addr);
+		priv->mac_list[i].rm_pending = false;
+		priv->mac_list[i].time = 0;
+		memset(priv->mac_list[i].mac_addr, 0, ETH_ALEN);
+	}
+
+	schedule_delayed_work(to_delayed_work(work), 10 * HZ);
+}
+
+static void xgmac_learn_mac(struct xgmac_priv *priv, struct sk_buff *skb)
+{
+	struct net_device *dev = priv->dev;
+	struct ethhdr *phdr = (struct ethhdr *)(skb->data);
+	char *src_addr = phdr->h_source;
+	int i, slot = -1, oldest_slot = -1;
+	unsigned long oldest_time = jiffies;
+
+	if (ether_addr_equal(src_addr, dev->dev_addr) ||
+	    !is_valid_ether_addr(src_addr))
+		return;
+
+	for (i = 0; i < priv->max_macs; i++) {
+		/* update timestamp if we already learned the address */
+		if (ether_addr_equal(priv->mac_list[i].mac_addr, src_addr)) {
+			priv->mac_list[i].time = jiffies;
+			return;
+		}
+		/* find empty slot */
+		if (slot >= 0)
+			continue;
+		if (!priv->mac_list[i].time)
+			slot = i;
+		else if (time_before(priv->mac_list[i].time, oldest_time)) {
+			oldest_time = priv->mac_list[i].time;
+			oldest_slot = i;
+		}
+	}
+	if ((slot < 0) && (oldest_slot >= 0)) {
+		slot = oldest_slot;
+		priv->xstats.learning_drop++;
+		dev_uc_del(dev, priv->mac_list[slot].mac_addr);
+	}
+
+	memcpy(priv->mac_list[slot].mac_addr, src_addr, ETH_ALEN);
+	dev_uc_add_excl(dev, src_addr);
+	priv->mac_list[slot].time = jiffies;
+}
+
 /**
  *  xgmac_xmit:
  *  @skb : the socket buffer
@@ -1155,6 +1272,9 @@ static netdev_tx_t xgmac_xmit(struct sk_buff *skb, struct net_device *dev)
 		if (tx_dma_ring_space(priv) > MAX_SKB_FRAGS)
 			netif_start_queue(dev);
 	}
+
+	xgmac_learn_mac(priv, skb);
+
 	return NETDEV_TX_OK;
 
 dma_err:
@@ -1291,6 +1411,8 @@ static void xgmac_set_rx_mode(struct net_device *dev)
 	netdev_dbg(priv->dev, "# mcasts %d, # unicast %d\n",
 		 netdev_mc_count(dev), netdev_uc_count(dev));
 
+	xgmac_check_mac_addrs(priv);
+
 	if (dev->flags & IFF_PROMISC)
 		value |= XGMAC_FRAME_FILTER_PR;
 
@@ -1309,6 +1431,8 @@ static void xgmac_set_rx_mode(struct net_device *dev)
 			 * within the register. */
 			hash_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
 		} else {
+			if (xgmac_mac_rm_pending(priv, ha->addr))
+				continue;
 			xgmac_set_mac_addr(ioaddr, ha->addr, reg);
 			reg++;
 		}
@@ -1605,6 +1729,7 @@ static const struct xgmac_stats xgmac_gstrings_stats[] = {
 	XGMAC_STAT(rx_ip_header_error),
 	XGMAC_STAT(rx_da_filter_fail),
 	XGMAC_STAT(fatal_bus_error),
+	XGMAC_STAT(learning_drop),
 	XGMAC_HW_STAT(rx_watchdog, XGMAC_MMC_RXWATCHDOG),
 	XGMAC_HW_STAT(tx_vlan, XGMAC_MMC_TXVLANFRAME),
 	XGMAC_HW_STAT(rx_vlan, XGMAC_MMC_RXVLANFRAME),
@@ -1743,6 +1868,7 @@ static int xgmac_probe(struct platform_device *pdev)
 	SET_ETHTOOL_OPS(ndev, &xgmac_ethtool_ops);
 	spin_lock_init(&priv->stats_lock);
 	INIT_WORK(&priv->tx_timeout_work, xgmac_tx_timeout_work);
+	INIT_DELAYED_WORK(&priv->mac_aging_work, xgmac_mac_aging_work);
 
 	priv->device = &pdev->dev;
 	priv->dev = ndev;
