]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/ks8842.c
drivers/net: Add Micrel KS8841/42 support to ks8842 driver
[karo-tx-linux.git] / drivers / net / ks8842.c
index b91492f4e48a9cb61cf2e5889102421dae0a0cac..ee69dea69be82868301932ca4e0ed267f378f703 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * ks8842_main.c timberdale KS8842 ethernet driver
+ * ks8842.c timberdale KS8842 ethernet driver
  * Copyright (c) 2009 Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
 
 /* Supports:
  * The Micrel KS8842 behind the timberdale FPGA
+ * The genuine Micrel KS8841/42 device with ISA 16/32bit bus interface
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -28,6 +29,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
+#include <linux/ks8842.h>
 
 #define DRV_NAME "ks8842"
 
 #define REG_P1CR4              0x02
 #define REG_P1SR               0x04
 
+/* flags passed by platform_device for configuration */
+#define        MICREL_KS884X           0x01    /* 0=Timeberdale(FPGA), 1=Micrel */
+#define        KS884X_16BIT            0x02    /*  1=16bit, 0=32bit */
+
 struct ks8842_adapter {
        void __iomem    *hw_addr;
        int             irq;
+       unsigned long   conf_flags;     /* copy of platform_device config */
        struct tasklet_struct   tasklet;
        spinlock_t      lock; /* spinlock to be interrupt safe */
-       struct platform_device *pdev;
+       struct work_struct timeout_work;
+       struct net_device *netdev;
 };
 
 static inline void ks8842_select_bank(struct ks8842_adapter *adapter, u16 bank)
