* [Patch] Micrel KS8695 intergrated ethernet driver
@ 2008-12-05 15:06 Daniel Silverstone
2008-12-05 17:57 ` Ben Hutchings
0 siblings, 1 reply; 16+ messages in thread
From: Daniel Silverstone @ 2008-12-05 15:06 UTC (permalink / raw)
To: netdev
[-- Attachment #1: Type: text/plain, Size: 303 bytes --]
Hello,
Attached is a patch which implements the Micrel KS8695 SoC's Ethernet
interfaces.
The driver is only of use on the KS8695.
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
[-- Attachment #2: ks8695-ethernet.patch --]
[-- Type: text/x-patch, Size: 50784 bytes --]
KS8695: Add support for the KS8695 ethernet devices.
Implements the KS8695 ethernet device (ks8695net).
Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
Acked-by: Ben Dooks <ben@simtec.co.uk>
Index: linux-2.6.27/drivers/net/arm/Kconfig
===================================================================
--- linux-2.6.27.orig/drivers/net/arm/Kconfig 2008-12-05 11:29:29.629076651 +0000
+++ linux-2.6.27/drivers/net/arm/Kconfig 2008-12-05 11:30:15.357075120 +0000
@@ -40,6 +40,14 @@
If you wish to compile a kernel for the AT91RM9200 and enable
ethernet support, then you should always answer Y to this.
+config ARM_KS8695_ETHER
+ tristate "KS8695 Ethernet support"
+ depends on ARM && ARCH_KS8695
+ select MII
+ help
+ If you wish to compile a kernel for the KS8695 and want to
+ use the internal ethernet then you should answer Y to this.
+
config EP93XX_ETH
tristate "EP93xx Ethernet support"
depends on ARM && ARCH_EP93XX
Index: linux-2.6.27/drivers/net/arm/Makefile
===================================================================
--- linux-2.6.27.orig/drivers/net/arm/Makefile 2008-12-05 11:29:29.649077064 +0000
+++ linux-2.6.27/drivers/net/arm/Makefile 2008-12-05 11:30:15.357075120 +0000
@@ -8,5 +8,6 @@
obj-$(CONFIG_ARM_ETHER3) += ether3.o
obj-$(CONFIG_ARM_ETHER1) += ether1.o
obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
+obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
Index: linux-2.6.27/drivers/net/arm/ks8695net.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/drivers/net/arm/ks8695net.c 2008-12-05 13:07:56.773076323 +0000
@@ -0,0 +1,1661 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+
+#include <mach/regs-switch.h>
+#include <mach/regs-misc.h>
+
+#include "ks8695net.h"
+
+#define MODULENAME "ks8695_ether"
+#define MODULEVERSION "1.00"
+
+/*
+ * Transmit timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+
+/* Hardware structures */
+
+/**
+ * struct rx_ring_desc - Receive descriptor ring element
+ * @status: The status of the descriptor element (E.g. who owns it)
+ * @length: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct rx_ring_desc {
+ __le32 status;
+ __le32 length;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct tx_ring_desc - Transmit descriptor ring element
+ * @owner: Who owns the descriptor
+ * @status: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct tx_ring_desc {
+ __le32 owner;
+ __le32 status;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct ks8695_skbuff - sk_buff wrapper for rx/tx rings.
+ * @skb: The buffer in the ring
+ * @dma_ptr: The mapped DMA pointer of the buffer
+ * @length: The number of bytes mapped to dma_ptr
+ */
+struct ks8695_skbuff {
+ struct sk_buff *skb;
+ dma_addr_t dma_ptr;
+ u32 length;
+};
+
+/* Private device structure */
+
+#define MAX_TX_DESC 8
+#define MAX_RX_DESC 16
+#define MAX_RXBUF_SIZE 0x700
+
+#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
+#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC)
+#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)
+
+/**
+ * enum ks8695_dtype - Device type
+ * @KS8695_DTYPE_WAN: This device is a WAN interface
+ * @KS8695_DTYPE_LAN: This device is a LAN interface
+ * @KS8695_DTYPE_HPNA: This device is an HPNA interfacew
+ */
+enum ks8695_dtype {
+ KS8695_DTYPE_WAN,
+ KS8695_DTYPE_LAN,
+ KS8695_DTYPE_HPNA,
+};
+
+/**
+ * struct ks8695_priv - Private data for the KS8695 Ethernet
+ * @in_suspend: Flag to indicate if we're suspending/resuming
+ * @ndev: The net_device for this interface
+ * @dev: The device object for this interface
+ * @dtype: The type of this device
+ * @io_regs: The ioremapped registers for this interface
+ * @rx_irq_name: The textual name of the RX IRQ from the platform data
+ * @tx_irq_name: The textual name of the TX IRQ from the platform data
+ * @link_irq_name: The textual name of the link IRQ from the
+ * platform data if available
+ * @rx_irq: The IRQ number for the RX IRQ
+ * @tx_irq: The IRQ number for the TX IRQ
+ * @link_irq: The IRQ number for the link IRQ if available
+ * @regs_req: The resource request for the registers region
+ * @phyiface_req: The resource request for the phy/switch region
+ * if available
+ * @phyiface_regs: The ioremapped registers for the phy/switch if available
+ * @ring_base: The base pointer of the dma coherent memory for the rings
+ * @ring_base_dma: The DMA mapped equivalent of ring_base
+ * @tx_ring: The pointer in ring_base of the TX ring
+ * @tx_ring_used: The number of slots in the TX ring which are occupied
+ * @tx_ring_next_slot: The next slot to fill in the TX ring
+ * @tx_ring_dma: The DMA mapped equivalent of tx_ring
+ * @tx_buffers: The sk_buff mappings for the TX ring
+ * @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables
+ * @rx_ring: The pointer in ring_base of the RX ring
+ * @rx_ring_dma: The DMA mapped equivalent of rx_ring
+ * @rx_buffers: The sk_buff mappings for the RX ring
+ * @next_rx_desc_read: The next RX descriptor to read from on IRQ
+ * @msg_enable: The flags for which messages to emit
+ */
+struct ks8695_priv {
+ int in_suspend;
+ struct net_device *ndev;
+ struct device *dev;
+ enum ks8695_dtype dtype;
+ void __iomem *io_regs;
+
+ const char *rx_irq_name, *tx_irq_name, *link_irq_name;
+ int rx_irq, tx_irq, link_irq;
+
+ struct resource *regs_req, *phyiface_req;
+ void __iomem *phyiface_regs;
+
+ void *ring_base;
+ dma_addr_t ring_base_dma;
+
+ struct tx_ring_desc *tx_ring;
+ int tx_ring_used;
+ int tx_ring_next_slot;
+ dma_addr_t tx_ring_dma;
+ struct ks8695_skbuff tx_buffers[MAX_TX_DESC];
+ spinlock_t txq_lock;
+
+ struct rx_ring_desc *rx_ring;
+ dma_addr_t rx_ring_dma;
+ struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
+ int next_rx_desc_read;
+
+ int msg_enable;
+};
+
+/* Register access */
+
+/**
+ * ks8695_readreg - Read from a KS8695 ethernet register
+ * @ksp: The device to read from
+ * @reg: The register to read
+ */
+static inline u32
+ks8695_readreg(struct ks8695_priv *ksp, int reg)
+{
+ return readl(ksp->io_regs + reg);
+}
+
+/**
+ * ks8695_writereg - Write to a KS8695 ethernet register
+ * @ksp: The device to write to
+ * @reg: The register to write
+ * @value: The value to write to the register
+ */
+static inline void
+ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value)
+{
+ writel(value, ksp->io_regs + reg);
+}
+
+/* Utility functions */
+
+/**
+ * ks8695_get_priv - Retrieve the private data from a net_device.
+ * @ndev: The net_device to read the private data pointer from
+ *
+ * Returns the private structure for the KS8695 Ethernet device.
+ */
+static inline struct ks8695_priv *
+ks8695_get_priv(struct net_device *ndev)
+{
+ return ndev->priv;
+}
+
+/**
+ * ks8695_port_type - Retrieve port-type as user-friendly string
+ * @ksp: The device to return the type for
+ *
+ * Returns a string indicating which of the WAN, LAN or HPNA
+ * ports this device is likely to represent.
+ */
+static const char *
+ks8695_port_type(struct ks8695_priv *ksp)
+{
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_LAN:
+ return "LAN";
+ case KS8695_DTYPE_WAN:
+ return "WAN";
+ case KS8695_DTYPE_HPNA:
+ return "HPNA";
+ }
+
+ return "UNKNOWN";
+}
+
+/**
+ * ks8695_update_mac - Update the MAC registers in the device
+ * @ksp: The device to update
+ *
+ * Updates the MAC registers in the KS8695 device from the address in the
+ * net_device structure associated with this interface.
+ */
+static void
+ks8695_update_mac(struct ks8695_priv *ksp)
+{
+ /* Update the HW with the MAC from the net_device */
+ struct net_device *ndev = ksp->ndev;
+ u32 machigh, maclow;
+
+ maclow = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5] << 0));
+ machigh = ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1] << 0));
+
+ ks8695_writereg(ksp, KS8695_MAL, maclow);
+ ks8695_writereg(ksp, KS8695_MAH, machigh);
+
+}
+
+/**
+ * ks8695_refill_rxbuffers - Re-fill the RX buffer ring
+ * @ksp: The device to refill
+ *
+ * Iterates the RX ring of the device looking for empty slots.
+ * For each empty slot, we allocate and map a new SKB and give it
+ * to the hardware.
+ * This can be called from interrupt context safely.
+ */
+static void
+ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
+{
+ /* Run around the RX ring, filling in any missing sk_buff's */
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (!ksp->rx_buffers[buff_n].skb) {
+ struct sk_buff *skb = dev_alloc_skb(MAX_RXBUF_SIZE);
+ dma_addr_t mapping;
+
+ ksp->rx_buffers[buff_n].skb = skb;
+ if (skb == NULL) {
+ /* Failed to allocate one, perhaps
+ * we'll try again later.
+ */
+ break;
+ }
+
+ mapping = dma_map_single(ksp->dev, skb->data,
+ MAX_RXBUF_SIZE,
+ DMA_FROM_DEVICE);
+ ksp->rx_buffers[buff_n].dma_ptr = mapping;
+ skb->dev = ksp->ndev;
+ ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
+
+ /* Record this into the DMA ring */
+ ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
+ ksp->rx_ring[buff_n].length =
+ cpu_to_le32(MAX_RXBUF_SIZE);
+
+ /* And give ownership over to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+ }
+ }
+}
+
+/* Maximum number of multicast addresses which the KS8695 HW supports */
+#define KS8695_NR_ADDRESSES 16
+
+/**
+ * ks8695_init_partial_multicast - Init the mcast addr registers
+ * @ksp: The device to initialise
+ * @addr: The multicast address list to use
+ * @nr_addr: The number of addresses in the list
+ *
+ * This routine is a helper for ks8695_set_multicast - it writes
+ * the additional-address registers in the KS8695 ethernet device
+ * and cleans up any others left behind.
+ */
+static void
+ks8695_init_partial_multicast(struct ks8695_priv *ksp,
+ struct dev_mc_list *addr,
+ int nr_addr)
+{
+ u32 low, high;
+ int i;
+
+ for (i = 0; i < nr_addr; i++, addr = addr->next) {
+ /* Ran out of addresses? */
+ if (!addr)
+ break;
+ /* Ran out of space in chip? */
+ if (i == KS8695_NR_ADDRESSES)
+ break;
+
+ low = (addr->dmi_addr[2] << 24) | (addr->dmi_addr[3] << 16) |
+ (addr->dmi_addr[4] << 8) | (addr->dmi_addr[5]);
+ high = (addr->dmi_addr[0] << 8) | (addr->dmi_addr[1]);
+
+ ks8695_writereg(ksp, KS8695_AAL_(i), low);
+ ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high);
+ }
+
+ /* Clear the remaining Additional Station Addresses */
+ for (; i < KS8695_NR_ADDRESSES; i++) {
+ ks8695_writereg(ksp, KS8695_AAL_(i), 0);
+ ks8695_writereg(ksp, KS8695_AAH_(i), 0);
+ }
+}
+
+/* Interrupt handling */
+
+/**
+ * ks8695_tx_irq - Transmit IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the TX ring, clearing out any transmitted slots.
+ * Allows the net_device to pass us new packets once slots are
+ * freed.
+ */
+static irqreturn_t
+ks8695_tx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb &&
+ !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
+ /* An SKB which is not owned by HW is present */
+ /* Update the stats for the net_device */
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;
+
+ /* Free the packet from the ring */
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Free the sk_buff */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ ksp->tx_ring_used--;
+ }
+ }
+
+ netif_wake_queue(ndev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_rx_irq - Receive IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the RX ring, passing any received packets up to the
+ * host. If we received anything other than errors, we then
+ * refill the ring.
+ */
+static irqreturn_t
+ks8695_rx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ struct sk_buff *skb;
+ int buff_n;
+ u32 flags;
+ int pktlen;
+ int last_rx_processed = -1;
+
+ buff_n = ksp->next_rx_desc_read;
+ do {
+ if (ksp->rx_buffers[buff_n].skb &&
+ !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) {
+ flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
+ /* Found an SKB which we own, this means we
+ * received a packet
+ */
+ if ((flags & (RDES_FS | RDES_LS)) !=
+ (RDES_FS | RDES_LS)) {
+ /* This packet is not the first and
+ * the last segment. Therefore it is
+ * a "spanning" packet and we can't
+ * handle it
+ */
+ goto rx_failure;
+ }
+
+ if (flags & (RDES_ES | RDES_RE)) {
+ /* It's an error packet */
+ ndev->stats.rx_errors++;
+ if (flags & RDES_TL)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_RF)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_CE)
+ ndev->stats.rx_crc_errors++;
+ if (flags & RDES_RE)
+ ndev->stats.rx_missed_errors++;
+
+ goto rx_failure;
+ }
+
+ pktlen = flags & RDES_FLEN;
+ pktlen -= 4; /* Drop the CRC */
+
+ skb = ksp->rx_buffers[buff_n].skb;
+
+ /* Extract the sk_buff from the ring */
+ ksp->rx_buffers[buff_n].skb = NULL;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+
+ /* Relinquish the SKB to the network layer */
+ skb_put(skb, pktlen);
+ skb->protocol = eth_type_trans(skb, ndev);
+ netif_rx(skb);
+
+ /* Record stats */
+ ndev->last_rx = jiffies;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += pktlen;
+ goto rx_finished;
+
+rx_failure:
+ /* This ring entry is an error, but we can
+ * re-use the skb
+ */
+ /* Give the ring entry back to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+rx_finished:
+ /* And note this as processed so we can start
+ * from here next time
+ */
+ last_rx_processed = buff_n;
+ } else {
+ /* Ran out of things to process, stop now */
+ break;
+ }
+ buff_n = (buff_n + 1) % MAX_RX_DESC;
+ } while (buff_n != ksp->next_rx_desc_read);
+
+ /* And note which RX descriptor we last did anything with */
+ if (likely(last_rx_processed != -1))
+ ksp->next_rx_desc_read = (last_rx_processed + 1) % MAX_RX_DESC;
+
+ /* And refill the buffers */
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Kick the RX DMA engine, in case it became suspended */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_link_irq - Link change IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * The WAN interface can generate an IRQ when the link changes,
+ * report this to the net layer and the user.
+ */
+static irqreturn_t
+ks8695_link_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ u32 ctrl;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if (ctrl & WMC_WLS) {
+ netif_carrier_on(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev,
+ "%s: Link is now up (10%sMbps/%s-duplex)\n",
+ ndev->name,
+ (ctrl & WMC_WSS) ? "0" : "",
+ (ctrl & WMC_WDS) ? "Full" : "Half");
+ } else {
+ netif_carrier_off(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev, "%s: Link is now down.\n",
+ ndev->name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+/* KS8695 Device functions */
+
+/**
+ * ks8695_reset - Reset a KS8695 ethernet interface
+ * @ksp: The interface to reset
+ *
+ * Perform an engine reset of the interface and re-program it
+ * with sensible defaults.
+ */
+static void
+ks8695_reset(struct ks8695_priv *ksp)
+{
+ int reset_timeout = watchdog;
+ /* Issue the reset via the TX DMA control register */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
+ while (reset_timeout--) {
+ if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
+ break;
+ msleep(1);
+ }
+
+ if (reset_timeout == 0) {
+ dev_crit(ksp->dev,
+ "Timeout waiting for DMA engines to reset\n");
+ /* And blithely carry on */
+ }
+
+ /* Definitely wait long enough before attempting to program
+ * the engines
+ */
+ msleep(10);
+
+ /* RX: unicast and broadcast */
+ ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB);
+ /* TX: pad and add CRC */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC);
+}
+
+/**
+ * ks8695_shutdown - Shut down a KS8695 ethernet interface
+ * @ksp: The interface to shut down
+ *
+ * This disables packet RX/TX, cleans up IRQs, drains the rings,
+ * and basically places the interface into a clean shutdown
+ * state.
+ */
+static void
+ks8695_shutdown(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+ int buff_n;
+
+ /* Disable packet transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE);
+
+ /* Disable packet reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE);
+
+ /* Release the IRQs */
+ free_irq(ksp->rx_irq, ksp->ndev);
+ free_irq(ksp->tx_irq, ksp->ndev);
+ if (ksp->link_irq != -1)
+ free_irq(ksp->link_irq, ksp->ndev);
+
+ /* Throw away any pending TX packets */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb) {
+ /* Remove this SKB from the TX ring */
+ ksp->tx_ring[buff_n].owner = 0;
+ ksp->tx_ring[buff_n].status = 0;
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin this SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ }
+ }
+
+ /* Purge the RX buffers */
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (ksp->rx_buffers[buff_n].skb) {
+ /* Remove the SKB from the RX ring */
+ ksp->rx_ring[buff_n].status = 0;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb);
+ ksp->rx_buffers[buff_n].skb = NULL;
+ }
+ }
+}
+
+
+/**
+ * ks8695_setup_irq - IRQ setup helper function
+ * @irq: The IRQ number to claim
+ * @irq_name: The name to give the IRQ claimant
+ * @handler: The function to call to handle the IRQ
+ * @ndev: The net_device to pass in as the dev_id argument to the handler
+ *
+ * Return 0 on success.
+ */
+static int
+ks8695_setup_irq(int irq, const char *irq_name,
+ irq_handler_t handler, struct net_device *ndev)
+{
+ int ret;
+
+ ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);
+
+ if (ret) {
+ dev_err(&ndev->dev, "failure to request IRQ %d\n", irq);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_init_net - Initialise a KS8695 ethernet interface
+ * @ksp: The interface to initialise
+ *
+ * This routine fills the RX ring, initialises the DMA engines,
+ * allocates the IRQs and then starts the packet TX and RX
+ * engines.
+ */
+static int
+ks8695_init_net(struct ks8695_priv *ksp)
+{
+ int ret;
+ u32 ctrl;
+
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Initialise the DMA engines */
+ ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
+ ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
+
+ /* Request the IRQs */
+ ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
+ ks8695_rx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
+ ks8695_tx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ if (ksp->link_irq != -1) {
+ ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
+ ks8695_link_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ }
+
+ /* Set up the ring indices */
+ ksp->next_rx_desc_read = 0;
+ ksp->tx_ring_next_slot = 0;
+ ksp->tx_ring_used = 0;
+
+ /* Bring up transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ /* Enable packet transmission */
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
+
+ /* Bring up the reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ /* Enable packet reception */
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
+ /* And start the DMA engine */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ /* All done */
+ return 0;
+}
+
+/**
+ * ks8695_release_device - HW resource release for KS8695 e-net
+ * @ksp: The device to be freed
+ *
+ * This unallocates io memory regions, dma-coherent regions etc
+ * which were allocated in ks8695_probe.
+ */
+static void
+ks8695_release_device(struct ks8695_priv *ksp)
+{
+ /* Unmap the registers */
+ iounmap(ksp->io_regs);
+ if (ksp->phyiface_regs)
+ iounmap(ksp->phyiface_regs);
+
+ /* And release the request */
+ release_resource(ksp->regs_req);
+ kfree(ksp->regs_req);
+ if (ksp->phyiface_req) {
+ release_resource(ksp->phyiface_req);
+ kfree(ksp->phyiface_req);
+ }
+
+ /* Free the ring buffers */
+ dma_free_coherent(ksp->dev, RING_DMA_SIZE,
+ ksp->ring_base, ksp->ring_base_dma);
+}
+
+/* Ethtool support */
+
+/**
+ * ks8695_get_msglevel - Get the messages enabled for emission
+ * @ndev: The network device to read from
+ */
+static u32
+ks8695_get_msglevel(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+
+ return ksp->msg_enable;
+}
+
+/**
+ * ks8695_set_msglevel - Set the messages enabled for emission
+ * @ndev: The network device to configure
+ * @value: The messages to set for emission
+ */
+static void
+ks8695_set_msglevel(struct net_device *ndev, u32 value)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+
+ ksp->msg_enable = value;
+}
+
+/**
+ * ks8695_get_settings - Get device-specific settings.
+ * @ndev: The network device to read settings from
+ * @cmd: The ethtool structure to read into
+ */
+static int
+ks8695_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ u32 ctrl;
+
+ /* All ports on the KS8695 support these... */
+ cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+ SUPPORTED_TP | SUPPORTED_MII);
+ cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
+ cmd->port = PORT_MII;
+ cmd->transceiver = XCVR_INTERNAL;
+
+ /* Port specific extras */
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ cmd->phy_address = 0;
+ /* not supported for HPNA */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ /* BUG: Erm, dtype hpna implies no phy regs */
+ /*
+ ctrl = readl(KS8695_MISC_VA + KS8695_HMC);
+ cmd->speed = (ctrl & HMC_HSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & HMC_HDS) ? DUPLEX_FULL : DUPLEX_HALF;
+ */
+ break;
+ case KS8695_DTYPE_WAN:
+ cmd->supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause);
+ cmd->phy_address = 0;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if ((ctrl & WMC_WAND) == 0) {
+ /* auto-negotiation is enabled */
+ cmd->advertising |= ADVERTISED_Autoneg;
+ if (ctrl & WMC_WANA100F)
+ cmd->advertising |= ADVERTISED_100baseT_Full;
+ if (ctrl & WMC_WANA100H)
+ cmd->advertising |= ADVERTISED_100baseT_Half;
+ if (ctrl & WMC_WANA10F)
+ cmd->advertising |= ADVERTISED_10baseT_Full;
+ if (ctrl & WMC_WANA10H)
+ cmd->advertising |= ADVERTISED_10baseT_Half;
+ if (ctrl & WMC_WANAP)
+ cmd->advertising |= ADVERTISED_Pause;
+ cmd->autoneg = AUTONEG_ENABLE;
+
+ cmd->speed = (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WDS) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ } else {
+ /* auto-negotiation is disabled */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ cmd->speed = (ctrl & WMC_WANF100) ?
+ SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WANFF) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ }
+ break;
+ case KS8695_DTYPE_LAN:
+ /* TODO: Implement this for the switch ports */
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_set_settings - Set device-specific settings.
+ * @ndev: The network device to configure
+ * @cmd: The settings to configure
+ */
+static int
+ks8695_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ u32 ctrl;
+
+ if ((cmd->speed != SPEED_10) && (cmd->speed != SPEED_100))
+ return -EINVAL;
+ if ((cmd->duplex != DUPLEX_HALF) && (cmd->duplex != DUPLEX_FULL))
+ return -EINVAL;
+ if (cmd->port != PORT_MII)
+ return -EINVAL;
+ if (cmd->transceiver != XCVR_INTERNAL)
+ return -EINVAL;
+ if ((cmd->autoneg != AUTONEG_DISABLE) &&
+ (cmd->autoneg != AUTONEG_ENABLE))
+ return -EINVAL;
+
+ if (cmd->autoneg == AUTONEG_ENABLE) {
+ if ((cmd->advertising & (ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full)) == 0)
+ return -EINVAL;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA does not support auto-negotiation. */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+ if (cmd->advertising & ADVERTISED_100baseT_Full)
+ ctrl |= WMC_WANA100F;
+ if (cmd->advertising & ADVERTISED_100baseT_Half)
+ ctrl |= WMC_WANA100H;
+ if (cmd->advertising & ADVERTISED_10baseT_Full)
+ ctrl |= WMC_WANA10F;
+ if (cmd->advertising & ADVERTISED_10baseT_Half)
+ ctrl |= WMC_WANA10H;
+
+ /* force a re-negotiation */
+ ctrl |= WMC_WANR;
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ /* TODO: Implement this for the switch ports */
+ break;
+ }
+
+ } else {
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* BUG: dtype_hpna implies no phy registers */
+ /*
+ ctrl = __raw_readl(KS8695_MISC_VA + KS8695_HMC);
+
+ ctrl &= ~(HMC_HSS | HMC_HDS);
+ if (cmd->speed == SPEED_100)
+ ctrl |= HMC_HSS;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= HMC_HDS;
+
+ __raw_writel(ctrl, KS8695_MISC_VA + KS8695_HMC);
+ */
+ break;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* disable auto-negotiation */
+ ctrl |= WMC_WAND;
+ ctrl &= ~(WMC_WANF100 | WMC_WANFF);
+
+ if (cmd->speed == SPEED_100)
+ ctrl |= WMC_WANF100;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= WMC_WANFF;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ /* TODO: Implement this for the switch ports */
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_nwayreset - Restart the autonegotiation on the port.
+ * @ndev: The network device to restart autoneotiation on
+ */
+static int
+ks8695_nwayreset(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy means no autonegotiation on hpna */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ if ((ctrl & WMC_WAND) == 0)
+ writel(ctrl | WMC_WANR,
+ ksp->phyiface_regs + KS8695_WMC);
+ else
+ /* auto-negotiation not enabled */
+ return -EINVAL;
+ break;
+ case KS8695_DTYPE_LAN:
+ /* TODO: Implement this for the switch ports */
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_get_link - Retrieve link status of network interface
+ * @ndev: The network interface to retrive the link status of.
+ */
+static u32
+ks8695_get_link(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA always has link */
+ return 1;
+ case KS8695_DTYPE_WAN:
+ /* WAN we can read the PHY for */
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ return ctrl & WMC_WLS;
+ case KS8695_DTYPE_LAN:
+ /* TODO: implement switch port link detection */
+ break;
+ }
+ return 0;
+}
+
+/**
+ * ks8695_get_pause - Retrieve network pause/flow-control advertising
+ * @ndev: The device to retrieve settings from
+ * @param: The structure to fill out with the information
+ */
+static void
+ks8695_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy link on hpna to configure */
+ break;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* advertise Pause */
+ param->autoneg = (ctrl & WMC_WANAP);
+
+ /* current Rx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ param->rx_pause = (ctrl & DRXC_RFCE);
+
+ /* current Tx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ param->tx_pause = (ctrl & DTXC_TFCE);
+ break;
+ case KS8695_DTYPE_LAN:
+ /* TODO: Implement for switch ports */
+ break;
+ }
+}
+
+/**
+ * ks8695_set_pause - Configure pause/flow-control
+ * @ndev: The device to configure
+ * @param: The pause parameters to set
+ *
+ * TODO: Implement this
+ */
+static int
+ks8695_set_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ return 0;
+}
+
+/**
+ * ks8695_get_drvinfo - Retrieve driver information
+ * @ndev: The network device to retrieve info about
+ * @info: The info structure to fill out.
+ */
+static void
+ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, MODULENAME, sizeof(info->driver));
+ strlcpy(info->version, MODULEVERSION, sizeof(info->version));
+ strlcpy(info->bus_info, ndev->dev.parent->bus_id,
+ sizeof(info->bus_info));
+}
+
+static struct ethtool_ops ks8695_ethtool_ops = {
+ .get_msglevel = ks8695_get_msglevel,
+ .set_msglevel = ks8695_set_msglevel,
+ .get_settings = ks8695_get_settings,
+ .set_settings = ks8695_set_settings,
+ .nway_reset = ks8695_nwayreset,
+ .get_link = ks8695_get_link,
+ .get_pauseparam = ks8695_get_pause,
+ .set_pauseparam = ks8695_set_pause,
+ .get_drvinfo = ks8695_get_drvinfo,
+};
+
+/* Network device interface functions */
+
+/**
+ * ks8695_set_mac - Update MAC in net dev and HW
+ * @ndev: The network device to update
+ * @addr: The new MAC address to set
+ */
+static int
+ks8695_set_mac(struct net_device *ndev, void *addr)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ struct sockaddr *address = addr;
+ DECLARE_MAC_BUF(mac);
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+
+ ks8695_update_mac(ksp);
+
+ dev_dbg(ksp->dev, "%s: Updated MAC address to %s\n",
+ ndev->name, print_mac(mac, ndev->dev_addr));
+
+ return 0;
+}
+
+/**
+ * ks8695_set_multicast - Set up the multicast behaviour of the interface
+ * @ndev: The net_device to configure
+ *
+ * This routine, called by the net layer, configures promiscuity
+ * and multicast reception behaviour for the interface.
+ */
+static void
+ks8695_set_multicast(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ u32 ctrl;
+
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+
+ if (ndev->flags & IFF_PROMISC) {
+ /* enable promiscuous mode */
+ ctrl |= DRXC_RA;
+ } else if (ndev->flags & ~IFF_PROMISC) {
+ /* disable promiscuous mode */
+ ctrl &= ~DRXC_RA;
+ }
+
+ if (ndev->flags & IFF_ALLMULTI) {
+ /* enable all multicast mode */
+ ctrl |= DRXC_RM;
+ } else if (ndev->mc_count > KS8695_NR_ADDRESSES) {
+ /* more specific multicast addresses than can be
+ * handled in hardware
+ */
+ ctrl |= DRXC_RM;
+ } else if (ndev->mc_count > 0) {
+ /* enable specific multicasts */
+ ctrl &= ~DRXC_RM;
+ ks8695_init_partial_multicast(ksp, ndev->mc_list,
+ ndev->mc_count);
+ } else if (ndev->flags & ~IFF_ALLMULTI) {
+ /* disable multicast mode */
+ ctrl &= ~DRXC_RM;
+ ks8695_init_partial_multicast(ksp, NULL, 0);
+ }
+
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl);
+}
+
+/**
+ * ks8695_timeout - Handle a network tx/rx timeout.
+ * @ndev: The net_device which timed out.
+ *
+ * A network transaction timed out, reset the device.
+ */
+static void
+ks8695_timeout(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+
+ netif_stop_queue(ndev);
+ ks8695_shutdown(ksp);
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ /* We ignore the return from this since it managed to init
+ * before it probably will be okay to init again.
+ */
+ ks8695_init_net(ksp);
+
+ netif_start_queue(ndev);
+}
+
+/**
+ * ks8695_start_xmit - Start a packet transmission
+ * @skb: The packet to transmit
+ * @ndev: The network device to send the packet on
+ *
+ * This routine, called by the net layer, takes ownership of the
+ * sk_buff and adds it to the TX ring. It then kicks the TX DMA
+ * engine to ensure transmission begins.
+ */
+static int
+ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ int buff_n;
+
+ spin_lock_irq(&ksp->txq_lock);
+
+ if (ksp->tx_ring_used == MAX_TX_DESC) {
+ /* Somehow we got entered when we have no room */
+ spin_unlock_irq(&ksp->txq_lock);
+ return -ENOSPC;
+ }
+
+ buff_n = ksp->tx_ring_next_slot;
+ ksp->tx_ring_next_slot = (buff_n + 1) % MAX_TX_DESC;
+
+ if (ksp->tx_buffers[buff_n].skb) {
+ /* This slot is used, try later */
+ spin_unlock_irq(&ksp->txq_lock);
+ return -EAGAIN;
+ }
+
+ ksp->tx_buffers[buff_n].skb = skb;
+ ksp->tx_buffers[buff_n].length = skb->len;
+ ksp->tx_buffers[buff_n].dma_ptr = dma_map_single(
+ ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
+
+ /* Fill out the TX descriptor */
+ ksp->tx_ring[buff_n].data_ptr =
+ cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
+ ksp->tx_ring[buff_n].status =
+ cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
+ (skb->len & TDES_TBS));
+
+ /* Hand it over to the hardware */
+ ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);
+
+ if (++ksp->tx_ring_used == MAX_TX_DESC)
+ netif_stop_queue(ndev);
+
+ ndev->trans_start = jiffies;
+
+ /* Kick the TX DMA in case it decided to go IDLE */
+ ks8695_writereg(ksp, KS8695_DTSC, 0);
+
+ spin_unlock_irq(&ksp->txq_lock);
+ return 0;
+}
+
+/**
+ * ks8695_stop - Stop (shutdown) a KS8695 ethernet interface
+ * @ndev: The net_device to stop
+ *
+ * This disables the TX queue and cleans up a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_stop(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+
+ ks8695_shutdown(ksp);
+
+ return 0;
+}
+
+/**
+ * ks8695_open - Open (bring up) a KS8695 ethernet interface
+ * @ndev: The net_device to open
+ *
+ * This resets, configures the MAC, initialises the RX ring and
+ * DMA engines and starts the TX queue for a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_open(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+ int ret;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ ret = ks8695_init_net(ksp);
+ if (ret) {
+ ks8695_shutdown(ksp);
+ return ret;
+ }
+
+ netif_start_queue(ndev);
+
+ return 0;
+}
+
+/* Platform device driver */
+
+/**
+ * ks8695_init_switch - Init LAN switch to known good defaults.
+ * @ksp: The device to initialise
+ *
+ * This initialises the LAN switch in the KS8695 to a known-good
+ * set of defaults.
+ */
+static void __devinit
+ks8695_init_switch(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Default value for SEC0 according to datasheet */
+ ctrl = 0x40819e00;
+
+ /* LED0 = Speed LED1 = Link/Activity */
+ ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S);
+ ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY);
+
+ /* Enable Switch */
+ ctrl |= SEC0_ENABLE;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_SEC0);
+
+ /* Defaults for SEC1 */
+ writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
+}
+
+/**
+ * ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults
+ * @ksp: The device to initialise
+ *
+ * This initialises a KS8695's WAN phy to sensible values for
+ * autonegotiation etc.
+ */
+static void __devinit
+ks8695_init_wan_phy(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Support auto-negotiation */
+ ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+
+ /* LED0 = Activity , LED1 = Link */
+ ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK);
+
+ /* Restart Auto-negotiation */
+ ctrl |= WMC_WANR;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+
+ writel(0, ksp->phyiface_regs + KS8695_WPPM);
+ writel(0, ksp->phyiface_regs + KS8695_PPS);
+}
+
+/**
+ * ks8695_probe - Probe and initialise a KS8695 ethernet interface
+ * @pdev: The platform device to probe
+ *
+ * Initialise a KS8695 ethernet device from platform data.
+ *
+ * This driver requires at least one IORESOURCE_MEM for the
+ * registers and two IORESOURCE_IRQ for the RX and TX IRQs
+ * respectively. It can optionally take an additional
+ * IORESOURCE_MEM for the switch or phy in the case of the lan or
+ * wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan
+ * port.
+ */
+static int __devinit
+ks8695_probe(struct platform_device *pdev)
+{
+ struct ks8695_priv *ksp;
+ struct net_device *ndev;
+ struct resource *regs_res, *phyiface_res;
+ struct resource *rxirq_res, *txirq_res, *linkirq_res;
+ int ret = 0;
+ int buff_n;
+ u32 machigh, maclow;
+
+ /* Initialise a net_device */
+ ndev = alloc_etherdev(sizeof(struct ks8695_priv));
+ if (!ndev) {
+ dev_err(&pdev->dev, "could not allocate device.\n");
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ dev_dbg(&pdev->dev, "ks8695_probe() called\n");
+
+ /* Configure our private structure a little */
+ ksp = ks8695_get_priv(ndev);
+ memset(ksp, 0, sizeof(struct ks8695_priv));
+
+ ksp->dev = &pdev->dev;
+ ksp->ndev = ndev;
+ ksp->msg_enable = NETIF_MSG_LINK;
+
+ /* Retrieve resources */
+ regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+
+ if (!(regs_res && rxirq_res && txirq_res)) {
+ dev_err(ksp->dev, "insufficient resources\n");
+ ret = -ENOENT;
+ goto failure;
+ }
+
+ ksp->regs_req = request_mem_region(regs_res->start,
+ resource_size(regs_res),
+ pdev->name);
+
+ if (!ksp->regs_req) {
+ dev_err(ksp->dev, "cannot claim register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res));
+
+ if (!ksp->io_regs) {
+ dev_err(ksp->dev, "failed to ioremap registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ if (phyiface_res) {
+ ksp->phyiface_req =
+ request_mem_region(phyiface_res->start,
+ resource_size(phyiface_res),
+ phyiface_res->name);
+
+ if (!ksp->phyiface_req) {
+ dev_err(ksp->dev,
+ "cannot claim switch register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->phyiface_regs = ioremap(phyiface_res->start,
+ resource_size(phyiface_res));
+
+ if (!ksp->phyiface_regs) {
+ dev_err(ksp->dev,
+ "failed to ioremap switch registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+ }
+
+ ksp->rx_irq = rxirq_res->start;
+ ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX";
+ ksp->tx_irq = txirq_res->start;
+ ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX";
+ ksp->link_irq = (linkirq_res ? linkirq_res->start : -1);
+ ksp->link_irq_name = (linkirq_res && linkirq_res->name) ?
+ linkirq_res->name : "Ethernet Link";
+
+ /* Tell the net_device all about us */
+ ndev->base_addr = (unsigned long)ksp->io_regs;
+ ndev->irq = ksp->rx_irq;
+
+ /* driver system setup */
+ ether_setup(ndev);
+
+ ndev->open = &ks8695_open;
+ ndev->stop = &ks8695_stop;
+ ndev->hard_start_xmit = &ks8695_start_xmit;
+ ndev->tx_timeout = &ks8695_timeout;
+ ndev->set_multicast_list = &ks8695_set_multicast;
+ ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+ ndev->ethtool_ops = &ks8695_ethtool_ops;
+ ndev->set_mac_address = &ks8695_set_mac;
+
+ /* Retrieve the default MAC addr from the chip. */
+ /* The bootloader should have left it in there for us. */
+
+ machigh = ks8695_readreg(ksp, KS8695_MAH);
+ maclow = ks8695_readreg(ksp, KS8695_MAL);
+
+ ndev->dev_addr[0] = (machigh >> 8) & 0xFF;
+ ndev->dev_addr[1] = machigh & 0xFF;
+ ndev->dev_addr[2] = (maclow >> 24) & 0xFF;
+ ndev->dev_addr[3] = (maclow >> 16) & 0xFF;
+ ndev->dev_addr[4] = (maclow >> 8) & 0xFF;
+ ndev->dev_addr[5] = maclow & 0xFF;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please "
+ "set using ifconfig\n", ndev->name);
+
+ /* In order to be efficient memory-wise, we allocate both
+ * rings in one go.
+ */
+ ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE,
+ &ksp->ring_base_dma, GFP_KERNEL);
+ if (!ksp->ring_base) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ /* Specify the TX DMA ring buffer */
+ ksp->tx_ring = ksp->ring_base;
+ ksp->tx_ring_dma = ksp->ring_base_dma;
+
+ /* And initialise the queue's lock */
+ spin_lock_init(&ksp->txq_lock);
+
+ /* Specify the RX DMA ring buffer */
+ ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
+ ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE;
+
+ /* Zero the descriptor rings */
+ memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE);
+ memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE);
+
+ /* Build the rings */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ ksp->tx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->tx_ring_dma +
+ (sizeof(struct tx_ring_desc) *
+ ((buff_n + 1) % MAX_TX_DESC)));
+ }
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ ksp->rx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->rx_ring_dma +
+ (sizeof(struct rx_ring_desc) *
+ ((buff_n + 1) % MAX_RX_DESC)));
+ }
+
+ /* Initialise the port (physically) */
+ if (ksp->phyiface_regs && ksp->link_irq == -1) {
+ ks8695_init_switch(ksp);
+ ksp->dtype = KS8695_DTYPE_LAN;
+ } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
+ ks8695_init_wan_phy(ksp);
+ ksp->dtype = KS8695_DTYPE_WAN;
+ } else {
+ /* No initialisation since HPNA does not have a PHY */
+ ksp->dtype = KS8695_DTYPE_HPNA;
+ }
+
+ /* And bring up the net_device with the net core */
+ platform_set_drvdata(pdev, ndev);
+ ret = register_netdev(ndev);
+
+ if (ret == 0) {
+ DECLARE_MAC_BUF(mac);
+ dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %s\n",
+ ks8695_port_type(ksp), print_mac(mac, ndev->dev_addr));
+ } else {
+ /* Report the failure to register the net_device */
+ goto failure;
+ }
+
+ /* All is well */
+ return 0;
+
+ /* Error exit path */
+failure:
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ return ret;
+}
+
+/**
+ * ks8695_drv_suspend - Suspend a KS8695 ethernet platform device.
+ * @pdev: The device to suspend
+ * @state: The suspend state
+ *
+ * This routine detaches and shuts down a KS8695 ethernet device.
+ */
+static int
+ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+
+ ksp->in_suspend = 1;
+
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ ks8695_shutdown(ksp);
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_resume - Resume a KS8695 ethernet platform device.
+ * @pdev: The device to resume
+ *
+ * This routine re-initialises and re-attaches a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_drv_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+
+ if (netif_running(ndev)) {
+ ks8695_reset(ksp);
+ ks8695_init_net(ksp);
+ netif_device_attach(ndev);
+ }
+
+ ksp->in_suspend = 0;
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_remove - Remove a KS8695 net device on driver unload.
+ * @pdev: The platform device to remove
+ *
+ * This unregisters and releases a KS8695 ethernet device.
+ */
+static int __devexit
+ks8695_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = ks8695_get_priv(ndev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(ndev);
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ dev_dbg(&pdev->dev, "released and freed device\n");
+ return 0;
+}
+
+static struct platform_driver ks8695_driver = {
+ .driver = {
+ .name = MODULENAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ks8695_probe,
+ .remove = __devexit_p(ks8695_drv_remove),
+ .suspend = ks8695_drv_suspend,
+ .resume = ks8695_drv_resume,
+};
+
+/* Module interface */
+
+static int __init
+ks8695_init(void)
+{
+ printk(KERN_INFO "%s Ethernet driver, V%s\n",
+ MODULENAME, MODULEVERSION);
+
+ return platform_driver_register(&ks8695_driver);
+}
+
+static void __exit
+ks8695_cleanup(void)
+{
+ platform_driver_unregister(&ks8695_driver);
+}
+
+module_init(ks8695_init);
+module_exit(ks8695_cleanup);
+
+MODULE_AUTHOR("Simtec Electronics")
+MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MODULENAME);
+
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
Index: linux-2.6.27/drivers/net/arm/ks8695net.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/drivers/net/arm/ks8695net.h 2008-12-05 11:30:15.401074725 +0000
@@ -0,0 +1,107 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#ifndef KS8695NET_H
+#define KS8695NET_H
+
+/* Receive descriptor flags */
+#define RDES_OWN (1 << 31) /* Ownership */
+#define RDES_FS (1 << 30) /* First Descriptor */
+#define RDES_LS (1 << 29) /* Last Descriptor */
+#define RDES_IPE (1 << 28) /* IP Checksum error */
+#define RDES_TCPE (1 << 27) /* TCP Checksum error */
+#define RDES_UDPE (1 << 26) /* UDP Checksum error */
+#define RDES_ES (1 << 25) /* Error summary */
+#define RDES_MF (1 << 24) /* Multicast Frame */
+#define RDES_RE (1 << 19) /* MII Error reported */
+#define RDES_TL (1 << 18) /* Frame too Long */
+#define RDES_RF (1 << 17) /* Runt Frame */
+#define RDES_CE (1 << 16) /* CRC error */
+#define RDES_FT (1 << 15) /* Frame Type */
+#define RDES_FLEN (0x7ff) /* Frame Length */
+
+#define RDES_RER (1 << 25) /* Receive End of Ring */
+#define RDES_RBS (0x7ff) /* Receive Buffer Size */
+
+/* Transmit descriptor flags */
+
+#define TDES_OWN (1 << 31) /* Ownership */
+
+#define TDES_IC (1 << 31) /* Interrupt on Completion */
+#define TDES_FS (1 << 30) /* First Segment */
+#define TDES_LS (1 << 29) /* Last Segment */
+#define TDES_IPCKG (1 << 28) /* IP Checksum generate */
+#define TDES_TCPCKG (1 << 27) /* TCP Checksum generate */
+#define TDES_UDPCKG (1 << 26) /* UDP Checksum generate */
+#define TDES_TER (1 << 25) /* Transmit End of Ring */
+#define TDES_TBS (0x7ff) /* Transmit Buffer Size */
+
+/*
+ * Network controller register offsets
+ */
+#define KS8695_DTXC (0x00) /* DMA Transmit Control */
+#define KS8695_DRXC (0x04) /* DMA Receive Control */
+#define KS8695_DTSC (0x08) /* DMA Transmit Start Command */
+#define KS8695_DRSC (0x0c) /* DMA Receive Start Command */
+#define KS8695_TDLB (0x10) /* Transmit Descriptor List
+ * Base Address
+ */
+#define KS8695_RDLB (0x14) /* Receive Descriptor List
+ * Base Address
+ */
+#define KS8695_MAL (0x18) /* MAC Station Address Low */
+#define KS8695_MAH (0x1c) /* MAC Station Address High */
+#define KS8695_AAL_(n) (0x80 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) Low
+ */
+#define KS8695_AAH_(n) (0x84 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) High
+ */
+
+
+/* DMA Transmit Control Register */
+#define DTXC_TRST (1 << 31) /* Soft Reset */
+#define DTXC_TBS (0x3f << 24) /* Transmit Burst Size */
+#define DTXC_TUCG (1 << 18) /* Transmit UDP
+ * Checksum Generate
+ */
+#define DTXC_TTCG (1 << 17) /* Transmit TCP
+ * Checksum Generate
+ */
+#define DTXC_TICG (1 << 16) /* Transmit IP
+ * Checksum Generate
+ */
+#define DTXC_TFCE (1 << 9) /* Transmit Flow
+ * Control Enable
+ */
+#define DTXC_TLB (1 << 8) /* Loopback mode */
+#define DTXC_TEP (1 << 2) /* Transmit Enable Padding */
+#define DTXC_TAC (1 << 1) /* Transmit Add CRC */
+#define DTXC_TE (1 << 0) /* TX Enable */
+
+/* DMA Receive Control Register */
+#define DRXC_RBS (0x3f << 24) /* Receive Burst Size */
+#define DRXC_RUCC (1 << 18) /* Receive UDP Checksum check */
+#define DRXC_RTCG (1 << 17) /* Receive TCP Checksum check */
+#define DRXC_RICG (1 << 16) /* Receive IP Checksum check */
+#define DRXC_RFCE (1 << 9) /* Receive Flow Control
+ * Enable
+ */
+#define DRXC_RB (1 << 6) /* Receive Broadcast */
+#define DRXC_RM (1 << 5) /* Receive Multicast */
+#define DRXC_RU (1 << 4) /* Receive Unicast */
+#define DRXC_RERR (1 << 3) /* Receive Error Frame */
+#define DRXC_RA (1 << 2) /* Receive All */
+#define DRXC_RE (1 << 0) /* RX Enable */
+
+/* Additional Station Address High */
+#define AAH_E (1 << 31) /* Address Enabled */
+
+#endif /* KS8695NET_H */
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 intergrated ethernet driver
2008-12-05 15:06 [Patch] Micrel KS8695 intergrated ethernet driver Daniel Silverstone
@ 2008-12-05 17:57 ` Ben Hutchings
2008-12-08 11:05 ` Daniel Silverstone
2008-12-08 13:08 ` Daniel Silverstone
0 siblings, 2 replies; 16+ messages in thread
From: Ben Hutchings @ 2008-12-05 17:57 UTC (permalink / raw)
To: Daniel Silverstone; +Cc: netdev
On Fri, 2008-12-05 at 15:06 +0000, Daniel Silverstone wrote:
> Hello,
>
> Attached is a patch which implements the Micrel KS8695 SoC's Ethernet
> interfaces.
>
> The driver is only of use on the KS8695.
> KS8695: Add support for the KS8695 ethernet devices.
>
> Implements the KS8695 ethernet device (ks8695net).
>
> Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
> Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
> Acked-by: Ben Dooks <ben@simtec.co.uk>
> Index: linux-2.6.27/drivers/net/arm/Kconfig
> ===================================================================
> --- linux-2.6.27.orig/drivers/net/arm/Kconfig 2008-12-05 11:29:29.629076651 +0000
> +++ linux-2.6.27/drivers/net/arm/Kconfig 2008-12-05 11:30:15.357075120 +0000
netdev patches should really be based on David Miller's net-next-2.6.
> Index: linux-2.6.27/drivers/net/arm/ks8695net.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.27/drivers/net/arm/ks8695net.c 2008-12-05 13:07:56.773076323 +0000
[...]
> +/**
> + * enum ks8695_dtype - Device type
> + * @KS8695_DTYPE_WAN: This device is a WAN interface
> + * @KS8695_DTYPE_LAN: This device is a LAN interface
> + * @KS8695_DTYPE_HPNA: This device is an HPNA interfacew
> + */
> +enum ks8695_dtype {
> + KS8695_DTYPE_WAN,
> + KS8695_DTYPE_LAN,
> + KS8695_DTYPE_HPNA,
> +};
The enumerators should be indented with tabs.
> +/**
> + * struct ks8695_priv - Private data for the KS8695 Ethernet
> + * @in_suspend: Flag to indicate if we're suspending/resuming
> + * @ndev: The net_device for this interface
> + * @dev: The device object for this interface
Which device object - the platform device?
[...]
> +/**
> + * ks8695_get_priv - Retrieve the private data from a net_device.
> + * @ndev: The net_device to read the private data pointer from
> + *
> + * Returns the private structure for the KS8695 Ethernet device.
> + */
> +static inline struct ks8695_priv *
> +ks8695_get_priv(struct net_device *ndev)
> +{
> + return ndev->priv;
> +}
Don't use ndev->priv, use netdev_priv(ndev). Which makes this function
redundant, except for the implicit pointer conversion.
[...]
> +/**
> + * ks8695_refill_rxbuffers - Re-fill the RX buffer ring
> + * @ksp: The device to refill
> + *
> + * Iterates the RX ring of the device looking for empty slots.
> + * For each empty slot, we allocate and map a new SKB and give it
> + * to the hardware.
> + * This can be called from interrupt context safely.
> + */
> +static void
> +ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
> +{
> + /* Run around the RX ring, filling in any missing sk_buff's */
> + int buff_n;
> +
> + for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
> + if (!ksp->rx_buffers[buff_n].skb) {
> + struct sk_buff *skb = dev_alloc_skb(MAX_RXBUF_SIZE);
> + dma_addr_t mapping;
> +
> + ksp->rx_buffers[buff_n].skb = skb;
> + if (skb == NULL) {
> + /* Failed to allocate one, perhaps
> + * we'll try again later.
> + */
> + break;
> + }
> +
> + mapping = dma_map_single(ksp->dev, skb->data,
> + MAX_RXBUF_SIZE,
> + DMA_FROM_DEVICE);
I take it that dma_map_single() can never fail on ARM?
> + ksp->rx_buffers[buff_n].dma_ptr = mapping;
> + skb->dev = ksp->ndev;
> + ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
> +
> + /* Record this into the DMA ring */
> + ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
> + ksp->rx_ring[buff_n].length =
> + cpu_to_le32(MAX_RXBUF_SIZE);
You need a wmb() in here, otherwise the descriptor might not be complete
when the hardware sees RDES_OWN set.
> + /* And give ownership over to the hardware */
> + ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
> + }
> + }
> +}
[...]
> +
> +/* Interrupt handling */
> +
> +/**
> + * ks8695_tx_irq - Transmit IRQ handler
> + * @irq: The IRQ which went off (ignored)
> + * @dev_id: The net_device for the interrupt
> + *
> + * Process the TX ring, clearing out any transmitted slots.
> + * Allows the net_device to pass us new packets once slots are
> + * freed.
> + */
> +static irqreturn_t
> +ks8695_tx_irq(int irq, void *dev_id)
> +{
> + struct net_device *ndev = (struct net_device *)dev_id;
> + struct ks8695_priv *ksp = ks8695_get_priv(ndev);
> + int buff_n;
> +
> + for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
> + if (ksp->tx_buffers[buff_n].skb &&
> + !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
rmb()
> + /* An SKB which is not owned by HW is present */
> + /* Update the stats for the net_device */
> + ndev->stats.tx_packets++;
> + ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;
> +
> + /* Free the packet from the ring */
> + ksp->tx_ring[buff_n].data_ptr = 0;
Clearing data_ptr seems redundant. The TDES_OWN flag indicates whether
the descriptor is pending and the skb pointer indicates whether a buffer
needs to be freed.
> + /* Free the sk_buff */
> + dma_unmap_single(ksp->dev,
> + ksp->tx_buffers[buff_n].dma_ptr,
> + ksp->tx_buffers[buff_n].length,
> + DMA_TO_DEVICE);
> + dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
> + ksp->tx_buffers[buff_n].skb = NULL;
> + ksp->tx_ring_used--;
> + }
> + }
Do you need to test all the descriptors or can you keep track of which
to check first and then stop when you find one still pending? Maybe it
doesn't matter when you only have 8 of them...
> + netif_wake_queue(ndev);
> +
> + return IRQ_HANDLED;
> +}
> +
> +/**
> + * ks8695_rx_irq - Receive IRQ handler
> + * @irq: The IRQ which went off (ignored)
> + * @dev_id: The net_device for the interrupt
> + *
> + * Process the RX ring, passing any received packets up to the
> + * host. If we received anything other than errors, we then
> + * refill the ring.
> + */
> +static irqreturn_t
> +ks8695_rx_irq(int irq, void *dev_id)
> +{
> + struct net_device *ndev = (struct net_device *)dev_id;
> + struct ks8695_priv *ksp = ks8695_get_priv(ndev);
> + struct sk_buff *skb;
> + int buff_n;
> + u32 flags;
> + int pktlen;
> + int last_rx_processed = -1;
> +
> + buff_n = ksp->next_rx_desc_read;
> + do {
> + if (ksp->rx_buffers[buff_n].skb &&
> + !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) {
rmb()
> + flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
> + /* Found an SKB which we own, this means we
> + * received a packet
> + */
> + if ((flags & (RDES_FS | RDES_LS)) !=
> + (RDES_FS | RDES_LS)) {
> + /* This packet is not the first and
> + * the last segment. Therefore it is
> + * a "spanning" packet and we can't
> + * handle it
> + */
> + goto rx_failure;
> + }
> +
> + if (flags & (RDES_ES | RDES_RE)) {
> + /* It's an error packet */
> + ndev->stats.rx_errors++;
> + if (flags & RDES_TL)
> + ndev->stats.rx_length_errors++;
> + if (flags & RDES_RF)
> + ndev->stats.rx_length_errors++;
> + if (flags & RDES_CE)
> + ndev->stats.rx_crc_errors++;
> + if (flags & RDES_RE)
> + ndev->stats.rx_missed_errors++;
> +
> + goto rx_failure;
> + }
> +
> + pktlen = flags & RDES_FLEN;
> + pktlen -= 4; /* Drop the CRC */
> +
> + skb = ksp->rx_buffers[buff_n].skb;
I think this line belongs after the following comment.
> + /* Extract the sk_buff from the ring */
> + ksp->rx_buffers[buff_n].skb = NULL;
> + ksp->rx_ring[buff_n].data_ptr = 0;
>
> + /* Unmap the SKB */
> + dma_unmap_single(ksp->dev,
> + ksp->rx_buffers[buff_n].dma_ptr,
> + ksp->rx_buffers[buff_n].length,
> + DMA_FROM_DEVICE);
> +
> + /* Relinquish the SKB to the network layer */
> + skb_put(skb, pktlen);
> + skb->protocol = eth_type_trans(skb, ndev);
> + netif_rx(skb);
> +
> + /* Record stats */
> + ndev->last_rx = jiffies;
> + ndev->stats.rx_packets++;
> + ndev->stats.rx_bytes += pktlen;
> + goto rx_finished;
> +
> +rx_failure:
> + /* This ring entry is an error, but we can
> + * re-use the skb
> + */
> + /* Give the ring entry back to the hardware */
> + ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
> +rx_finished:
> + /* And note this as processed so we can start
> + * from here next time
> + */
> + last_rx_processed = buff_n;
> + } else {
> + /* Ran out of things to process, stop now */
> + break;
> + }
> + buff_n = (buff_n + 1) % MAX_RX_DESC;
That's another expensive division.
> + } while (buff_n != ksp->next_rx_desc_read);
> +
> + /* And note which RX descriptor we last did anything with */
> + if (likely(last_rx_processed != -1))
> + ksp->next_rx_desc_read = (last_rx_processed + 1) % MAX_RX_DESC;
> +
> + /* And refill the buffers */
> + ks8695_refill_rxbuffers(ksp);
> +
> + /* Kick the RX DMA engine, in case it became suspended */
> + ks8695_writereg(ksp, KS8695_DRSC, 0);
> +
> + return IRQ_HANDLED;
> +}
[...]
> +
> +/* KS8695 Device functions */
> +
> +/**
> + * ks8695_reset - Reset a KS8695 ethernet interface
> + * @ksp: The interface to reset
> + *
> + * Perform an engine reset of the interface and re-program it
> + * with sensible defaults.
> + */
> +static void
> +ks8695_reset(struct ks8695_priv *ksp)
> +{
> + int reset_timeout = watchdog;
> + /* Issue the reset via the TX DMA control register */
> + ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
> + while (reset_timeout--) {
> + if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
> + break;
> + msleep(1);
> + }
Is there any reason this timeout should be tied to the TX watchdog
timeout?
[...]
> +/**
> + * ks8695_init_net - Initialise a KS8695 ethernet interface
> + * @ksp: The interface to initialise
> + *
> + * This routine fills the RX ring, initialises the DMA engines,
> + * allocates the IRQs and then starts the packet TX and RX
> + * engines.
> + */
> +static int
> +ks8695_init_net(struct ks8695_priv *ksp)
> +{
> + int ret;
> + u32 ctrl;
> +
> + ks8695_refill_rxbuffers(ksp);
> +
> + /* Initialise the DMA engines */
> + ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
> + ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
> +
> + /* Request the IRQs */
> + ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
> + ks8695_rx_irq, ksp->ndev);
> + if (ret)
> + return ret;
> + ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
> + ks8695_tx_irq, ksp->ndev);
> + if (ret)
> + return ret;
> + if (ksp->link_irq != -1) {
> + ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
> + ks8695_link_irq, ksp->ndev);
> + if (ret)
> + return ret;
> + }
The RX and TX IRQ handlers need to be released in case the TX or link
IRQ setup fails.
> + /* Set up the ring indices */
> + ksp->next_rx_desc_read = 0;
> + ksp->tx_ring_next_slot = 0;
> + ksp->tx_ring_used = 0;
> +
> + /* Bring up transmission */
> + ctrl = ks8695_readreg(ksp, KS8695_DTXC);
> + /* Enable packet transmission */
> + ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
> +
> + /* Bring up the reception */
> + ctrl = ks8695_readreg(ksp, KS8695_DRXC);
> + /* Enable packet reception */
> + ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
> + /* And start the DMA engine */
> + ks8695_writereg(ksp, KS8695_DRSC, 0);
> +
> + /* All done */
> + return 0;
> +}
> +
> +/**
> + * ks8695_release_device - HW resource release for KS8695 e-net
> + * @ksp: The device to be freed
> + *
> + * This unallocates io memory regions, dma-coherent regions etc
> + * which were allocated in ks8695_probe.
> + */
> +static void
> +ks8695_release_device(struct ks8695_priv *ksp)
> +{
> + /* Unmap the registers */
> + iounmap(ksp->io_regs);
> + if (ksp->phyiface_regs)
> + iounmap(ksp->phyiface_regs);
> +
> + /* And release the request */
> + release_resource(ksp->regs_req);
> + kfree(ksp->regs_req);
> + if (ksp->phyiface_req) {
> + release_resource(ksp->phyiface_req);
> + kfree(ksp->phyiface_req);
> + }
> +
> + /* Free the ring buffers */
> + dma_free_coherent(ksp->dev, RING_DMA_SIZE,
> + ksp->ring_base, ksp->ring_base_dma);
> +}
> +/**
> + * ks8695_get_settings - Get device-specific settings.
> + * @ndev: The network device to read settings from
> + * @cmd: The ethtool structure to read into
> + */
> +static int
> +ks8695_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
> +{
> + struct ks8695_priv *ksp = ks8695_get_priv(ndev);
> + u32 ctrl;
> +
> + /* All ports on the KS8695 support these... */
> + cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
> + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
> + SUPPORTED_TP | SUPPORTED_MII);
> + cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
> + cmd->port = PORT_MII;
> + cmd->transceiver = XCVR_INTERNAL;
I think the TP and MII flags should be dependent on ksp->dtype.
> + /* Port specific extras */
> + switch (ksp->dtype) {
> + case KS8695_DTYPE_HPNA:
> + cmd->phy_address = 0;
> + /* not supported for HPNA */
> + cmd->autoneg = AUTONEG_DISABLE;
> +
> + /* BUG: Erm, dtype hpna implies no phy regs */
> + /*
> + ctrl = readl(KS8695_MISC_VA + KS8695_HMC);
> + cmd->speed = (ctrl & HMC_HSS) ? SPEED_100 : SPEED_10;
> + cmd->duplex = (ctrl & HMC_HDS) ? DUPLEX_FULL : DUPLEX_HALF;
> + */
> + break;
[...]
> + case KS8695_DTYPE_LAN:
> + /* TODO: Implement this for the switch ports */
> + break;
> + }
You'd better implement these or return -EOPNOTSUPP. Similarly in
ks8695_set_settings() and ks8695_nwayreset().
> + return 0;
> +}
[...]
> +/**
> + * ks8695_set_pause - Configure pause/flow-control
> + * @ndev: The device to configure
> + * @param: The pause parameters to set
> + *
> + * TODO: Implement this
Yes, or return -EOPNOTSUPP.
> + */
> +static int
> +ks8695_set_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
> +{
> + return 0;
> +}
[...]
> +/**
> + * ks8695_timeout - Handle a network tx/rx timeout.
> + * @ndev: The net_device which timed out.
> + *
> + * A network transaction timed out, reset the device.
> + */
> +static void
> +ks8695_timeout(struct net_device *ndev)
> +{
> + struct ks8695_priv *ksp = ks8695_get_priv(ndev);
> +
> + netif_stop_queue(ndev);
> + ks8695_shutdown(ksp);
> +
> + ks8695_reset(ksp);
> +
> + ks8695_update_mac(ksp);
> +
> + /* We ignore the return from this since it managed to init
> + * before it probably will be okay to init again.
> + */
> + ks8695_init_net(ksp);
> +
> + netif_start_queue(ndev);
> +}
It looks like this might lose multicast settings.
> +/**
> + * ks8695_start_xmit - Start a packet transmission
> + * @skb: The packet to transmit
> + * @ndev: The network device to send the packet on
> + *
> + * This routine, called by the net layer, takes ownership of the
> + * sk_buff and adds it to the TX ring. It then kicks the TX DMA
> + * engine to ensure transmission begins.
> + */
> +static int
> +ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + struct ks8695_priv *ksp = ks8695_get_priv(ndev);
> + int buff_n;
> +
> + spin_lock_irq(&ksp->txq_lock);
> +
> + if (ksp->tx_ring_used == MAX_TX_DESC) {
> + /* Somehow we got entered when we have no room */
> + spin_unlock_irq(&ksp->txq_lock);
> + return -ENOSPC;
You must return NETDEV_TX_OK or NETDEV_TX_BUSY, not 0 or a negative
error code.
> + }
> +
> + buff_n = ksp->tx_ring_next_slot;
> + ksp->tx_ring_next_slot = (buff_n + 1) % MAX_TX_DESC;
That's an expensive division, which could be avoided by either (1)
declaring buff_n unsigned or (2) explicitly masking.
> + if (ksp->tx_buffers[buff_n].skb) {
> + /* This slot is used, try later */
> + spin_unlock_irq(&ksp->txq_lock);
> + return -EAGAIN;
Since you already tested tx_ring_used, wouldn't this indicate a bug?
> + }
> +
> + ksp->tx_buffers[buff_n].skb = skb;
> + ksp->tx_buffers[buff_n].length = skb->len;
> + ksp->tx_buffers[buff_n].dma_ptr = dma_map_single(
> + ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
> +
> + /* Fill out the TX descriptor */
> + ksp->tx_ring[buff_n].data_ptr =
> + cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
> + ksp->tx_ring[buff_n].status =
> + cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
> + (skb->len & TDES_TBS));
wmb()
> + /* Hand it over to the hardware */
> + ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);
[...]
> +/**
> + * ks8695_probe - Probe and initialise a KS8695 ethernet interface
> + * @pdev: The platform device to probe
> + *
> + * Initialise a KS8695 ethernet device from platform data.
> + *
> + * This driver requires at least one IORESOURCE_MEM for the
> + * registers and two IORESOURCE_IRQ for the RX and TX IRQs
> + * respectively. It can optionally take an additional
> + * IORESOURCE_MEM for the switch or phy in the case of the lan or
> + * wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan
> + * port.
> + */
> +static int __devinit
> +ks8695_probe(struct platform_device *pdev)
> +{
[...]
> + /* Tell the net_device all about us */
> + ndev->base_addr = (unsigned long)ksp->io_regs;
> + ndev->irq = ksp->rx_irq;
Don't bother setting base_addr and irq; they are for ancient ISA crap.
> + /* driver system setup */
> + ether_setup(ndev);
> +
> + ndev->open = &ks8695_open;
> + ndev->stop = &ks8695_stop;
> + ndev->hard_start_xmit = &ks8695_start_xmit;
> + ndev->tx_timeout = &ks8695_timeout;
> + ndev->set_multicast_list = &ks8695_set_multicast;
> + ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
> + ndev->ethtool_ops = &ks8695_ethtool_ops;
> + ndev->set_mac_address = &ks8695_set_mac;
You should use net_device_ops now.
> + /* Initialise the port (physically) */
> + if (ksp->phyiface_regs && ksp->link_irq == -1) {
> + ks8695_init_switch(ksp);
> + ksp->dtype = KS8695_DTYPE_LAN;
> + } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
> + ks8695_init_wan_phy(ksp);
> + ksp->dtype = KS8695_DTYPE_WAN;
> + } else {
> + /* No initialisation since HPNA does not have a PHY */
> + ksp->dtype = KS8695_DTYPE_HPNA;
> + }
> +
> + /* And bring up the net_device with the net core */
> + platform_set_drvdata(pdev, ndev);
> + ret = register_netdev(ndev);
> +
> + if (ret == 0) {
> + DECLARE_MAC_BUF(mac);
> + dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %s\n",
> + ks8695_port_type(ksp), print_mac(mac, ndev->dev_addr));
Use %pM to format a MAC address instead of %s and print_mac().
> + } else {
> + /* Report the failure to register the net_device */
> + goto failure;
I don't think this reports anything!
> + }
> +
> + /* All is well */
> + return 0;
> +
> + /* Error exit path */
> +failure:
> + ks8695_release_device(ksp);
> + free_netdev(ndev);
> +
> + return ret;
> +}
[...]
> +/**
> + * ks8695_drv_resume - Resume a KS8695 ethernet platform device.
> + * @pdev: The device to resume
> + *
> + * This routine re-initialises and re-attaches a KS8695 ethernet
> + * device.
> + */
> +static int
> +ks8695_drv_resume(struct platform_device *pdev)
> +{
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct ks8695_priv *ksp = ks8695_get_priv(ndev);
> +
> + if (netif_running(ndev)) {
> + ks8695_reset(ksp);
> + ks8695_init_net(ksp);
Does this reinit the MAC address or multicast?
> + netif_device_attach(ndev);
> + }
> +
> + ksp->in_suspend = 0;
> +
> + return 0;
> +}
[...]
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 intergrated ethernet driver
2008-12-05 17:57 ` Ben Hutchings
@ 2008-12-08 11:05 ` Daniel Silverstone
2008-12-08 15:17 ` Ben Hutchings
` (2 more replies)
2008-12-08 13:08 ` Daniel Silverstone
1 sibling, 3 replies; 16+ messages in thread
From: Daniel Silverstone @ 2008-12-08 11:05 UTC (permalink / raw)
To: netdev
On Fri, 2008-12-05 at 17:57 +0000, Ben Hutchings wrote:
> > Index: linux-2.6.27/drivers/net/arm/Kconfig
> netdev patches should really be based on David Miller's net-next-2.6.
Noted, I shall forward-port to that tree before issuing a new patch.
> > Index: linux-2.6.27/drivers/net/arm/ks8695net.c
> > +enum ks8695_dtype {
> > + KS8695_DTYPE_WAN,
> > + KS8695_DTYPE_LAN,
> > + KS8695_DTYPE_HPNA,
> > +};
> The enumerators should be indented with tabs.
Gah, I wonder how checkpatch missed that, fixed, thanks.
> > +/**
> > + * struct ks8695_priv - Private data for the KS8695 Ethernet
> > + * @in_suspend: Flag to indicate if we're suspending/resuming
> > + * @ndev: The net_device for this interface
> > + * @dev: The device object for this interface
> Which device object - the platform device?
Assuming the struct device obtained from the struct platform_device is
the "platform device object" then yes. I've updated the comment.
> > +static inline struct ks8695_priv *
> > +ks8695_get_priv(struct net_device *ndev)
> Don't use ndev->priv, use netdev_priv(ndev). Which makes this function
> redundant, except for the implicit pointer conversion.
Aah, I didn't know about that one, updated.
> > +static void
> > +ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
> > + mapping = dma_map_single(ksp->dev, skb->data,
> > + MAX_RXBUF_SIZE,
> > + DMA_FROM_DEVICE);
> I take it that dma_map_single() can never fail on ARM?
No it can't. It essentially boils down to some pointer arithmetic on
this platform.
> > + ksp->rx_ring[buff_n].length =
> > + cpu_to_le32(MAX_RXBUF_SIZE);
>
> You need a wmb() in here, otherwise the descriptor might not be complete
> when the hardware sees RDES_OWN set.
Noted and added.
> > + for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
> > + if (ksp->tx_buffers[buff_n].skb &&
> > + !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
> rmb()
Added.
> > + /* Free the packet from the ring */
> > + ksp->tx_ring[buff_n].data_ptr = 0;
> Clearing data_ptr seems redundant. The TDES_OWN flag indicates whether
> the descriptor is pending and the skb pointer indicates whether a buffer
> needs to be freed.
This was simply me being neat. If you feel that it is an overly
expensive thing to do then I shall elide it.
> Do you need to test all the descriptors or can you keep track of which
> to check first and then stop when you find one still pending? Maybe it
> doesn't matter when you only have 8 of them...
Keeping track of which to check first seemed to make the overall code
larger, and with a small number of TX descriptors, as you say, it really
doesn't matter.
Also, as a point, I have tested shovelling as much data as I can through
this over various protocols including NFS and basic TCP with multiple
simultaneous streams and I have yet to exhaust the TX descriptor ring. I
think the 266MHz ARM processor simply can't stoke it fast enough.
> > +static irqreturn_t
> > +ks8695_rx_irq(int irq, void *dev_id)
> > + if (ksp->rx_buffers[buff_n].skb &&
> > + !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) {
> rmb()
Added.
> > + skb = ksp->rx_buffers[buff_n].skb;
>
> I think this line belongs after the following comment.
>
> > + /* Extract the sk_buff from the ring */
> > + ksp->rx_buffers[buff_n].skb = NULL;
> > + ksp->rx_ring[buff_n].data_ptr = 0;
I've shifted the comment and reworded it slightly so that the first like
is marked 'retrieve the sk_buff' and the latter lines 'clear it from the
ring' which I believe better describes the situation.
> > + buff_n = (buff_n + 1) % MAX_RX_DESC;
> That's another expensive division.
I was expecting the compiler to go "aaha! mod <powerof2> => bitwise
mask".
However, it does indeed seem to not be the case. I have reworked these
into & MAX_RX_DESC_MASK etc.
> > +static void
> > +ks8695_reset(struct ks8695_priv *ksp)
> Is there any reason this timeout should be tied to the TX watchdog
> timeout?
It was a convenient timeout. Basically I wanted something which would
allow me to time-out the reset of the chip, but I didn't feel it
worthwhile to add another module parameter for it, and I didn't want to
pluck a number out of the air per-se. If you feel it deserves its own
separate timeout then I will add one.
> > +static int
> > +ks8695_init_net(struct ks8695_priv *ksp)
> The RX and TX IRQ handlers need to be released in case the TX or link
> IRQ setup fails.
The only time that this might fail, I.E. initial device open, we check
the return value of ks8695_init_net and call ks86985_shutdown if needed
which will release the IRQs.
It seemed daft to replicate the cleanup code in two places. If however
that is the accepted style then I shall add the cleanups to
ks8695_init_net() -- Is my method acceptable or should I change?
> > +static int
> > +ks8695_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
> > +{
> > + cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
> > + cmd->port = PORT_MII;
> I think the TP and MII flags should be dependent on ksp->dtype.
Certainly until I can decode this switch datasheet I agree. Changed.
> > + /* Port specific extras */
> > + switch (ksp->dtype) {
> > + case KS8695_DTYPE_HPNA:
> > + cmd->phy_address = 0;
> > + /* not supported for HPNA */
> > + cmd->autoneg = AUTONEG_DISABLE;
> > +
> > + /* BUG: Erm, dtype hpna implies no phy regs */
> > + /*
> > + ctrl = readl(KS8695_MISC_VA + KS8695_HMC);
> > + cmd->speed = (ctrl & HMC_HSS) ? SPEED_100 : SPEED_10;
> > + cmd->duplex = (ctrl & HMC_HDS) ? DUPLEX_FULL : DUPLEX_HALF;
> > + */
> > + break;
> [...]
> > + case KS8695_DTYPE_LAN:
> > + /* TODO: Implement this for the switch ports */
> > + break;
> > + }
> You'd better implement these or return -EOPNOTSUPP. Similarly in
> ks8695_set_settings() and ks8695_nwayreset().
Unfortunately I currently lack any hardware with HPNA on it, and the
only reference for implementing the LAN switch stuff is an immensely
confusing and likely-to-be-incorrect datasheet from Micrel which I have
yet to successfully decode.
For now, all those functions will return -EOPNOTSUPP, thanks for the
pointer.
> > + * TODO: Implement this
> Yes, or return -EOPNOTSUPP.
Until I can decode this datasheet, it's -EOPNOTSUPP :-(
> > +static void
> > +ks8695_timeout(struct net_device *ndev)
> It looks like this might lose multicast settings.
Yep, when I wrote this routine, I hadn't gotten around to the multicast
stuff, and I forgot to go back and correct it. I'll do so now.
> > +static int
> > +ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> > +{
> > + struct ks8695_priv *ksp = ks8695_get_priv(ndev);
> > + int buff_n;
> > +
> > + spin_lock_irq(&ksp->txq_lock);
> > +
> > + if (ksp->tx_ring_used == MAX_TX_DESC) {
> > + /* Somehow we got entered when we have no room */
> > + spin_unlock_irq(&ksp->txq_lock);
> > + return -ENOSPC;
> You must return NETDEV_TX_OK or NETDEV_TX_BUSY, not 0 or a negative
> error code.
Aha, thanks.
> > + buff_n = ksp->tx_ring_next_slot;
> > + ksp->tx_ring_next_slot = (buff_n + 1) % MAX_TX_DESC;
>
> That's an expensive division, which could be avoided by either (1)
> declaring buff_n unsigned or (2) explicitly masking.
Using masking as elsewhere you noted this.
> > + if (ksp->tx_buffers[buff_n].skb) {
> > + /* This slot is used, try later */
> > + spin_unlock_irq(&ksp->txq_lock);
> > + return -EAGAIN;
> Since you already tested tx_ring_used, wouldn't this indicate a bug?
Yep, this was old code before I kept a track in tx_ring_used. Changed
to:
BUG_ON(ksp->tx_buffers[buff_n].skb);
> > + ksp->tx_ring[buff_n].status =
> > + cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
> > + (skb->len & TDES_TBS));
>
> wmb()
Added
> > +static int __devinit
> > +ks8695_probe(struct platform_device *pdev)
> > + /* Tell the net_device all about us */
> > + ndev->base_addr = (unsigned long)ksp->io_regs;
> > + ndev->irq = ksp->rx_irq;
> Don't bother setting base_addr and irq; they are for ancient ISA crap.
Removed.
> > + ndev->open = &ks8695_open;
> > + ndev->stop = &ks8695_stop;
> > + ndev->hard_start_xmit = &ks8695_start_xmit;
> > + ndev->tx_timeout = &ks8695_timeout;
> > + ndev->set_multicast_list = &ks8695_set_multicast;
> > + ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
> > + ndev->ethtool_ops = &ks8695_ethtool_ops;
> > + ndev->set_mac_address = &ks8695_set_mac;
> You should use net_device_ops now.
I shall do that as I bring it up to net-next-2.6 in a bit.
> > + dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %s\n",
> > + ks8695_port_type(ksp), print_mac(mac, ndev->dev_addr));
> Use %pM to format a MAC address instead of %s and print_mac().
I believe this must be new since 2.6.27 so I'll get that as I move to
net-next-2.6, thanks for the info.
> > + } else {
> > + /* Report the failure to register the net_device */
> > + goto failure;
> I don't think this reports anything!
It reports to the next level up via the return code, which IIRC prints a
message, but I've added a dev_err() anyway before the goto failure.
> > +static int
> > +ks8695_drv_resume(struct platform_device *pdev)
> > + if (netif_running(ndev)) {
> > + ks8695_reset(ksp);
> > + ks8695_init_net(ksp);
> Does this reinit the MAC address or multicast?
Nope, as per the timeout, I'd missed adding it here. That is now
corrected.
Thank you very much for your comments. Once this pull of net-next-2.6 is
complete and I can do my forward-porting, I'll issue a new patch. Can I
ask what the magic runes are which I need in subject / precis to
actually request that my driver be merged rather than just reviewed?
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 intergrated ethernet driver
2008-12-05 17:57 ` Ben Hutchings
2008-12-08 11:05 ` Daniel Silverstone
@ 2008-12-08 13:08 ` Daniel Silverstone
2008-12-09 17:00 ` Daniel Silverstone
[not found] ` <1228764912.3177.39.camel@achroite>
1 sibling, 2 replies; 16+ messages in thread
From: Daniel Silverstone @ 2008-12-08 13:08 UTC (permalink / raw)
To: netdev
[-- Attachment #1: Type: text/plain, Size: 530 bytes --]
On Fri, 2008-12-05 at 17:57 +0000, Ben Hutchings wrote:
> netdev patches should really be based on David Miller's net-next-2.6.
Attached is a patch against net-next-2.6 as cloned this morning, with
the %pM instead of print_mac() and the net_device_ops instead of direct
assignment of pointers.
Assuming this is okay, I'd very much like for it to be merged.
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
[-- Attachment #2: KS8695 Ethernet Driver (ks8695net) --]
[-- Type: text/x-patch, Size: 51759 bytes --]
KS8695: Add support for the KS8695 ethernet devices.
Implements the KS8695 ethernet device (ks8695net).
This driver is only of use on the KS8695 which is an ARM9 based SoC. The
documentation on this SoC is sparse and poor, with barely a register
description and a rough outline of how the ethernet works, this driver was
therefore written with strong reference to the Micrel supplied Linux 2.6.9
port, and to Andrew Victor's ks8695eth driver.
Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
Acked-by: Ben Dooks <ben@simtec.co.uk>
Kconfig | 8
Makefile | 1
ks8695net.c | 1675 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ks8695net.h | 107 +++
4 files changed, 1791 insertions(+)
Index: net-next-2.6/drivers/net/arm/Kconfig
===================================================================
--- net-next-2.6.orig/drivers/net/arm/Kconfig 2008-12-08 11:08:30.593160941 +0000
+++ net-next-2.6/drivers/net/arm/Kconfig 2008-12-08 11:51:56.665660087 +0000
@@ -40,6 +40,14 @@
If you wish to compile a kernel for the AT91RM9200 and enable
ethernet support, then you should always answer Y to this.
+config ARM_KS8695_ETHER
+ tristate "KS8695 Ethernet support"
+ depends on ARM && ARCH_KS8695
+ select MII
+ help
+ If you wish to compile a kernel for the KS8695 and want to
+ use the internal ethernet then you should answer Y to this.
+
config EP93XX_ETH
tristate "EP93xx Ethernet support"
depends on ARM && ARCH_EP93XX
Index: net-next-2.6/drivers/net/arm/Makefile
===================================================================
--- net-next-2.6.orig/drivers/net/arm/Makefile 2008-12-08 11:08:30.593160941 +0000
+++ net-next-2.6/drivers/net/arm/Makefile 2008-12-08 11:51:56.665660087 +0000
@@ -8,5 +8,6 @@
obj-$(CONFIG_ARM_ETHER3) += ether3.o
obj-$(CONFIG_ARM_ETHER1) += ether1.o
obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
+obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
Index: net-next-2.6/drivers/net/arm/ks8695net.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-next-2.6/drivers/net/arm/ks8695net.c 2008-12-08 12:55:27.121659225 +0000
@@ -0,0 +1,1675 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+
+#include <mach/regs-switch.h>
+#include <mach/regs-misc.h>
+
+#include "ks8695net.h"
+
+#define MODULENAME "ks8695_ether"
+#define MODULEVERSION "1.00"
+
+/*
+ * Transmit timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+
+/* Hardware structures */
+
+/**
+ * struct rx_ring_desc - Receive descriptor ring element
+ * @status: The status of the descriptor element (E.g. who owns it)
+ * @length: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct rx_ring_desc {
+ __le32 status;
+ __le32 length;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct tx_ring_desc - Transmit descriptor ring element
+ * @owner: Who owns the descriptor
+ * @status: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct tx_ring_desc {
+ __le32 owner;
+ __le32 status;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct ks8695_skbuff - sk_buff wrapper for rx/tx rings.
+ * @skb: The buffer in the ring
+ * @dma_ptr: The mapped DMA pointer of the buffer
+ * @length: The number of bytes mapped to dma_ptr
+ */
+struct ks8695_skbuff {
+ struct sk_buff *skb;
+ dma_addr_t dma_ptr;
+ u32 length;
+};
+
+/* Private device structure */
+
+#define MAX_TX_DESC 8
+#define MAX_TX_DESC_MASK 0x7
+#define MAX_RX_DESC 16
+#define MAX_RX_DESC_MASK 0xf
+
+#define MAX_RXBUF_SIZE 0x700
+
+#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
+#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC)
+#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)
+
+/**
+ * enum ks8695_dtype - Device type
+ * @KS8695_DTYPE_WAN: This device is a WAN interface
+ * @KS8695_DTYPE_LAN: This device is a LAN interface
+ * @KS8695_DTYPE_HPNA: This device is an HPNA interfacew
+ */
+enum ks8695_dtype {
+ KS8695_DTYPE_WAN,
+ KS8695_DTYPE_LAN,
+ KS8695_DTYPE_HPNA,
+};
+
+/**
+ * struct ks8695_priv - Private data for the KS8695 Ethernet
+ * @in_suspend: Flag to indicate if we're suspending/resuming
+ * @ndev: The net_device for this interface
+ * @dev: The platform device object for this interface
+ * @dtype: The type of this device
+ * @io_regs: The ioremapped registers for this interface
+ * @rx_irq_name: The textual name of the RX IRQ from the platform data
+ * @tx_irq_name: The textual name of the TX IRQ from the platform data
+ * @link_irq_name: The textual name of the link IRQ from the
+ * platform data if available
+ * @rx_irq: The IRQ number for the RX IRQ
+ * @tx_irq: The IRQ number for the TX IRQ
+ * @link_irq: The IRQ number for the link IRQ if available
+ * @regs_req: The resource request for the registers region
+ * @phyiface_req: The resource request for the phy/switch region
+ * if available
+ * @phyiface_regs: The ioremapped registers for the phy/switch if available
+ * @ring_base: The base pointer of the dma coherent memory for the rings
+ * @ring_base_dma: The DMA mapped equivalent of ring_base
+ * @tx_ring: The pointer in ring_base of the TX ring
+ * @tx_ring_used: The number of slots in the TX ring which are occupied
+ * @tx_ring_next_slot: The next slot to fill in the TX ring
+ * @tx_ring_dma: The DMA mapped equivalent of tx_ring
+ * @tx_buffers: The sk_buff mappings for the TX ring
+ * @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables
+ * @rx_ring: The pointer in ring_base of the RX ring
+ * @rx_ring_dma: The DMA mapped equivalent of rx_ring
+ * @rx_buffers: The sk_buff mappings for the RX ring
+ * @next_rx_desc_read: The next RX descriptor to read from on IRQ
+ * @msg_enable: The flags for which messages to emit
+ */
+struct ks8695_priv {
+ int in_suspend;
+ struct net_device *ndev;
+ struct device *dev;
+ enum ks8695_dtype dtype;
+ void __iomem *io_regs;
+
+ const char *rx_irq_name, *tx_irq_name, *link_irq_name;
+ int rx_irq, tx_irq, link_irq;
+
+ struct resource *regs_req, *phyiface_req;
+ void __iomem *phyiface_regs;
+
+ void *ring_base;
+ dma_addr_t ring_base_dma;
+
+ struct tx_ring_desc *tx_ring;
+ int tx_ring_used;
+ int tx_ring_next_slot;
+ dma_addr_t tx_ring_dma;
+ struct ks8695_skbuff tx_buffers[MAX_TX_DESC];
+ spinlock_t txq_lock;
+
+ struct rx_ring_desc *rx_ring;
+ dma_addr_t rx_ring_dma;
+ struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
+ int next_rx_desc_read;
+
+ int msg_enable;
+};
+
+/* Register access */
+
+/**
+ * ks8695_readreg - Read from a KS8695 ethernet register
+ * @ksp: The device to read from
+ * @reg: The register to read
+ */
+static inline u32
+ks8695_readreg(struct ks8695_priv *ksp, int reg)
+{
+ return readl(ksp->io_regs + reg);
+}
+
+/**
+ * ks8695_writereg - Write to a KS8695 ethernet register
+ * @ksp: The device to write to
+ * @reg: The register to write
+ * @value: The value to write to the register
+ */
+static inline void
+ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value)
+{
+ writel(value, ksp->io_regs + reg);
+}
+
+/* Utility functions */
+
+/**
+ * ks8695_get_priv - Retrieve the private data from a net_device.
+ * @ndev: The net_device to read the private data pointer from
+ *
+ * Returns the private structure for the KS8695 Ethernet device.
+ */
+static inline struct ks8695_priv *
+ks8695_get_priv(struct net_device *ndev)
+{
+ return ndev->priv;
+}
+
+/**
+ * ks8695_port_type - Retrieve port-type as user-friendly string
+ * @ksp: The device to return the type for
+ *
+ * Returns a string indicating which of the WAN, LAN or HPNA
+ * ports this device is likely to represent.
+ */
+static const char *
+ks8695_port_type(struct ks8695_priv *ksp)
+{
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_LAN:
+ return "LAN";
+ case KS8695_DTYPE_WAN:
+ return "WAN";
+ case KS8695_DTYPE_HPNA:
+ return "HPNA";
+ }
+
+ return "UNKNOWN";
+}
+
+/**
+ * ks8695_update_mac - Update the MAC registers in the device
+ * @ksp: The device to update
+ *
+ * Updates the MAC registers in the KS8695 device from the address in the
+ * net_device structure associated with this interface.
+ */
+static void
+ks8695_update_mac(struct ks8695_priv *ksp)
+{
+ /* Update the HW with the MAC from the net_device */
+ struct net_device *ndev = ksp->ndev;
+ u32 machigh, maclow;
+
+ maclow = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5] << 0));
+ machigh = ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1] << 0));
+
+ ks8695_writereg(ksp, KS8695_MAL, maclow);
+ ks8695_writereg(ksp, KS8695_MAH, machigh);
+
+}
+
+/**
+ * ks8695_refill_rxbuffers - Re-fill the RX buffer ring
+ * @ksp: The device to refill
+ *
+ * Iterates the RX ring of the device looking for empty slots.
+ * For each empty slot, we allocate and map a new SKB and give it
+ * to the hardware.
+ * This can be called from interrupt context safely.
+ */
+static void
+ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
+{
+ /* Run around the RX ring, filling in any missing sk_buff's */
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (!ksp->rx_buffers[buff_n].skb) {
+ struct sk_buff *skb = dev_alloc_skb(MAX_RXBUF_SIZE);
+ dma_addr_t mapping;
+
+ ksp->rx_buffers[buff_n].skb = skb;
+ if (skb == NULL) {
+ /* Failed to allocate one, perhaps
+ * we'll try again later.
+ */
+ break;
+ }
+
+ mapping = dma_map_single(ksp->dev, skb->data,
+ MAX_RXBUF_SIZE,
+ DMA_FROM_DEVICE);
+ ksp->rx_buffers[buff_n].dma_ptr = mapping;
+ skb->dev = ksp->ndev;
+ ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
+
+ /* Record this into the DMA ring */
+ ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
+ ksp->rx_ring[buff_n].length =
+ cpu_to_le32(MAX_RXBUF_SIZE);
+
+ wmb();
+
+ /* And give ownership over to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+ }
+ }
+}
+
+/* Maximum number of multicast addresses which the KS8695 HW supports */
+#define KS8695_NR_ADDRESSES 16
+
+/**
+ * ks8695_init_partial_multicast - Init the mcast addr registers
+ * @ksp: The device to initialise
+ * @addr: The multicast address list to use
+ * @nr_addr: The number of addresses in the list
+ *
+ * This routine is a helper for ks8695_set_multicast - it writes
+ * the additional-address registers in the KS8695 ethernet device
+ * and cleans up any others left behind.
+ */
+static void
+ks8695_init_partial_multicast(struct ks8695_priv *ksp,
+ struct dev_mc_list *addr,
+ int nr_addr)
+{
+ u32 low, high;
+ int i;
+
+ for (i = 0; i < nr_addr; i++, addr = addr->next) {
+ /* Ran out of addresses? */
+ if (!addr)
+ break;
+ /* Ran out of space in chip? */
+ if (i == KS8695_NR_ADDRESSES)
+ break;
+
+ low = (addr->dmi_addr[2] << 24) | (addr->dmi_addr[3] << 16) |
+ (addr->dmi_addr[4] << 8) | (addr->dmi_addr[5]);
+ high = (addr->dmi_addr[0] << 8) | (addr->dmi_addr[1]);
+
+ ks8695_writereg(ksp, KS8695_AAL_(i), low);
+ ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high);
+ }
+
+ /* Clear the remaining Additional Station Addresses */
+ for (; i < KS8695_NR_ADDRESSES; i++) {
+ ks8695_writereg(ksp, KS8695_AAL_(i), 0);
+ ks8695_writereg(ksp, KS8695_AAH_(i), 0);
+ }
+}
+
+/* Interrupt handling */
+
+/**
+ * ks8695_tx_irq - Transmit IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the TX ring, clearing out any transmitted slots.
+ * Allows the net_device to pass us new packets once slots are
+ * freed.
+ */
+static irqreturn_t
+ks8695_tx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb &&
+ !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
+ rmb();
+ /* An SKB which is not owned by HW is present */
+ /* Update the stats for the net_device */
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;
+
+ /* Free the packet from the ring */
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Free the sk_buff */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ ksp->tx_ring_used--;
+ }
+ }
+
+ netif_wake_queue(ndev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_rx_irq - Receive IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the RX ring, passing any received packets up to the
+ * host. If we received anything other than errors, we then
+ * refill the ring.
+ */
+static irqreturn_t
+ks8695_rx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ struct sk_buff *skb;
+ int buff_n;
+ u32 flags;
+ int pktlen;
+ int last_rx_processed = -1;
+
+ buff_n = ksp->next_rx_desc_read;
+ do {
+ if (ksp->rx_buffers[buff_n].skb &&
+ !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) {
+ rmb();
+ flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
+ /* Found an SKB which we own, this means we
+ * received a packet
+ */
+ if ((flags & (RDES_FS | RDES_LS)) !=
+ (RDES_FS | RDES_LS)) {
+ /* This packet is not the first and
+ * the last segment. Therefore it is
+ * a "spanning" packet and we can't
+ * handle it
+ */
+ goto rx_failure;
+ }
+
+ if (flags & (RDES_ES | RDES_RE)) {
+ /* It's an error packet */
+ ndev->stats.rx_errors++;
+ if (flags & RDES_TL)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_RF)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_CE)
+ ndev->stats.rx_crc_errors++;
+ if (flags & RDES_RE)
+ ndev->stats.rx_missed_errors++;
+
+ goto rx_failure;
+ }
+
+ pktlen = flags & RDES_FLEN;
+ pktlen -= 4; /* Drop the CRC */
+
+ /* Retrieve the sk_buff */
+ skb = ksp->rx_buffers[buff_n].skb;
+
+ /* Clear it from the ring */
+ ksp->rx_buffers[buff_n].skb = NULL;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+
+ /* Relinquish the SKB to the network layer */
+ skb_put(skb, pktlen);
+ skb->protocol = eth_type_trans(skb, ndev);
+ netif_rx(skb);
+
+ /* Record stats */
+ ndev->last_rx = jiffies;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += pktlen;
+ goto rx_finished;
+
+rx_failure:
+ /* This ring entry is an error, but we can
+ * re-use the skb
+ */
+ /* Give the ring entry back to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+rx_finished:
+ /* And note this as processed so we can start
+ * from here next time
+ */
+ last_rx_processed = buff_n;
+ } else {
+ /* Ran out of things to process, stop now */
+ break;
+ }
+ buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
+ } while (buff_n != ksp->next_rx_desc_read);
+
+ /* And note which RX descriptor we last did anything with */
+ if (likely(last_rx_processed != -1))
+ ksp->next_rx_desc_read =
+ (last_rx_processed + 1) & MAX_RX_DESC_MASK;
+
+ /* And refill the buffers */
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Kick the RX DMA engine, in case it became suspended */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_link_irq - Link change IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * The WAN interface can generate an IRQ when the link changes,
+ * report this to the net layer and the user.
+ */
+static irqreturn_t
+ks8695_link_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if (ctrl & WMC_WLS) {
+ netif_carrier_on(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev,
+ "%s: Link is now up (10%sMbps/%s-duplex)\n",
+ ndev->name,
+ (ctrl & WMC_WSS) ? "0" : "",
+ (ctrl & WMC_WDS) ? "Full" : "Half");
+ } else {
+ netif_carrier_off(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev, "%s: Link is now down.\n",
+ ndev->name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+/* KS8695 Device functions */
+
+/**
+ * ks8695_reset - Reset a KS8695 ethernet interface
+ * @ksp: The interface to reset
+ *
+ * Perform an engine reset of the interface and re-program it
+ * with sensible defaults.
+ */
+static void
+ks8695_reset(struct ks8695_priv *ksp)
+{
+ int reset_timeout = watchdog;
+ /* Issue the reset via the TX DMA control register */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
+ while (reset_timeout--) {
+ if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
+ break;
+ msleep(1);
+ }
+
+ if (reset_timeout == 0) {
+ dev_crit(ksp->dev,
+ "Timeout waiting for DMA engines to reset\n");
+ /* And blithely carry on */
+ }
+
+ /* Definitely wait long enough before attempting to program
+ * the engines
+ */
+ msleep(10);
+
+ /* RX: unicast and broadcast */
+ ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB);
+ /* TX: pad and add CRC */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC);
+}
+
+/**
+ * ks8695_shutdown - Shut down a KS8695 ethernet interface
+ * @ksp: The interface to shut down
+ *
+ * This disables packet RX/TX, cleans up IRQs, drains the rings,
+ * and basically places the interface into a clean shutdown
+ * state.
+ */
+static void
+ks8695_shutdown(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+ int buff_n;
+
+ /* Disable packet transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE);
+
+ /* Disable packet reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE);
+
+ /* Release the IRQs */
+ free_irq(ksp->rx_irq, ksp->ndev);
+ free_irq(ksp->tx_irq, ksp->ndev);
+ if (ksp->link_irq != -1)
+ free_irq(ksp->link_irq, ksp->ndev);
+
+ /* Throw away any pending TX packets */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb) {
+ /* Remove this SKB from the TX ring */
+ ksp->tx_ring[buff_n].owner = 0;
+ ksp->tx_ring[buff_n].status = 0;
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin this SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ }
+ }
+
+ /* Purge the RX buffers */
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (ksp->rx_buffers[buff_n].skb) {
+ /* Remove the SKB from the RX ring */
+ ksp->rx_ring[buff_n].status = 0;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb);
+ ksp->rx_buffers[buff_n].skb = NULL;
+ }
+ }
+}
+
+
+/**
+ * ks8695_setup_irq - IRQ setup helper function
+ * @irq: The IRQ number to claim
+ * @irq_name: The name to give the IRQ claimant
+ * @handler: The function to call to handle the IRQ
+ * @ndev: The net_device to pass in as the dev_id argument to the handler
+ *
+ * Return 0 on success.
+ */
+static int
+ks8695_setup_irq(int irq, const char *irq_name,
+ irq_handler_t handler, struct net_device *ndev)
+{
+ int ret;
+
+ ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);
+
+ if (ret) {
+ dev_err(&ndev->dev, "failure to request IRQ %d\n", irq);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_init_net - Initialise a KS8695 ethernet interface
+ * @ksp: The interface to initialise
+ *
+ * This routine fills the RX ring, initialises the DMA engines,
+ * allocates the IRQs and then starts the packet TX and RX
+ * engines.
+ */
+static int
+ks8695_init_net(struct ks8695_priv *ksp)
+{
+ int ret;
+ u32 ctrl;
+
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Initialise the DMA engines */
+ ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
+ ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
+
+ /* Request the IRQs */
+ ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
+ ks8695_rx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
+ ks8695_tx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ if (ksp->link_irq != -1) {
+ ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
+ ks8695_link_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ }
+
+ /* Set up the ring indices */
+ ksp->next_rx_desc_read = 0;
+ ksp->tx_ring_next_slot = 0;
+ ksp->tx_ring_used = 0;
+
+ /* Bring up transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ /* Enable packet transmission */
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
+
+ /* Bring up the reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ /* Enable packet reception */
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
+ /* And start the DMA engine */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ /* All done */
+ return 0;
+}
+
+/**
+ * ks8695_release_device - HW resource release for KS8695 e-net
+ * @ksp: The device to be freed
+ *
+ * This unallocates io memory regions, dma-coherent regions etc
+ * which were allocated in ks8695_probe.
+ */
+static void
+ks8695_release_device(struct ks8695_priv *ksp)
+{
+ /* Unmap the registers */
+ iounmap(ksp->io_regs);
+ if (ksp->phyiface_regs)
+ iounmap(ksp->phyiface_regs);
+
+ /* And release the request */
+ release_resource(ksp->regs_req);
+ kfree(ksp->regs_req);
+ if (ksp->phyiface_req) {
+ release_resource(ksp->phyiface_req);
+ kfree(ksp->phyiface_req);
+ }
+
+ /* Free the ring buffers */
+ dma_free_coherent(ksp->dev, RING_DMA_SIZE,
+ ksp->ring_base, ksp->ring_base_dma);
+}
+
+/* Ethtool support */
+
+/**
+ * ks8695_get_msglevel - Get the messages enabled for emission
+ * @ndev: The network device to read from
+ */
+static u32
+ks8695_get_msglevel(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ return ksp->msg_enable;
+}
+
+/**
+ * ks8695_set_msglevel - Set the messages enabled for emission
+ * @ndev: The network device to configure
+ * @value: The messages to set for emission
+ */
+static void
+ks8695_set_msglevel(struct net_device *ndev, u32 value)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ ksp->msg_enable = value;
+}
+
+/**
+ * ks8695_get_settings - Get device-specific settings.
+ * @ndev: The network device to read settings from
+ * @cmd: The ethtool structure to read into
+ */
+static int
+ks8695_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ /* All ports on the KS8695 support these... */
+ cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+ SUPPORTED_TP | SUPPORTED_MII);
+ cmd->transceiver = XCVR_INTERNAL;
+
+ /* Port specific extras */
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ cmd->phy_address = 0;
+ /* not supported for HPNA */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ /* BUG: Erm, dtype hpna implies no phy regs */
+ /*
+ ctrl = readl(KS8695_MISC_VA + KS8695_HMC);
+ cmd->speed = (ctrl & HMC_HSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & HMC_HDS) ? DUPLEX_FULL : DUPLEX_HALF;
+ */
+ return -EOPNOTSUPP;
+ case KS8695_DTYPE_WAN:
+ cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
+ cmd->port = PORT_MII;
+ cmd->supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause);
+ cmd->phy_address = 0;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if ((ctrl & WMC_WAND) == 0) {
+ /* auto-negotiation is enabled */
+ cmd->advertising |= ADVERTISED_Autoneg;
+ if (ctrl & WMC_WANA100F)
+ cmd->advertising |= ADVERTISED_100baseT_Full;
+ if (ctrl & WMC_WANA100H)
+ cmd->advertising |= ADVERTISED_100baseT_Half;
+ if (ctrl & WMC_WANA10F)
+ cmd->advertising |= ADVERTISED_10baseT_Full;
+ if (ctrl & WMC_WANA10H)
+ cmd->advertising |= ADVERTISED_10baseT_Half;
+ if (ctrl & WMC_WANAP)
+ cmd->advertising |= ADVERTISED_Pause;
+ cmd->autoneg = AUTONEG_ENABLE;
+
+ cmd->speed = (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WDS) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ } else {
+ /* auto-negotiation is disabled */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ cmd->speed = (ctrl & WMC_WANF100) ?
+ SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WANFF) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ }
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_set_settings - Set device-specific settings.
+ * @ndev: The network device to configure
+ * @cmd: The settings to configure
+ */
+static int
+ks8695_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ if ((cmd->speed != SPEED_10) && (cmd->speed != SPEED_100))
+ return -EINVAL;
+ if ((cmd->duplex != DUPLEX_HALF) && (cmd->duplex != DUPLEX_FULL))
+ return -EINVAL;
+ if (cmd->port != PORT_MII)
+ return -EINVAL;
+ if (cmd->transceiver != XCVR_INTERNAL)
+ return -EINVAL;
+ if ((cmd->autoneg != AUTONEG_DISABLE) &&
+ (cmd->autoneg != AUTONEG_ENABLE))
+ return -EINVAL;
+
+ if (cmd->autoneg == AUTONEG_ENABLE) {
+ if ((cmd->advertising & (ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full)) == 0)
+ return -EINVAL;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA does not support auto-negotiation. */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+ if (cmd->advertising & ADVERTISED_100baseT_Full)
+ ctrl |= WMC_WANA100F;
+ if (cmd->advertising & ADVERTISED_100baseT_Half)
+ ctrl |= WMC_WANA100H;
+ if (cmd->advertising & ADVERTISED_10baseT_Full)
+ ctrl |= WMC_WANA10F;
+ if (cmd->advertising & ADVERTISED_10baseT_Half)
+ ctrl |= WMC_WANA10H;
+
+ /* force a re-negotiation */
+ ctrl |= WMC_WANR;
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ } else {
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* BUG: dtype_hpna implies no phy registers */
+ /*
+ ctrl = __raw_readl(KS8695_MISC_VA + KS8695_HMC);
+
+ ctrl &= ~(HMC_HSS | HMC_HDS);
+ if (cmd->speed == SPEED_100)
+ ctrl |= HMC_HSS;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= HMC_HDS;
+
+ __raw_writel(ctrl, KS8695_MISC_VA + KS8695_HMC);
+ */
+ return -EOPNOTSUPP;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* disable auto-negotiation */
+ ctrl |= WMC_WAND;
+ ctrl &= ~(WMC_WANF100 | WMC_WANFF);
+
+ if (cmd->speed == SPEED_100)
+ ctrl |= WMC_WANF100;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= WMC_WANFF;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_nwayreset - Restart the autonegotiation on the port.
+ * @ndev: The network device to restart autoneotiation on
+ */
+static int
+ks8695_nwayreset(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy means no autonegotiation on hpna */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ if ((ctrl & WMC_WAND) == 0)
+ writel(ctrl | WMC_WANR,
+ ksp->phyiface_regs + KS8695_WMC);
+ else
+ /* auto-negotiation not enabled */
+ return -EINVAL;
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_get_link - Retrieve link status of network interface
+ * @ndev: The network interface to retrive the link status of.
+ */
+static u32
+ks8695_get_link(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA always has link */
+ return 1;
+ case KS8695_DTYPE_WAN:
+ /* WAN we can read the PHY for */
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ return ctrl & WMC_WLS;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/**
+ * ks8695_get_pause - Retrieve network pause/flow-control advertising
+ * @ndev: The device to retrieve settings from
+ * @param: The structure to fill out with the information
+ */
+static void
+ks8695_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy link on hpna to configure */
+ return;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* advertise Pause */
+ param->autoneg = (ctrl & WMC_WANAP);
+
+ /* current Rx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ param->rx_pause = (ctrl & DRXC_RFCE);
+
+ /* current Tx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ param->tx_pause = (ctrl & DTXC_TFCE);
+ break;
+ case KS8695_DTYPE_LAN:
+ /* The LAN's "phy" is a direct-attached switch */
+ return;
+ }
+}
+
+/**
+ * ks8695_set_pause - Configure pause/flow-control
+ * @ndev: The device to configure
+ * @param: The pause parameters to set
+ *
+ * TODO: Implement this
+ */
+static int
+ks8695_set_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ return -EOPNOTSUPP;
+}
+
+/**
+ * ks8695_get_drvinfo - Retrieve driver information
+ * @ndev: The network device to retrieve info about
+ * @info: The info structure to fill out.
+ */
+static void
+ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, MODULENAME, sizeof(info->driver));
+ strlcpy(info->version, MODULEVERSION, sizeof(info->version));
+ strlcpy(info->bus_info, ndev->dev.parent->bus_id,
+ sizeof(info->bus_info));
+}
+
+static struct ethtool_ops ks8695_ethtool_ops = {
+ .get_msglevel = ks8695_get_msglevel,
+ .set_msglevel = ks8695_set_msglevel,
+ .get_settings = ks8695_get_settings,
+ .set_settings = ks8695_set_settings,
+ .nway_reset = ks8695_nwayreset,
+ .get_link = ks8695_get_link,
+ .get_pauseparam = ks8695_get_pause,
+ .set_pauseparam = ks8695_set_pause,
+ .get_drvinfo = ks8695_get_drvinfo,
+};
+
+/* Network device interface functions */
+
+/**
+ * ks8695_set_mac - Update MAC in net dev and HW
+ * @ndev: The network device to update
+ * @addr: The new MAC address to set
+ */
+static int
+ks8695_set_mac(struct net_device *ndev, void *addr)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+
+ ks8695_update_mac(ksp);
+
+ dev_dbg(ksp->dev, "%s: Updated MAC address to %pM\n",
+ ndev->name, ndev->dev_addr);
+
+ return 0;
+}
+
+/**
+ * ks8695_set_multicast - Set up the multicast behaviour of the interface
+ * @ndev: The net_device to configure
+ *
+ * This routine, called by the net layer, configures promiscuity
+ * and multicast reception behaviour for the interface.
+ */
+static void
+ks8695_set_multicast(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+
+ if (ndev->flags & IFF_PROMISC) {
+ /* enable promiscuous mode */
+ ctrl |= DRXC_RA;
+ } else if (ndev->flags & ~IFF_PROMISC) {
+ /* disable promiscuous mode */
+ ctrl &= ~DRXC_RA;
+ }
+
+ if (ndev->flags & IFF_ALLMULTI) {
+ /* enable all multicast mode */
+ ctrl |= DRXC_RM;
+ } else if (ndev->mc_count > KS8695_NR_ADDRESSES) {
+ /* more specific multicast addresses than can be
+ * handled in hardware
+ */
+ ctrl |= DRXC_RM;
+ } else if (ndev->mc_count > 0) {
+ /* enable specific multicasts */
+ ctrl &= ~DRXC_RM;
+ ks8695_init_partial_multicast(ksp, ndev->mc_list,
+ ndev->mc_count);
+ } else if (ndev->flags & ~IFF_ALLMULTI) {
+ /* disable multicast mode */
+ ctrl &= ~DRXC_RM;
+ ks8695_init_partial_multicast(ksp, NULL, 0);
+ }
+
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl);
+}
+
+/**
+ * ks8695_timeout - Handle a network tx/rx timeout.
+ * @ndev: The net_device which timed out.
+ *
+ * A network transaction timed out, reset the device.
+ */
+static void
+ks8695_timeout(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ ks8695_shutdown(ksp);
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ /* We ignore the return from this since it managed to init
+ * before it probably will be okay to init again.
+ */
+ ks8695_init_net(ksp);
+
+ /* Reconfigure promiscuity etc */
+ ks8695_set_multicast(ndev);
+
+ /* And start the TX queue once more */
+ netif_start_queue(ndev);
+}
+
+/**
+ * ks8695_start_xmit - Start a packet transmission
+ * @skb: The packet to transmit
+ * @ndev: The network device to send the packet on
+ *
+ * This routine, called by the net layer, takes ownership of the
+ * sk_buff and adds it to the TX ring. It then kicks the TX DMA
+ * engine to ensure transmission begins.
+ */
+static int
+ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int buff_n;
+
+ spin_lock_irq(&ksp->txq_lock);
+
+ if (ksp->tx_ring_used == MAX_TX_DESC) {
+ /* Somehow we got entered when we have no room */
+ spin_unlock_irq(&ksp->txq_lock);
+ return NETDEV_TX_BUSY;
+ }
+
+ buff_n = ksp->tx_ring_next_slot;
+ ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK;
+
+ BUG_ON(ksp->tx_buffers[buff_n].skb);
+
+ ksp->tx_buffers[buff_n].skb = skb;
+ ksp->tx_buffers[buff_n].length = skb->len;
+ ksp->tx_buffers[buff_n].dma_ptr = dma_map_single(
+ ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
+
+ /* Fill out the TX descriptor */
+ ksp->tx_ring[buff_n].data_ptr =
+ cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
+ ksp->tx_ring[buff_n].status =
+ cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
+ (skb->len & TDES_TBS));
+
+ wmb();
+
+ /* Hand it over to the hardware */
+ ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);
+
+ if (++ksp->tx_ring_used == MAX_TX_DESC)
+ netif_stop_queue(ndev);
+
+ ndev->trans_start = jiffies;
+
+ /* Kick the TX DMA in case it decided to go IDLE */
+ ks8695_writereg(ksp, KS8695_DTSC, 0);
+
+ spin_unlock_irq(&ksp->txq_lock);
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ks8695_stop - Stop (shutdown) a KS8695 ethernet interface
+ * @ndev: The net_device to stop
+ *
+ * This disables the TX queue and cleans up a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_stop(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+
+ ks8695_shutdown(ksp);
+
+ return 0;
+}
+
+/**
+ * ks8695_open - Open (bring up) a KS8695 ethernet interface
+ * @ndev: The net_device to open
+ *
+ * This resets, configures the MAC, initialises the RX ring and
+ * DMA engines and starts the TX queue for a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_open(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int ret;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ ret = ks8695_init_net(ksp);
+ if (ret) {
+ ks8695_shutdown(ksp);
+ return ret;
+ }
+
+ netif_start_queue(ndev);
+
+ return 0;
+}
+
+/* Platform device driver */
+
+/**
+ * ks8695_init_switch - Init LAN switch to known good defaults.
+ * @ksp: The device to initialise
+ *
+ * This initialises the LAN switch in the KS8695 to a known-good
+ * set of defaults.
+ */
+static void __devinit
+ks8695_init_switch(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Default value for SEC0 according to datasheet */
+ ctrl = 0x40819e00;
+
+ /* LED0 = Speed LED1 = Link/Activity */
+ ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S);
+ ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY);
+
+ /* Enable Switch */
+ ctrl |= SEC0_ENABLE;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_SEC0);
+
+ /* Defaults for SEC1 */
+ writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
+}
+
+/**
+ * ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults
+ * @ksp: The device to initialise
+ *
+ * This initialises a KS8695's WAN phy to sensible values for
+ * autonegotiation etc.
+ */
+static void __devinit
+ks8695_init_wan_phy(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Support auto-negotiation */
+ ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+
+ /* LED0 = Activity , LED1 = Link */
+ ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK);
+
+ /* Restart Auto-negotiation */
+ ctrl |= WMC_WANR;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+
+ writel(0, ksp->phyiface_regs + KS8695_WPPM);
+ writel(0, ksp->phyiface_regs + KS8695_PPS);
+}
+
+static const struct net_device_ops ks8695_netdev_ops = {
+ .ndo_open = ks8695_open,
+ .ndo_stop = ks8695_stop,
+ .ndo_start_xmit = ks8695_start_xmit,
+ .ndo_tx_timeout = ks8695_timeout,
+ .ndo_set_mac_address = ks8695_set_mac,
+ .ndo_set_multicast_list = ks8695_set_multicast,
+};
+
+/**
+ * ks8695_probe - Probe and initialise a KS8695 ethernet interface
+ * @pdev: The platform device to probe
+ *
+ * Initialise a KS8695 ethernet device from platform data.
+ *
+ * This driver requires at least one IORESOURCE_MEM for the
+ * registers and two IORESOURCE_IRQ for the RX and TX IRQs
+ * respectively. It can optionally take an additional
+ * IORESOURCE_MEM for the switch or phy in the case of the lan or
+ * wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan
+ * port.
+ */
+static int __devinit
+ks8695_probe(struct platform_device *pdev)
+{
+ struct ks8695_priv *ksp;
+ struct net_device *ndev;
+ struct resource *regs_res, *phyiface_res;
+ struct resource *rxirq_res, *txirq_res, *linkirq_res;
+ int ret = 0;
+ int buff_n;
+ u32 machigh, maclow;
+
+ /* Initialise a net_device */
+ ndev = alloc_etherdev(sizeof(struct ks8695_priv));
+ if (!ndev) {
+ dev_err(&pdev->dev, "could not allocate device.\n");
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ dev_dbg(&pdev->dev, "ks8695_probe() called\n");
+
+ /* Configure our private structure a little */
+ ksp = netdev_priv(ndev);
+ memset(ksp, 0, sizeof(struct ks8695_priv));
+
+ ksp->dev = &pdev->dev;
+ ksp->ndev = ndev;
+ ksp->msg_enable = NETIF_MSG_LINK;
+
+ /* Retrieve resources */
+ regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+
+ if (!(regs_res && rxirq_res && txirq_res)) {
+ dev_err(ksp->dev, "insufficient resources\n");
+ ret = -ENOENT;
+ goto failure;
+ }
+
+ ksp->regs_req = request_mem_region(regs_res->start,
+ resource_size(regs_res),
+ pdev->name);
+
+ if (!ksp->regs_req) {
+ dev_err(ksp->dev, "cannot claim register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res));
+
+ if (!ksp->io_regs) {
+ dev_err(ksp->dev, "failed to ioremap registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ if (phyiface_res) {
+ ksp->phyiface_req =
+ request_mem_region(phyiface_res->start,
+ resource_size(phyiface_res),
+ phyiface_res->name);
+
+ if (!ksp->phyiface_req) {
+ dev_err(ksp->dev,
+ "cannot claim switch register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->phyiface_regs = ioremap(phyiface_res->start,
+ resource_size(phyiface_res));
+
+ if (!ksp->phyiface_regs) {
+ dev_err(ksp->dev,
+ "failed to ioremap switch registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+ }
+
+ ksp->rx_irq = rxirq_res->start;
+ ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX";
+ ksp->tx_irq = txirq_res->start;
+ ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX";
+ ksp->link_irq = (linkirq_res ? linkirq_res->start : -1);
+ ksp->link_irq_name = (linkirq_res && linkirq_res->name) ?
+ linkirq_res->name : "Ethernet Link";
+
+ /* driver system setup */
+ ndev->netdev_ops = &ks8695_netdev_ops;
+ SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+ ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+
+ /* Retrieve the default MAC addr from the chip. */
+ /* The bootloader should have left it in there for us. */
+
+ machigh = ks8695_readreg(ksp, KS8695_MAH);
+ maclow = ks8695_readreg(ksp, KS8695_MAL);
+
+ ndev->dev_addr[0] = (machigh >> 8) & 0xFF;
+ ndev->dev_addr[1] = machigh & 0xFF;
+ ndev->dev_addr[2] = (maclow >> 24) & 0xFF;
+ ndev->dev_addr[3] = (maclow >> 16) & 0xFF;
+ ndev->dev_addr[4] = (maclow >> 8) & 0xFF;
+ ndev->dev_addr[5] = maclow & 0xFF;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please "
+ "set using ifconfig\n", ndev->name);
+
+ /* In order to be efficient memory-wise, we allocate both
+ * rings in one go.
+ */
+ ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE,
+ &ksp->ring_base_dma, GFP_KERNEL);
+ if (!ksp->ring_base) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ /* Specify the TX DMA ring buffer */
+ ksp->tx_ring = ksp->ring_base;
+ ksp->tx_ring_dma = ksp->ring_base_dma;
+
+ /* And initialise the queue's lock */
+ spin_lock_init(&ksp->txq_lock);
+
+ /* Specify the RX DMA ring buffer */
+ ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
+ ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE;
+
+ /* Zero the descriptor rings */
+ memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE);
+ memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE);
+
+ /* Build the rings */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ ksp->tx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->tx_ring_dma +
+ (sizeof(struct tx_ring_desc) *
+ ((buff_n + 1) & MAX_TX_DESC_MASK)));
+ }
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ ksp->rx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->rx_ring_dma +
+ (sizeof(struct rx_ring_desc) *
+ ((buff_n + 1) & MAX_RX_DESC_MASK)));
+ }
+
+ /* Initialise the port (physically) */
+ if (ksp->phyiface_regs && ksp->link_irq == -1) {
+ ks8695_init_switch(ksp);
+ ksp->dtype = KS8695_DTYPE_LAN;
+ } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
+ ks8695_init_wan_phy(ksp);
+ ksp->dtype = KS8695_DTYPE_WAN;
+ } else {
+ /* No initialisation since HPNA does not have a PHY */
+ ksp->dtype = KS8695_DTYPE_HPNA;
+ }
+
+ /* And bring up the net_device with the net core */
+ platform_set_drvdata(pdev, ndev);
+ ret = register_netdev(ndev);
+
+ if (ret == 0) {
+ dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n",
+ ks8695_port_type(ksp), ndev->dev_addr);
+ } else {
+ /* Report the failure to register the net_device */
+ dev_err(ksp->dev, "ks8695net: failed to register netdev.\n");
+ goto failure;
+ }
+
+ /* All is well */
+ return 0;
+
+ /* Error exit path */
+failure:
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ return ret;
+}
+
+/**
+ * ks8695_drv_suspend - Suspend a KS8695 ethernet platform device.
+ * @pdev: The device to suspend
+ * @state: The suspend state
+ *
+ * This routine detaches and shuts down a KS8695 ethernet device.
+ */
+static int
+ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ ksp->in_suspend = 1;
+
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ ks8695_shutdown(ksp);
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_resume - Resume a KS8695 ethernet platform device.
+ * @pdev: The device to resume
+ *
+ * This routine re-initialises and re-attaches a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_drv_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ ks8695_reset(ksp);
+ ks8695_init_net(ksp);
+ ks8695_set_multicast(ndev);
+ netif_device_attach(ndev);
+ }
+
+ ksp->in_suspend = 0;
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_remove - Remove a KS8695 net device on driver unload.
+ * @pdev: The platform device to remove
+ *
+ * This unregisters and releases a KS8695 ethernet device.
+ */
+static int __devexit
+ks8695_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(ndev);
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ dev_dbg(&pdev->dev, "released and freed device\n");
+ return 0;
+}
+
+static struct platform_driver ks8695_driver = {
+ .driver = {
+ .name = MODULENAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ks8695_probe,
+ .remove = __devexit_p(ks8695_drv_remove),
+ .suspend = ks8695_drv_suspend,
+ .resume = ks8695_drv_resume,
+};
+
+/* Module interface */
+
+static int __init
+ks8695_init(void)
+{
+ printk(KERN_INFO "%s Ethernet driver, V%s\n",
+ MODULENAME, MODULEVERSION);
+
+ return platform_driver_register(&ks8695_driver);
+}
+
+static void __exit
+ks8695_cleanup(void)
+{
+ platform_driver_unregister(&ks8695_driver);
+}
+
+module_init(ks8695_init);
+module_exit(ks8695_cleanup);
+
+MODULE_AUTHOR("Simtec Electronics")
+MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MODULENAME);
+
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
Index: net-next-2.6/drivers/net/arm/ks8695net.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-next-2.6/drivers/net/arm/ks8695net.h 2008-12-08 11:51:56.853659021 +0000
@@ -0,0 +1,107 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#ifndef KS8695NET_H
+#define KS8695NET_H
+
+/* Receive descriptor flags */
+#define RDES_OWN (1 << 31) /* Ownership */
+#define RDES_FS (1 << 30) /* First Descriptor */
+#define RDES_LS (1 << 29) /* Last Descriptor */
+#define RDES_IPE (1 << 28) /* IP Checksum error */
+#define RDES_TCPE (1 << 27) /* TCP Checksum error */
+#define RDES_UDPE (1 << 26) /* UDP Checksum error */
+#define RDES_ES (1 << 25) /* Error summary */
+#define RDES_MF (1 << 24) /* Multicast Frame */
+#define RDES_RE (1 << 19) /* MII Error reported */
+#define RDES_TL (1 << 18) /* Frame too Long */
+#define RDES_RF (1 << 17) /* Runt Frame */
+#define RDES_CE (1 << 16) /* CRC error */
+#define RDES_FT (1 << 15) /* Frame Type */
+#define RDES_FLEN (0x7ff) /* Frame Length */
+
+#define RDES_RER (1 << 25) /* Receive End of Ring */
+#define RDES_RBS (0x7ff) /* Receive Buffer Size */
+
+/* Transmit descriptor flags */
+
+#define TDES_OWN (1 << 31) /* Ownership */
+
+#define TDES_IC (1 << 31) /* Interrupt on Completion */
+#define TDES_FS (1 << 30) /* First Segment */
+#define TDES_LS (1 << 29) /* Last Segment */
+#define TDES_IPCKG (1 << 28) /* IP Checksum generate */
+#define TDES_TCPCKG (1 << 27) /* TCP Checksum generate */
+#define TDES_UDPCKG (1 << 26) /* UDP Checksum generate */
+#define TDES_TER (1 << 25) /* Transmit End of Ring */
+#define TDES_TBS (0x7ff) /* Transmit Buffer Size */
+
+/*
+ * Network controller register offsets
+ */
+#define KS8695_DTXC (0x00) /* DMA Transmit Control */
+#define KS8695_DRXC (0x04) /* DMA Receive Control */
+#define KS8695_DTSC (0x08) /* DMA Transmit Start Command */
+#define KS8695_DRSC (0x0c) /* DMA Receive Start Command */
+#define KS8695_TDLB (0x10) /* Transmit Descriptor List
+ * Base Address
+ */
+#define KS8695_RDLB (0x14) /* Receive Descriptor List
+ * Base Address
+ */
+#define KS8695_MAL (0x18) /* MAC Station Address Low */
+#define KS8695_MAH (0x1c) /* MAC Station Address High */
+#define KS8695_AAL_(n) (0x80 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) Low
+ */
+#define KS8695_AAH_(n) (0x84 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) High
+ */
+
+
+/* DMA Transmit Control Register */
+#define DTXC_TRST (1 << 31) /* Soft Reset */
+#define DTXC_TBS (0x3f << 24) /* Transmit Burst Size */
+#define DTXC_TUCG (1 << 18) /* Transmit UDP
+ * Checksum Generate
+ */
+#define DTXC_TTCG (1 << 17) /* Transmit TCP
+ * Checksum Generate
+ */
+#define DTXC_TICG (1 << 16) /* Transmit IP
+ * Checksum Generate
+ */
+#define DTXC_TFCE (1 << 9) /* Transmit Flow
+ * Control Enable
+ */
+#define DTXC_TLB (1 << 8) /* Loopback mode */
+#define DTXC_TEP (1 << 2) /* Transmit Enable Padding */
+#define DTXC_TAC (1 << 1) /* Transmit Add CRC */
+#define DTXC_TE (1 << 0) /* TX Enable */
+
+/* DMA Receive Control Register */
+#define DRXC_RBS (0x3f << 24) /* Receive Burst Size */
+#define DRXC_RUCC (1 << 18) /* Receive UDP Checksum check */
+#define DRXC_RTCG (1 << 17) /* Receive TCP Checksum check */
+#define DRXC_RICG (1 << 16) /* Receive IP Checksum check */
+#define DRXC_RFCE (1 << 9) /* Receive Flow Control
+ * Enable
+ */
+#define DRXC_RB (1 << 6) /* Receive Broadcast */
+#define DRXC_RM (1 << 5) /* Receive Multicast */
+#define DRXC_RU (1 << 4) /* Receive Unicast */
+#define DRXC_RERR (1 << 3) /* Receive Error Frame */
+#define DRXC_RA (1 << 2) /* Receive All */
+#define DRXC_RE (1 << 0) /* RX Enable */
+
+/* Additional Station Address High */
+#define AAH_E (1 << 31) /* Address Enabled */
+
+#endif /* KS8695NET_H */
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 intergrated ethernet driver
2008-12-08 11:05 ` Daniel Silverstone
@ 2008-12-08 15:17 ` Ben Hutchings
2008-12-08 20:29 ` David Miller
2008-12-09 8:57 ` Christoph Hellwig
2008-12-13 19:07 ` Kalle Valo
2 siblings, 1 reply; 16+ messages in thread
From: Ben Hutchings @ 2008-12-08 15:17 UTC (permalink / raw)
To: dsilvers; +Cc: netdev
On Mon, 2008-12-08 at 11:05 +0000, Daniel Silverstone wrote:
[...]
> > > +static inline struct ks8695_priv *
> > > +ks8695_get_priv(struct net_device *ndev)
> > Don't use ndev->priv, use netdev_priv(ndev). Which makes this function
> > redundant, except for the implicit pointer conversion.
>
> Aah, I didn't know about that one, updated.
Until recently they have been equivalent by default; direct access to
priv is now deprecated and it will be removed.
[...]
> > > + /* Free the packet from the ring */
> > > + ksp->tx_ring[buff_n].data_ptr = 0;
> > Clearing data_ptr seems redundant. The TDES_OWN flag indicates whether
> > the descriptor is pending and the skb pointer indicates whether a buffer
> > needs to be freed.
>
> This was simply me being neat. If you feel that it is an overly
> expensive thing to do then I shall elide it.
It's hardly expensive, but the comment is inaccurate because this field
does not indicate whether the descriptor is free or not.
[...]
> > > + buff_n = (buff_n + 1) % MAX_RX_DESC;
> > That's another expensive division.
>
> I was expecting the compiler to go "aaha! mod <powerof2> => bitwise
> mask".
It can only do this if it knows the dividend is non-negative.
I wrote "another" in reference to my comment further down which I
actually wrote earlier.
[...]
> > > +static int
> > > +ks8695_init_net(struct ks8695_priv *ksp)
>
> > The RX and TX IRQ handlers need to be released in case the TX or link
> > IRQ setup fails.
>
> The only time that this might fail, I.E. initial device open, we check
> the return value of ks8695_init_net and call ks86985_shutdown if needed
> which will release the IRQs.
>
> It seemed daft to replicate the cleanup code in two places. If however
> that is the accepted style then I shall add the cleanups to
> ks8695_init_net() -- Is my method acceptable or should I change?
Local cleanup is normal but I think this is OK.
[...]
> > > + dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %s\n",
> > > + ks8695_port_type(ksp), print_mac(mac, ndev->dev_addr));
> > Use %pM to format a MAC address instead of %s and print_mac().
>
> I believe this must be new since 2.6.27 so I'll get that as I move to
> net-next-2.6, thanks for the info.
Yes, the %p<modifier> formats are a recent invention.
[...]
> Thank you very much for your comments. Once this pull of net-next-2.6 is
> complete and I can do my forward-porting, I'll issue a new patch. Can I
> ask what the magic runes are which I need in subject / precis to
> actually request that my driver be merged rather than just reviewed?
Unless you add [RFC] to the subject it's implicit that you want the
patch merged. You can check the patch status at
<http://patchwork.ozlabs.org/project/netdev/list/>.
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 intergrated ethernet driver
2008-12-08 15:17 ` Ben Hutchings
@ 2008-12-08 20:29 ` David Miller
0 siblings, 0 replies; 16+ messages in thread
From: David Miller @ 2008-12-08 20:29 UTC (permalink / raw)
To: bhutchings; +Cc: dsilvers, netdev
From: Ben Hutchings <bhutchings@solarflare.com>
Date: Mon, 08 Dec 2008 15:17:53 +0000
> On Mon, 2008-12-08 at 11:05 +0000, Daniel Silverstone wrote:
> [...]
> > > > +static inline struct ks8695_priv *
> > > > +ks8695_get_priv(struct net_device *ndev)
> > > Don't use ndev->priv, use netdev_priv(ndev). Which makes this function
> > > redundant, except for the implicit pointer conversion.
> >
> > Aah, I didn't know about that one, updated.
>
> Until recently they have been equivalent by default; direct access to
> priv is now deprecated and it will be removed.
Not just "will be removed", but "is removed" in the current
net-next-2.6 tree so this driver wouldn't even compile :-)
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 intergrated ethernet driver
2008-12-08 11:05 ` Daniel Silverstone
2008-12-08 15:17 ` Ben Hutchings
@ 2008-12-09 8:57 ` Christoph Hellwig
2008-12-09 9:05 ` David Miller
2008-12-13 19:07 ` Kalle Valo
2 siblings, 1 reply; 16+ messages in thread
From: Christoph Hellwig @ 2008-12-09 8:57 UTC (permalink / raw)
To: Daniel Silverstone; +Cc: netdev
On Mon, Dec 08, 2008 at 11:05:18AM +0000, Daniel Silverstone wrote:
> > > + mapping = dma_map_single(ksp->dev, skb->data,
> > > + MAX_RXBUF_SIZE,
> > > + DMA_FROM_DEVICE);
> > I take it that dma_map_single() can never fail on ARM?
>
> No it can't. It essentially boils down to some pointer arithmetic on
> this platform.
You should check for errors anyway. The driver might be ported to an
architecture where it can fail (e.g. the SOC block gets reused), ARM
variants may come up where it can fail, or people might use the driver
as an example for their own new driver.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 intergrated ethernet driver
2008-12-09 8:57 ` Christoph Hellwig
@ 2008-12-09 9:05 ` David Miller
2008-12-09 9:29 ` Daniel Silverstone
0 siblings, 1 reply; 16+ messages in thread
From: David Miller @ 2008-12-09 9:05 UTC (permalink / raw)
To: hch; +Cc: dsilvers, netdev
From: Christoph Hellwig <hch@infradead.org>
Date: Tue, 9 Dec 2008 03:57:39 -0500
> On Mon, Dec 08, 2008 at 11:05:18AM +0000, Daniel Silverstone wrote:
> > > > + mapping = dma_map_single(ksp->dev, skb->data,
> > > > + MAX_RXBUF_SIZE,
> > > > + DMA_FROM_DEVICE);
> > > I take it that dma_map_single() can never fail on ARM?
> >
> > No it can't. It essentially boils down to some pointer arithmetic on
> > this platform.
>
> You should check for errors anyway. The driver might be ported to an
> architecture where it can fail (e.g. the SOC block gets reused), ARM
> variants may come up where it can fail, or people might use the driver
> as an example for their own new driver.
Agreed.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 intergrated ethernet driver
2008-12-09 9:05 ` David Miller
@ 2008-12-09 9:29 ` Daniel Silverstone
0 siblings, 0 replies; 16+ messages in thread
From: Daniel Silverstone @ 2008-12-09 9:29 UTC (permalink / raw)
To: David Miller; +Cc: hch, netdev
On Tue, 2008-12-09 at 01:05 -0800, David Miller wrote:
> > > > > + mapping = dma_map_single(ksp->dev, skb->data,
> > > > > + MAX_RXBUF_SIZE,
> > > > > + DMA_FROM_DEVICE);
> > > > I take it that dma_map_single() can never fail on ARM?
> > > No it can't. It essentially boils down to some pointer arithmetic on
> > > this platform.
> > You should check for errors anyway. The driver might be ported to an
> > architecture where it can fail (e.g. the SOC block gets reused), ARM
> > variants may come up where it can fail, or people might use the driver
> > as an example for their own new driver.
> Agreed.
Very well, I shall sort out an ammended patch. Thanks for your help in
reviewing the code.
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 intergrated ethernet driver
2008-12-08 13:08 ` Daniel Silverstone
@ 2008-12-09 17:00 ` Daniel Silverstone
[not found] ` <1228764912.3177.39.camel@achroite>
1 sibling, 0 replies; 16+ messages in thread
From: Daniel Silverstone @ 2008-12-09 17:00 UTC (permalink / raw)
To: netdev
[-- Attachment #1: Type: text/plain, Size: 440 bytes --]
Hello,
Attached is what I believe to be a reasonable final-cut of the driver.
I believe I have addressed all the concerns raised by Ben Hutchings,
Christoph Hellwig and David Miller.
Assuming I have done so successfully, I'd like to request a merge of
this version.
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
[-- Attachment #2: Type: text/x-patch, Size: 52296 bytes --]
KS8695: Add support for the KS8695 ethernet devices.
Implements the KS8695 ethernet device (ks8695net).
This driver is only of use on the KS8695 which is an ARM9 based SoC. The
documentation on this SoC is sparse and poor, with barely a register
description and a rough outline of how the ethernet works, this driver was
therefore written with strong reference to the Micrel supplied Linux 2.6.9
port, and to Andrew Victor's ks8695eth driver.
Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
Acked-by: Ben Dooks <ben@simtec.co.uk>
Kconfig | 8
Makefile | 1
ks8695net.c | 1691 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ks8695net.h | 107 +++
4 files changed, 1807 insertions(+)
Index: net-next-2.6/drivers/net/arm/Kconfig
===================================================================
--- net-next-2.6.orig/drivers/net/arm/Kconfig 2008-12-08 12:55:46.817658739 +0000
+++ net-next-2.6/drivers/net/arm/Kconfig 2008-12-08 12:56:10.213660241 +0000
@@ -40,6 +40,14 @@
If you wish to compile a kernel for the AT91RM9200 and enable
ethernet support, then you should always answer Y to this.
+config ARM_KS8695_ETHER
+ tristate "KS8695 Ethernet support"
+ depends on ARM && ARCH_KS8695
+ select MII
+ help
+ If you wish to compile a kernel for the KS8695 and want to
+ use the internal ethernet then you should answer Y to this.
+
config EP93XX_ETH
tristate "EP93xx Ethernet support"
depends on ARM && ARCH_EP93XX
Index: net-next-2.6/drivers/net/arm/Makefile
===================================================================
--- net-next-2.6.orig/drivers/net/arm/Makefile 2008-12-08 12:55:46.841662533 +0000
+++ net-next-2.6/drivers/net/arm/Makefile 2008-12-08 12:56:10.213660241 +0000
@@ -8,5 +8,6 @@
obj-$(CONFIG_ARM_ETHER3) += ether3.o
obj-$(CONFIG_ARM_ETHER1) += ether1.o
obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
+obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
Index: net-next-2.6/drivers/net/arm/ks8695net.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-next-2.6/drivers/net/arm/ks8695net.c 2008-12-09 16:57:51.313660285 +0000
@@ -0,0 +1,1691 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+
+#include <mach/regs-switch.h>
+#include <mach/regs-misc.h>
+
+#include "ks8695net.h"
+
+#define MODULENAME "ks8695_ether"
+#define MODULEVERSION "1.00"
+
+/*
+ * Transmit timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+
+/* Hardware structures */
+
+/**
+ * struct rx_ring_desc - Receive descriptor ring element
+ * @status: The status of the descriptor element (E.g. who owns it)
+ * @length: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct rx_ring_desc {
+ __le32 status;
+ __le32 length;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct tx_ring_desc - Transmit descriptor ring element
+ * @owner: Who owns the descriptor
+ * @status: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct tx_ring_desc {
+ __le32 owner;
+ __le32 status;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct ks8695_skbuff - sk_buff wrapper for rx/tx rings.
+ * @skb: The buffer in the ring
+ * @dma_ptr: The mapped DMA pointer of the buffer
+ * @length: The number of bytes mapped to dma_ptr
+ */
+struct ks8695_skbuff {
+ struct sk_buff *skb;
+ dma_addr_t dma_ptr;
+ u32 length;
+};
+
+/* Private device structure */
+
+#define MAX_TX_DESC 8
+#define MAX_TX_DESC_MASK 0x7
+#define MAX_RX_DESC 16
+#define MAX_RX_DESC_MASK 0xf
+
+#define MAX_RXBUF_SIZE 0x700
+
+#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
+#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC)
+#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)
+
+/**
+ * enum ks8695_dtype - Device type
+ * @KS8695_DTYPE_WAN: This device is a WAN interface
+ * @KS8695_DTYPE_LAN: This device is a LAN interface
+ * @KS8695_DTYPE_HPNA: This device is an HPNA interfacew
+ */
+enum ks8695_dtype {
+ KS8695_DTYPE_WAN,
+ KS8695_DTYPE_LAN,
+ KS8695_DTYPE_HPNA,
+};
+
+/**
+ * struct ks8695_priv - Private data for the KS8695 Ethernet
+ * @in_suspend: Flag to indicate if we're suspending/resuming
+ * @ndev: The net_device for this interface
+ * @dev: The platform device object for this interface
+ * @dtype: The type of this device
+ * @io_regs: The ioremapped registers for this interface
+ * @rx_irq_name: The textual name of the RX IRQ from the platform data
+ * @tx_irq_name: The textual name of the TX IRQ from the platform data
+ * @link_irq_name: The textual name of the link IRQ from the
+ * platform data if available
+ * @rx_irq: The IRQ number for the RX IRQ
+ * @tx_irq: The IRQ number for the TX IRQ
+ * @link_irq: The IRQ number for the link IRQ if available
+ * @regs_req: The resource request for the registers region
+ * @phyiface_req: The resource request for the phy/switch region
+ * if available
+ * @phyiface_regs: The ioremapped registers for the phy/switch if available
+ * @ring_base: The base pointer of the dma coherent memory for the rings
+ * @ring_base_dma: The DMA mapped equivalent of ring_base
+ * @tx_ring: The pointer in ring_base of the TX ring
+ * @tx_ring_used: The number of slots in the TX ring which are occupied
+ * @tx_ring_next_slot: The next slot to fill in the TX ring
+ * @tx_ring_dma: The DMA mapped equivalent of tx_ring
+ * @tx_buffers: The sk_buff mappings for the TX ring
+ * @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables
+ * @rx_ring: The pointer in ring_base of the RX ring
+ * @rx_ring_dma: The DMA mapped equivalent of rx_ring
+ * @rx_buffers: The sk_buff mappings for the RX ring
+ * @next_rx_desc_read: The next RX descriptor to read from on IRQ
+ * @msg_enable: The flags for which messages to emit
+ */
+struct ks8695_priv {
+ int in_suspend;
+ struct net_device *ndev;
+ struct device *dev;
+ enum ks8695_dtype dtype;
+ void __iomem *io_regs;
+
+ const char *rx_irq_name, *tx_irq_name, *link_irq_name;
+ int rx_irq, tx_irq, link_irq;
+
+ struct resource *regs_req, *phyiface_req;
+ void __iomem *phyiface_regs;
+
+ void *ring_base;
+ dma_addr_t ring_base_dma;
+
+ struct tx_ring_desc *tx_ring;
+ int tx_ring_used;
+ int tx_ring_next_slot;
+ dma_addr_t tx_ring_dma;
+ struct ks8695_skbuff tx_buffers[MAX_TX_DESC];
+ spinlock_t txq_lock;
+
+ struct rx_ring_desc *rx_ring;
+ dma_addr_t rx_ring_dma;
+ struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
+ int next_rx_desc_read;
+
+ int msg_enable;
+};
+
+/* Register access */
+
+/**
+ * ks8695_readreg - Read from a KS8695 ethernet register
+ * @ksp: The device to read from
+ * @reg: The register to read
+ */
+static inline u32
+ks8695_readreg(struct ks8695_priv *ksp, int reg)
+{
+ return readl(ksp->io_regs + reg);
+}
+
+/**
+ * ks8695_writereg - Write to a KS8695 ethernet register
+ * @ksp: The device to write to
+ * @reg: The register to write
+ * @value: The value to write to the register
+ */
+static inline void
+ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value)
+{
+ writel(value, ksp->io_regs + reg);
+}
+
+/* Utility functions */
+
+/**
+ * ks8695_get_priv - Retrieve the private data from a net_device.
+ * @ndev: The net_device to read the private data pointer from
+ *
+ * Returns the private structure for the KS8695 Ethernet device.
+ */
+static inline struct ks8695_priv *
+ks8695_get_priv(struct net_device *ndev)
+{
+ return ndev->priv;
+}
+
+/**
+ * ks8695_port_type - Retrieve port-type as user-friendly string
+ * @ksp: The device to return the type for
+ *
+ * Returns a string indicating which of the WAN, LAN or HPNA
+ * ports this device is likely to represent.
+ */
+static const char *
+ks8695_port_type(struct ks8695_priv *ksp)
+{
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_LAN:
+ return "LAN";
+ case KS8695_DTYPE_WAN:
+ return "WAN";
+ case KS8695_DTYPE_HPNA:
+ return "HPNA";
+ }
+
+ return "UNKNOWN";
+}
+
+/**
+ * ks8695_update_mac - Update the MAC registers in the device
+ * @ksp: The device to update
+ *
+ * Updates the MAC registers in the KS8695 device from the address in the
+ * net_device structure associated with this interface.
+ */
+static void
+ks8695_update_mac(struct ks8695_priv *ksp)
+{
+ /* Update the HW with the MAC from the net_device */
+ struct net_device *ndev = ksp->ndev;
+ u32 machigh, maclow;
+
+ maclow = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5] << 0));
+ machigh = ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1] << 0));
+
+ ks8695_writereg(ksp, KS8695_MAL, maclow);
+ ks8695_writereg(ksp, KS8695_MAH, machigh);
+
+}
+
+/**
+ * ks8695_refill_rxbuffers - Re-fill the RX buffer ring
+ * @ksp: The device to refill
+ *
+ * Iterates the RX ring of the device looking for empty slots.
+ * For each empty slot, we allocate and map a new SKB and give it
+ * to the hardware.
+ * This can be called from interrupt context safely.
+ */
+static void
+ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
+{
+ /* Run around the RX ring, filling in any missing sk_buff's */
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (!ksp->rx_buffers[buff_n].skb) {
+ struct sk_buff *skb = dev_alloc_skb(MAX_RXBUF_SIZE);
+ dma_addr_t mapping;
+
+ ksp->rx_buffers[buff_n].skb = skb;
+ if (skb == NULL) {
+ /* Failed to allocate one, perhaps
+ * we'll try again later.
+ */
+ break;
+ }
+
+ mapping = dma_map_single(ksp->dev, skb->data,
+ MAX_RXBUF_SIZE,
+ DMA_FROM_DEVICE);
+ if (!mapping) {
+ /* Failed to DMA map this SKB, try later */
+ dev_kfree_skb_irq(skb);
+ ksp->rx_buffers[buff_n].skb = NULL;
+ break;
+ }
+ ksp->rx_buffers[buff_n].dma_ptr = mapping;
+ skb->dev = ksp->ndev;
+ ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
+
+ /* Record this into the DMA ring */
+ ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
+ ksp->rx_ring[buff_n].length =
+ cpu_to_le32(MAX_RXBUF_SIZE);
+
+ wmb();
+
+ /* And give ownership over to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+ }
+ }
+}
+
+/* Maximum number of multicast addresses which the KS8695 HW supports */
+#define KS8695_NR_ADDRESSES 16
+
+/**
+ * ks8695_init_partial_multicast - Init the mcast addr registers
+ * @ksp: The device to initialise
+ * @addr: The multicast address list to use
+ * @nr_addr: The number of addresses in the list
+ *
+ * This routine is a helper for ks8695_set_multicast - it writes
+ * the additional-address registers in the KS8695 ethernet device
+ * and cleans up any others left behind.
+ */
+static void
+ks8695_init_partial_multicast(struct ks8695_priv *ksp,
+ struct dev_mc_list *addr,
+ int nr_addr)
+{
+ u32 low, high;
+ int i;
+
+ for (i = 0; i < nr_addr; i++, addr = addr->next) {
+ /* Ran out of addresses? */
+ if (!addr)
+ break;
+ /* Ran out of space in chip? */
+ if (i == KS8695_NR_ADDRESSES)
+ break;
+
+ low = (addr->dmi_addr[2] << 24) | (addr->dmi_addr[3] << 16) |
+ (addr->dmi_addr[4] << 8) | (addr->dmi_addr[5]);
+ high = (addr->dmi_addr[0] << 8) | (addr->dmi_addr[1]);
+
+ ks8695_writereg(ksp, KS8695_AAL_(i), low);
+ ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high);
+ }
+
+ /* Clear the remaining Additional Station Addresses */
+ for (; i < KS8695_NR_ADDRESSES; i++) {
+ ks8695_writereg(ksp, KS8695_AAL_(i), 0);
+ ks8695_writereg(ksp, KS8695_AAH_(i), 0);
+ }
+}
+
+/* Interrupt handling */
+
+/**
+ * ks8695_tx_irq - Transmit IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the TX ring, clearing out any transmitted slots.
+ * Allows the net_device to pass us new packets once slots are
+ * freed.
+ */
+static irqreturn_t
+ks8695_tx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb &&
+ !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
+ rmb();
+ /* An SKB which is not owned by HW is present */
+ /* Update the stats for the net_device */
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;
+
+ /* Free the packet from the ring */
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Free the sk_buff */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ ksp->tx_ring_used--;
+ }
+ }
+
+ netif_wake_queue(ndev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_rx_irq - Receive IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the RX ring, passing any received packets up to the
+ * host. If we received anything other than errors, we then
+ * refill the ring.
+ */
+static irqreturn_t
+ks8695_rx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ struct sk_buff *skb;
+ int buff_n;
+ u32 flags;
+ int pktlen;
+ int last_rx_processed = -1;
+
+ buff_n = ksp->next_rx_desc_read;
+ do {
+ if (ksp->rx_buffers[buff_n].skb &&
+ !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) {
+ rmb();
+ flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
+ /* Found an SKB which we own, this means we
+ * received a packet
+ */
+ if ((flags & (RDES_FS | RDES_LS)) !=
+ (RDES_FS | RDES_LS)) {
+ /* This packet is not the first and
+ * the last segment. Therefore it is
+ * a "spanning" packet and we can't
+ * handle it
+ */
+ goto rx_failure;
+ }
+
+ if (flags & (RDES_ES | RDES_RE)) {
+ /* It's an error packet */
+ ndev->stats.rx_errors++;
+ if (flags & RDES_TL)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_RF)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_CE)
+ ndev->stats.rx_crc_errors++;
+ if (flags & RDES_RE)
+ ndev->stats.rx_missed_errors++;
+
+ goto rx_failure;
+ }
+
+ pktlen = flags & RDES_FLEN;
+ pktlen -= 4; /* Drop the CRC */
+
+ /* Retrieve the sk_buff */
+ skb = ksp->rx_buffers[buff_n].skb;
+
+ /* Clear it from the ring */
+ ksp->rx_buffers[buff_n].skb = NULL;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+
+ /* Relinquish the SKB to the network layer */
+ skb_put(skb, pktlen);
+ skb->protocol = eth_type_trans(skb, ndev);
+ netif_rx(skb);
+
+ /* Record stats */
+ ndev->last_rx = jiffies;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += pktlen;
+ goto rx_finished;
+
+rx_failure:
+ /* This ring entry is an error, but we can
+ * re-use the skb
+ */
+ /* Give the ring entry back to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+rx_finished:
+ /* And note this as processed so we can start
+ * from here next time
+ */
+ last_rx_processed = buff_n;
+ } else {
+ /* Ran out of things to process, stop now */
+ break;
+ }
+ buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
+ } while (buff_n != ksp->next_rx_desc_read);
+
+ /* And note which RX descriptor we last did anything with */
+ if (likely(last_rx_processed != -1))
+ ksp->next_rx_desc_read =
+ (last_rx_processed + 1) & MAX_RX_DESC_MASK;
+
+ /* And refill the buffers */
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Kick the RX DMA engine, in case it became suspended */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_link_irq - Link change IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * The WAN interface can generate an IRQ when the link changes,
+ * report this to the net layer and the user.
+ */
+static irqreturn_t
+ks8695_link_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if (ctrl & WMC_WLS) {
+ netif_carrier_on(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev,
+ "%s: Link is now up (10%sMbps/%s-duplex)\n",
+ ndev->name,
+ (ctrl & WMC_WSS) ? "0" : "",
+ (ctrl & WMC_WDS) ? "Full" : "Half");
+ } else {
+ netif_carrier_off(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev, "%s: Link is now down.\n",
+ ndev->name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+/* KS8695 Device functions */
+
+/**
+ * ks8695_reset - Reset a KS8695 ethernet interface
+ * @ksp: The interface to reset
+ *
+ * Perform an engine reset of the interface and re-program it
+ * with sensible defaults.
+ */
+static void
+ks8695_reset(struct ks8695_priv *ksp)
+{
+ int reset_timeout = watchdog;
+ /* Issue the reset via the TX DMA control register */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
+ while (reset_timeout--) {
+ if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
+ break;
+ msleep(1);
+ }
+
+ if (reset_timeout == 0) {
+ dev_crit(ksp->dev,
+ "Timeout waiting for DMA engines to reset\n");
+ /* And blithely carry on */
+ }
+
+ /* Definitely wait long enough before attempting to program
+ * the engines
+ */
+ msleep(10);
+
+ /* RX: unicast and broadcast */
+ ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB);
+ /* TX: pad and add CRC */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC);
+}
+
+/**
+ * ks8695_shutdown - Shut down a KS8695 ethernet interface
+ * @ksp: The interface to shut down
+ *
+ * This disables packet RX/TX, cleans up IRQs, drains the rings,
+ * and basically places the interface into a clean shutdown
+ * state.
+ */
+static void
+ks8695_shutdown(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+ int buff_n;
+
+ /* Disable packet transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE);
+
+ /* Disable packet reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE);
+
+ /* Release the IRQs */
+ free_irq(ksp->rx_irq, ksp->ndev);
+ free_irq(ksp->tx_irq, ksp->ndev);
+ if (ksp->link_irq != -1)
+ free_irq(ksp->link_irq, ksp->ndev);
+
+ /* Throw away any pending TX packets */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb) {
+ /* Remove this SKB from the TX ring */
+ ksp->tx_ring[buff_n].owner = 0;
+ ksp->tx_ring[buff_n].status = 0;
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin this SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ }
+ }
+
+ /* Purge the RX buffers */
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (ksp->rx_buffers[buff_n].skb) {
+ /* Remove the SKB from the RX ring */
+ ksp->rx_ring[buff_n].status = 0;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb);
+ ksp->rx_buffers[buff_n].skb = NULL;
+ }
+ }
+}
+
+
+/**
+ * ks8695_setup_irq - IRQ setup helper function
+ * @irq: The IRQ number to claim
+ * @irq_name: The name to give the IRQ claimant
+ * @handler: The function to call to handle the IRQ
+ * @ndev: The net_device to pass in as the dev_id argument to the handler
+ *
+ * Return 0 on success.
+ */
+static int
+ks8695_setup_irq(int irq, const char *irq_name,
+ irq_handler_t handler, struct net_device *ndev)
+{
+ int ret;
+
+ ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);
+
+ if (ret) {
+ dev_err(&ndev->dev, "failure to request IRQ %d\n", irq);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_init_net - Initialise a KS8695 ethernet interface
+ * @ksp: The interface to initialise
+ *
+ * This routine fills the RX ring, initialises the DMA engines,
+ * allocates the IRQs and then starts the packet TX and RX
+ * engines.
+ */
+static int
+ks8695_init_net(struct ks8695_priv *ksp)
+{
+ int ret;
+ u32 ctrl;
+
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Initialise the DMA engines */
+ ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
+ ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
+
+ /* Request the IRQs */
+ ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
+ ks8695_rx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
+ ks8695_tx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ if (ksp->link_irq != -1) {
+ ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
+ ks8695_link_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ }
+
+ /* Set up the ring indices */
+ ksp->next_rx_desc_read = 0;
+ ksp->tx_ring_next_slot = 0;
+ ksp->tx_ring_used = 0;
+
+ /* Bring up transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ /* Enable packet transmission */
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
+
+ /* Bring up the reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ /* Enable packet reception */
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
+ /* And start the DMA engine */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ /* All done */
+ return 0;
+}
+
+/**
+ * ks8695_release_device - HW resource release for KS8695 e-net
+ * @ksp: The device to be freed
+ *
+ * This unallocates io memory regions, dma-coherent regions etc
+ * which were allocated in ks8695_probe.
+ */
+static void
+ks8695_release_device(struct ks8695_priv *ksp)
+{
+ /* Unmap the registers */
+ iounmap(ksp->io_regs);
+ if (ksp->phyiface_regs)
+ iounmap(ksp->phyiface_regs);
+
+ /* And release the request */
+ release_resource(ksp->regs_req);
+ kfree(ksp->regs_req);
+ if (ksp->phyiface_req) {
+ release_resource(ksp->phyiface_req);
+ kfree(ksp->phyiface_req);
+ }
+
+ /* Free the ring buffers */
+ dma_free_coherent(ksp->dev, RING_DMA_SIZE,
+ ksp->ring_base, ksp->ring_base_dma);
+}
+
+/* Ethtool support */
+
+/**
+ * ks8695_get_msglevel - Get the messages enabled for emission
+ * @ndev: The network device to read from
+ */
+static u32
+ks8695_get_msglevel(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ return ksp->msg_enable;
+}
+
+/**
+ * ks8695_set_msglevel - Set the messages enabled for emission
+ * @ndev: The network device to configure
+ * @value: The messages to set for emission
+ */
+static void
+ks8695_set_msglevel(struct net_device *ndev, u32 value)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ ksp->msg_enable = value;
+}
+
+/**
+ * ks8695_get_settings - Get device-specific settings.
+ * @ndev: The network device to read settings from
+ * @cmd: The ethtool structure to read into
+ */
+static int
+ks8695_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ /* All ports on the KS8695 support these... */
+ cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+ SUPPORTED_TP | SUPPORTED_MII);
+ cmd->transceiver = XCVR_INTERNAL;
+
+ /* Port specific extras */
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ cmd->phy_address = 0;
+ /* not supported for HPNA */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ /* BUG: Erm, dtype hpna implies no phy regs */
+ /*
+ ctrl = readl(KS8695_MISC_VA + KS8695_HMC);
+ cmd->speed = (ctrl & HMC_HSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & HMC_HDS) ? DUPLEX_FULL : DUPLEX_HALF;
+ */
+ return -EOPNOTSUPP;
+ case KS8695_DTYPE_WAN:
+ cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
+ cmd->port = PORT_MII;
+ cmd->supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause);
+ cmd->phy_address = 0;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if ((ctrl & WMC_WAND) == 0) {
+ /* auto-negotiation is enabled */
+ cmd->advertising |= ADVERTISED_Autoneg;
+ if (ctrl & WMC_WANA100F)
+ cmd->advertising |= ADVERTISED_100baseT_Full;
+ if (ctrl & WMC_WANA100H)
+ cmd->advertising |= ADVERTISED_100baseT_Half;
+ if (ctrl & WMC_WANA10F)
+ cmd->advertising |= ADVERTISED_10baseT_Full;
+ if (ctrl & WMC_WANA10H)
+ cmd->advertising |= ADVERTISED_10baseT_Half;
+ if (ctrl & WMC_WANAP)
+ cmd->advertising |= ADVERTISED_Pause;
+ cmd->autoneg = AUTONEG_ENABLE;
+
+ cmd->speed = (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WDS) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ } else {
+ /* auto-negotiation is disabled */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ cmd->speed = (ctrl & WMC_WANF100) ?
+ SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WANFF) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ }
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_set_settings - Set device-specific settings.
+ * @ndev: The network device to configure
+ * @cmd: The settings to configure
+ */
+static int
+ks8695_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ if ((cmd->speed != SPEED_10) && (cmd->speed != SPEED_100))
+ return -EINVAL;
+ if ((cmd->duplex != DUPLEX_HALF) && (cmd->duplex != DUPLEX_FULL))
+ return -EINVAL;
+ if (cmd->port != PORT_MII)
+ return -EINVAL;
+ if (cmd->transceiver != XCVR_INTERNAL)
+ return -EINVAL;
+ if ((cmd->autoneg != AUTONEG_DISABLE) &&
+ (cmd->autoneg != AUTONEG_ENABLE))
+ return -EINVAL;
+
+ if (cmd->autoneg == AUTONEG_ENABLE) {
+ if ((cmd->advertising & (ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full)) == 0)
+ return -EINVAL;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA does not support auto-negotiation. */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+ if (cmd->advertising & ADVERTISED_100baseT_Full)
+ ctrl |= WMC_WANA100F;
+ if (cmd->advertising & ADVERTISED_100baseT_Half)
+ ctrl |= WMC_WANA100H;
+ if (cmd->advertising & ADVERTISED_10baseT_Full)
+ ctrl |= WMC_WANA10F;
+ if (cmd->advertising & ADVERTISED_10baseT_Half)
+ ctrl |= WMC_WANA10H;
+
+ /* force a re-negotiation */
+ ctrl |= WMC_WANR;
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ } else {
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* BUG: dtype_hpna implies no phy registers */
+ /*
+ ctrl = __raw_readl(KS8695_MISC_VA + KS8695_HMC);
+
+ ctrl &= ~(HMC_HSS | HMC_HDS);
+ if (cmd->speed == SPEED_100)
+ ctrl |= HMC_HSS;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= HMC_HDS;
+
+ __raw_writel(ctrl, KS8695_MISC_VA + KS8695_HMC);
+ */
+ return -EOPNOTSUPP;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* disable auto-negotiation */
+ ctrl |= WMC_WAND;
+ ctrl &= ~(WMC_WANF100 | WMC_WANFF);
+
+ if (cmd->speed == SPEED_100)
+ ctrl |= WMC_WANF100;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= WMC_WANFF;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_nwayreset - Restart the autonegotiation on the port.
+ * @ndev: The network device to restart autoneotiation on
+ */
+static int
+ks8695_nwayreset(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy means no autonegotiation on hpna */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ if ((ctrl & WMC_WAND) == 0)
+ writel(ctrl | WMC_WANR,
+ ksp->phyiface_regs + KS8695_WMC);
+ else
+ /* auto-negotiation not enabled */
+ return -EINVAL;
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_get_link - Retrieve link status of network interface
+ * @ndev: The network interface to retrive the link status of.
+ */
+static u32
+ks8695_get_link(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA always has link */
+ return 1;
+ case KS8695_DTYPE_WAN:
+ /* WAN we can read the PHY for */
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ return ctrl & WMC_WLS;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/**
+ * ks8695_get_pause - Retrieve network pause/flow-control advertising
+ * @ndev: The device to retrieve settings from
+ * @param: The structure to fill out with the information
+ */
+static void
+ks8695_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy link on hpna to configure */
+ return;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* advertise Pause */
+ param->autoneg = (ctrl & WMC_WANAP);
+
+ /* current Rx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ param->rx_pause = (ctrl & DRXC_RFCE);
+
+ /* current Tx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ param->tx_pause = (ctrl & DTXC_TFCE);
+ break;
+ case KS8695_DTYPE_LAN:
+ /* The LAN's "phy" is a direct-attached switch */
+ return;
+ }
+}
+
+/**
+ * ks8695_set_pause - Configure pause/flow-control
+ * @ndev: The device to configure
+ * @param: The pause parameters to set
+ *
+ * TODO: Implement this
+ */
+static int
+ks8695_set_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ return -EOPNOTSUPP;
+}
+
+/**
+ * ks8695_get_drvinfo - Retrieve driver information
+ * @ndev: The network device to retrieve info about
+ * @info: The info structure to fill out.
+ */
+static void
+ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, MODULENAME, sizeof(info->driver));
+ strlcpy(info->version, MODULEVERSION, sizeof(info->version));
+ strlcpy(info->bus_info, ndev->dev.parent->bus_id,
+ sizeof(info->bus_info));
+}
+
+static struct ethtool_ops ks8695_ethtool_ops = {
+ .get_msglevel = ks8695_get_msglevel,
+ .set_msglevel = ks8695_set_msglevel,
+ .get_settings = ks8695_get_settings,
+ .set_settings = ks8695_set_settings,
+ .nway_reset = ks8695_nwayreset,
+ .get_link = ks8695_get_link,
+ .get_pauseparam = ks8695_get_pause,
+ .set_pauseparam = ks8695_set_pause,
+ .get_drvinfo = ks8695_get_drvinfo,
+};
+
+/* Network device interface functions */
+
+/**
+ * ks8695_set_mac - Update MAC in net dev and HW
+ * @ndev: The network device to update
+ * @addr: The new MAC address to set
+ */
+static int
+ks8695_set_mac(struct net_device *ndev, void *addr)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+
+ ks8695_update_mac(ksp);
+
+ dev_dbg(ksp->dev, "%s: Updated MAC address to %pM\n",
+ ndev->name, ndev->dev_addr);
+
+ return 0;
+}
+
+/**
+ * ks8695_set_multicast - Set up the multicast behaviour of the interface
+ * @ndev: The net_device to configure
+ *
+ * This routine, called by the net layer, configures promiscuity
+ * and multicast reception behaviour for the interface.
+ */
+static void
+ks8695_set_multicast(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+
+ if (ndev->flags & IFF_PROMISC) {
+ /* enable promiscuous mode */
+ ctrl |= DRXC_RA;
+ } else if (ndev->flags & ~IFF_PROMISC) {
+ /* disable promiscuous mode */
+ ctrl &= ~DRXC_RA;
+ }
+
+ if (ndev->flags & IFF_ALLMULTI) {
+ /* enable all multicast mode */
+ ctrl |= DRXC_RM;
+ } else if (ndev->mc_count > KS8695_NR_ADDRESSES) {
+ /* more specific multicast addresses than can be
+ * handled in hardware
+ */
+ ctrl |= DRXC_RM;
+ } else if (ndev->mc_count > 0) {
+ /* enable specific multicasts */
+ ctrl &= ~DRXC_RM;
+ ks8695_init_partial_multicast(ksp, ndev->mc_list,
+ ndev->mc_count);
+ } else if (ndev->flags & ~IFF_ALLMULTI) {
+ /* disable multicast mode */
+ ctrl &= ~DRXC_RM;
+ ks8695_init_partial_multicast(ksp, NULL, 0);
+ }
+
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl);
+}
+
+/**
+ * ks8695_timeout - Handle a network tx/rx timeout.
+ * @ndev: The net_device which timed out.
+ *
+ * A network transaction timed out, reset the device.
+ */
+static void
+ks8695_timeout(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ ks8695_shutdown(ksp);
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ /* We ignore the return from this since it managed to init
+ * before it probably will be okay to init again.
+ */
+ ks8695_init_net(ksp);
+
+ /* Reconfigure promiscuity etc */
+ ks8695_set_multicast(ndev);
+
+ /* And start the TX queue once more */
+ netif_start_queue(ndev);
+}
+
+/**
+ * ks8695_start_xmit - Start a packet transmission
+ * @skb: The packet to transmit
+ * @ndev: The network device to send the packet on
+ *
+ * This routine, called by the net layer, takes ownership of the
+ * sk_buff and adds it to the TX ring. It then kicks the TX DMA
+ * engine to ensure transmission begins.
+ */
+static int
+ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int buff_n;
+
+ spin_lock_irq(&ksp->txq_lock);
+
+ if (ksp->tx_ring_used == MAX_TX_DESC) {
+ /* Somehow we got entered when we have no room */
+ spin_unlock_irq(&ksp->txq_lock);
+ return NETDEV_TX_BUSY;
+ }
+
+ buff_n = ksp->tx_ring_next_slot;
+
+ BUG_ON(ksp->tx_buffers[buff_n].skb);
+
+ ksp->tx_buffers[buff_n].dma_ptr = dma_map_single(
+ ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
+ if (!ksp->tx_buffers[buff_n].dma_ptr) {
+ /* Failed to DMA map this SKB, give it back for now */
+ spin_unlock_irq(&ksp->txq_lock);
+ dev_dbg(ksp->dev, "%s: Could not map DMA memory for "\
+ "transmission, trying later\n", ndev->name);
+ return NETDEV_TX_BUSY;
+ }
+ /* Mapped okay, store the buffer pointer and length for later */
+ ksp->tx_buffers[buff_n].skb = skb;
+ ksp->tx_buffers[buff_n].length = skb->len;
+
+ /* Fill out the TX descriptor */
+ ksp->tx_ring[buff_n].data_ptr =
+ cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
+ ksp->tx_ring[buff_n].status =
+ cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
+ (skb->len & TDES_TBS));
+
+ wmb();
+
+ /* Hand it over to the hardware */
+ ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);
+
+ if (++ksp->tx_ring_used == MAX_TX_DESC)
+ netif_stop_queue(ndev);
+
+ ndev->trans_start = jiffies;
+
+ /* Kick the TX DMA in case it decided to go IDLE */
+ ks8695_writereg(ksp, KS8695_DTSC, 0);
+
+ /* And update the next ring slot */
+ ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK;
+
+ spin_unlock_irq(&ksp->txq_lock);
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ks8695_stop - Stop (shutdown) a KS8695 ethernet interface
+ * @ndev: The net_device to stop
+ *
+ * This disables the TX queue and cleans up a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_stop(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+
+ ks8695_shutdown(ksp);
+
+ return 0;
+}
+
+/**
+ * ks8695_open - Open (bring up) a KS8695 ethernet interface
+ * @ndev: The net_device to open
+ *
+ * This resets, configures the MAC, initialises the RX ring and
+ * DMA engines and starts the TX queue for a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_open(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int ret;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ ret = ks8695_init_net(ksp);
+ if (ret) {
+ ks8695_shutdown(ksp);
+ return ret;
+ }
+
+ netif_start_queue(ndev);
+
+ return 0;
+}
+
+/* Platform device driver */
+
+/**
+ * ks8695_init_switch - Init LAN switch to known good defaults.
+ * @ksp: The device to initialise
+ *
+ * This initialises the LAN switch in the KS8695 to a known-good
+ * set of defaults.
+ */
+static void __devinit
+ks8695_init_switch(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Default value for SEC0 according to datasheet */
+ ctrl = 0x40819e00;
+
+ /* LED0 = Speed LED1 = Link/Activity */
+ ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S);
+ ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY);
+
+ /* Enable Switch */
+ ctrl |= SEC0_ENABLE;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_SEC0);
+
+ /* Defaults for SEC1 */
+ writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
+}
+
+/**
+ * ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults
+ * @ksp: The device to initialise
+ *
+ * This initialises a KS8695's WAN phy to sensible values for
+ * autonegotiation etc.
+ */
+static void __devinit
+ks8695_init_wan_phy(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Support auto-negotiation */
+ ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+
+ /* LED0 = Activity , LED1 = Link */
+ ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK);
+
+ /* Restart Auto-negotiation */
+ ctrl |= WMC_WANR;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+
+ writel(0, ksp->phyiface_regs + KS8695_WPPM);
+ writel(0, ksp->phyiface_regs + KS8695_PPS);
+}
+
+static const struct net_device_ops ks8695_netdev_ops = {
+ .ndo_open = ks8695_open,
+ .ndo_stop = ks8695_stop,
+ .ndo_start_xmit = ks8695_start_xmit,
+ .ndo_tx_timeout = ks8695_timeout,
+ .ndo_set_mac_address = ks8695_set_mac,
+ .ndo_set_multicast_list = ks8695_set_multicast,
+};
+
+/**
+ * ks8695_probe - Probe and initialise a KS8695 ethernet interface
+ * @pdev: The platform device to probe
+ *
+ * Initialise a KS8695 ethernet device from platform data.
+ *
+ * This driver requires at least one IORESOURCE_MEM for the
+ * registers and two IORESOURCE_IRQ for the RX and TX IRQs
+ * respectively. It can optionally take an additional
+ * IORESOURCE_MEM for the switch or phy in the case of the lan or
+ * wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan
+ * port.
+ */
+static int __devinit
+ks8695_probe(struct platform_device *pdev)
+{
+ struct ks8695_priv *ksp;
+ struct net_device *ndev;
+ struct resource *regs_res, *phyiface_res;
+ struct resource *rxirq_res, *txirq_res, *linkirq_res;
+ int ret = 0;
+ int buff_n;
+ u32 machigh, maclow;
+
+ /* Initialise a net_device */
+ ndev = alloc_etherdev(sizeof(struct ks8695_priv));
+ if (!ndev) {
+ dev_err(&pdev->dev, "could not allocate device.\n");
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ dev_dbg(&pdev->dev, "ks8695_probe() called\n");
+
+ /* Configure our private structure a little */
+ ksp = netdev_priv(ndev);
+ memset(ksp, 0, sizeof(struct ks8695_priv));
+
+ ksp->dev = &pdev->dev;
+ ksp->ndev = ndev;
+ ksp->msg_enable = NETIF_MSG_LINK;
+
+ /* Retrieve resources */
+ regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+
+ if (!(regs_res && rxirq_res && txirq_res)) {
+ dev_err(ksp->dev, "insufficient resources\n");
+ ret = -ENOENT;
+ goto failure;
+ }
+
+ ksp->regs_req = request_mem_region(regs_res->start,
+ resource_size(regs_res),
+ pdev->name);
+
+ if (!ksp->regs_req) {
+ dev_err(ksp->dev, "cannot claim register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res));
+
+ if (!ksp->io_regs) {
+ dev_err(ksp->dev, "failed to ioremap registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ if (phyiface_res) {
+ ksp->phyiface_req =
+ request_mem_region(phyiface_res->start,
+ resource_size(phyiface_res),
+ phyiface_res->name);
+
+ if (!ksp->phyiface_req) {
+ dev_err(ksp->dev,
+ "cannot claim switch register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->phyiface_regs = ioremap(phyiface_res->start,
+ resource_size(phyiface_res));
+
+ if (!ksp->phyiface_regs) {
+ dev_err(ksp->dev,
+ "failed to ioremap switch registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+ }
+
+ ksp->rx_irq = rxirq_res->start;
+ ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX";
+ ksp->tx_irq = txirq_res->start;
+ ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX";
+ ksp->link_irq = (linkirq_res ? linkirq_res->start : -1);
+ ksp->link_irq_name = (linkirq_res && linkirq_res->name) ?
+ linkirq_res->name : "Ethernet Link";
+
+ /* driver system setup */
+ ndev->netdev_ops = &ks8695_netdev_ops;
+ SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+ ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+
+ /* Retrieve the default MAC addr from the chip. */
+ /* The bootloader should have left it in there for us. */
+
+ machigh = ks8695_readreg(ksp, KS8695_MAH);
+ maclow = ks8695_readreg(ksp, KS8695_MAL);
+
+ ndev->dev_addr[0] = (machigh >> 8) & 0xFF;
+ ndev->dev_addr[1] = machigh & 0xFF;
+ ndev->dev_addr[2] = (maclow >> 24) & 0xFF;
+ ndev->dev_addr[3] = (maclow >> 16) & 0xFF;
+ ndev->dev_addr[4] = (maclow >> 8) & 0xFF;
+ ndev->dev_addr[5] = maclow & 0xFF;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please "
+ "set using ifconfig\n", ndev->name);
+
+ /* In order to be efficient memory-wise, we allocate both
+ * rings in one go.
+ */
+ ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE,
+ &ksp->ring_base_dma, GFP_KERNEL);
+ if (!ksp->ring_base) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ /* Specify the TX DMA ring buffer */
+ ksp->tx_ring = ksp->ring_base;
+ ksp->tx_ring_dma = ksp->ring_base_dma;
+
+ /* And initialise the queue's lock */
+ spin_lock_init(&ksp->txq_lock);
+
+ /* Specify the RX DMA ring buffer */
+ ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
+ ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE;
+
+ /* Zero the descriptor rings */
+ memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE);
+ memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE);
+
+ /* Build the rings */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ ksp->tx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->tx_ring_dma +
+ (sizeof(struct tx_ring_desc) *
+ ((buff_n + 1) & MAX_TX_DESC_MASK)));
+ }
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ ksp->rx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->rx_ring_dma +
+ (sizeof(struct rx_ring_desc) *
+ ((buff_n + 1) & MAX_RX_DESC_MASK)));
+ }
+
+ /* Initialise the port (physically) */
+ if (ksp->phyiface_regs && ksp->link_irq == -1) {
+ ks8695_init_switch(ksp);
+ ksp->dtype = KS8695_DTYPE_LAN;
+ } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
+ ks8695_init_wan_phy(ksp);
+ ksp->dtype = KS8695_DTYPE_WAN;
+ } else {
+ /* No initialisation since HPNA does not have a PHY */
+ ksp->dtype = KS8695_DTYPE_HPNA;
+ }
+
+ /* And bring up the net_device with the net core */
+ platform_set_drvdata(pdev, ndev);
+ ret = register_netdev(ndev);
+
+ if (ret == 0) {
+ dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n",
+ ks8695_port_type(ksp), ndev->dev_addr);
+ } else {
+ /* Report the failure to register the net_device */
+ dev_err(ksp->dev, "ks8695net: failed to register netdev.\n");
+ goto failure;
+ }
+
+ /* All is well */
+ return 0;
+
+ /* Error exit path */
+failure:
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ return ret;
+}
+
+/**
+ * ks8695_drv_suspend - Suspend a KS8695 ethernet platform device.
+ * @pdev: The device to suspend
+ * @state: The suspend state
+ *
+ * This routine detaches and shuts down a KS8695 ethernet device.
+ */
+static int
+ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ ksp->in_suspend = 1;
+
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ ks8695_shutdown(ksp);
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_resume - Resume a KS8695 ethernet platform device.
+ * @pdev: The device to resume
+ *
+ * This routine re-initialises and re-attaches a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_drv_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ ks8695_reset(ksp);
+ ks8695_init_net(ksp);
+ ks8695_set_multicast(ndev);
+ netif_device_attach(ndev);
+ }
+
+ ksp->in_suspend = 0;
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_remove - Remove a KS8695 net device on driver unload.
+ * @pdev: The platform device to remove
+ *
+ * This unregisters and releases a KS8695 ethernet device.
+ */
+static int __devexit
+ks8695_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(ndev);
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ dev_dbg(&pdev->dev, "released and freed device\n");
+ return 0;
+}
+
+static struct platform_driver ks8695_driver = {
+ .driver = {
+ .name = MODULENAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ks8695_probe,
+ .remove = __devexit_p(ks8695_drv_remove),
+ .suspend = ks8695_drv_suspend,
+ .resume = ks8695_drv_resume,
+};
+
+/* Module interface */
+
+static int __init
+ks8695_init(void)
+{
+ printk(KERN_INFO "%s Ethernet driver, V%s\n",
+ MODULENAME, MODULEVERSION);
+
+ return platform_driver_register(&ks8695_driver);
+}
+
+static void __exit
+ks8695_cleanup(void)
+{
+ platform_driver_unregister(&ks8695_driver);
+}
+
+module_init(ks8695_init);
+module_exit(ks8695_cleanup);
+
+MODULE_AUTHOR("Simtec Electronics")
+MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MODULENAME);
+
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
Index: net-next-2.6/drivers/net/arm/ks8695net.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-next-2.6/drivers/net/arm/ks8695net.h 2008-12-08 12:56:10.281158951 +0000
@@ -0,0 +1,107 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#ifndef KS8695NET_H
+#define KS8695NET_H
+
+/* Receive descriptor flags */
+#define RDES_OWN (1 << 31) /* Ownership */
+#define RDES_FS (1 << 30) /* First Descriptor */
+#define RDES_LS (1 << 29) /* Last Descriptor */
+#define RDES_IPE (1 << 28) /* IP Checksum error */
+#define RDES_TCPE (1 << 27) /* TCP Checksum error */
+#define RDES_UDPE (1 << 26) /* UDP Checksum error */
+#define RDES_ES (1 << 25) /* Error summary */
+#define RDES_MF (1 << 24) /* Multicast Frame */
+#define RDES_RE (1 << 19) /* MII Error reported */
+#define RDES_TL (1 << 18) /* Frame too Long */
+#define RDES_RF (1 << 17) /* Runt Frame */
+#define RDES_CE (1 << 16) /* CRC error */
+#define RDES_FT (1 << 15) /* Frame Type */
+#define RDES_FLEN (0x7ff) /* Frame Length */
+
+#define RDES_RER (1 << 25) /* Receive End of Ring */
+#define RDES_RBS (0x7ff) /* Receive Buffer Size */
+
+/* Transmit descriptor flags */
+
+#define TDES_OWN (1 << 31) /* Ownership */
+
+#define TDES_IC (1 << 31) /* Interrupt on Completion */
+#define TDES_FS (1 << 30) /* First Segment */
+#define TDES_LS (1 << 29) /* Last Segment */
+#define TDES_IPCKG (1 << 28) /* IP Checksum generate */
+#define TDES_TCPCKG (1 << 27) /* TCP Checksum generate */
+#define TDES_UDPCKG (1 << 26) /* UDP Checksum generate */
+#define TDES_TER (1 << 25) /* Transmit End of Ring */
+#define TDES_TBS (0x7ff) /* Transmit Buffer Size */
+
+/*
+ * Network controller register offsets
+ */
+#define KS8695_DTXC (0x00) /* DMA Transmit Control */
+#define KS8695_DRXC (0x04) /* DMA Receive Control */
+#define KS8695_DTSC (0x08) /* DMA Transmit Start Command */
+#define KS8695_DRSC (0x0c) /* DMA Receive Start Command */
+#define KS8695_TDLB (0x10) /* Transmit Descriptor List
+ * Base Address
+ */
+#define KS8695_RDLB (0x14) /* Receive Descriptor List
+ * Base Address
+ */
+#define KS8695_MAL (0x18) /* MAC Station Address Low */
+#define KS8695_MAH (0x1c) /* MAC Station Address High */
+#define KS8695_AAL_(n) (0x80 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) Low
+ */
+#define KS8695_AAH_(n) (0x84 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) High
+ */
+
+
+/* DMA Transmit Control Register */
+#define DTXC_TRST (1 << 31) /* Soft Reset */
+#define DTXC_TBS (0x3f << 24) /* Transmit Burst Size */
+#define DTXC_TUCG (1 << 18) /* Transmit UDP
+ * Checksum Generate
+ */
+#define DTXC_TTCG (1 << 17) /* Transmit TCP
+ * Checksum Generate
+ */
+#define DTXC_TICG (1 << 16) /* Transmit IP
+ * Checksum Generate
+ */
+#define DTXC_TFCE (1 << 9) /* Transmit Flow
+ * Control Enable
+ */
+#define DTXC_TLB (1 << 8) /* Loopback mode */
+#define DTXC_TEP (1 << 2) /* Transmit Enable Padding */
+#define DTXC_TAC (1 << 1) /* Transmit Add CRC */
+#define DTXC_TE (1 << 0) /* TX Enable */
+
+/* DMA Receive Control Register */
+#define DRXC_RBS (0x3f << 24) /* Receive Burst Size */
+#define DRXC_RUCC (1 << 18) /* Receive UDP Checksum check */
+#define DRXC_RTCG (1 << 17) /* Receive TCP Checksum check */
+#define DRXC_RICG (1 << 16) /* Receive IP Checksum check */
+#define DRXC_RFCE (1 << 9) /* Receive Flow Control
+ * Enable
+ */
+#define DRXC_RB (1 << 6) /* Receive Broadcast */
+#define DRXC_RM (1 << 5) /* Receive Multicast */
+#define DRXC_RU (1 << 4) /* Receive Unicast */
+#define DRXC_RERR (1 << 3) /* Receive Error Frame */
+#define DRXC_RA (1 << 2) /* Receive All */
+#define DRXC_RE (1 << 0) /* RX Enable */
+
+/* Additional Station Address High */
+#define AAH_E (1 << 31) /* Address Enabled */
+
+#endif /* KS8695NET_H */
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 integrated ethernet driver
[not found] ` <1228764912.3177.39.camel@achroite>
@ 2008-12-09 17:30 ` Daniel Silverstone
2008-12-09 18:10 ` Ben Hutchings
2008-12-10 7:05 ` David Miller
0 siblings, 2 replies; 16+ messages in thread
From: Daniel Silverstone @ 2008-12-09 17:30 UTC (permalink / raw)
To: Ben Hutchings; +Cc: netdev
[-- Attachment #1: Type: text/plain, Size: 4068 bytes --]
Ben pointed out that he had sent this to me but not CCed the list, and
that I'd missed it. I've worked through it now.
On Mon, 2008-12-08 at 19:35 +0000, Ben Hutchings wrote:
> There's a spelling error in your subject; it should say "integrated".
That's just a spello on the mailing list subject line, the subject line
in the patch is okay.
> On Mon, 2008-12-08 at 13:08 +0000, Daniel Silverstone wrote:
> [...]
> > +#define MODULENAME "ks8695_ether"
> Shouldn't this be "ks8695net"?
I was replacing Andrew Victor's ks8695_ether driver, so the platform
data is for ks8695_ether, but for a while I had both drivers in-tree in
order to check behaviour. I am happy for it to be merged either as
ks8695_ether or ks8695net and if necessary I can change the platform
data for the boards. However I was trying for path of least resistance
on this particular front.
> > +/*
> > + * Transmit timeout, default 5 seconds.
> > + */
> > +static int watchdog = 5000;
> The comment should say this is also the reset timeout.
Yep, changed, thanks.
> > + * enum ks8695_dtype - Device type
> > + * @KS8695_DTYPE_WAN: This device is a WAN interface
> > + * @KS8695_DTYPE_LAN: This device is a LAN interface
> > + * @KS8695_DTYPE_HPNA: This device is an HPNA interfacew
> Typo: there's a rogue "w" on the end.
Argh, fixed.
> > +static inline struct ks8695_priv *
> > +ks8695_get_priv(struct net_device *ndev)
> > +{
> > + return ndev->priv;
> > +}
> This function needs to be removed.
Yes, I did the netdev_priv() change but forgot to remove it once I had
compile-and-run-tested the kernel. I have removed it now, thanks.
> > +static void
> > +ks8695_init_partial_multicast(struct ks8695_priv *ksp,
> > + /* Ran out of space in chip? */
> > + if (i == KS8695_NR_ADDRESSES)
> > + break;
> Currently the caller already checks that you won't run out of space, so
> you can use BUG_ON() here. Certainly I don't think this should quietly
> ignore the error.
Accepted. I'm still getting used to BUG_ON etc. Changed.
> > + if (ndev->flags & IFF_ALLMULTI) {
> > + /* enable all multicast mode */
> > + ctrl |= DRXC_RM;
> > + } else if (ndev->mc_count > KS8695_NR_ADDRESSES) {
> > + /* more specific multicast addresses than can be
> > + * handled in hardware
> > + */
> > + ctrl |= DRXC_RM;
> > + } else if (ndev->mc_count > 0) {
> > + /* enable specific multicasts */
> > + ctrl &= ~DRXC_RM;
> > + ks8695_init_partial_multicast(ksp, ndev->mc_list,
> > + ndev->mc_count);
> > + } else if (ndev->flags & ~IFF_ALLMULTI) {
> > + /* disable multicast mode */
> > + ctrl &= ~DRXC_RM;
> > + ks8695_init_partial_multicast(ksp, NULL, 0);
> > + }
> [...]
>
> This last condition is bogus. I think it's supposed to mean
> !(ndev->flags & IFF_ALLMULTI) but that will always be true here.
>
> Further, I think the code for ndev->mc_count > 0 will also work for
> ndev->mc_count == 0, so you could remove both conditions and the last
> block.
Yep, makes sense. That'll teach me to blindly go "and now this case, and
now this case, and now this case" without trying to compress the cases.
Done.
New patch attached, which, regardless of my previous assertion, I *now*
believe covers everything (providing you're happy about the driver name
malarky). To celebrate this, I even changed the version a bit so we can
be sure we're all reviewing the same code.
Thanks again, and hopefully this will be good enough for merging. Sorry
for the repeated posting of patches, but I think I'm learning.
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
[-- Attachment #2: Type: text/x-patch, Size: 51818 bytes --]
KS8695: Add support for the KS8695 ethernet devices.
Implements the KS8695 ethernet device (ks8695net).
This driver is only of use on the KS8695 which is an ARM9 based SoC. The
documentation on this SoC is sparse and poor, with barely a register
description and a rough outline of how the ethernet works, this driver was
therefore written with strong reference to the Micrel supplied Linux 2.6.9
port, and to Andrew Victor's ks8695eth driver.
Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
Acked-by: Ben Dooks <ben@simtec.co.uk>
Kconfig | 8
Makefile | 1
ks8695net.c | 1674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ks8695net.h | 107 +++
4 files changed, 1790 insertions(+)
Index: net-next-2.6/drivers/net/arm/Kconfig
===================================================================
--- net-next-2.6.orig/drivers/net/arm/Kconfig 2008-12-08 12:55:46.817658739 +0000
+++ net-next-2.6/drivers/net/arm/Kconfig 2008-12-08 12:56:10.213660241 +0000
@@ -40,6 +40,14 @@
If you wish to compile a kernel for the AT91RM9200 and enable
ethernet support, then you should always answer Y to this.
+config ARM_KS8695_ETHER
+ tristate "KS8695 Ethernet support"
+ depends on ARM && ARCH_KS8695
+ select MII
+ help
+ If you wish to compile a kernel for the KS8695 and want to
+ use the internal ethernet then you should answer Y to this.
+
config EP93XX_ETH
tristate "EP93xx Ethernet support"
depends on ARM && ARCH_EP93XX
Index: net-next-2.6/drivers/net/arm/Makefile
===================================================================
--- net-next-2.6.orig/drivers/net/arm/Makefile 2008-12-08 12:55:46.841662533 +0000
+++ net-next-2.6/drivers/net/arm/Makefile 2008-12-08 12:56:10.213660241 +0000
@@ -8,5 +8,6 @@
obj-$(CONFIG_ARM_ETHER3) += ether3.o
obj-$(CONFIG_ARM_ETHER1) += ether1.o
obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
+obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
Index: net-next-2.6/drivers/net/arm/ks8695net.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-next-2.6/drivers/net/arm/ks8695net.c 2008-12-09 17:26:28.469658828 +0000
@@ -0,0 +1,1674 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+
+#include <mach/regs-switch.h>
+#include <mach/regs-misc.h>
+
+#include "ks8695net.h"
+
+#define MODULENAME "ks8695_ether"
+#define MODULEVERSION "1.01"
+
+/*
+ * Transmit and device reset timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+
+/* Hardware structures */
+
+/**
+ * struct rx_ring_desc - Receive descriptor ring element
+ * @status: The status of the descriptor element (E.g. who owns it)
+ * @length: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct rx_ring_desc {
+ __le32 status;
+ __le32 length;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct tx_ring_desc - Transmit descriptor ring element
+ * @owner: Who owns the descriptor
+ * @status: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct tx_ring_desc {
+ __le32 owner;
+ __le32 status;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct ks8695_skbuff - sk_buff wrapper for rx/tx rings.
+ * @skb: The buffer in the ring
+ * @dma_ptr: The mapped DMA pointer of the buffer
+ * @length: The number of bytes mapped to dma_ptr
+ */
+struct ks8695_skbuff {
+ struct sk_buff *skb;
+ dma_addr_t dma_ptr;
+ u32 length;
+};
+
+/* Private device structure */
+
+#define MAX_TX_DESC 8
+#define MAX_TX_DESC_MASK 0x7
+#define MAX_RX_DESC 16
+#define MAX_RX_DESC_MASK 0xf
+
+#define MAX_RXBUF_SIZE 0x700
+
+#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
+#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC)
+#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)
+
+/**
+ * enum ks8695_dtype - Device type
+ * @KS8695_DTYPE_WAN: This device is a WAN interface
+ * @KS8695_DTYPE_LAN: This device is a LAN interface
+ * @KS8695_DTYPE_HPNA: This device is an HPNA interface
+ */
+enum ks8695_dtype {
+ KS8695_DTYPE_WAN,
+ KS8695_DTYPE_LAN,
+ KS8695_DTYPE_HPNA,
+};
+
+/**
+ * struct ks8695_priv - Private data for the KS8695 Ethernet
+ * @in_suspend: Flag to indicate if we're suspending/resuming
+ * @ndev: The net_device for this interface
+ * @dev: The platform device object for this interface
+ * @dtype: The type of this device
+ * @io_regs: The ioremapped registers for this interface
+ * @rx_irq_name: The textual name of the RX IRQ from the platform data
+ * @tx_irq_name: The textual name of the TX IRQ from the platform data
+ * @link_irq_name: The textual name of the link IRQ from the
+ * platform data if available
+ * @rx_irq: The IRQ number for the RX IRQ
+ * @tx_irq: The IRQ number for the TX IRQ
+ * @link_irq: The IRQ number for the link IRQ if available
+ * @regs_req: The resource request for the registers region
+ * @phyiface_req: The resource request for the phy/switch region
+ * if available
+ * @phyiface_regs: The ioremapped registers for the phy/switch if available
+ * @ring_base: The base pointer of the dma coherent memory for the rings
+ * @ring_base_dma: The DMA mapped equivalent of ring_base
+ * @tx_ring: The pointer in ring_base of the TX ring
+ * @tx_ring_used: The number of slots in the TX ring which are occupied
+ * @tx_ring_next_slot: The next slot to fill in the TX ring
+ * @tx_ring_dma: The DMA mapped equivalent of tx_ring
+ * @tx_buffers: The sk_buff mappings for the TX ring
+ * @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables
+ * @rx_ring: The pointer in ring_base of the RX ring
+ * @rx_ring_dma: The DMA mapped equivalent of rx_ring
+ * @rx_buffers: The sk_buff mappings for the RX ring
+ * @next_rx_desc_read: The next RX descriptor to read from on IRQ
+ * @msg_enable: The flags for which messages to emit
+ */
+struct ks8695_priv {
+ int in_suspend;
+ struct net_device *ndev;
+ struct device *dev;
+ enum ks8695_dtype dtype;
+ void __iomem *io_regs;
+
+ const char *rx_irq_name, *tx_irq_name, *link_irq_name;
+ int rx_irq, tx_irq, link_irq;
+
+ struct resource *regs_req, *phyiface_req;
+ void __iomem *phyiface_regs;
+
+ void *ring_base;
+ dma_addr_t ring_base_dma;
+
+ struct tx_ring_desc *tx_ring;
+ int tx_ring_used;
+ int tx_ring_next_slot;
+ dma_addr_t tx_ring_dma;
+ struct ks8695_skbuff tx_buffers[MAX_TX_DESC];
+ spinlock_t txq_lock;
+
+ struct rx_ring_desc *rx_ring;
+ dma_addr_t rx_ring_dma;
+ struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
+ int next_rx_desc_read;
+
+ int msg_enable;
+};
+
+/* Register access */
+
+/**
+ * ks8695_readreg - Read from a KS8695 ethernet register
+ * @ksp: The device to read from
+ * @reg: The register to read
+ */
+static inline u32
+ks8695_readreg(struct ks8695_priv *ksp, int reg)
+{
+ return readl(ksp->io_regs + reg);
+}
+
+/**
+ * ks8695_writereg - Write to a KS8695 ethernet register
+ * @ksp: The device to write to
+ * @reg: The register to write
+ * @value: The value to write to the register
+ */
+static inline void
+ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value)
+{
+ writel(value, ksp->io_regs + reg);
+}
+
+/* Utility functions */
+
+/**
+ * ks8695_port_type - Retrieve port-type as user-friendly string
+ * @ksp: The device to return the type for
+ *
+ * Returns a string indicating which of the WAN, LAN or HPNA
+ * ports this device is likely to represent.
+ */
+static const char *
+ks8695_port_type(struct ks8695_priv *ksp)
+{
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_LAN:
+ return "LAN";
+ case KS8695_DTYPE_WAN:
+ return "WAN";
+ case KS8695_DTYPE_HPNA:
+ return "HPNA";
+ }
+
+ return "UNKNOWN";
+}
+
+/**
+ * ks8695_update_mac - Update the MAC registers in the device
+ * @ksp: The device to update
+ *
+ * Updates the MAC registers in the KS8695 device from the address in the
+ * net_device structure associated with this interface.
+ */
+static void
+ks8695_update_mac(struct ks8695_priv *ksp)
+{
+ /* Update the HW with the MAC from the net_device */
+ struct net_device *ndev = ksp->ndev;
+ u32 machigh, maclow;
+
+ maclow = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5] << 0));
+ machigh = ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1] << 0));
+
+ ks8695_writereg(ksp, KS8695_MAL, maclow);
+ ks8695_writereg(ksp, KS8695_MAH, machigh);
+
+}
+
+/**
+ * ks8695_refill_rxbuffers - Re-fill the RX buffer ring
+ * @ksp: The device to refill
+ *
+ * Iterates the RX ring of the device looking for empty slots.
+ * For each empty slot, we allocate and map a new SKB and give it
+ * to the hardware.
+ * This can be called from interrupt context safely.
+ */
+static void
+ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
+{
+ /* Run around the RX ring, filling in any missing sk_buff's */
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (!ksp->rx_buffers[buff_n].skb) {
+ struct sk_buff *skb = dev_alloc_skb(MAX_RXBUF_SIZE);
+ dma_addr_t mapping;
+
+ ksp->rx_buffers[buff_n].skb = skb;
+ if (skb == NULL) {
+ /* Failed to allocate one, perhaps
+ * we'll try again later.
+ */
+ break;
+ }
+
+ mapping = dma_map_single(ksp->dev, skb->data,
+ MAX_RXBUF_SIZE,
+ DMA_FROM_DEVICE);
+ if (!mapping) {
+ /* Failed to DMA map this SKB, try later */
+ dev_kfree_skb_irq(skb);
+ ksp->rx_buffers[buff_n].skb = NULL;
+ break;
+ }
+ ksp->rx_buffers[buff_n].dma_ptr = mapping;
+ skb->dev = ksp->ndev;
+ ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
+
+ /* Record this into the DMA ring */
+ ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
+ ksp->rx_ring[buff_n].length =
+ cpu_to_le32(MAX_RXBUF_SIZE);
+
+ wmb();
+
+ /* And give ownership over to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+ }
+ }
+}
+
+/* Maximum number of multicast addresses which the KS8695 HW supports */
+#define KS8695_NR_ADDRESSES 16
+
+/**
+ * ks8695_init_partial_multicast - Init the mcast addr registers
+ * @ksp: The device to initialise
+ * @addr: The multicast address list to use
+ * @nr_addr: The number of addresses in the list
+ *
+ * This routine is a helper for ks8695_set_multicast - it writes
+ * the additional-address registers in the KS8695 ethernet device
+ * and cleans up any others left behind.
+ */
+static void
+ks8695_init_partial_multicast(struct ks8695_priv *ksp,
+ struct dev_mc_list *addr,
+ int nr_addr)
+{
+ u32 low, high;
+ int i;
+
+ for (i = 0; i < nr_addr; i++, addr = addr->next) {
+ /* Ran out of addresses? */
+ if (!addr)
+ break;
+ /* Ran out of space in chip? */
+ BUG_ON(i == KS8695_NR_ADDRESSES);
+
+ low = (addr->dmi_addr[2] << 24) | (addr->dmi_addr[3] << 16) |
+ (addr->dmi_addr[4] << 8) | (addr->dmi_addr[5]);
+ high = (addr->dmi_addr[0] << 8) | (addr->dmi_addr[1]);
+
+ ks8695_writereg(ksp, KS8695_AAL_(i), low);
+ ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high);
+ }
+
+ /* Clear the remaining Additional Station Addresses */
+ for (; i < KS8695_NR_ADDRESSES; i++) {
+ ks8695_writereg(ksp, KS8695_AAL_(i), 0);
+ ks8695_writereg(ksp, KS8695_AAH_(i), 0);
+ }
+}
+
+/* Interrupt handling */
+
+/**
+ * ks8695_tx_irq - Transmit IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the TX ring, clearing out any transmitted slots.
+ * Allows the net_device to pass us new packets once slots are
+ * freed.
+ */
+static irqreturn_t
+ks8695_tx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb &&
+ !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
+ rmb();
+ /* An SKB which is not owned by HW is present */
+ /* Update the stats for the net_device */
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;
+
+ /* Free the packet from the ring */
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Free the sk_buff */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ ksp->tx_ring_used--;
+ }
+ }
+
+ netif_wake_queue(ndev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_rx_irq - Receive IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the RX ring, passing any received packets up to the
+ * host. If we received anything other than errors, we then
+ * refill the ring.
+ */
+static irqreturn_t
+ks8695_rx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ struct sk_buff *skb;
+ int buff_n;
+ u32 flags;
+ int pktlen;
+ int last_rx_processed = -1;
+
+ buff_n = ksp->next_rx_desc_read;
+ do {
+ if (ksp->rx_buffers[buff_n].skb &&
+ !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) {
+ rmb();
+ flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
+ /* Found an SKB which we own, this means we
+ * received a packet
+ */
+ if ((flags & (RDES_FS | RDES_LS)) !=
+ (RDES_FS | RDES_LS)) {
+ /* This packet is not the first and
+ * the last segment. Therefore it is
+ * a "spanning" packet and we can't
+ * handle it
+ */
+ goto rx_failure;
+ }
+
+ if (flags & (RDES_ES | RDES_RE)) {
+ /* It's an error packet */
+ ndev->stats.rx_errors++;
+ if (flags & RDES_TL)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_RF)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_CE)
+ ndev->stats.rx_crc_errors++;
+ if (flags & RDES_RE)
+ ndev->stats.rx_missed_errors++;
+
+ goto rx_failure;
+ }
+
+ pktlen = flags & RDES_FLEN;
+ pktlen -= 4; /* Drop the CRC */
+
+ /* Retrieve the sk_buff */
+ skb = ksp->rx_buffers[buff_n].skb;
+
+ /* Clear it from the ring */
+ ksp->rx_buffers[buff_n].skb = NULL;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+
+ /* Relinquish the SKB to the network layer */
+ skb_put(skb, pktlen);
+ skb->protocol = eth_type_trans(skb, ndev);
+ netif_rx(skb);
+
+ /* Record stats */
+ ndev->last_rx = jiffies;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += pktlen;
+ goto rx_finished;
+
+rx_failure:
+ /* This ring entry is an error, but we can
+ * re-use the skb
+ */
+ /* Give the ring entry back to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+rx_finished:
+ /* And note this as processed so we can start
+ * from here next time
+ */
+ last_rx_processed = buff_n;
+ } else {
+ /* Ran out of things to process, stop now */
+ break;
+ }
+ buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
+ } while (buff_n != ksp->next_rx_desc_read);
+
+ /* And note which RX descriptor we last did anything with */
+ if (likely(last_rx_processed != -1))
+ ksp->next_rx_desc_read =
+ (last_rx_processed + 1) & MAX_RX_DESC_MASK;
+
+ /* And refill the buffers */
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Kick the RX DMA engine, in case it became suspended */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_link_irq - Link change IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * The WAN interface can generate an IRQ when the link changes,
+ * report this to the net layer and the user.
+ */
+static irqreturn_t
+ks8695_link_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if (ctrl & WMC_WLS) {
+ netif_carrier_on(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev,
+ "%s: Link is now up (10%sMbps/%s-duplex)\n",
+ ndev->name,
+ (ctrl & WMC_WSS) ? "0" : "",
+ (ctrl & WMC_WDS) ? "Full" : "Half");
+ } else {
+ netif_carrier_off(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev, "%s: Link is now down.\n",
+ ndev->name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+/* KS8695 Device functions */
+
+/**
+ * ks8695_reset - Reset a KS8695 ethernet interface
+ * @ksp: The interface to reset
+ *
+ * Perform an engine reset of the interface and re-program it
+ * with sensible defaults.
+ */
+static void
+ks8695_reset(struct ks8695_priv *ksp)
+{
+ int reset_timeout = watchdog;
+ /* Issue the reset via the TX DMA control register */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
+ while (reset_timeout--) {
+ if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
+ break;
+ msleep(1);
+ }
+
+ if (reset_timeout == 0) {
+ dev_crit(ksp->dev,
+ "Timeout waiting for DMA engines to reset\n");
+ /* And blithely carry on */
+ }
+
+ /* Definitely wait long enough before attempting to program
+ * the engines
+ */
+ msleep(10);
+
+ /* RX: unicast and broadcast */
+ ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB);
+ /* TX: pad and add CRC */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC);
+}
+
+/**
+ * ks8695_shutdown - Shut down a KS8695 ethernet interface
+ * @ksp: The interface to shut down
+ *
+ * This disables packet RX/TX, cleans up IRQs, drains the rings,
+ * and basically places the interface into a clean shutdown
+ * state.
+ */
+static void
+ks8695_shutdown(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+ int buff_n;
+
+ /* Disable packet transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE);
+
+ /* Disable packet reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE);
+
+ /* Release the IRQs */
+ free_irq(ksp->rx_irq, ksp->ndev);
+ free_irq(ksp->tx_irq, ksp->ndev);
+ if (ksp->link_irq != -1)
+ free_irq(ksp->link_irq, ksp->ndev);
+
+ /* Throw away any pending TX packets */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb) {
+ /* Remove this SKB from the TX ring */
+ ksp->tx_ring[buff_n].owner = 0;
+ ksp->tx_ring[buff_n].status = 0;
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin this SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ }
+ }
+
+ /* Purge the RX buffers */
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (ksp->rx_buffers[buff_n].skb) {
+ /* Remove the SKB from the RX ring */
+ ksp->rx_ring[buff_n].status = 0;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb);
+ ksp->rx_buffers[buff_n].skb = NULL;
+ }
+ }
+}
+
+
+/**
+ * ks8695_setup_irq - IRQ setup helper function
+ * @irq: The IRQ number to claim
+ * @irq_name: The name to give the IRQ claimant
+ * @handler: The function to call to handle the IRQ
+ * @ndev: The net_device to pass in as the dev_id argument to the handler
+ *
+ * Return 0 on success.
+ */
+static int
+ks8695_setup_irq(int irq, const char *irq_name,
+ irq_handler_t handler, struct net_device *ndev)
+{
+ int ret;
+
+ ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);
+
+ if (ret) {
+ dev_err(&ndev->dev, "failure to request IRQ %d\n", irq);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_init_net - Initialise a KS8695 ethernet interface
+ * @ksp: The interface to initialise
+ *
+ * This routine fills the RX ring, initialises the DMA engines,
+ * allocates the IRQs and then starts the packet TX and RX
+ * engines.
+ */
+static int
+ks8695_init_net(struct ks8695_priv *ksp)
+{
+ int ret;
+ u32 ctrl;
+
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Initialise the DMA engines */
+ ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
+ ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
+
+ /* Request the IRQs */
+ ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
+ ks8695_rx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
+ ks8695_tx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ if (ksp->link_irq != -1) {
+ ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
+ ks8695_link_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ }
+
+ /* Set up the ring indices */
+ ksp->next_rx_desc_read = 0;
+ ksp->tx_ring_next_slot = 0;
+ ksp->tx_ring_used = 0;
+
+ /* Bring up transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ /* Enable packet transmission */
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
+
+ /* Bring up the reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ /* Enable packet reception */
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
+ /* And start the DMA engine */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ /* All done */
+ return 0;
+}
+
+/**
+ * ks8695_release_device - HW resource release for KS8695 e-net
+ * @ksp: The device to be freed
+ *
+ * This unallocates io memory regions, dma-coherent regions etc
+ * which were allocated in ks8695_probe.
+ */
+static void
+ks8695_release_device(struct ks8695_priv *ksp)
+{
+ /* Unmap the registers */
+ iounmap(ksp->io_regs);
+ if (ksp->phyiface_regs)
+ iounmap(ksp->phyiface_regs);
+
+ /* And release the request */
+ release_resource(ksp->regs_req);
+ kfree(ksp->regs_req);
+ if (ksp->phyiface_req) {
+ release_resource(ksp->phyiface_req);
+ kfree(ksp->phyiface_req);
+ }
+
+ /* Free the ring buffers */
+ dma_free_coherent(ksp->dev, RING_DMA_SIZE,
+ ksp->ring_base, ksp->ring_base_dma);
+}
+
+/* Ethtool support */
+
+/**
+ * ks8695_get_msglevel - Get the messages enabled for emission
+ * @ndev: The network device to read from
+ */
+static u32
+ks8695_get_msglevel(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ return ksp->msg_enable;
+}
+
+/**
+ * ks8695_set_msglevel - Set the messages enabled for emission
+ * @ndev: The network device to configure
+ * @value: The messages to set for emission
+ */
+static void
+ks8695_set_msglevel(struct net_device *ndev, u32 value)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ ksp->msg_enable = value;
+}
+
+/**
+ * ks8695_get_settings - Get device-specific settings.
+ * @ndev: The network device to read settings from
+ * @cmd: The ethtool structure to read into
+ */
+static int
+ks8695_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ /* All ports on the KS8695 support these... */
+ cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+ SUPPORTED_TP | SUPPORTED_MII);
+ cmd->transceiver = XCVR_INTERNAL;
+
+ /* Port specific extras */
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ cmd->phy_address = 0;
+ /* not supported for HPNA */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ /* BUG: Erm, dtype hpna implies no phy regs */
+ /*
+ ctrl = readl(KS8695_MISC_VA + KS8695_HMC);
+ cmd->speed = (ctrl & HMC_HSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & HMC_HDS) ? DUPLEX_FULL : DUPLEX_HALF;
+ */
+ return -EOPNOTSUPP;
+ case KS8695_DTYPE_WAN:
+ cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
+ cmd->port = PORT_MII;
+ cmd->supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause);
+ cmd->phy_address = 0;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if ((ctrl & WMC_WAND) == 0) {
+ /* auto-negotiation is enabled */
+ cmd->advertising |= ADVERTISED_Autoneg;
+ if (ctrl & WMC_WANA100F)
+ cmd->advertising |= ADVERTISED_100baseT_Full;
+ if (ctrl & WMC_WANA100H)
+ cmd->advertising |= ADVERTISED_100baseT_Half;
+ if (ctrl & WMC_WANA10F)
+ cmd->advertising |= ADVERTISED_10baseT_Full;
+ if (ctrl & WMC_WANA10H)
+ cmd->advertising |= ADVERTISED_10baseT_Half;
+ if (ctrl & WMC_WANAP)
+ cmd->advertising |= ADVERTISED_Pause;
+ cmd->autoneg = AUTONEG_ENABLE;
+
+ cmd->speed = (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WDS) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ } else {
+ /* auto-negotiation is disabled */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ cmd->speed = (ctrl & WMC_WANF100) ?
+ SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WANFF) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ }
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_set_settings - Set device-specific settings.
+ * @ndev: The network device to configure
+ * @cmd: The settings to configure
+ */
+static int
+ks8695_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ if ((cmd->speed != SPEED_10) && (cmd->speed != SPEED_100))
+ return -EINVAL;
+ if ((cmd->duplex != DUPLEX_HALF) && (cmd->duplex != DUPLEX_FULL))
+ return -EINVAL;
+ if (cmd->port != PORT_MII)
+ return -EINVAL;
+ if (cmd->transceiver != XCVR_INTERNAL)
+ return -EINVAL;
+ if ((cmd->autoneg != AUTONEG_DISABLE) &&
+ (cmd->autoneg != AUTONEG_ENABLE))
+ return -EINVAL;
+
+ if (cmd->autoneg == AUTONEG_ENABLE) {
+ if ((cmd->advertising & (ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full)) == 0)
+ return -EINVAL;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA does not support auto-negotiation. */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+ if (cmd->advertising & ADVERTISED_100baseT_Full)
+ ctrl |= WMC_WANA100F;
+ if (cmd->advertising & ADVERTISED_100baseT_Half)
+ ctrl |= WMC_WANA100H;
+ if (cmd->advertising & ADVERTISED_10baseT_Full)
+ ctrl |= WMC_WANA10F;
+ if (cmd->advertising & ADVERTISED_10baseT_Half)
+ ctrl |= WMC_WANA10H;
+
+ /* force a re-negotiation */
+ ctrl |= WMC_WANR;
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ } else {
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* BUG: dtype_hpna implies no phy registers */
+ /*
+ ctrl = __raw_readl(KS8695_MISC_VA + KS8695_HMC);
+
+ ctrl &= ~(HMC_HSS | HMC_HDS);
+ if (cmd->speed == SPEED_100)
+ ctrl |= HMC_HSS;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= HMC_HDS;
+
+ __raw_writel(ctrl, KS8695_MISC_VA + KS8695_HMC);
+ */
+ return -EOPNOTSUPP;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* disable auto-negotiation */
+ ctrl |= WMC_WAND;
+ ctrl &= ~(WMC_WANF100 | WMC_WANFF);
+
+ if (cmd->speed == SPEED_100)
+ ctrl |= WMC_WANF100;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= WMC_WANFF;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_nwayreset - Restart the autonegotiation on the port.
+ * @ndev: The network device to restart autoneotiation on
+ */
+static int
+ks8695_nwayreset(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy means no autonegotiation on hpna */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ if ((ctrl & WMC_WAND) == 0)
+ writel(ctrl | WMC_WANR,
+ ksp->phyiface_regs + KS8695_WMC);
+ else
+ /* auto-negotiation not enabled */
+ return -EINVAL;
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_get_link - Retrieve link status of network interface
+ * @ndev: The network interface to retrive the link status of.
+ */
+static u32
+ks8695_get_link(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA always has link */
+ return 1;
+ case KS8695_DTYPE_WAN:
+ /* WAN we can read the PHY for */
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ return ctrl & WMC_WLS;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/**
+ * ks8695_get_pause - Retrieve network pause/flow-control advertising
+ * @ndev: The device to retrieve settings from
+ * @param: The structure to fill out with the information
+ */
+static void
+ks8695_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy link on hpna to configure */
+ return;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* advertise Pause */
+ param->autoneg = (ctrl & WMC_WANAP);
+
+ /* current Rx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ param->rx_pause = (ctrl & DRXC_RFCE);
+
+ /* current Tx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ param->tx_pause = (ctrl & DTXC_TFCE);
+ break;
+ case KS8695_DTYPE_LAN:
+ /* The LAN's "phy" is a direct-attached switch */
+ return;
+ }
+}
+
+/**
+ * ks8695_set_pause - Configure pause/flow-control
+ * @ndev: The device to configure
+ * @param: The pause parameters to set
+ *
+ * TODO: Implement this
+ */
+static int
+ks8695_set_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ return -EOPNOTSUPP;
+}
+
+/**
+ * ks8695_get_drvinfo - Retrieve driver information
+ * @ndev: The network device to retrieve info about
+ * @info: The info structure to fill out.
+ */
+static void
+ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, MODULENAME, sizeof(info->driver));
+ strlcpy(info->version, MODULEVERSION, sizeof(info->version));
+ strlcpy(info->bus_info, ndev->dev.parent->bus_id,
+ sizeof(info->bus_info));
+}
+
+static struct ethtool_ops ks8695_ethtool_ops = {
+ .get_msglevel = ks8695_get_msglevel,
+ .set_msglevel = ks8695_set_msglevel,
+ .get_settings = ks8695_get_settings,
+ .set_settings = ks8695_set_settings,
+ .nway_reset = ks8695_nwayreset,
+ .get_link = ks8695_get_link,
+ .get_pauseparam = ks8695_get_pause,
+ .set_pauseparam = ks8695_set_pause,
+ .get_drvinfo = ks8695_get_drvinfo,
+};
+
+/* Network device interface functions */
+
+/**
+ * ks8695_set_mac - Update MAC in net dev and HW
+ * @ndev: The network device to update
+ * @addr: The new MAC address to set
+ */
+static int
+ks8695_set_mac(struct net_device *ndev, void *addr)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+
+ ks8695_update_mac(ksp);
+
+ dev_dbg(ksp->dev, "%s: Updated MAC address to %pM\n",
+ ndev->name, ndev->dev_addr);
+
+ return 0;
+}
+
+/**
+ * ks8695_set_multicast - Set up the multicast behaviour of the interface
+ * @ndev: The net_device to configure
+ *
+ * This routine, called by the net layer, configures promiscuity
+ * and multicast reception behaviour for the interface.
+ */
+static void
+ks8695_set_multicast(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+
+ if (ndev->flags & IFF_PROMISC) {
+ /* enable promiscuous mode */
+ ctrl |= DRXC_RA;
+ } else if (ndev->flags & ~IFF_PROMISC) {
+ /* disable promiscuous mode */
+ ctrl &= ~DRXC_RA;
+ }
+
+ if (ndev->flags & IFF_ALLMULTI) {
+ /* enable all multicast mode */
+ ctrl |= DRXC_RM;
+ } else if (ndev->mc_count > KS8695_NR_ADDRESSES) {
+ /* more specific multicast addresses than can be
+ * handled in hardware
+ */
+ ctrl |= DRXC_RM;
+ } else {
+ /* enable specific multicasts */
+ ctrl &= ~DRXC_RM;
+ ks8695_init_partial_multicast(ksp, ndev->mc_list,
+ ndev->mc_count);
+ }
+
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl);
+}
+
+/**
+ * ks8695_timeout - Handle a network tx/rx timeout.
+ * @ndev: The net_device which timed out.
+ *
+ * A network transaction timed out, reset the device.
+ */
+static void
+ks8695_timeout(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ ks8695_shutdown(ksp);
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ /* We ignore the return from this since it managed to init
+ * before it probably will be okay to init again.
+ */
+ ks8695_init_net(ksp);
+
+ /* Reconfigure promiscuity etc */
+ ks8695_set_multicast(ndev);
+
+ /* And start the TX queue once more */
+ netif_start_queue(ndev);
+}
+
+/**
+ * ks8695_start_xmit - Start a packet transmission
+ * @skb: The packet to transmit
+ * @ndev: The network device to send the packet on
+ *
+ * This routine, called by the net layer, takes ownership of the
+ * sk_buff and adds it to the TX ring. It then kicks the TX DMA
+ * engine to ensure transmission begins.
+ */
+static int
+ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int buff_n;
+
+ spin_lock_irq(&ksp->txq_lock);
+
+ if (ksp->tx_ring_used == MAX_TX_DESC) {
+ /* Somehow we got entered when we have no room */
+ spin_unlock_irq(&ksp->txq_lock);
+ return NETDEV_TX_BUSY;
+ }
+
+ buff_n = ksp->tx_ring_next_slot;
+
+ BUG_ON(ksp->tx_buffers[buff_n].skb);
+
+ ksp->tx_buffers[buff_n].dma_ptr = dma_map_single(
+ ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
+ if (!ksp->tx_buffers[buff_n].dma_ptr) {
+ /* Failed to DMA map this SKB, give it back for now */
+ spin_unlock_irq(&ksp->txq_lock);
+ dev_dbg(ksp->dev, "%s: Could not map DMA memory for "\
+ "transmission, trying later\n", ndev->name);
+ return NETDEV_TX_BUSY;
+ }
+ /* Mapped okay, store the buffer pointer and length for later */
+ ksp->tx_buffers[buff_n].skb = skb;
+ ksp->tx_buffers[buff_n].length = skb->len;
+
+ /* Fill out the TX descriptor */
+ ksp->tx_ring[buff_n].data_ptr =
+ cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
+ ksp->tx_ring[buff_n].status =
+ cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
+ (skb->len & TDES_TBS));
+
+ wmb();
+
+ /* Hand it over to the hardware */
+ ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);
+
+ if (++ksp->tx_ring_used == MAX_TX_DESC)
+ netif_stop_queue(ndev);
+
+ ndev->trans_start = jiffies;
+
+ /* Kick the TX DMA in case it decided to go IDLE */
+ ks8695_writereg(ksp, KS8695_DTSC, 0);
+
+ /* And update the next ring slot */
+ ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK;
+
+ spin_unlock_irq(&ksp->txq_lock);
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ks8695_stop - Stop (shutdown) a KS8695 ethernet interface
+ * @ndev: The net_device to stop
+ *
+ * This disables the TX queue and cleans up a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_stop(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+
+ ks8695_shutdown(ksp);
+
+ return 0;
+}
+
+/**
+ * ks8695_open - Open (bring up) a KS8695 ethernet interface
+ * @ndev: The net_device to open
+ *
+ * This resets, configures the MAC, initialises the RX ring and
+ * DMA engines and starts the TX queue for a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_open(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int ret;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ ret = ks8695_init_net(ksp);
+ if (ret) {
+ ks8695_shutdown(ksp);
+ return ret;
+ }
+
+ netif_start_queue(ndev);
+
+ return 0;
+}
+
+/* Platform device driver */
+
+/**
+ * ks8695_init_switch - Init LAN switch to known good defaults.
+ * @ksp: The device to initialise
+ *
+ * This initialises the LAN switch in the KS8695 to a known-good
+ * set of defaults.
+ */
+static void __devinit
+ks8695_init_switch(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Default value for SEC0 according to datasheet */
+ ctrl = 0x40819e00;
+
+ /* LED0 = Speed LED1 = Link/Activity */
+ ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S);
+ ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY);
+
+ /* Enable Switch */
+ ctrl |= SEC0_ENABLE;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_SEC0);
+
+ /* Defaults for SEC1 */
+ writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
+}
+
+/**
+ * ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults
+ * @ksp: The device to initialise
+ *
+ * This initialises a KS8695's WAN phy to sensible values for
+ * autonegotiation etc.
+ */
+static void __devinit
+ks8695_init_wan_phy(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Support auto-negotiation */
+ ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+
+ /* LED0 = Activity , LED1 = Link */
+ ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK);
+
+ /* Restart Auto-negotiation */
+ ctrl |= WMC_WANR;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+
+ writel(0, ksp->phyiface_regs + KS8695_WPPM);
+ writel(0, ksp->phyiface_regs + KS8695_PPS);
+}
+
+static const struct net_device_ops ks8695_netdev_ops = {
+ .ndo_open = ks8695_open,
+ .ndo_stop = ks8695_stop,
+ .ndo_start_xmit = ks8695_start_xmit,
+ .ndo_tx_timeout = ks8695_timeout,
+ .ndo_set_mac_address = ks8695_set_mac,
+ .ndo_set_multicast_list = ks8695_set_multicast,
+};
+
+/**
+ * ks8695_probe - Probe and initialise a KS8695 ethernet interface
+ * @pdev: The platform device to probe
+ *
+ * Initialise a KS8695 ethernet device from platform data.
+ *
+ * This driver requires at least one IORESOURCE_MEM for the
+ * registers and two IORESOURCE_IRQ for the RX and TX IRQs
+ * respectively. It can optionally take an additional
+ * IORESOURCE_MEM for the switch or phy in the case of the lan or
+ * wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan
+ * port.
+ */
+static int __devinit
+ks8695_probe(struct platform_device *pdev)
+{
+ struct ks8695_priv *ksp;
+ struct net_device *ndev;
+ struct resource *regs_res, *phyiface_res;
+ struct resource *rxirq_res, *txirq_res, *linkirq_res;
+ int ret = 0;
+ int buff_n;
+ u32 machigh, maclow;
+
+ /* Initialise a net_device */
+ ndev = alloc_etherdev(sizeof(struct ks8695_priv));
+ if (!ndev) {
+ dev_err(&pdev->dev, "could not allocate device.\n");
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ dev_dbg(&pdev->dev, "ks8695_probe() called\n");
+
+ /* Configure our private structure a little */
+ ksp = netdev_priv(ndev);
+ memset(ksp, 0, sizeof(struct ks8695_priv));
+
+ ksp->dev = &pdev->dev;
+ ksp->ndev = ndev;
+ ksp->msg_enable = NETIF_MSG_LINK;
+
+ /* Retrieve resources */
+ regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+
+ if (!(regs_res && rxirq_res && txirq_res)) {
+ dev_err(ksp->dev, "insufficient resources\n");
+ ret = -ENOENT;
+ goto failure;
+ }
+
+ ksp->regs_req = request_mem_region(regs_res->start,
+ resource_size(regs_res),
+ pdev->name);
+
+ if (!ksp->regs_req) {
+ dev_err(ksp->dev, "cannot claim register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res));
+
+ if (!ksp->io_regs) {
+ dev_err(ksp->dev, "failed to ioremap registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ if (phyiface_res) {
+ ksp->phyiface_req =
+ request_mem_region(phyiface_res->start,
+ resource_size(phyiface_res),
+ phyiface_res->name);
+
+ if (!ksp->phyiface_req) {
+ dev_err(ksp->dev,
+ "cannot claim switch register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->phyiface_regs = ioremap(phyiface_res->start,
+ resource_size(phyiface_res));
+
+ if (!ksp->phyiface_regs) {
+ dev_err(ksp->dev,
+ "failed to ioremap switch registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+ }
+
+ ksp->rx_irq = rxirq_res->start;
+ ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX";
+ ksp->tx_irq = txirq_res->start;
+ ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX";
+ ksp->link_irq = (linkirq_res ? linkirq_res->start : -1);
+ ksp->link_irq_name = (linkirq_res && linkirq_res->name) ?
+ linkirq_res->name : "Ethernet Link";
+
+ /* driver system setup */
+ ndev->netdev_ops = &ks8695_netdev_ops;
+ SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+ ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+
+ /* Retrieve the default MAC addr from the chip. */
+ /* The bootloader should have left it in there for us. */
+
+ machigh = ks8695_readreg(ksp, KS8695_MAH);
+ maclow = ks8695_readreg(ksp, KS8695_MAL);
+
+ ndev->dev_addr[0] = (machigh >> 8) & 0xFF;
+ ndev->dev_addr[1] = machigh & 0xFF;
+ ndev->dev_addr[2] = (maclow >> 24) & 0xFF;
+ ndev->dev_addr[3] = (maclow >> 16) & 0xFF;
+ ndev->dev_addr[4] = (maclow >> 8) & 0xFF;
+ ndev->dev_addr[5] = maclow & 0xFF;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please "
+ "set using ifconfig\n", ndev->name);
+
+ /* In order to be efficient memory-wise, we allocate both
+ * rings in one go.
+ */
+ ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE,
+ &ksp->ring_base_dma, GFP_KERNEL);
+ if (!ksp->ring_base) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ /* Specify the TX DMA ring buffer */
+ ksp->tx_ring = ksp->ring_base;
+ ksp->tx_ring_dma = ksp->ring_base_dma;
+
+ /* And initialise the queue's lock */
+ spin_lock_init(&ksp->txq_lock);
+
+ /* Specify the RX DMA ring buffer */
+ ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
+ ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE;
+
+ /* Zero the descriptor rings */
+ memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE);
+ memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE);
+
+ /* Build the rings */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ ksp->tx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->tx_ring_dma +
+ (sizeof(struct tx_ring_desc) *
+ ((buff_n + 1) & MAX_TX_DESC_MASK)));
+ }
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ ksp->rx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->rx_ring_dma +
+ (sizeof(struct rx_ring_desc) *
+ ((buff_n + 1) & MAX_RX_DESC_MASK)));
+ }
+
+ /* Initialise the port (physically) */
+ if (ksp->phyiface_regs && ksp->link_irq == -1) {
+ ks8695_init_switch(ksp);
+ ksp->dtype = KS8695_DTYPE_LAN;
+ } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
+ ks8695_init_wan_phy(ksp);
+ ksp->dtype = KS8695_DTYPE_WAN;
+ } else {
+ /* No initialisation since HPNA does not have a PHY */
+ ksp->dtype = KS8695_DTYPE_HPNA;
+ }
+
+ /* And bring up the net_device with the net core */
+ platform_set_drvdata(pdev, ndev);
+ ret = register_netdev(ndev);
+
+ if (ret == 0) {
+ dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n",
+ ks8695_port_type(ksp), ndev->dev_addr);
+ } else {
+ /* Report the failure to register the net_device */
+ dev_err(ksp->dev, "ks8695net: failed to register netdev.\n");
+ goto failure;
+ }
+
+ /* All is well */
+ return 0;
+
+ /* Error exit path */
+failure:
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ return ret;
+}
+
+/**
+ * ks8695_drv_suspend - Suspend a KS8695 ethernet platform device.
+ * @pdev: The device to suspend
+ * @state: The suspend state
+ *
+ * This routine detaches and shuts down a KS8695 ethernet device.
+ */
+static int
+ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ ksp->in_suspend = 1;
+
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ ks8695_shutdown(ksp);
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_resume - Resume a KS8695 ethernet platform device.
+ * @pdev: The device to resume
+ *
+ * This routine re-initialises and re-attaches a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_drv_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ ks8695_reset(ksp);
+ ks8695_init_net(ksp);
+ ks8695_set_multicast(ndev);
+ netif_device_attach(ndev);
+ }
+
+ ksp->in_suspend = 0;
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_remove - Remove a KS8695 net device on driver unload.
+ * @pdev: The platform device to remove
+ *
+ * This unregisters and releases a KS8695 ethernet device.
+ */
+static int __devexit
+ks8695_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(ndev);
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ dev_dbg(&pdev->dev, "released and freed device\n");
+ return 0;
+}
+
+static struct platform_driver ks8695_driver = {
+ .driver = {
+ .name = MODULENAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ks8695_probe,
+ .remove = __devexit_p(ks8695_drv_remove),
+ .suspend = ks8695_drv_suspend,
+ .resume = ks8695_drv_resume,
+};
+
+/* Module interface */
+
+static int __init
+ks8695_init(void)
+{
+ printk(KERN_INFO "%s Ethernet driver, V%s\n",
+ MODULENAME, MODULEVERSION);
+
+ return platform_driver_register(&ks8695_driver);
+}
+
+static void __exit
+ks8695_cleanup(void)
+{
+ platform_driver_unregister(&ks8695_driver);
+}
+
+module_init(ks8695_init);
+module_exit(ks8695_cleanup);
+
+MODULE_AUTHOR("Simtec Electronics")
+MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MODULENAME);
+
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
Index: net-next-2.6/drivers/net/arm/ks8695net.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-next-2.6/drivers/net/arm/ks8695net.h 2008-12-08 12:56:10.281158951 +0000
@@ -0,0 +1,107 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#ifndef KS8695NET_H
+#define KS8695NET_H
+
+/* Receive descriptor flags */
+#define RDES_OWN (1 << 31) /* Ownership */
+#define RDES_FS (1 << 30) /* First Descriptor */
+#define RDES_LS (1 << 29) /* Last Descriptor */
+#define RDES_IPE (1 << 28) /* IP Checksum error */
+#define RDES_TCPE (1 << 27) /* TCP Checksum error */
+#define RDES_UDPE (1 << 26) /* UDP Checksum error */
+#define RDES_ES (1 << 25) /* Error summary */
+#define RDES_MF (1 << 24) /* Multicast Frame */
+#define RDES_RE (1 << 19) /* MII Error reported */
+#define RDES_TL (1 << 18) /* Frame too Long */
+#define RDES_RF (1 << 17) /* Runt Frame */
+#define RDES_CE (1 << 16) /* CRC error */
+#define RDES_FT (1 << 15) /* Frame Type */
+#define RDES_FLEN (0x7ff) /* Frame Length */
+
+#define RDES_RER (1 << 25) /* Receive End of Ring */
+#define RDES_RBS (0x7ff) /* Receive Buffer Size */
+
+/* Transmit descriptor flags */
+
+#define TDES_OWN (1 << 31) /* Ownership */
+
+#define TDES_IC (1 << 31) /* Interrupt on Completion */
+#define TDES_FS (1 << 30) /* First Segment */
+#define TDES_LS (1 << 29) /* Last Segment */
+#define TDES_IPCKG (1 << 28) /* IP Checksum generate */
+#define TDES_TCPCKG (1 << 27) /* TCP Checksum generate */
+#define TDES_UDPCKG (1 << 26) /* UDP Checksum generate */
+#define TDES_TER (1 << 25) /* Transmit End of Ring */
+#define TDES_TBS (0x7ff) /* Transmit Buffer Size */
+
+/*
+ * Network controller register offsets
+ */
+#define KS8695_DTXC (0x00) /* DMA Transmit Control */
+#define KS8695_DRXC (0x04) /* DMA Receive Control */
+#define KS8695_DTSC (0x08) /* DMA Transmit Start Command */
+#define KS8695_DRSC (0x0c) /* DMA Receive Start Command */
+#define KS8695_TDLB (0x10) /* Transmit Descriptor List
+ * Base Address
+ */
+#define KS8695_RDLB (0x14) /* Receive Descriptor List
+ * Base Address
+ */
+#define KS8695_MAL (0x18) /* MAC Station Address Low */
+#define KS8695_MAH (0x1c) /* MAC Station Address High */
+#define KS8695_AAL_(n) (0x80 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) Low
+ */
+#define KS8695_AAH_(n) (0x84 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) High
+ */
+
+
+/* DMA Transmit Control Register */
+#define DTXC_TRST (1 << 31) /* Soft Reset */
+#define DTXC_TBS (0x3f << 24) /* Transmit Burst Size */
+#define DTXC_TUCG (1 << 18) /* Transmit UDP
+ * Checksum Generate
+ */
+#define DTXC_TTCG (1 << 17) /* Transmit TCP
+ * Checksum Generate
+ */
+#define DTXC_TICG (1 << 16) /* Transmit IP
+ * Checksum Generate
+ */
+#define DTXC_TFCE (1 << 9) /* Transmit Flow
+ * Control Enable
+ */
+#define DTXC_TLB (1 << 8) /* Loopback mode */
+#define DTXC_TEP (1 << 2) /* Transmit Enable Padding */
+#define DTXC_TAC (1 << 1) /* Transmit Add CRC */
+#define DTXC_TE (1 << 0) /* TX Enable */
+
+/* DMA Receive Control Register */
+#define DRXC_RBS (0x3f << 24) /* Receive Burst Size */
+#define DRXC_RUCC (1 << 18) /* Receive UDP Checksum check */
+#define DRXC_RTCG (1 << 17) /* Receive TCP Checksum check */
+#define DRXC_RICG (1 << 16) /* Receive IP Checksum check */
+#define DRXC_RFCE (1 << 9) /* Receive Flow Control
+ * Enable
+ */
+#define DRXC_RB (1 << 6) /* Receive Broadcast */
+#define DRXC_RM (1 << 5) /* Receive Multicast */
+#define DRXC_RU (1 << 4) /* Receive Unicast */
+#define DRXC_RERR (1 << 3) /* Receive Error Frame */
+#define DRXC_RA (1 << 2) /* Receive All */
+#define DRXC_RE (1 << 0) /* RX Enable */
+
+/* Additional Station Address High */
+#define AAH_E (1 << 31) /* Address Enabled */
+
+#endif /* KS8695NET_H */
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 integrated ethernet driver
2008-12-09 17:30 ` [Patch] Micrel KS8695 integrated " Daniel Silverstone
@ 2008-12-09 18:10 ` Ben Hutchings
2008-12-10 7:05 ` David Miller
1 sibling, 0 replies; 16+ messages in thread
From: Ben Hutchings @ 2008-12-09 18:10 UTC (permalink / raw)
To: David Miller; +Cc: dsilvers, netdev
On Tue, 2008-12-09 at 17:30 +0000, Daniel Silverstone wrote:
[...]
> KS8695: Add support for the KS8695 ethernet devices.
>
> Implements the KS8695 ethernet device (ks8695net).
>
> This driver is only of use on the KS8695 which is an ARM9 based SoC. The
> documentation on this SoC is sparse and poor, with barely a register
> description and a rough outline of how the ethernet works, this driver was
> therefore written with strong reference to the Micrel supplied Linux 2.6.9
> port, and to Andrew Victor's ks8695eth driver.
>
> Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
> Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
> Acked-by: Ben Dooks <ben@simtec.co.uk>
Reviewed-by: Ben Hutchings <bhutchings@solarflare.com>
> Kconfig | 8
> Makefile | 1
> ks8695net.c | 1674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> ks8695net.h | 107 +++
> 4 files changed, 1790 insertions(+)
>
> Index: net-next-2.6/drivers/net/arm/Kconfig
> ===================================================================
> --- net-next-2.6.orig/drivers/net/arm/Kconfig 2008-12-08 12:55:46.817658739 +0000
> +++ net-next-2.6/drivers/net/arm/Kconfig 2008-12-08 12:56:10.213660241 +0000
> @@ -40,6 +40,14 @@
> If you wish to compile a kernel for the AT91RM9200 and enable
> ethernet support, then you should always answer Y to this.
>
> +config ARM_KS8695_ETHER
> + tristate "KS8695 Ethernet support"
> + depends on ARM && ARCH_KS8695
> + select MII
> + help
> + If you wish to compile a kernel for the KS8695 and want to
> + use the internal ethernet then you should answer Y to this.
> +
> config EP93XX_ETH
> tristate "EP93xx Ethernet support"
> depends on ARM && ARCH_EP93XX
> Index: net-next-2.6/drivers/net/arm/Makefile
> ===================================================================
> --- net-next-2.6.orig/drivers/net/arm/Makefile 2008-12-08 12:55:46.841662533 +0000
> +++ net-next-2.6/drivers/net/arm/Makefile 2008-12-08 12:56:10.213660241 +0000
> @@ -8,5 +8,6 @@
> obj-$(CONFIG_ARM_ETHER3) += ether3.o
> obj-$(CONFIG_ARM_ETHER1) += ether1.o
> obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
> +obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
> obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
> obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
> Index: net-next-2.6/drivers/net/arm/ks8695net.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-next-2.6/drivers/net/arm/ks8695net.c 2008-12-09 17:26:28.469658828 +0000
> @@ -0,0 +1,1674 @@
> +/*
> + * Micrel KS8695 (Centaur) Ethernet.
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * Copyright 2008 Simtec Electronics
> + * Daniel Silverstone <dsilvers@simtec.co.uk>
> + * Vincent Sanders <vince@simtec.co.uk>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/ioport.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/init.h>
> +#include <linux/skbuff.h>
> +#include <linux/spinlock.h>
> +#include <linux/crc32.h>
> +#include <linux/mii.h>
> +#include <linux/ethtool.h>
> +#include <linux/delay.h>
> +#include <linux/platform_device.h>
> +#include <linux/irq.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +
> +#include <asm/irq.h>
> +
> +#include <mach/regs-switch.h>
> +#include <mach/regs-misc.h>
> +
> +#include "ks8695net.h"
> +
> +#define MODULENAME "ks8695_ether"
> +#define MODULEVERSION "1.01"
[...]
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 integrated ethernet driver
2008-12-09 17:30 ` [Patch] Micrel KS8695 integrated " Daniel Silverstone
2008-12-09 18:10 ` Ben Hutchings
@ 2008-12-10 7:05 ` David Miller
2008-12-10 15:41 ` Daniel Silverstone
1 sibling, 1 reply; 16+ messages in thread
From: David Miller @ 2008-12-10 7:05 UTC (permalink / raw)
To: dsilvers; +Cc: bhutchings, netdev
From: Daniel Silverstone <dsilvers@simtec.co.uk>
Date: Tue, 09 Dec 2008 17:30:09 +0000
> + mapping = dma_map_single(ksp->dev, skb->data,
> + MAX_RXBUF_SIZE,
> + DMA_FROM_DEVICE);
> + if (!mapping) {
There is a proper way to check for DMA mapping failures.
Comparing against zero is not it :-)
> + ksp->tx_buffers[buff_n].dma_ptr = dma_map_single(
> + ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
> + if (!ksp->tx_buffers[buff_n].dma_ptr) {
Likewise.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 integrated ethernet driver
2008-12-10 7:05 ` David Miller
@ 2008-12-10 15:41 ` Daniel Silverstone
2008-12-12 5:01 ` David Miller
0 siblings, 1 reply; 16+ messages in thread
From: Daniel Silverstone @ 2008-12-10 15:41 UTC (permalink / raw)
To: David Miller; +Cc: bhutchings, netdev
[-- Attachment #1: Type: text/plain, Size: 914 bytes --]
On Tue, 2008-12-09 at 23:05 -0800, David Miller wrote:
> > + mapping = dma_map_single(ksp->dev, skb->data,
> > + MAX_RXBUF_SIZE,
> > + DMA_FROM_DEVICE);
> > + if (!mapping) {
> There is a proper way to check for DMA mapping failures.
> Comparing against zero is not it :-)
> > + ksp->tx_buffers[buff_n].dma_ptr = dma_map_single(
> > + ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
> > + if (!ksp->tx_buffers[buff_n].dma_ptr) {
> Likewise.
When I had a quick frobble around for other drivers using dma_map_single
I didn't see any error checking so I guessed. I guessed wrongly :-(
I've attached a driver which is the same as before but uses
dma_mapping_error() to check the result.
I trust that this is the last issue :-)
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
[-- Attachment #2: ks8695-ethernet-net-next-2.6.patch --]
[-- Type: text/x-patch, Size: 51899 bytes --]
KS8695: Add support for the KS8695 ethernet devices.
Implements the KS8695 ethernet device (ks8695net).
This driver is only of use on the KS8695 which is an ARM9 based SoC. The
documentation on this SoC is sparse and poor, with barely a register
description and a rough outline of how the ethernet works, this driver was
therefore written with strong reference to the Micrel supplied Linux 2.6.9
port, and to Andrew Victor's ks8695eth driver.
Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
Acked-by: Ben Dooks <ben@simtec.co.uk>
Kconfig | 8
Makefile | 1
ks8695net.c | 1676 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ks8695net.h | 107 +++
4 files changed, 1792 insertions(+)
Index: net-next-2.6/drivers/net/arm/Kconfig
===================================================================
--- net-next-2.6.orig/drivers/net/arm/Kconfig 2008-12-08 12:55:46.817658739 +0000
+++ net-next-2.6/drivers/net/arm/Kconfig 2008-12-08 12:56:10.213660241 +0000
@@ -40,6 +40,14 @@
If you wish to compile a kernel for the AT91RM9200 and enable
ethernet support, then you should always answer Y to this.
+config ARM_KS8695_ETHER
+ tristate "KS8695 Ethernet support"
+ depends on ARM && ARCH_KS8695
+ select MII
+ help
+ If you wish to compile a kernel for the KS8695 and want to
+ use the internal ethernet then you should answer Y to this.
+
config EP93XX_ETH
tristate "EP93xx Ethernet support"
depends on ARM && ARCH_EP93XX
Index: net-next-2.6/drivers/net/arm/Makefile
===================================================================
--- net-next-2.6.orig/drivers/net/arm/Makefile 2008-12-08 12:55:46.841662533 +0000
+++ net-next-2.6/drivers/net/arm/Makefile 2008-12-08 12:56:10.213660241 +0000
@@ -8,5 +8,6 @@
obj-$(CONFIG_ARM_ETHER3) += ether3.o
obj-$(CONFIG_ARM_ETHER1) += ether1.o
obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
+obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
Index: net-next-2.6/drivers/net/arm/ks8695net.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-next-2.6/drivers/net/arm/ks8695net.c 2008-12-10 15:37:26.446800665 +0000
@@ -0,0 +1,1676 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+
+#include <mach/regs-switch.h>
+#include <mach/regs-misc.h>
+
+#include "ks8695net.h"
+
+#define MODULENAME "ks8695_ether"
+#define MODULEVERSION "1.01"
+
+/*
+ * Transmit and device reset timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+
+/* Hardware structures */
+
+/**
+ * struct rx_ring_desc - Receive descriptor ring element
+ * @status: The status of the descriptor element (E.g. who owns it)
+ * @length: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct rx_ring_desc {
+ __le32 status;
+ __le32 length;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct tx_ring_desc - Transmit descriptor ring element
+ * @owner: Who owns the descriptor
+ * @status: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct tx_ring_desc {
+ __le32 owner;
+ __le32 status;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct ks8695_skbuff - sk_buff wrapper for rx/tx rings.
+ * @skb: The buffer in the ring
+ * @dma_ptr: The mapped DMA pointer of the buffer
+ * @length: The number of bytes mapped to dma_ptr
+ */
+struct ks8695_skbuff {
+ struct sk_buff *skb;
+ dma_addr_t dma_ptr;
+ u32 length;
+};
+
+/* Private device structure */
+
+#define MAX_TX_DESC 8
+#define MAX_TX_DESC_MASK 0x7
+#define MAX_RX_DESC 16
+#define MAX_RX_DESC_MASK 0xf
+
+#define MAX_RXBUF_SIZE 0x700
+
+#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
+#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC)
+#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)
+
+/**
+ * enum ks8695_dtype - Device type
+ * @KS8695_DTYPE_WAN: This device is a WAN interface
+ * @KS8695_DTYPE_LAN: This device is a LAN interface
+ * @KS8695_DTYPE_HPNA: This device is an HPNA interface
+ */
+enum ks8695_dtype {
+ KS8695_DTYPE_WAN,
+ KS8695_DTYPE_LAN,
+ KS8695_DTYPE_HPNA,
+};
+
+/**
+ * struct ks8695_priv - Private data for the KS8695 Ethernet
+ * @in_suspend: Flag to indicate if we're suspending/resuming
+ * @ndev: The net_device for this interface
+ * @dev: The platform device object for this interface
+ * @dtype: The type of this device
+ * @io_regs: The ioremapped registers for this interface
+ * @rx_irq_name: The textual name of the RX IRQ from the platform data
+ * @tx_irq_name: The textual name of the TX IRQ from the platform data
+ * @link_irq_name: The textual name of the link IRQ from the
+ * platform data if available
+ * @rx_irq: The IRQ number for the RX IRQ
+ * @tx_irq: The IRQ number for the TX IRQ
+ * @link_irq: The IRQ number for the link IRQ if available
+ * @regs_req: The resource request for the registers region
+ * @phyiface_req: The resource request for the phy/switch region
+ * if available
+ * @phyiface_regs: The ioremapped registers for the phy/switch if available
+ * @ring_base: The base pointer of the dma coherent memory for the rings
+ * @ring_base_dma: The DMA mapped equivalent of ring_base
+ * @tx_ring: The pointer in ring_base of the TX ring
+ * @tx_ring_used: The number of slots in the TX ring which are occupied
+ * @tx_ring_next_slot: The next slot to fill in the TX ring
+ * @tx_ring_dma: The DMA mapped equivalent of tx_ring
+ * @tx_buffers: The sk_buff mappings for the TX ring
+ * @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables
+ * @rx_ring: The pointer in ring_base of the RX ring
+ * @rx_ring_dma: The DMA mapped equivalent of rx_ring
+ * @rx_buffers: The sk_buff mappings for the RX ring
+ * @next_rx_desc_read: The next RX descriptor to read from on IRQ
+ * @msg_enable: The flags for which messages to emit
+ */
+struct ks8695_priv {
+ int in_suspend;
+ struct net_device *ndev;
+ struct device *dev;
+ enum ks8695_dtype dtype;
+ void __iomem *io_regs;
+
+ const char *rx_irq_name, *tx_irq_name, *link_irq_name;
+ int rx_irq, tx_irq, link_irq;
+
+ struct resource *regs_req, *phyiface_req;
+ void __iomem *phyiface_regs;
+
+ void *ring_base;
+ dma_addr_t ring_base_dma;
+
+ struct tx_ring_desc *tx_ring;
+ int tx_ring_used;
+ int tx_ring_next_slot;
+ dma_addr_t tx_ring_dma;
+ struct ks8695_skbuff tx_buffers[MAX_TX_DESC];
+ spinlock_t txq_lock;
+
+ struct rx_ring_desc *rx_ring;
+ dma_addr_t rx_ring_dma;
+ struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
+ int next_rx_desc_read;
+
+ int msg_enable;
+};
+
+/* Register access */
+
+/**
+ * ks8695_readreg - Read from a KS8695 ethernet register
+ * @ksp: The device to read from
+ * @reg: The register to read
+ */
+static inline u32
+ks8695_readreg(struct ks8695_priv *ksp, int reg)
+{
+ return readl(ksp->io_regs + reg);
+}
+
+/**
+ * ks8695_writereg - Write to a KS8695 ethernet register
+ * @ksp: The device to write to
+ * @reg: The register to write
+ * @value: The value to write to the register
+ */
+static inline void
+ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value)
+{
+ writel(value, ksp->io_regs + reg);
+}
+
+/* Utility functions */
+
+/**
+ * ks8695_port_type - Retrieve port-type as user-friendly string
+ * @ksp: The device to return the type for
+ *
+ * Returns a string indicating which of the WAN, LAN or HPNA
+ * ports this device is likely to represent.
+ */
+static const char *
+ks8695_port_type(struct ks8695_priv *ksp)
+{
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_LAN:
+ return "LAN";
+ case KS8695_DTYPE_WAN:
+ return "WAN";
+ case KS8695_DTYPE_HPNA:
+ return "HPNA";
+ }
+
+ return "UNKNOWN";
+}
+
+/**
+ * ks8695_update_mac - Update the MAC registers in the device
+ * @ksp: The device to update
+ *
+ * Updates the MAC registers in the KS8695 device from the address in the
+ * net_device structure associated with this interface.
+ */
+static void
+ks8695_update_mac(struct ks8695_priv *ksp)
+{
+ /* Update the HW with the MAC from the net_device */
+ struct net_device *ndev = ksp->ndev;
+ u32 machigh, maclow;
+
+ maclow = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5] << 0));
+ machigh = ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1] << 0));
+
+ ks8695_writereg(ksp, KS8695_MAL, maclow);
+ ks8695_writereg(ksp, KS8695_MAH, machigh);
+
+}
+
+/**
+ * ks8695_refill_rxbuffers - Re-fill the RX buffer ring
+ * @ksp: The device to refill
+ *
+ * Iterates the RX ring of the device looking for empty slots.
+ * For each empty slot, we allocate and map a new SKB and give it
+ * to the hardware.
+ * This can be called from interrupt context safely.
+ */
+static void
+ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
+{
+ /* Run around the RX ring, filling in any missing sk_buff's */
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (!ksp->rx_buffers[buff_n].skb) {
+ struct sk_buff *skb = dev_alloc_skb(MAX_RXBUF_SIZE);
+ dma_addr_t mapping;
+
+ ksp->rx_buffers[buff_n].skb = skb;
+ if (skb == NULL) {
+ /* Failed to allocate one, perhaps
+ * we'll try again later.
+ */
+ break;
+ }
+
+ mapping = dma_map_single(ksp->dev, skb->data,
+ MAX_RXBUF_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(ksp->dev, mapping))) {
+ /* Failed to DMA map this SKB, try later */
+ dev_kfree_skb_irq(skb);
+ ksp->rx_buffers[buff_n].skb = NULL;
+ break;
+ }
+ ksp->rx_buffers[buff_n].dma_ptr = mapping;
+ skb->dev = ksp->ndev;
+ ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
+
+ /* Record this into the DMA ring */
+ ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
+ ksp->rx_ring[buff_n].length =
+ cpu_to_le32(MAX_RXBUF_SIZE);
+
+ wmb();
+
+ /* And give ownership over to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+ }
+ }
+}
+
+/* Maximum number of multicast addresses which the KS8695 HW supports */
+#define KS8695_NR_ADDRESSES 16
+
+/**
+ * ks8695_init_partial_multicast - Init the mcast addr registers
+ * @ksp: The device to initialise
+ * @addr: The multicast address list to use
+ * @nr_addr: The number of addresses in the list
+ *
+ * This routine is a helper for ks8695_set_multicast - it writes
+ * the additional-address registers in the KS8695 ethernet device
+ * and cleans up any others left behind.
+ */
+static void
+ks8695_init_partial_multicast(struct ks8695_priv *ksp,
+ struct dev_mc_list *addr,
+ int nr_addr)
+{
+ u32 low, high;
+ int i;
+
+ for (i = 0; i < nr_addr; i++, addr = addr->next) {
+ /* Ran out of addresses? */
+ if (!addr)
+ break;
+ /* Ran out of space in chip? */
+ BUG_ON(i == KS8695_NR_ADDRESSES);
+
+ low = (addr->dmi_addr[2] << 24) | (addr->dmi_addr[3] << 16) |
+ (addr->dmi_addr[4] << 8) | (addr->dmi_addr[5]);
+ high = (addr->dmi_addr[0] << 8) | (addr->dmi_addr[1]);
+
+ ks8695_writereg(ksp, KS8695_AAL_(i), low);
+ ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high);
+ }
+
+ /* Clear the remaining Additional Station Addresses */
+ for (; i < KS8695_NR_ADDRESSES; i++) {
+ ks8695_writereg(ksp, KS8695_AAL_(i), 0);
+ ks8695_writereg(ksp, KS8695_AAH_(i), 0);
+ }
+}
+
+/* Interrupt handling */
+
+/**
+ * ks8695_tx_irq - Transmit IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the TX ring, clearing out any transmitted slots.
+ * Allows the net_device to pass us new packets once slots are
+ * freed.
+ */
+static irqreturn_t
+ks8695_tx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb &&
+ !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
+ rmb();
+ /* An SKB which is not owned by HW is present */
+ /* Update the stats for the net_device */
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;
+
+ /* Free the packet from the ring */
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Free the sk_buff */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ ksp->tx_ring_used--;
+ }
+ }
+
+ netif_wake_queue(ndev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_rx_irq - Receive IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the RX ring, passing any received packets up to the
+ * host. If we received anything other than errors, we then
+ * refill the ring.
+ */
+static irqreturn_t
+ks8695_rx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ struct sk_buff *skb;
+ int buff_n;
+ u32 flags;
+ int pktlen;
+ int last_rx_processed = -1;
+
+ buff_n = ksp->next_rx_desc_read;
+ do {
+ if (ksp->rx_buffers[buff_n].skb &&
+ !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) {
+ rmb();
+ flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
+ /* Found an SKB which we own, this means we
+ * received a packet
+ */
+ if ((flags & (RDES_FS | RDES_LS)) !=
+ (RDES_FS | RDES_LS)) {
+ /* This packet is not the first and
+ * the last segment. Therefore it is
+ * a "spanning" packet and we can't
+ * handle it
+ */
+ goto rx_failure;
+ }
+
+ if (flags & (RDES_ES | RDES_RE)) {
+ /* It's an error packet */
+ ndev->stats.rx_errors++;
+ if (flags & RDES_TL)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_RF)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_CE)
+ ndev->stats.rx_crc_errors++;
+ if (flags & RDES_RE)
+ ndev->stats.rx_missed_errors++;
+
+ goto rx_failure;
+ }
+
+ pktlen = flags & RDES_FLEN;
+ pktlen -= 4; /* Drop the CRC */
+
+ /* Retrieve the sk_buff */
+ skb = ksp->rx_buffers[buff_n].skb;
+
+ /* Clear it from the ring */
+ ksp->rx_buffers[buff_n].skb = NULL;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+
+ /* Relinquish the SKB to the network layer */
+ skb_put(skb, pktlen);
+ skb->protocol = eth_type_trans(skb, ndev);
+ netif_rx(skb);
+
+ /* Record stats */
+ ndev->last_rx = jiffies;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += pktlen;
+ goto rx_finished;
+
+rx_failure:
+ /* This ring entry is an error, but we can
+ * re-use the skb
+ */
+ /* Give the ring entry back to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+rx_finished:
+ /* And note this as processed so we can start
+ * from here next time
+ */
+ last_rx_processed = buff_n;
+ } else {
+ /* Ran out of things to process, stop now */
+ break;
+ }
+ buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
+ } while (buff_n != ksp->next_rx_desc_read);
+
+ /* And note which RX descriptor we last did anything with */
+ if (likely(last_rx_processed != -1))
+ ksp->next_rx_desc_read =
+ (last_rx_processed + 1) & MAX_RX_DESC_MASK;
+
+ /* And refill the buffers */
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Kick the RX DMA engine, in case it became suspended */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_link_irq - Link change IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * The WAN interface can generate an IRQ when the link changes,
+ * report this to the net layer and the user.
+ */
+static irqreturn_t
+ks8695_link_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if (ctrl & WMC_WLS) {
+ netif_carrier_on(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev,
+ "%s: Link is now up (10%sMbps/%s-duplex)\n",
+ ndev->name,
+ (ctrl & WMC_WSS) ? "0" : "",
+ (ctrl & WMC_WDS) ? "Full" : "Half");
+ } else {
+ netif_carrier_off(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev, "%s: Link is now down.\n",
+ ndev->name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+/* KS8695 Device functions */
+
+/**
+ * ks8695_reset - Reset a KS8695 ethernet interface
+ * @ksp: The interface to reset
+ *
+ * Perform an engine reset of the interface and re-program it
+ * with sensible defaults.
+ */
+static void
+ks8695_reset(struct ks8695_priv *ksp)
+{
+ int reset_timeout = watchdog;
+ /* Issue the reset via the TX DMA control register */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
+ while (reset_timeout--) {
+ if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
+ break;
+ msleep(1);
+ }
+
+ if (reset_timeout == 0) {
+ dev_crit(ksp->dev,
+ "Timeout waiting for DMA engines to reset\n");
+ /* And blithely carry on */
+ }
+
+ /* Definitely wait long enough before attempting to program
+ * the engines
+ */
+ msleep(10);
+
+ /* RX: unicast and broadcast */
+ ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB);
+ /* TX: pad and add CRC */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC);
+}
+
+/**
+ * ks8695_shutdown - Shut down a KS8695 ethernet interface
+ * @ksp: The interface to shut down
+ *
+ * This disables packet RX/TX, cleans up IRQs, drains the rings,
+ * and basically places the interface into a clean shutdown
+ * state.
+ */
+static void
+ks8695_shutdown(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+ int buff_n;
+
+ /* Disable packet transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE);
+
+ /* Disable packet reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE);
+
+ /* Release the IRQs */
+ free_irq(ksp->rx_irq, ksp->ndev);
+ free_irq(ksp->tx_irq, ksp->ndev);
+ if (ksp->link_irq != -1)
+ free_irq(ksp->link_irq, ksp->ndev);
+
+ /* Throw away any pending TX packets */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb) {
+ /* Remove this SKB from the TX ring */
+ ksp->tx_ring[buff_n].owner = 0;
+ ksp->tx_ring[buff_n].status = 0;
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin this SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ }
+ }
+
+ /* Purge the RX buffers */
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (ksp->rx_buffers[buff_n].skb) {
+ /* Remove the SKB from the RX ring */
+ ksp->rx_ring[buff_n].status = 0;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb);
+ ksp->rx_buffers[buff_n].skb = NULL;
+ }
+ }
+}
+
+
+/**
+ * ks8695_setup_irq - IRQ setup helper function
+ * @irq: The IRQ number to claim
+ * @irq_name: The name to give the IRQ claimant
+ * @handler: The function to call to handle the IRQ
+ * @ndev: The net_device to pass in as the dev_id argument to the handler
+ *
+ * Return 0 on success.
+ */
+static int
+ks8695_setup_irq(int irq, const char *irq_name,
+ irq_handler_t handler, struct net_device *ndev)
+{
+ int ret;
+
+ ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);
+
+ if (ret) {
+ dev_err(&ndev->dev, "failure to request IRQ %d\n", irq);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_init_net - Initialise a KS8695 ethernet interface
+ * @ksp: The interface to initialise
+ *
+ * This routine fills the RX ring, initialises the DMA engines,
+ * allocates the IRQs and then starts the packet TX and RX
+ * engines.
+ */
+static int
+ks8695_init_net(struct ks8695_priv *ksp)
+{
+ int ret;
+ u32 ctrl;
+
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Initialise the DMA engines */
+ ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
+ ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
+
+ /* Request the IRQs */
+ ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
+ ks8695_rx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
+ ks8695_tx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ if (ksp->link_irq != -1) {
+ ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
+ ks8695_link_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ }
+
+ /* Set up the ring indices */
+ ksp->next_rx_desc_read = 0;
+ ksp->tx_ring_next_slot = 0;
+ ksp->tx_ring_used = 0;
+
+ /* Bring up transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ /* Enable packet transmission */
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
+
+ /* Bring up the reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ /* Enable packet reception */
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
+ /* And start the DMA engine */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ /* All done */
+ return 0;
+}
+
+/**
+ * ks8695_release_device - HW resource release for KS8695 e-net
+ * @ksp: The device to be freed
+ *
+ * This unallocates io memory regions, dma-coherent regions etc
+ * which were allocated in ks8695_probe.
+ */
+static void
+ks8695_release_device(struct ks8695_priv *ksp)
+{
+ /* Unmap the registers */
+ iounmap(ksp->io_regs);
+ if (ksp->phyiface_regs)
+ iounmap(ksp->phyiface_regs);
+
+ /* And release the request */
+ release_resource(ksp->regs_req);
+ kfree(ksp->regs_req);
+ if (ksp->phyiface_req) {
+ release_resource(ksp->phyiface_req);
+ kfree(ksp->phyiface_req);
+ }
+
+ /* Free the ring buffers */
+ dma_free_coherent(ksp->dev, RING_DMA_SIZE,
+ ksp->ring_base, ksp->ring_base_dma);
+}
+
+/* Ethtool support */
+
+/**
+ * ks8695_get_msglevel - Get the messages enabled for emission
+ * @ndev: The network device to read from
+ */
+static u32
+ks8695_get_msglevel(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ return ksp->msg_enable;
+}
+
+/**
+ * ks8695_set_msglevel - Set the messages enabled for emission
+ * @ndev: The network device to configure
+ * @value: The messages to set for emission
+ */
+static void
+ks8695_set_msglevel(struct net_device *ndev, u32 value)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ ksp->msg_enable = value;
+}
+
+/**
+ * ks8695_get_settings - Get device-specific settings.
+ * @ndev: The network device to read settings from
+ * @cmd: The ethtool structure to read into
+ */
+static int
+ks8695_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ /* All ports on the KS8695 support these... */
+ cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+ SUPPORTED_TP | SUPPORTED_MII);
+ cmd->transceiver = XCVR_INTERNAL;
+
+ /* Port specific extras */
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ cmd->phy_address = 0;
+ /* not supported for HPNA */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ /* BUG: Erm, dtype hpna implies no phy regs */
+ /*
+ ctrl = readl(KS8695_MISC_VA + KS8695_HMC);
+ cmd->speed = (ctrl & HMC_HSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & HMC_HDS) ? DUPLEX_FULL : DUPLEX_HALF;
+ */
+ return -EOPNOTSUPP;
+ case KS8695_DTYPE_WAN:
+ cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
+ cmd->port = PORT_MII;
+ cmd->supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause);
+ cmd->phy_address = 0;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if ((ctrl & WMC_WAND) == 0) {
+ /* auto-negotiation is enabled */
+ cmd->advertising |= ADVERTISED_Autoneg;
+ if (ctrl & WMC_WANA100F)
+ cmd->advertising |= ADVERTISED_100baseT_Full;
+ if (ctrl & WMC_WANA100H)
+ cmd->advertising |= ADVERTISED_100baseT_Half;
+ if (ctrl & WMC_WANA10F)
+ cmd->advertising |= ADVERTISED_10baseT_Full;
+ if (ctrl & WMC_WANA10H)
+ cmd->advertising |= ADVERTISED_10baseT_Half;
+ if (ctrl & WMC_WANAP)
+ cmd->advertising |= ADVERTISED_Pause;
+ cmd->autoneg = AUTONEG_ENABLE;
+
+ cmd->speed = (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WDS) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ } else {
+ /* auto-negotiation is disabled */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ cmd->speed = (ctrl & WMC_WANF100) ?
+ SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WANFF) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ }
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_set_settings - Set device-specific settings.
+ * @ndev: The network device to configure
+ * @cmd: The settings to configure
+ */
+static int
+ks8695_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ if ((cmd->speed != SPEED_10) && (cmd->speed != SPEED_100))
+ return -EINVAL;
+ if ((cmd->duplex != DUPLEX_HALF) && (cmd->duplex != DUPLEX_FULL))
+ return -EINVAL;
+ if (cmd->port != PORT_MII)
+ return -EINVAL;
+ if (cmd->transceiver != XCVR_INTERNAL)
+ return -EINVAL;
+ if ((cmd->autoneg != AUTONEG_DISABLE) &&
+ (cmd->autoneg != AUTONEG_ENABLE))
+ return -EINVAL;
+
+ if (cmd->autoneg == AUTONEG_ENABLE) {
+ if ((cmd->advertising & (ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full)) == 0)
+ return -EINVAL;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA does not support auto-negotiation. */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+ if (cmd->advertising & ADVERTISED_100baseT_Full)
+ ctrl |= WMC_WANA100F;
+ if (cmd->advertising & ADVERTISED_100baseT_Half)
+ ctrl |= WMC_WANA100H;
+ if (cmd->advertising & ADVERTISED_10baseT_Full)
+ ctrl |= WMC_WANA10F;
+ if (cmd->advertising & ADVERTISED_10baseT_Half)
+ ctrl |= WMC_WANA10H;
+
+ /* force a re-negotiation */
+ ctrl |= WMC_WANR;
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ } else {
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* BUG: dtype_hpna implies no phy registers */
+ /*
+ ctrl = __raw_readl(KS8695_MISC_VA + KS8695_HMC);
+
+ ctrl &= ~(HMC_HSS | HMC_HDS);
+ if (cmd->speed == SPEED_100)
+ ctrl |= HMC_HSS;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= HMC_HDS;
+
+ __raw_writel(ctrl, KS8695_MISC_VA + KS8695_HMC);
+ */
+ return -EOPNOTSUPP;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* disable auto-negotiation */
+ ctrl |= WMC_WAND;
+ ctrl &= ~(WMC_WANF100 | WMC_WANFF);
+
+ if (cmd->speed == SPEED_100)
+ ctrl |= WMC_WANF100;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= WMC_WANFF;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_nwayreset - Restart the autonegotiation on the port.
+ * @ndev: The network device to restart autoneotiation on
+ */
+static int
+ks8695_nwayreset(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy means no autonegotiation on hpna */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ if ((ctrl & WMC_WAND) == 0)
+ writel(ctrl | WMC_WANR,
+ ksp->phyiface_regs + KS8695_WMC);
+ else
+ /* auto-negotiation not enabled */
+ return -EINVAL;
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_get_link - Retrieve link status of network interface
+ * @ndev: The network interface to retrive the link status of.
+ */
+static u32
+ks8695_get_link(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA always has link */
+ return 1;
+ case KS8695_DTYPE_WAN:
+ /* WAN we can read the PHY for */
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ return ctrl & WMC_WLS;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/**
+ * ks8695_get_pause - Retrieve network pause/flow-control advertising
+ * @ndev: The device to retrieve settings from
+ * @param: The structure to fill out with the information
+ */
+static void
+ks8695_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy link on hpna to configure */
+ return;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* advertise Pause */
+ param->autoneg = (ctrl & WMC_WANAP);
+
+ /* current Rx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ param->rx_pause = (ctrl & DRXC_RFCE);
+
+ /* current Tx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ param->tx_pause = (ctrl & DTXC_TFCE);
+ break;
+ case KS8695_DTYPE_LAN:
+ /* The LAN's "phy" is a direct-attached switch */
+ return;
+ }
+}
+
+/**
+ * ks8695_set_pause - Configure pause/flow-control
+ * @ndev: The device to configure
+ * @param: The pause parameters to set
+ *
+ * TODO: Implement this
+ */
+static int
+ks8695_set_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ return -EOPNOTSUPP;
+}
+
+/**
+ * ks8695_get_drvinfo - Retrieve driver information
+ * @ndev: The network device to retrieve info about
+ * @info: The info structure to fill out.
+ */
+static void
+ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, MODULENAME, sizeof(info->driver));
+ strlcpy(info->version, MODULEVERSION, sizeof(info->version));
+ strlcpy(info->bus_info, ndev->dev.parent->bus_id,
+ sizeof(info->bus_info));
+}
+
+static struct ethtool_ops ks8695_ethtool_ops = {
+ .get_msglevel = ks8695_get_msglevel,
+ .set_msglevel = ks8695_set_msglevel,
+ .get_settings = ks8695_get_settings,
+ .set_settings = ks8695_set_settings,
+ .nway_reset = ks8695_nwayreset,
+ .get_link = ks8695_get_link,
+ .get_pauseparam = ks8695_get_pause,
+ .set_pauseparam = ks8695_set_pause,
+ .get_drvinfo = ks8695_get_drvinfo,
+};
+
+/* Network device interface functions */
+
+/**
+ * ks8695_set_mac - Update MAC in net dev and HW
+ * @ndev: The network device to update
+ * @addr: The new MAC address to set
+ */
+static int
+ks8695_set_mac(struct net_device *ndev, void *addr)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+
+ ks8695_update_mac(ksp);
+
+ dev_dbg(ksp->dev, "%s: Updated MAC address to %pM\n",
+ ndev->name, ndev->dev_addr);
+
+ return 0;
+}
+
+/**
+ * ks8695_set_multicast - Set up the multicast behaviour of the interface
+ * @ndev: The net_device to configure
+ *
+ * This routine, called by the net layer, configures promiscuity
+ * and multicast reception behaviour for the interface.
+ */
+static void
+ks8695_set_multicast(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+
+ if (ndev->flags & IFF_PROMISC) {
+ /* enable promiscuous mode */
+ ctrl |= DRXC_RA;
+ } else if (ndev->flags & ~IFF_PROMISC) {
+ /* disable promiscuous mode */
+ ctrl &= ~DRXC_RA;
+ }
+
+ if (ndev->flags & IFF_ALLMULTI) {
+ /* enable all multicast mode */
+ ctrl |= DRXC_RM;
+ } else if (ndev->mc_count > KS8695_NR_ADDRESSES) {
+ /* more specific multicast addresses than can be
+ * handled in hardware
+ */
+ ctrl |= DRXC_RM;
+ } else {
+ /* enable specific multicasts */
+ ctrl &= ~DRXC_RM;
+ ks8695_init_partial_multicast(ksp, ndev->mc_list,
+ ndev->mc_count);
+ }
+
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl);
+}
+
+/**
+ * ks8695_timeout - Handle a network tx/rx timeout.
+ * @ndev: The net_device which timed out.
+ *
+ * A network transaction timed out, reset the device.
+ */
+static void
+ks8695_timeout(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ ks8695_shutdown(ksp);
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ /* We ignore the return from this since it managed to init
+ * before it probably will be okay to init again.
+ */
+ ks8695_init_net(ksp);
+
+ /* Reconfigure promiscuity etc */
+ ks8695_set_multicast(ndev);
+
+ /* And start the TX queue once more */
+ netif_start_queue(ndev);
+}
+
+/**
+ * ks8695_start_xmit - Start a packet transmission
+ * @skb: The packet to transmit
+ * @ndev: The network device to send the packet on
+ *
+ * This routine, called by the net layer, takes ownership of the
+ * sk_buff and adds it to the TX ring. It then kicks the TX DMA
+ * engine to ensure transmission begins.
+ */
+static int
+ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int buff_n;
+ dma_addr_t dmap;
+
+ spin_lock_irq(&ksp->txq_lock);
+
+ if (ksp->tx_ring_used == MAX_TX_DESC) {
+ /* Somehow we got entered when we have no room */
+ spin_unlock_irq(&ksp->txq_lock);
+ return NETDEV_TX_BUSY;
+ }
+
+ buff_n = ksp->tx_ring_next_slot;
+
+ BUG_ON(ksp->tx_buffers[buff_n].skb);
+
+ dmap = dma_map_single(ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(ksp->dev, dmap))) {
+ /* Failed to DMA map this SKB, give it back for now */
+ spin_unlock_irq(&ksp->txq_lock);
+ dev_dbg(ksp->dev, "%s: Could not map DMA memory for "\
+ "transmission, trying later\n", ndev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ ksp->tx_buffers[buff_n].dma_ptr = dmap;
+ /* Mapped okay, store the buffer pointer and length for later */
+ ksp->tx_buffers[buff_n].skb = skb;
+ ksp->tx_buffers[buff_n].length = skb->len;
+
+ /* Fill out the TX descriptor */
+ ksp->tx_ring[buff_n].data_ptr =
+ cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
+ ksp->tx_ring[buff_n].status =
+ cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
+ (skb->len & TDES_TBS));
+
+ wmb();
+
+ /* Hand it over to the hardware */
+ ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);
+
+ if (++ksp->tx_ring_used == MAX_TX_DESC)
+ netif_stop_queue(ndev);
+
+ ndev->trans_start = jiffies;
+
+ /* Kick the TX DMA in case it decided to go IDLE */
+ ks8695_writereg(ksp, KS8695_DTSC, 0);
+
+ /* And update the next ring slot */
+ ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK;
+
+ spin_unlock_irq(&ksp->txq_lock);
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ks8695_stop - Stop (shutdown) a KS8695 ethernet interface
+ * @ndev: The net_device to stop
+ *
+ * This disables the TX queue and cleans up a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_stop(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+
+ ks8695_shutdown(ksp);
+
+ return 0;
+}
+
+/**
+ * ks8695_open - Open (bring up) a KS8695 ethernet interface
+ * @ndev: The net_device to open
+ *
+ * This resets, configures the MAC, initialises the RX ring and
+ * DMA engines and starts the TX queue for a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_open(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int ret;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ ret = ks8695_init_net(ksp);
+ if (ret) {
+ ks8695_shutdown(ksp);
+ return ret;
+ }
+
+ netif_start_queue(ndev);
+
+ return 0;
+}
+
+/* Platform device driver */
+
+/**
+ * ks8695_init_switch - Init LAN switch to known good defaults.
+ * @ksp: The device to initialise
+ *
+ * This initialises the LAN switch in the KS8695 to a known-good
+ * set of defaults.
+ */
+static void __devinit
+ks8695_init_switch(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Default value for SEC0 according to datasheet */
+ ctrl = 0x40819e00;
+
+ /* LED0 = Speed LED1 = Link/Activity */
+ ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S);
+ ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY);
+
+ /* Enable Switch */
+ ctrl |= SEC0_ENABLE;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_SEC0);
+
+ /* Defaults for SEC1 */
+ writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
+}
+
+/**
+ * ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults
+ * @ksp: The device to initialise
+ *
+ * This initialises a KS8695's WAN phy to sensible values for
+ * autonegotiation etc.
+ */
+static void __devinit
+ks8695_init_wan_phy(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Support auto-negotiation */
+ ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+
+ /* LED0 = Activity , LED1 = Link */
+ ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK);
+
+ /* Restart Auto-negotiation */
+ ctrl |= WMC_WANR;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+
+ writel(0, ksp->phyiface_regs + KS8695_WPPM);
+ writel(0, ksp->phyiface_regs + KS8695_PPS);
+}
+
+static const struct net_device_ops ks8695_netdev_ops = {
+ .ndo_open = ks8695_open,
+ .ndo_stop = ks8695_stop,
+ .ndo_start_xmit = ks8695_start_xmit,
+ .ndo_tx_timeout = ks8695_timeout,
+ .ndo_set_mac_address = ks8695_set_mac,
+ .ndo_set_multicast_list = ks8695_set_multicast,
+};
+
+/**
+ * ks8695_probe - Probe and initialise a KS8695 ethernet interface
+ * @pdev: The platform device to probe
+ *
+ * Initialise a KS8695 ethernet device from platform data.
+ *
+ * This driver requires at least one IORESOURCE_MEM for the
+ * registers and two IORESOURCE_IRQ for the RX and TX IRQs
+ * respectively. It can optionally take an additional
+ * IORESOURCE_MEM for the switch or phy in the case of the lan or
+ * wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan
+ * port.
+ */
+static int __devinit
+ks8695_probe(struct platform_device *pdev)
+{
+ struct ks8695_priv *ksp;
+ struct net_device *ndev;
+ struct resource *regs_res, *phyiface_res;
+ struct resource *rxirq_res, *txirq_res, *linkirq_res;
+ int ret = 0;
+ int buff_n;
+ u32 machigh, maclow;
+
+ /* Initialise a net_device */
+ ndev = alloc_etherdev(sizeof(struct ks8695_priv));
+ if (!ndev) {
+ dev_err(&pdev->dev, "could not allocate device.\n");
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ dev_dbg(&pdev->dev, "ks8695_probe() called\n");
+
+ /* Configure our private structure a little */
+ ksp = netdev_priv(ndev);
+ memset(ksp, 0, sizeof(struct ks8695_priv));
+
+ ksp->dev = &pdev->dev;
+ ksp->ndev = ndev;
+ ksp->msg_enable = NETIF_MSG_LINK;
+
+ /* Retrieve resources */
+ regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+
+ if (!(regs_res && rxirq_res && txirq_res)) {
+ dev_err(ksp->dev, "insufficient resources\n");
+ ret = -ENOENT;
+ goto failure;
+ }
+
+ ksp->regs_req = request_mem_region(regs_res->start,
+ resource_size(regs_res),
+ pdev->name);
+
+ if (!ksp->regs_req) {
+ dev_err(ksp->dev, "cannot claim register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res));
+
+ if (!ksp->io_regs) {
+ dev_err(ksp->dev, "failed to ioremap registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ if (phyiface_res) {
+ ksp->phyiface_req =
+ request_mem_region(phyiface_res->start,
+ resource_size(phyiface_res),
+ phyiface_res->name);
+
+ if (!ksp->phyiface_req) {
+ dev_err(ksp->dev,
+ "cannot claim switch register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->phyiface_regs = ioremap(phyiface_res->start,
+ resource_size(phyiface_res));
+
+ if (!ksp->phyiface_regs) {
+ dev_err(ksp->dev,
+ "failed to ioremap switch registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+ }
+
+ ksp->rx_irq = rxirq_res->start;
+ ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX";
+ ksp->tx_irq = txirq_res->start;
+ ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX";
+ ksp->link_irq = (linkirq_res ? linkirq_res->start : -1);
+ ksp->link_irq_name = (linkirq_res && linkirq_res->name) ?
+ linkirq_res->name : "Ethernet Link";
+
+ /* driver system setup */
+ ndev->netdev_ops = &ks8695_netdev_ops;
+ SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+ ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+
+ /* Retrieve the default MAC addr from the chip. */
+ /* The bootloader should have left it in there for us. */
+
+ machigh = ks8695_readreg(ksp, KS8695_MAH);
+ maclow = ks8695_readreg(ksp, KS8695_MAL);
+
+ ndev->dev_addr[0] = (machigh >> 8) & 0xFF;
+ ndev->dev_addr[1] = machigh & 0xFF;
+ ndev->dev_addr[2] = (maclow >> 24) & 0xFF;
+ ndev->dev_addr[3] = (maclow >> 16) & 0xFF;
+ ndev->dev_addr[4] = (maclow >> 8) & 0xFF;
+ ndev->dev_addr[5] = maclow & 0xFF;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please "
+ "set using ifconfig\n", ndev->name);
+
+ /* In order to be efficient memory-wise, we allocate both
+ * rings in one go.
+ */
+ ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE,
+ &ksp->ring_base_dma, GFP_KERNEL);
+ if (!ksp->ring_base) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ /* Specify the TX DMA ring buffer */
+ ksp->tx_ring = ksp->ring_base;
+ ksp->tx_ring_dma = ksp->ring_base_dma;
+
+ /* And initialise the queue's lock */
+ spin_lock_init(&ksp->txq_lock);
+
+ /* Specify the RX DMA ring buffer */
+ ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
+ ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE;
+
+ /* Zero the descriptor rings */
+ memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE);
+ memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE);
+
+ /* Build the rings */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ ksp->tx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->tx_ring_dma +
+ (sizeof(struct tx_ring_desc) *
+ ((buff_n + 1) & MAX_TX_DESC_MASK)));
+ }
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ ksp->rx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->rx_ring_dma +
+ (sizeof(struct rx_ring_desc) *
+ ((buff_n + 1) & MAX_RX_DESC_MASK)));
+ }
+
+ /* Initialise the port (physically) */
+ if (ksp->phyiface_regs && ksp->link_irq == -1) {
+ ks8695_init_switch(ksp);
+ ksp->dtype = KS8695_DTYPE_LAN;
+ } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
+ ks8695_init_wan_phy(ksp);
+ ksp->dtype = KS8695_DTYPE_WAN;
+ } else {
+ /* No initialisation since HPNA does not have a PHY */
+ ksp->dtype = KS8695_DTYPE_HPNA;
+ }
+
+ /* And bring up the net_device with the net core */
+ platform_set_drvdata(pdev, ndev);
+ ret = register_netdev(ndev);
+
+ if (ret == 0) {
+ dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n",
+ ks8695_port_type(ksp), ndev->dev_addr);
+ } else {
+ /* Report the failure to register the net_device */
+ dev_err(ksp->dev, "ks8695net: failed to register netdev.\n");
+ goto failure;
+ }
+
+ /* All is well */
+ return 0;
+
+ /* Error exit path */
+failure:
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ return ret;
+}
+
+/**
+ * ks8695_drv_suspend - Suspend a KS8695 ethernet platform device.
+ * @pdev: The device to suspend
+ * @state: The suspend state
+ *
+ * This routine detaches and shuts down a KS8695 ethernet device.
+ */
+static int
+ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ ksp->in_suspend = 1;
+
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ ks8695_shutdown(ksp);
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_resume - Resume a KS8695 ethernet platform device.
+ * @pdev: The device to resume
+ *
+ * This routine re-initialises and re-attaches a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_drv_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ ks8695_reset(ksp);
+ ks8695_init_net(ksp);
+ ks8695_set_multicast(ndev);
+ netif_device_attach(ndev);
+ }
+
+ ksp->in_suspend = 0;
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_remove - Remove a KS8695 net device on driver unload.
+ * @pdev: The platform device to remove
+ *
+ * This unregisters and releases a KS8695 ethernet device.
+ */
+static int __devexit
+ks8695_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(ndev);
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ dev_dbg(&pdev->dev, "released and freed device\n");
+ return 0;
+}
+
+static struct platform_driver ks8695_driver = {
+ .driver = {
+ .name = MODULENAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ks8695_probe,
+ .remove = __devexit_p(ks8695_drv_remove),
+ .suspend = ks8695_drv_suspend,
+ .resume = ks8695_drv_resume,
+};
+
+/* Module interface */
+
+static int __init
+ks8695_init(void)
+{
+ printk(KERN_INFO "%s Ethernet driver, V%s\n",
+ MODULENAME, MODULEVERSION);
+
+ return platform_driver_register(&ks8695_driver);
+}
+
+static void __exit
+ks8695_cleanup(void)
+{
+ platform_driver_unregister(&ks8695_driver);
+}
+
+module_init(ks8695_init);
+module_exit(ks8695_cleanup);
+
+MODULE_AUTHOR("Simtec Electronics")
+MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MODULENAME);
+
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
Index: net-next-2.6/drivers/net/arm/ks8695net.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-next-2.6/drivers/net/arm/ks8695net.h 2008-12-08 12:56:10.281158951 +0000
@@ -0,0 +1,107 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#ifndef KS8695NET_H
+#define KS8695NET_H
+
+/* Receive descriptor flags */
+#define RDES_OWN (1 << 31) /* Ownership */
+#define RDES_FS (1 << 30) /* First Descriptor */
+#define RDES_LS (1 << 29) /* Last Descriptor */
+#define RDES_IPE (1 << 28) /* IP Checksum error */
+#define RDES_TCPE (1 << 27) /* TCP Checksum error */
+#define RDES_UDPE (1 << 26) /* UDP Checksum error */
+#define RDES_ES (1 << 25) /* Error summary */
+#define RDES_MF (1 << 24) /* Multicast Frame */
+#define RDES_RE (1 << 19) /* MII Error reported */
+#define RDES_TL (1 << 18) /* Frame too Long */
+#define RDES_RF (1 << 17) /* Runt Frame */
+#define RDES_CE (1 << 16) /* CRC error */
+#define RDES_FT (1 << 15) /* Frame Type */
+#define RDES_FLEN (0x7ff) /* Frame Length */
+
+#define RDES_RER (1 << 25) /* Receive End of Ring */
+#define RDES_RBS (0x7ff) /* Receive Buffer Size */
+
+/* Transmit descriptor flags */
+
+#define TDES_OWN (1 << 31) /* Ownership */
+
+#define TDES_IC (1 << 31) /* Interrupt on Completion */
+#define TDES_FS (1 << 30) /* First Segment */
+#define TDES_LS (1 << 29) /* Last Segment */
+#define TDES_IPCKG (1 << 28) /* IP Checksum generate */
+#define TDES_TCPCKG (1 << 27) /* TCP Checksum generate */
+#define TDES_UDPCKG (1 << 26) /* UDP Checksum generate */
+#define TDES_TER (1 << 25) /* Transmit End of Ring */
+#define TDES_TBS (0x7ff) /* Transmit Buffer Size */
+
+/*
+ * Network controller register offsets
+ */
+#define KS8695_DTXC (0x00) /* DMA Transmit Control */
+#define KS8695_DRXC (0x04) /* DMA Receive Control */
+#define KS8695_DTSC (0x08) /* DMA Transmit Start Command */
+#define KS8695_DRSC (0x0c) /* DMA Receive Start Command */
+#define KS8695_TDLB (0x10) /* Transmit Descriptor List
+ * Base Address
+ */
+#define KS8695_RDLB (0x14) /* Receive Descriptor List
+ * Base Address
+ */
+#define KS8695_MAL (0x18) /* MAC Station Address Low */
+#define KS8695_MAH (0x1c) /* MAC Station Address High */
+#define KS8695_AAL_(n) (0x80 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) Low
+ */
+#define KS8695_AAH_(n) (0x84 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) High
+ */
+
+
+/* DMA Transmit Control Register */
+#define DTXC_TRST (1 << 31) /* Soft Reset */
+#define DTXC_TBS (0x3f << 24) /* Transmit Burst Size */
+#define DTXC_TUCG (1 << 18) /* Transmit UDP
+ * Checksum Generate
+ */
+#define DTXC_TTCG (1 << 17) /* Transmit TCP
+ * Checksum Generate
+ */
+#define DTXC_TICG (1 << 16) /* Transmit IP
+ * Checksum Generate
+ */
+#define DTXC_TFCE (1 << 9) /* Transmit Flow
+ * Control Enable
+ */
+#define DTXC_TLB (1 << 8) /* Loopback mode */
+#define DTXC_TEP (1 << 2) /* Transmit Enable Padding */
+#define DTXC_TAC (1 << 1) /* Transmit Add CRC */
+#define DTXC_TE (1 << 0) /* TX Enable */
+
+/* DMA Receive Control Register */
+#define DRXC_RBS (0x3f << 24) /* Receive Burst Size */
+#define DRXC_RUCC (1 << 18) /* Receive UDP Checksum check */
+#define DRXC_RTCG (1 << 17) /* Receive TCP Checksum check */
+#define DRXC_RICG (1 << 16) /* Receive IP Checksum check */
+#define DRXC_RFCE (1 << 9) /* Receive Flow Control
+ * Enable
+ */
+#define DRXC_RB (1 << 6) /* Receive Broadcast */
+#define DRXC_RM (1 << 5) /* Receive Multicast */
+#define DRXC_RU (1 << 4) /* Receive Unicast */
+#define DRXC_RERR (1 << 3) /* Receive Error Frame */
+#define DRXC_RA (1 << 2) /* Receive All */
+#define DRXC_RE (1 << 0) /* RX Enable */
+
+/* Additional Station Address High */
+#define AAH_E (1 << 31) /* Address Enabled */
+
+#endif /* KS8695NET_H */
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 integrated ethernet driver
2008-12-10 15:41 ` Daniel Silverstone
@ 2008-12-12 5:01 ` David Miller
0 siblings, 0 replies; 16+ messages in thread
From: David Miller @ 2008-12-12 5:01 UTC (permalink / raw)
To: dsilvers; +Cc: bhutchings, netdev
From: Daniel Silverstone <dsilvers@simtec.co.uk>
Date: Wed, 10 Dec 2008 15:41:19 +0000
> I've attached a driver which is the same as before but uses
> dma_mapping_error() to check the result.
>
> I trust that this is the last issue :-)
Added to net-next-2.6, thanks Daniel.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [Patch] Micrel KS8695 intergrated ethernet driver
2008-12-08 11:05 ` Daniel Silverstone
2008-12-08 15:17 ` Ben Hutchings
2008-12-09 8:57 ` Christoph Hellwig
@ 2008-12-13 19:07 ` Kalle Valo
2 siblings, 0 replies; 16+ messages in thread
From: Kalle Valo @ 2008-12-13 19:07 UTC (permalink / raw)
To: dsilvers; +Cc: netdev
Daniel Silverstone <dsilvers@simtec.co.uk> writes:
>> > + if (ksp->tx_buffers[buff_n].skb) {
>> > + /* This slot is used, try later */
>> > + spin_unlock_irq(&ksp->txq_lock);
>> > + return -EAGAIN;
>> Since you already tested tx_ring_used, wouldn't this indicate a bug?
>
> Yep, this was old code before I kept a track in tx_ring_used. Changed
> to:
>
> BUG_ON(ksp->tx_buffers[buff_n].skb);
I would say that a network driver should never use BUG_ON(), it just
makes both user's and developer's life difficult because the whole
kernel will stop the execution. WARN_ON() is much nicer, it just
prints a nice warning to the log which then can be reported either
manually or automatically.
--
Kalle Valo
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2008-12-13 19:08 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-12-05 15:06 [Patch] Micrel KS8695 intergrated ethernet driver Daniel Silverstone
2008-12-05 17:57 ` Ben Hutchings
2008-12-08 11:05 ` Daniel Silverstone
2008-12-08 15:17 ` Ben Hutchings
2008-12-08 20:29 ` David Miller
2008-12-09 8:57 ` Christoph Hellwig
2008-12-09 9:05 ` David Miller
2008-12-09 9:29 ` Daniel Silverstone
2008-12-13 19:07 ` Kalle Valo
2008-12-08 13:08 ` Daniel Silverstone
2008-12-09 17:00 ` Daniel Silverstone
[not found] ` <1228764912.3177.39.camel@achroite>
2008-12-09 17:30 ` [Patch] Micrel KS8695 integrated " Daniel Silverstone
2008-12-09 18:10 ` Ben Hutchings
2008-12-10 7:05 ` David Miller
2008-12-10 15:41 ` Daniel Silverstone
2008-12-12 5:01 ` David Miller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).