@@ -190,16 +198,21 @@ static inline u32 ks8842_read32(struct ks8842_adapter *adapter, u16 bank,
 
 static void ks8842_reset(struct ks8842_adapter *adapter)
 {
-       /* The KS8842 goes haywire when doing softare reset
-        * a work around in the timberdale IP is implemented to
-        * do a hardware reset instead
-       ks8842_write16(adapter, 3, 1, REG_GRR);
-       msleep(10);
-       iowrite16(0, adapter->hw_addr + REG_GRR);
-       */
-       iowrite16(32, adapter->hw_addr + REG_SELECT_BANK);
-       iowrite32(0x1, adapter->hw_addr + REG_TIMB_RST);
-       msleep(20);
+       if (adapter->conf_flags & MICREL_KS884X) {
+               ks8842_write16(adapter, 3, 1, REG_GRR);
+               msleep(10);
+               iowrite16(0, adapter->hw_addr + REG_GRR);
+       } else {
+               /* The KS8842 goes haywire when doing softare reset
+               * a work around in the timberdale IP is implemented to
+               * do a hardware reset instead
+               ks8842_write16(adapter, 3, 1, REG_GRR);
+               msleep(10);
+               iowrite16(0, adapter->hw_addr + REG_GRR);
+               */
+               iowrite32(0x1, adapter->hw_addr + REG_TIMB_RST);
+               msleep(20);
+       }
 }
 
 static void ks8842_update_link_status(struct net_device *netdev,
@@ -268,8 +281,10 @@ static void ks8842_reset_hw(struct ks8842_adapter *adapter)
 
        /* restart port auto-negotiation */
        ks8842_enable_bits(adapter, 49, 1 << 13, REG_P1CR4);
-       /* only advertise 10Mbps */
-       ks8842_clear_bits(adapter, 49, 3 << 2, REG_P1CR4);
+
+       if (!(adapter->conf_flags & MICREL_KS884X))
+               /* only advertise 10Mbps */
+               ks8842_clear_bits(adapter, 49, 3 << 2, REG_P1CR4);
 
        /* Enable the transmitter */
        ks8842_enable_tx(adapter);
@@ -295,13 +310,59 @@ static void ks8842_read_mac_addr(struct ks8842_adapter *adapter, u8 *dest)
        for (i = 0; i < ETH_ALEN; i++)
                dest[ETH_ALEN - i - 1] = ks8842_read8(adapter, 2, REG_MARL + i);
 
-       /* make sure the switch port uses the same MAC as the QMU */
-       mac = ks8842_read16(adapter, 2, REG_MARL);
-       ks8842_write16(adapter, 39, mac, REG_MACAR1);
-       mac = ks8842_read16(adapter, 2, REG_MARM);
-       ks8842_write16(adapter, 39, mac, REG_MACAR2);
-       mac = ks8842_read16(adapter, 2, REG_MARH);
-       ks8842_write16(adapter, 39, mac, REG_MACAR3);
+       if (adapter->conf_flags & MICREL_KS884X) {
+               /*
+               the sequence of saving mac addr between MAC and Switch is
+               different.
+               */
+
+               mac = ks8842_read16(adapter, 2, REG_MARL);
+               ks8842_write16(adapter, 39, mac, REG_MACAR3);
+               mac = ks8842_read16(adapter, 2, REG_MARM);
+               ks8842_write16(adapter, 39, mac, REG_MACAR2);
+               mac = ks8842_read16(adapter, 2, REG_MARH);
+               ks8842_write16(adapter, 39, mac, REG_MACAR1);
+       } else {
+
+               /* make sure the switch port uses the same MAC as the QMU */
+               mac = ks8842_read16(adapter, 2, REG_MARL);
+               ks8842_write16(adapter, 39, mac, REG_MACAR1);
+               mac = ks8842_read16(adapter, 2, REG_MARM);
+               ks8842_write16(adapter, 39, mac, REG_MACAR2);
+               mac = ks8842_read16(adapter, 2, REG_MARH);
+               ks8842_write16(adapter, 39, mac, REG_MACAR3);
+       }
+}
+
+static void ks8842_write_mac_addr(struct ks8842_adapter *adapter, u8 *mac)
+{
+       unsigned long flags;
+       unsigned i;
+
+       spin_lock_irqsave(&adapter->lock, flags);
+       for (i = 0; i < ETH_ALEN; i++) {
+               ks8842_write8(adapter, 2, mac[ETH_ALEN - i - 1], REG_MARL + i);
+               if (!(adapter->conf_flags & MICREL_KS884X))
+                       ks8842_write8(adapter, 39, mac[ETH_ALEN - i - 1],
+                               REG_MACAR1 + i);
+       }
+
+       if (adapter->conf_flags & MICREL_KS884X) {
+               /*
+               the sequence of saving mac addr between MAC and Switch is
+               different.
+               */
+
+               u16 mac;
+
+               mac = ks8842_read16(adapter, 2, REG_MARL);
+               ks8842_write16(adapter, 39, mac, REG_MACAR3);
+               mac = ks8842_read16(adapter, 2, REG_MARM);
+               ks8842_write16(adapter, 39, mac, REG_MACAR2);
+               mac = ks8842_read16(adapter, 2, REG_MARH);
+               ks8842_write16(adapter, 39, mac, REG_MACAR1);
+       }
+       spin_unlock_irqrestore(&adapter->lock, flags);
 }
 
 static inline u16 ks8842_tx_fifo_space(struct ks8842_adapter *adapter)
@@ -313,11 +374,8 @@ static int ks8842_tx_frame(struct sk_buff *skb, struct net_device *netdev)
 {
        struct ks8842_adapter *adapter = netdev_priv(netdev);
        int len = skb->len;
-       u32 *ptr = (u32 *)skb->data;
-       u32 ctrl;
 
-       dev_dbg(&adapter->pdev->dev,
-               "%s: len %u head %p data %p tail %p end %p\n",
+       netdev_dbg(netdev, "%s: len %u head %p data %p tail %p end %p\n",
                __func__, skb->len, skb->head, skb->data,
                skb_tail_pointer(skb), skb_end_pointer(skb));
 
@@ -325,17 +383,34 @@ static int ks8842_tx_frame(struct sk_buff *skb, struct net_device *netdev)
        if (ks8842_tx_fifo_space(adapter) < len + 8)
                return NETDEV_TX_BUSY;
 
-       /* the control word, enable IRQ, port 1 and the length */
-       ctrl = 0x8000 | 0x100 | (len << 16);
-       ks8842_write32(adapter, 17, ctrl, REG_QMU_DATA_LO);
+       if (adapter->conf_flags & KS884X_16BIT) {
+               u16 *ptr16 = (u16 *)skb->data;
+               ks8842_write16(adapter, 17, 0x8000 | 0x100, REG_QMU_DATA_LO);
+               ks8842_write16(adapter, 17, (u16)len, REG_QMU_DATA_HI);
+               netdev->stats.tx_bytes += len;
+
+               /* copy buffer */
+               while (len > 0) {
+                       iowrite16(*ptr16++, adapter->hw_addr + REG_QMU_DATA_LO);
+                       iowrite16(*ptr16++, adapter->hw_addr + REG_QMU_DATA_HI);
+                       len -= sizeof(u32);
+               }
+       } else {
+
+               u32 *ptr = (u32 *)skb->data;
+               u32 ctrl;
+               /* the control word, enable IRQ, port 1 and the length */
+               ctrl = 0x8000 | 0x100 | (len << 16);
+               ks8842_write32(adapter, 17, ctrl, REG_QMU_DATA_LO);
 
-       netdev->stats.tx_bytes += len;
+               netdev->stats.tx_bytes += len;
 
-       /* copy buffer */
-       while (len > 0) {
-               iowrite32(*ptr, adapter->hw_addr + REG_QMU_DATA_LO);
-               len -= sizeof(u32);
-               ptr++;
+               /* copy buffer */
+               while (len > 0) {
+                       iowrite32(*ptr, adapter->hw_addr + REG_QMU_DATA_LO);
+                       len -= sizeof(u32);
+                       ptr++;
+               }
        }
 
        /* enqueue packet */
@@ -349,43 +424,62 @@ static int ks8842_tx_frame(struct sk_buff *skb, struct net_device *netdev)
 static void ks8842_rx_frame(struct net_device *netdev,
        struct ks8842_adapter *adapter)
 {
-       u32 status = ks8842_read32(adapter, 17, REG_QMU_DATA_LO);
-       int len = (status >> 16) & 0x7ff;
+       u16 status16;
+       u32 status;
+       int len;
 
-       status &= 0xffff;
-
-       dev_dbg(&adapter->pdev->dev, "%s - rx_data: status: %x\n",
-               __func__, status);
+       if (adapter->conf_flags & KS884X_16BIT) {
+               status16 = ks8842_read16(adapter, 17, REG_QMU_DATA_LO);
+               len  = (int)ks8842_read16(adapter, 17, REG_QMU_DATA_HI);
+               len &= 0xffff;
+               netdev_dbg(netdev, "%s - rx_data: status: %x\n",
+                          __func__, status16);
+       } else {
+               status = ks8842_read32(adapter, 17, REG_QMU_DATA_LO);
+               len = (status >> 16) & 0x7ff;
+               status &= 0xffff;
+               netdev_dbg(netdev, "%s - rx_data: status: %x\n",
+                          __func__, status);
+       }
 
        /* check the status */
        if ((status & RXSR_VALID) && !(status & RXSR_ERROR)) {
                struct sk_buff *skb = netdev_alloc_skb_ip_align(netdev, len);
 
-               dev_dbg(&adapter->pdev->dev, "%s, got package, len: %d\n",
-                       __func__, len);
+               netdev_dbg(netdev, "%s, got package, len: %d\n", __func__, len);
                if (skb) {
-                       u32 *data;
 
                        netdev->stats.rx_packets++;
                        netdev->stats.rx_bytes += len;
                        if (status & RXSR_MULTICAST)
                                netdev->stats.multicast++;
 
-                       data = (u32 *)skb_put(skb, len);
-
-                       ks8842_select_bank(adapter, 17);
-                       while (len > 0) {
-                               *data++ = ioread32(adapter->hw_addr +
-                                       REG_QMU_DATA_LO);
-                               len -= sizeof(u32);
+                       if (adapter->conf_flags & KS884X_16BIT) {
+                               u16 *data16 = (u16 *)skb_put(skb, len);
+                               ks8842_select_bank(adapter, 17);
+                               while (len > 0) {
+                                       *data16++ = ioread16(adapter->hw_addr +
+                                               REG_QMU_DATA_LO);
+                                       *data16++ = ioread16(adapter->hw_addr +
+                                               REG_QMU_DATA_HI);
+                                       len -= sizeof(u32);
+                               }
+                       } else {
+                               u32 *data = (u32 *)skb_put(skb, len);
+
+                               ks8842_select_bank(adapter, 17);
+                               while (len > 0) {
+                                       *data++ = ioread32(adapter->hw_addr +
+                                               REG_QMU_DATA_LO);
+                                       len -= sizeof(u32);
+                               }
                        }
-
                        skb->protocol = eth_type_trans(skb, netdev);
                        netif_rx(skb);
                } else
                        netdev->stats.rx_dropped++;
        } else {
-               dev_dbg(&adapter->pdev->dev, "RX error, status: %x\n", status);
+               netdev_dbg(netdev, "RX error, status: %x\n", status);
                netdev->stats.rx_errors++;
                if (status & RXSR_TOO_LONG)
                        netdev->stats.rx_length_errors++;
@@ -408,8 +502,7 @@ static void ks8842_rx_frame(struct net_device *netdev,
 void ks8842_handle_rx(struct net_device *netdev, struct ks8842_adapter *adapter)
 {
        u16 rx_data = ks8842_read16(adapter, 16, REG_RXMIR) & 0x1fff;
-       dev_dbg(&adapter->pdev->dev, "%s Entry - rx_data: %d\n",
-               __func__, rx_data);
+       netdev_dbg(netdev, "%s Entry - rx_data: %d\n", __func__, rx_data);
        while (rx_data) {
                ks8842_rx_frame(netdev, adapter);
                rx_data = ks8842_read16(adapter, 16, REG_RXMIR) & 0x1fff;
@@ -419,7 +512,7 @@ void ks8842_handle_rx(struct net_device *netdev, struct ks8842_adapter *adapter)
 void ks8842_handle_tx(struct net_device *netdev, struct ks8842_adapter *adapter)
 {
        u16 sr = ks8842_read16(adapter, 16, REG_TXSR);
-       dev_dbg(&adapter->pdev->dev, "%s - entry, sr: %x\n", __func__, sr);
+       netdev_dbg(netdev, "%s - entry, sr: %x\n", __func__, sr);
        netdev->stats.tx_packets++;
        if (netif_queue_stopped(netdev))
                netif_wake_queue(netdev);
@@ -428,7 +521,7 @@ void ks8842_handle_tx(struct net_device *netdev, struct ks8842_adapter *adapter)
 void ks8842_handle_rx_overrun(struct net_device *netdev,
        struct ks8842_adapter *adapter)
 {
-       dev_dbg(&adapter->pdev->dev, "%s: entry\n", __func__);
+       netdev_dbg(netdev, "%s: entry\n", __func__);
        netdev->stats.rx_errors++;
        netdev->stats.rx_fifo_errors++;
 }
@@ -447,7 +540,7 @@ void ks8842_tasklet(unsigned long arg)
        spin_unlock_irqrestore(&adapter->lock, flags);
 
        isr = ks8842_read16(adapter, 18, REG_ISR);
-       dev_dbg(&adapter->pdev->dev, "%s - ISR: 0x%x\n", __func__, isr);
+       netdev_dbg(netdev, "%s - ISR: 0x%x\n", __func__, isr);
 
        /* Ack */
        ks8842_write16(adapter, 18, isr, REG_ISR);
@@ -486,13 +579,14 @@ void ks8842_tasklet(unsigned long arg)
 
 static irqreturn_t ks8842_irq(int irq, void *devid)
 {
-       struct ks8842_adapter *adapter = devid;
+       struct net_device *netdev = devid;
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
        u16 isr;
        u16 entry_bank = ioread16(adapter->hw_addr + REG_SELECT_BANK);
        irqreturn_t ret = IRQ_NONE;
 
        isr = ks8842_read16(adapter, 18, REG_ISR);
-       dev_dbg(&adapter->pdev->dev, "%s - ISR: 0x%x\n", __func__, isr);
+       netdev_dbg(netdev, "%s - ISR: 0x%x\n", __func__, isr);
 
        if (isr) {
                /* disable IRQ */
@@ -517,15 +611,17 @@ static int ks8842_open(struct net_device *netdev)
        struct ks8842_adapter *adapter = netdev_priv(netdev);
        int err;
 
-       dev_dbg(&adapter->pdev->dev, "%s - entry\n", __func__);
+       netdev_dbg(netdev, "%s - entry\n", __func__);
 
        /* reset the HW */
        ks8842_reset_hw(adapter);
 
+       ks8842_write_mac_addr(adapter, netdev->dev_addr);
+
        ks8842_update_link_status(netdev, adapter);
 
        err = request_irq(adapter->irq, ks8842_irq, IRQF_SHARED, DRV_NAME,
-               adapter);
+               netdev);
        if (err) {
                pr_err("Failed to request IRQ: %d: %d\n", adapter->irq, err);
                return err;
@@ -538,10 +634,12 @@ static int ks8842_close(struct net_device *netdev)
 {
        struct ks8842_adapter *adapter = netdev_priv(netdev);
 
-       dev_dbg(&adapter->pdev->dev, "%s - entry\n", __func__);
+       netdev_dbg(netdev, "%s - entry\n", __func__);
+
+       cancel_work_sync(&adapter->timeout_work);
 
        /* free the irq */
-       free_irq(adapter->irq, adapter);
+       free_irq(adapter->irq, netdev);
 
        /* disable the switch */
        ks8842_write16(adapter, 32, 0x0, REG_SW_ID_AND_ENABLE);
@@ -555,7 +653,7 @@ static netdev_tx_t ks8842_xmit_frame(struct sk_buff *skb,
        int ret;
        struct ks8842_adapter *adapter = netdev_priv(netdev);
 
-       dev_dbg(&adapter->pdev->dev, "%s: entry\n", __func__);
+       netdev_dbg(netdev, "%s: entry\n", __func__);
 
        ret = ks8842_tx_frame(skb, netdev);
 
@@ -568,46 +666,54 @@ static netdev_tx_t ks8842_xmit_frame(struct sk_buff *skb,
 static int ks8842_set_mac(struct net_device *netdev, void *p)
 {
        struct ks8842_adapter *adapter = netdev_priv(netdev);
-       unsigned long flags;
        struct sockaddr *addr = p;
        char *mac = (u8 *)addr->sa_data;
-       int i;
 
-       dev_dbg(&adapter->pdev->dev, "%s: entry\n", __func__);
+       netdev_dbg(netdev, "%s: entry\n", __func__);
 
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
 
        memcpy(netdev->dev_addr, mac, netdev->addr_len);
 
-       spin_lock_irqsave(&adapter->lock, flags);
-       for (i = 0; i < ETH_ALEN; i++) {
-               ks8842_write8(adapter, 2, mac[ETH_ALEN - i - 1], REG_MARL + i);
-               ks8842_write8(adapter, 39, mac[ETH_ALEN - i - 1],
-                       REG_MACAR1 + i);
-       }
-       spin_unlock_irqrestore(&adapter->lock, flags);
+       ks8842_write_mac_addr(adapter, mac);
        return 0;
 }
 
-static void ks8842_tx_timeout(struct net_device *netdev)
+static void ks8842_tx_timeout_work(struct work_struct *work)
 {
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       struct ks8842_adapter *adapter =
+               container_of(work, struct ks8842_adapter, timeout_work);
+       struct net_device *netdev = adapter->netdev;
        unsigned long flags;
 
-       dev_dbg(&adapter->pdev->dev, "%s: entry\n", __func__);
+       netdev_dbg(netdev, "%s: entry\n", __func__);
 
        spin_lock_irqsave(&adapter->lock, flags);
        /* disable interrupts */
        ks8842_write16(adapter, 18, 0, REG_IER);
        ks8842_write16(adapter, 18, 0xFFFF, REG_ISR);
+
+       netif_stop_queue(netdev);
+
        spin_unlock_irqrestore(&adapter->lock, flags);
 
        ks8842_reset_hw(adapter);
 
+       ks8842_write_mac_addr(adapter, netdev->dev_addr);
+
        ks8842_update_link_status(netdev, adapter);
 }
 
+static void ks8842_tx_timeout(struct net_device *netdev)
+{
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+
+       netdev_dbg(netdev, "%s: entry\n", __func__);
+
+       schedule_work(&adapter->timeout_work);
+}
+
 static const struct net_device_ops ks8842_netdev_ops = {
        .ndo_open               = ks8842_open,
        .ndo_stop               = ks8842_close,
@@ -627,7 +733,9 @@ static int __devinit ks8842_probe(struct platform_device *pdev)
        struct resource *iomem;
        struct net_device *netdev;
        struct ks8842_adapter *adapter;
+       struct ks8842_platform_data *pdata = pdev->dev.platform_data;
        u16 id;
+       unsigned i;
 
        iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!request_mem_region(iomem->start, resource_size(iomem), DRV_NAME))
@@ -640,7 +748,11 @@ static int __devinit ks8842_probe(struct platform_device *pdev)
        SET_NETDEV_DEV(netdev, &pdev->dev);
 
        adapter = netdev_priv(netdev);
+       adapter->netdev = netdev;
+       INIT_WORK(&adapter->timeout_work, ks8842_tx_timeout_work);
        adapter->hw_addr = ioremap(iomem->start, resource_size(iomem));
+       adapter->conf_flags = iomem->flags;
+
        if (!adapter->hw_addr)
                goto err_ioremap;
 
@@ -650,15 +762,31 @@ static int __devinit ks8842_probe(struct platform_device *pdev)
                goto err_get_irq;
        }
 
-       adapter->pdev = pdev;
-
        tasklet_init(&adapter->tasklet, ks8842_tasklet, (unsigned long)netdev);
        spin_lock_init(&adapter->lock);
 
        netdev->netdev_ops = &ks8842_netdev_ops;
        netdev->ethtool_ops = &ks8842_ethtool_ops;
 
-       ks8842_read_mac_addr(adapter, netdev->dev_addr);
+       /* Check if a mac address was given */
+       i = netdev->addr_len;
+       if (pdata) {
+               for (i = 0; i < netdev->addr_len; i++)
+                       if (pdata->macaddr[i] != 0)
+                               break;
+
+               if (i < netdev->addr_len)
+                       /* an address was passed, use it */
+                       memcpy(netdev->dev_addr, pdata->macaddr,
+                               netdev->addr_len);
+       }
+
+       if (i == netdev->addr_len) {
+               ks8842_read_mac_addr(adapter, netdev->dev_addr);
+
+               if (!is_valid_ether_addr(netdev->dev_addr))
+                       random_ether_addr(netdev->dev_addr);
+       }
 
        id = ks8842_read16(adapter, 32, REG_SW_ID_AND_ENABLE);