mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-22 15:02:32 +00:00
3e3d482c98
Drop the own PHY polling function and switch to using the kernel PHY state machine. This change allows driver to work correctly with devices that do not support PHY behaviour but whose driver could emulate autonegotiation completion (e.g. MV88E6060 and IP17xx switches). NB: earlier this driver rely on flaws in PHY core code and could use PHY device without really starting it. But now (at least in kernel 4.9) this trick no more work and network interface could stuck in not-running state. Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
1735 lines
48 KiB
Diff
1735 lines
48 KiB
Diff
--- a/drivers/net/ethernet/atheros/Makefile
|
|
+++ b/drivers/net/ethernet/atheros/Makefile
|
|
@@ -7,3 +7,4 @@ obj-$(CONFIG_ATL2) += atlx/
|
|
obj-$(CONFIG_ATL1E) += atl1e/
|
|
obj-$(CONFIG_ATL1C) += atl1c/
|
|
obj-$(CONFIG_ALX) += alx/
|
|
+obj-$(CONFIG_NET_AR231X) += ar231x/
|
|
--- a/drivers/net/ethernet/atheros/Kconfig
|
|
+++ b/drivers/net/ethernet/atheros/Kconfig
|
|
@@ -5,7 +5,7 @@
|
|
config NET_VENDOR_ATHEROS
|
|
bool "Atheros devices"
|
|
default y
|
|
- depends on PCI
|
|
+ depends on (PCI || ATH25)
|
|
---help---
|
|
If you have a network (Ethernet) card belonging to this class, say Y.
|
|
|
|
@@ -78,4 +78,10 @@ config ALX
|
|
To compile this driver as a module, choose M here. The module
|
|
will be called alx.
|
|
|
|
+config NET_AR231X
|
|
+ tristate "Atheros AR231X built-in Ethernet support"
|
|
+ depends on ATH25
|
|
+ help
|
|
+ Support for the AR231x/531x ethernet controller
|
|
+
|
|
endif # NET_VENDOR_ATHEROS
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/atheros/ar231x/Makefile
|
|
@@ -0,0 +1 @@
|
|
+obj-$(CONFIG_NET_AR231X) += ar231x.o
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/atheros/ar231x/ar231x.c
|
|
@@ -0,0 +1,1119 @@
|
|
+/*
|
|
+ * ar231x.c: Linux driver for the Atheros AR231x Ethernet device.
|
|
+ *
|
|
+ * Copyright (C) 2004 by Sameer Dekate <sdekate@arubanetworks.com>
|
|
+ * Copyright (C) 2006 Imre Kaloz <kaloz@openwrt.org>
|
|
+ * Copyright (C) 2006-2009 Felix Fietkau <nbd@nbd.name>
|
|
+ *
|
|
+ * Thanks to Atheros for providing hardware and documentation
|
|
+ * enabling me to write this driver.
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * Additional credits:
|
|
+ * This code is taken from John Taylor's Sibyte driver and then
|
|
+ * modified for the AR2313.
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/ioport.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/mii.h>
|
|
+#include <linux/phy.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/io.h>
|
|
+
|
|
+#define AR2313_MTU 1692
|
|
+#define AR2313_PRIOS 1
|
|
+#define AR2313_QUEUES (2*AR2313_PRIOS)
|
|
+#define AR2313_DESCR_ENTRIES 64
|
|
+
|
|
+#ifndef min
|
|
+#define min(a, b) (((a) < (b)) ? (a) : (b))
|
|
+#endif
|
|
+
|
|
+#ifndef SMP_CACHE_BYTES
|
|
+#define SMP_CACHE_BYTES L1_CACHE_BYTES
|
|
+#endif
|
|
+
|
|
+#define AR2313_MBOX_SET_BIT 0x8
|
|
+
|
|
+#include "ar231x.h"
|
|
+
|
|
+/**
|
|
+ * New interrupt handler strategy:
|
|
+ *
|
|
+ * An old interrupt handler worked using the traditional method of
|
|
+ * replacing an skbuff with a new one when a packet arrives. However
|
|
+ * the rx rings do not need to contain a static number of buffer
|
|
+ * descriptors, thus it makes sense to move the memory allocation out
|
|
+ * of the main interrupt handler and do it in a bottom half handler
|
|
+ * and only allocate new buffers when the number of buffers in the
|
|
+ * ring is below a certain threshold. In order to avoid starving the
|
|
+ * NIC under heavy load it is however necessary to force allocation
|
|
+ * when hitting a minimum threshold. The strategy for alloction is as
|
|
+ * follows:
|
|
+ *
|
|
+ * RX_LOW_BUF_THRES - allocate buffers in the bottom half
|
|
+ * RX_PANIC_LOW_THRES - we are very low on buffers, allocate
|
|
+ * the buffers in the interrupt handler
|
|
+ * RX_RING_THRES - maximum number of buffers in the rx ring
|
|
+ *
|
|
+ * One advantagous side effect of this allocation approach is that the
|
|
+ * entire rx processing can be done without holding any spin lock
|
|
+ * since the rx rings and registers are totally independent of the tx
|
|
+ * ring and its registers. This of course includes the kmalloc's of
|
|
+ * new skb's. Thus start_xmit can run in parallel with rx processing
|
|
+ * and the memory allocation on SMP systems.
|
|
+ *
|
|
+ * Note that running the skb reallocation in a bottom half opens up
|
|
+ * another can of races which needs to be handled properly. In
|
|
+ * particular it can happen that the interrupt handler tries to run
|
|
+ * the reallocation while the bottom half is either running on another
|
|
+ * CPU or was interrupted on the same CPU. To get around this the
|
|
+ * driver uses bitops to prevent the reallocation routines from being
|
|
+ * reentered.
|
|
+ *
|
|
+ * TX handling can also be done without holding any spin lock, wheee
|
|
+ * this is fun! since tx_csm is only written to by the interrupt
|
|
+ * handler.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * Threshold values for RX buffer allocation - the low water marks for
|
|
+ * when to start refilling the rings are set to 75% of the ring
|
|
+ * sizes. It seems to make sense to refill the rings entirely from the
|
|
+ * intrrupt handler once it gets below the panic threshold, that way
|
|
+ * we don't risk that the refilling is moved to another CPU when the
|
|
+ * one running the interrupt handler just got the slab code hot in its
|
|
+ * cache.
|
|
+ */
|
|
+#define RX_RING_SIZE AR2313_DESCR_ENTRIES
|
|
+#define RX_PANIC_THRES (RX_RING_SIZE/4)
|
|
+#define RX_LOW_THRES ((3*RX_RING_SIZE)/4)
|
|
+#define CRC_LEN 4
|
|
+#define RX_OFFSET 2
|
|
+
|
|
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
|
|
+#define VLAN_HDR 4
|
|
+#else
|
|
+#define VLAN_HDR 0
|
|
+#endif
|
|
+
|
|
+#define AR2313_BUFSIZE (AR2313_MTU + VLAN_HDR + ETH_HLEN + CRC_LEN + \
|
|
+ RX_OFFSET)
|
|
+
|
|
+#ifdef MODULE
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_AUTHOR("Sameer Dekate <sdekate@arubanetworks.com>, Imre Kaloz <kaloz@openwrt.org>, Felix Fietkau <nbd@nbd.name>");
|
|
+MODULE_DESCRIPTION("AR231x Ethernet driver");
|
|
+#endif
|
|
+
|
|
+#define virt_to_phys(x) ((u32)(x) & 0x1fffffff)
|
|
+
|
|
+/* prototypes */
|
|
+static void ar231x_halt(struct net_device *dev);
|
|
+static void rx_tasklet_func(unsigned long data);
|
|
+static void rx_tasklet_cleanup(struct net_device *dev);
|
|
+static void ar231x_multicast_list(struct net_device *dev);
|
|
+static void ar231x_tx_timeout(struct net_device *dev);
|
|
+
|
|
+static int ar231x_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum);
|
|
+static int ar231x_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,
|
|
+ u16 value);
|
|
+static int ar231x_mdiobus_reset(struct mii_bus *bus);
|
|
+static int ar231x_mdiobus_probe(struct net_device *dev);
|
|
+static void ar231x_adjust_link(struct net_device *dev);
|
|
+
|
|
+#ifndef ERR
|
|
+#define ERR(fmt, args...) printk("%s: " fmt, __func__, ##args)
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
+static void
|
|
+ar231x_netpoll(struct net_device *dev)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ local_irq_save(flags);
|
|
+ ar231x_interrupt(dev->irq, dev);
|
|
+ local_irq_restore(flags);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static const struct net_device_ops ar231x_ops = {
|
|
+ .ndo_open = ar231x_open,
|
|
+ .ndo_stop = ar231x_close,
|
|
+ .ndo_start_xmit = ar231x_start_xmit,
|
|
+ .ndo_set_rx_mode = ar231x_multicast_list,
|
|
+ .ndo_do_ioctl = ar231x_ioctl,
|
|
+ .ndo_change_mtu = eth_change_mtu,
|
|
+ .ndo_validate_addr = eth_validate_addr,
|
|
+ .ndo_set_mac_address = eth_mac_addr,
|
|
+ .ndo_tx_timeout = ar231x_tx_timeout,
|
|
+#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
+ .ndo_poll_controller = ar231x_netpoll,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static int ar231x_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct net_device *dev;
|
|
+ struct ar231x_private *sp;
|
|
+ struct resource *res;
|
|
+ unsigned long ar_eth_base;
|
|
+ char buf[64];
|
|
+
|
|
+ dev = alloc_etherdev(sizeof(struct ar231x_private));
|
|
+
|
|
+ if (dev == NULL) {
|
|
+ printk(KERN_ERR
|
|
+ "ar231x: Unable to allocate net_device structure!\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ platform_set_drvdata(pdev, dev);
|
|
+
|
|
+ SET_NETDEV_DEV(dev, &pdev->dev);
|
|
+
|
|
+ sp = netdev_priv(dev);
|
|
+ sp->dev = dev;
|
|
+ sp->cfg = pdev->dev.platform_data;
|
|
+
|
|
+ sprintf(buf, "eth%d_membase", pdev->id);
|
|
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, buf);
|
|
+ if (!res)
|
|
+ return -ENODEV;
|
|
+
|
|
+ sp->link = 0;
|
|
+ ar_eth_base = res->start;
|
|
+
|
|
+ sprintf(buf, "eth%d_irq", pdev->id);
|
|
+ dev->irq = platform_get_irq_byname(pdev, buf);
|
|
+
|
|
+ spin_lock_init(&sp->lock);
|
|
+
|
|
+ dev->features |= NETIF_F_HIGHDMA;
|
|
+ dev->netdev_ops = &ar231x_ops;
|
|
+
|
|
+ tasklet_init(&sp->rx_tasklet, rx_tasklet_func, (unsigned long)dev);
|
|
+ tasklet_disable(&sp->rx_tasklet);
|
|
+
|
|
+ sp->eth_regs = ioremap_nocache(ar_eth_base, sizeof(*sp->eth_regs));
|
|
+ if (!sp->eth_regs) {
|
|
+ printk("Can't remap eth registers\n");
|
|
+ return -ENXIO;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * When there's only one MAC, PHY regs are typically on ENET0,
|
|
+ * even though the MAC might be on ENET1.
|
|
+ * So remap PHY regs separately.
|
|
+ */
|
|
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "eth0_mii");
|
|
+ if (!res) {
|
|
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
|
+ "eth1_mii");
|
|
+ if (!res)
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ sp->phy_regs = ioremap_nocache(res->start, resource_size(res));
|
|
+ if (!sp->phy_regs) {
|
|
+ printk("Can't remap phy registers\n");
|
|
+ return -ENXIO;
|
|
+ }
|
|
+
|
|
+ sp->dma_regs = ioremap_nocache(ar_eth_base + 0x1000,
|
|
+ sizeof(*sp->dma_regs));
|
|
+ if (!sp->dma_regs) {
|
|
+ printk("Can't remap DMA registers\n");
|
|
+ return -ENXIO;
|
|
+ }
|
|
+ dev->base_addr = ar_eth_base + 0x1000;
|
|
+
|
|
+ strncpy(sp->name, "Atheros AR231x", sizeof(sp->name) - 1);
|
|
+ sp->name[sizeof(sp->name) - 1] = '\0';
|
|
+ memcpy(dev->dev_addr, sp->cfg->macaddr, 6);
|
|
+
|
|
+ if (ar231x_init(dev)) {
|
|
+ /* ar231x_init() calls ar231x_init_cleanup() on error */
|
|
+ kfree(dev);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ if (register_netdev(dev)) {
|
|
+ printk("%s: register_netdev failed\n", __func__);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ printk("%s: %s: %pM, irq %d\n", dev->name, sp->name, dev->dev_addr,
|
|
+ dev->irq);
|
|
+
|
|
+ sp->mii_bus = mdiobus_alloc();
|
|
+ if (sp->mii_bus == NULL)
|
|
+ return -1;
|
|
+
|
|
+ sp->mii_bus->priv = dev;
|
|
+ sp->mii_bus->read = ar231x_mdiobus_read;
|
|
+ sp->mii_bus->write = ar231x_mdiobus_write;
|
|
+ sp->mii_bus->reset = ar231x_mdiobus_reset;
|
|
+ sp->mii_bus->name = "ar231x_eth_mii";
|
|
+ snprintf(sp->mii_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
|
|
+
|
|
+ mdiobus_register(sp->mii_bus);
|
|
+
|
|
+ if (ar231x_mdiobus_probe(dev) != 0) {
|
|
+ printk(KERN_ERR "%s: mdiobus_probe failed\n", dev->name);
|
|
+ rx_tasklet_cleanup(dev);
|
|
+ ar231x_init_cleanup(dev);
|
|
+ unregister_netdev(dev);
|
|
+ kfree(dev);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void ar231x_multicast_list(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ unsigned int filter;
|
|
+
|
|
+ filter = sp->eth_regs->mac_control;
|
|
+
|
|
+ if (dev->flags & IFF_PROMISC)
|
|
+ filter |= MAC_CONTROL_PR;
|
|
+ else
|
|
+ filter &= ~MAC_CONTROL_PR;
|
|
+ if ((dev->flags & IFF_ALLMULTI) || (netdev_mc_count(dev) > 0))
|
|
+ filter |= MAC_CONTROL_PM;
|
|
+ else
|
|
+ filter &= ~MAC_CONTROL_PM;
|
|
+
|
|
+ sp->eth_regs->mac_control = filter;
|
|
+}
|
|
+
|
|
+static void rx_tasklet_cleanup(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+
|
|
+ /**
|
|
+ * Tasklet may be scheduled. Need to get it removed from the list
|
|
+ * since we're about to free the struct.
|
|
+ */
|
|
+
|
|
+ sp->unloading = 1;
|
|
+ tasklet_enable(&sp->rx_tasklet);
|
|
+ tasklet_kill(&sp->rx_tasklet);
|
|
+}
|
|
+
|
|
+static int ar231x_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct net_device *dev = platform_get_drvdata(pdev);
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+
|
|
+ rx_tasklet_cleanup(dev);
|
|
+ ar231x_init_cleanup(dev);
|
|
+ unregister_netdev(dev);
|
|
+ mdiobus_unregister(sp->mii_bus);
|
|
+ mdiobus_free(sp->mii_bus);
|
|
+ kfree(dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Restart the AR2313 ethernet controller.
|
|
+ */
|
|
+static int ar231x_restart(struct net_device *dev)
|
|
+{
|
|
+ /* disable interrupts */
|
|
+ disable_irq(dev->irq);
|
|
+
|
|
+ /* stop mac */
|
|
+ ar231x_halt(dev);
|
|
+
|
|
+ /* initialize */
|
|
+ ar231x_init(dev);
|
|
+
|
|
+ /* enable interrupts */
|
|
+ enable_irq(dev->irq);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct platform_driver ar231x_driver = {
|
|
+ .driver.name = "ar231x-eth",
|
|
+ .probe = ar231x_probe,
|
|
+ .remove = ar231x_remove,
|
|
+};
|
|
+
|
|
+module_platform_driver(ar231x_driver);
|
|
+
|
|
+static void ar231x_free_descriptors(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+
|
|
+ if (sp->rx_ring != NULL) {
|
|
+ kfree((void *)KSEG0ADDR(sp->rx_ring));
|
|
+ sp->rx_ring = NULL;
|
|
+ sp->tx_ring = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ar231x_allocate_descriptors(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ int size;
|
|
+ int j;
|
|
+ ar231x_descr_t *space;
|
|
+
|
|
+ if (sp->rx_ring != NULL) {
|
|
+ printk("%s: already done.\n", __func__);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ size = sizeof(ar231x_descr_t) * (AR2313_DESCR_ENTRIES * AR2313_QUEUES);
|
|
+ space = kmalloc(size, GFP_KERNEL);
|
|
+ if (space == NULL)
|
|
+ return 1;
|
|
+
|
|
+ /* invalidate caches */
|
|
+ dma_cache_inv((unsigned int)space, size);
|
|
+
|
|
+ /* now convert pointer to KSEG1 */
|
|
+ space = (ar231x_descr_t *)KSEG1ADDR(space);
|
|
+
|
|
+ memset((void *)space, 0, size);
|
|
+
|
|
+ sp->rx_ring = space;
|
|
+ space += AR2313_DESCR_ENTRIES;
|
|
+
|
|
+ sp->tx_ring = space;
|
|
+ space += AR2313_DESCR_ENTRIES;
|
|
+
|
|
+ /* Initialize the transmit Descriptors */
|
|
+ for (j = 0; j < AR2313_DESCR_ENTRIES; j++) {
|
|
+ ar231x_descr_t *td = &sp->tx_ring[j];
|
|
+
|
|
+ td->status = 0;
|
|
+ td->devcs = DMA_TX1_CHAINED;
|
|
+ td->addr = 0;
|
|
+ td->descr = virt_to_phys(&sp->tx_ring[DSC_NEXT(j)]);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Generic cleanup handling data allocated during init. Used when the
|
|
+ * module is unloaded or if an error occurs during initialization
|
|
+ */
|
|
+static void ar231x_init_cleanup(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ struct sk_buff *skb;
|
|
+ int j;
|
|
+
|
|
+ ar231x_free_descriptors(dev);
|
|
+
|
|
+ if (sp->eth_regs)
|
|
+ iounmap((void *)sp->eth_regs);
|
|
+ if (sp->dma_regs)
|
|
+ iounmap((void *)sp->dma_regs);
|
|
+ if (sp->phy_regs)
|
|
+ iounmap((void *)sp->phy_regs);
|
|
+
|
|
+ if (sp->rx_skb) {
|
|
+ for (j = 0; j < AR2313_DESCR_ENTRIES; j++) {
|
|
+ skb = sp->rx_skb[j];
|
|
+ if (skb) {
|
|
+ sp->rx_skb[j] = NULL;
|
|
+ dev_kfree_skb(skb);
|
|
+ }
|
|
+ }
|
|
+ kfree(sp->rx_skb);
|
|
+ sp->rx_skb = NULL;
|
|
+ }
|
|
+
|
|
+ if (sp->tx_skb) {
|
|
+ for (j = 0; j < AR2313_DESCR_ENTRIES; j++) {
|
|
+ skb = sp->tx_skb[j];
|
|
+ if (skb) {
|
|
+ sp->tx_skb[j] = NULL;
|
|
+ dev_kfree_skb(skb);
|
|
+ }
|
|
+ }
|
|
+ kfree(sp->tx_skb);
|
|
+ sp->tx_skb = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ar231x_reset_reg(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ unsigned int ethsal, ethsah;
|
|
+ unsigned int flags;
|
|
+
|
|
+ sp->cfg->reset_set(sp->cfg->reset_mac);
|
|
+ mdelay(10);
|
|
+ sp->cfg->reset_clear(sp->cfg->reset_mac);
|
|
+ mdelay(10);
|
|
+ sp->cfg->reset_set(sp->cfg->reset_phy);
|
|
+ mdelay(10);
|
|
+ sp->cfg->reset_clear(sp->cfg->reset_phy);
|
|
+ mdelay(10);
|
|
+
|
|
+ sp->dma_regs->bus_mode = (DMA_BUS_MODE_SWR);
|
|
+ mdelay(10);
|
|
+ sp->dma_regs->bus_mode =
|
|
+ ((32 << DMA_BUS_MODE_PBL_SHIFT) | DMA_BUS_MODE_BLE);
|
|
+
|
|
+ /* enable interrupts */
|
|
+ sp->dma_regs->intr_ena = DMA_STATUS_AIS | DMA_STATUS_NIS |
|
|
+ DMA_STATUS_RI | DMA_STATUS_TI |
|
|
+ DMA_STATUS_FBE;
|
|
+ sp->dma_regs->xmt_base = virt_to_phys(sp->tx_ring);
|
|
+ sp->dma_regs->rcv_base = virt_to_phys(sp->rx_ring);
|
|
+ sp->dma_regs->control =
|
|
+ (DMA_CONTROL_SR | DMA_CONTROL_ST | DMA_CONTROL_SF);
|
|
+
|
|
+ sp->eth_regs->flow_control = (FLOW_CONTROL_FCE);
|
|
+ sp->eth_regs->vlan_tag = (0x8100);
|
|
+
|
|
+ /* Enable Ethernet Interface */
|
|
+ flags = (MAC_CONTROL_TE | /* transmit enable */
|
|
+ MAC_CONTROL_PM | /* pass mcast */
|
|
+ MAC_CONTROL_F | /* full duplex */
|
|
+ MAC_CONTROL_HBD); /* heart beat disabled */
|
|
+
|
|
+ if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
|
|
+ flags |= MAC_CONTROL_PR;
|
|
+ }
|
|
+ sp->eth_regs->mac_control = flags;
|
|
+
|
|
+ /* Set all Ethernet station address registers to their initial values */
|
|
+ ethsah = (((u_int) (dev->dev_addr[5]) << 8) & (u_int) 0x0000FF00) |
|
|
+ (((u_int) (dev->dev_addr[4]) << 0) & (u_int) 0x000000FF);
|
|
+
|
|
+ ethsal = (((u_int) (dev->dev_addr[3]) << 24) & (u_int) 0xFF000000) |
|
|
+ (((u_int) (dev->dev_addr[2]) << 16) & (u_int) 0x00FF0000) |
|
|
+ (((u_int) (dev->dev_addr[1]) << 8) & (u_int) 0x0000FF00) |
|
|
+ (((u_int) (dev->dev_addr[0]) << 0) & (u_int) 0x000000FF);
|
|
+
|
|
+ sp->eth_regs->mac_addr[0] = ethsah;
|
|
+ sp->eth_regs->mac_addr[1] = ethsal;
|
|
+
|
|
+ mdelay(10);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ar231x_init(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ int ecode = 0;
|
|
+
|
|
+ /* Allocate descriptors */
|
|
+ if (ar231x_allocate_descriptors(dev)) {
|
|
+ printk("%s: %s: ar231x_allocate_descriptors failed\n",
|
|
+ dev->name, __func__);
|
|
+ ecode = -EAGAIN;
|
|
+ goto init_error;
|
|
+ }
|
|
+
|
|
+ /* Get the memory for the skb rings */
|
|
+ if (sp->rx_skb == NULL) {
|
|
+ sp->rx_skb =
|
|
+ kmalloc(sizeof(struct sk_buff *) * AR2313_DESCR_ENTRIES,
|
|
+ GFP_KERNEL);
|
|
+ if (!(sp->rx_skb)) {
|
|
+ printk("%s: %s: rx_skb kmalloc failed\n",
|
|
+ dev->name, __func__);
|
|
+ ecode = -EAGAIN;
|
|
+ goto init_error;
|
|
+ }
|
|
+ }
|
|
+ memset(sp->rx_skb, 0, sizeof(struct sk_buff *) * AR2313_DESCR_ENTRIES);
|
|
+
|
|
+ if (sp->tx_skb == NULL) {
|
|
+ sp->tx_skb =
|
|
+ kmalloc(sizeof(struct sk_buff *) * AR2313_DESCR_ENTRIES,
|
|
+ GFP_KERNEL);
|
|
+ if (!(sp->tx_skb)) {
|
|
+ printk("%s: %s: tx_skb kmalloc failed\n",
|
|
+ dev->name, __func__);
|
|
+ ecode = -EAGAIN;
|
|
+ goto init_error;
|
|
+ }
|
|
+ }
|
|
+ memset(sp->tx_skb, 0, sizeof(struct sk_buff *) * AR2313_DESCR_ENTRIES);
|
|
+
|
|
+ /**
|
|
+ * Set tx_csm before we start receiving interrupts, otherwise
|
|
+ * the interrupt handler might think it is supposed to process
|
|
+ * tx ints before we are up and running, which may cause a null
|
|
+ * pointer access in the int handler.
|
|
+ */
|
|
+ sp->rx_skbprd = 0;
|
|
+ sp->cur_rx = 0;
|
|
+ sp->tx_prd = 0;
|
|
+ sp->tx_csm = 0;
|
|
+
|
|
+ /* Zero the stats before starting the interface */
|
|
+ memset(&dev->stats, 0, sizeof(dev->stats));
|
|
+
|
|
+ /**
|
|
+ * We load the ring here as there seem to be no way to tell the
|
|
+ * firmware to wipe the ring without re-initializing it.
|
|
+ */
|
|
+ ar231x_load_rx_ring(dev, RX_RING_SIZE);
|
|
+
|
|
+ /* Init hardware */
|
|
+ ar231x_reset_reg(dev);
|
|
+
|
|
+ /* Get the IRQ */
|
|
+ ecode = request_irq(dev->irq, &ar231x_interrupt, 0,
|
|
+ dev->name, dev);
|
|
+ if (ecode) {
|
|
+ printk(KERN_WARNING "%s: %s: Requested IRQ %d is busy\n",
|
|
+ dev->name, __func__, dev->irq);
|
|
+ goto init_error;
|
|
+ }
|
|
+
|
|
+ tasklet_enable(&sp->rx_tasklet);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+init_error:
|
|
+ ar231x_init_cleanup(dev);
|
|
+ return ecode;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Load the rx ring.
|
|
+ *
|
|
+ * Loading rings is safe without holding the spin lock since this is
|
|
+ * done only before the device is enabled, thus no interrupts are
|
|
+ * generated and by the interrupt handler/tasklet handler.
|
|
+ */
|
|
+static void ar231x_load_rx_ring(struct net_device *dev, int nr_bufs)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ short i, idx;
|
|
+
|
|
+ idx = sp->rx_skbprd;
|
|
+
|
|
+ for (i = 0; i < nr_bufs; i++) {
|
|
+ struct sk_buff *skb;
|
|
+ ar231x_descr_t *rd;
|
|
+
|
|
+ if (sp->rx_skb[idx])
|
|
+ break;
|
|
+
|
|
+ skb = netdev_alloc_skb_ip_align(dev, AR2313_BUFSIZE);
|
|
+ if (!skb) {
|
|
+ printk("\n\n\n\n %s: No memory in system\n\n\n\n",
|
|
+ __func__);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Make sure IP header starts on a fresh cache line */
|
|
+ skb->dev = dev;
|
|
+ sp->rx_skb[idx] = skb;
|
|
+
|
|
+ rd = (ar231x_descr_t *)&sp->rx_ring[idx];
|
|
+
|
|
+ /* initialize dma descriptor */
|
|
+ rd->devcs = ((AR2313_BUFSIZE << DMA_RX1_BSIZE_SHIFT) |
|
|
+ DMA_RX1_CHAINED);
|
|
+ rd->addr = virt_to_phys(skb->data);
|
|
+ rd->descr = virt_to_phys(&sp->rx_ring[DSC_NEXT(idx)]);
|
|
+ rd->status = DMA_RX_OWN;
|
|
+
|
|
+ idx = DSC_NEXT(idx);
|
|
+ }
|
|
+
|
|
+ if (i)
|
|
+ sp->rx_skbprd = idx;
|
|
+}
|
|
+
|
|
+#define AR2313_MAX_PKTS_PER_CALL 64
|
|
+
|
|
+static int ar231x_rx_int(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ struct sk_buff *skb, *skb_new;
|
|
+ ar231x_descr_t *rxdesc;
|
|
+ unsigned int status;
|
|
+ u32 idx;
|
|
+ int pkts = 0;
|
|
+ int rval;
|
|
+
|
|
+ idx = sp->cur_rx;
|
|
+
|
|
+ /* process at most the entire ring and then wait for another int */
|
|
+ while (1) {
|
|
+ rxdesc = &sp->rx_ring[idx];
|
|
+ status = rxdesc->status;
|
|
+
|
|
+ if (status & DMA_RX_OWN) {
|
|
+ /* SiByte owns descriptor or descr not yet filled in */
|
|
+ rval = 0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (++pkts > AR2313_MAX_PKTS_PER_CALL) {
|
|
+ rval = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if ((status & DMA_RX_ERROR) && !(status & DMA_RX_LONG)) {
|
|
+ dev->stats.rx_errors++;
|
|
+ dev->stats.rx_dropped++;
|
|
+
|
|
+ /* add statistics counters */
|
|
+ if (status & DMA_RX_ERR_CRC)
|
|
+ dev->stats.rx_crc_errors++;
|
|
+ if (status & DMA_RX_ERR_COL)
|
|
+ dev->stats.rx_over_errors++;
|
|
+ if (status & DMA_RX_ERR_LENGTH)
|
|
+ dev->stats.rx_length_errors++;
|
|
+ if (status & DMA_RX_ERR_RUNT)
|
|
+ dev->stats.rx_over_errors++;
|
|
+ if (status & DMA_RX_ERR_DESC)
|
|
+ dev->stats.rx_over_errors++;
|
|
+
|
|
+ } else {
|
|
+ /* alloc new buffer. */
|
|
+ skb_new = netdev_alloc_skb_ip_align(dev,
|
|
+ AR2313_BUFSIZE);
|
|
+ if (skb_new != NULL) {
|
|
+ skb = sp->rx_skb[idx];
|
|
+ /* set skb */
|
|
+ skb_put(skb, ((status >> DMA_RX_LEN_SHIFT) &
|
|
+ 0x3fff) - CRC_LEN);
|
|
+
|
|
+ dev->stats.rx_bytes += skb->len;
|
|
+ skb->protocol = eth_type_trans(skb, dev);
|
|
+ /* pass the packet to upper layers */
|
|
+ netif_rx(skb);
|
|
+
|
|
+ skb_new->dev = dev;
|
|
+ /* reset descriptor's curr_addr */
|
|
+ rxdesc->addr = virt_to_phys(skb_new->data);
|
|
+
|
|
+ dev->stats.rx_packets++;
|
|
+ sp->rx_skb[idx] = skb_new;
|
|
+ } else {
|
|
+ dev->stats.rx_dropped++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ rxdesc->devcs = ((AR2313_BUFSIZE << DMA_RX1_BSIZE_SHIFT) |
|
|
+ DMA_RX1_CHAINED);
|
|
+ rxdesc->status = DMA_RX_OWN;
|
|
+
|
|
+ idx = DSC_NEXT(idx);
|
|
+ }
|
|
+
|
|
+ sp->cur_rx = idx;
|
|
+
|
|
+ return rval;
|
|
+}
|
|
+
|
|
+static void ar231x_tx_int(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ u32 idx;
|
|
+ struct sk_buff *skb;
|
|
+ ar231x_descr_t *txdesc;
|
|
+ unsigned int status = 0;
|
|
+
|
|
+ idx = sp->tx_csm;
|
|
+
|
|
+ while (idx != sp->tx_prd) {
|
|
+ txdesc = &sp->tx_ring[idx];
|
|
+ status = txdesc->status;
|
|
+
|
|
+ if (status & DMA_TX_OWN) {
|
|
+ /* ar231x dma still owns descr */
|
|
+ break;
|
|
+ }
|
|
+ /* done with this descriptor */
|
|
+ dma_unmap_single(NULL, txdesc->addr,
|
|
+ txdesc->devcs & DMA_TX1_BSIZE_MASK,
|
|
+ DMA_TO_DEVICE);
|
|
+ txdesc->status = 0;
|
|
+
|
|
+ if (status & DMA_TX_ERROR) {
|
|
+ dev->stats.tx_errors++;
|
|
+ dev->stats.tx_dropped++;
|
|
+ if (status & DMA_TX_ERR_UNDER)
|
|
+ dev->stats.tx_fifo_errors++;
|
|
+ if (status & DMA_TX_ERR_HB)
|
|
+ dev->stats.tx_heartbeat_errors++;
|
|
+ if (status & (DMA_TX_ERR_LOSS | DMA_TX_ERR_LINK))
|
|
+ dev->stats.tx_carrier_errors++;
|
|
+ if (status & (DMA_TX_ERR_LATE | DMA_TX_ERR_COL |
|
|
+ DMA_TX_ERR_JABBER | DMA_TX_ERR_DEFER))
|
|
+ dev->stats.tx_aborted_errors++;
|
|
+ } else {
|
|
+ /* transmit OK */
|
|
+ dev->stats.tx_packets++;
|
|
+ }
|
|
+
|
|
+ skb = sp->tx_skb[idx];
|
|
+ sp->tx_skb[idx] = NULL;
|
|
+ idx = DSC_NEXT(idx);
|
|
+ dev->stats.tx_bytes += skb->len;
|
|
+ dev_kfree_skb_irq(skb);
|
|
+ }
|
|
+
|
|
+ sp->tx_csm = idx;
|
|
+}
|
|
+
|
|
+static void rx_tasklet_func(unsigned long data)
|
|
+{
|
|
+ struct net_device *dev = (struct net_device *)data;
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+
|
|
+ if (sp->unloading)
|
|
+ return;
|
|
+
|
|
+ if (ar231x_rx_int(dev)) {
|
|
+ tasklet_hi_schedule(&sp->rx_tasklet);
|
|
+ } else {
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&sp->lock, flags);
|
|
+ sp->dma_regs->intr_ena |= DMA_STATUS_RI;
|
|
+ spin_unlock_irqrestore(&sp->lock, flags);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void rx_schedule(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+
|
|
+ sp->dma_regs->intr_ena &= ~DMA_STATUS_RI;
|
|
+
|
|
+ tasklet_hi_schedule(&sp->rx_tasklet);
|
|
+}
|
|
+
|
|
+static irqreturn_t ar231x_interrupt(int irq, void *dev_id)
|
|
+{
|
|
+ struct net_device *dev = (struct net_device *)dev_id;
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ unsigned int status, enabled;
|
|
+
|
|
+ /* clear interrupt */
|
|
+ /* Don't clear RI bit if currently disabled */
|
|
+ status = sp->dma_regs->status;
|
|
+ enabled = sp->dma_regs->intr_ena;
|
|
+ sp->dma_regs->status = status & enabled;
|
|
+
|
|
+ if (status & DMA_STATUS_NIS) {
|
|
+ /* normal status */
|
|
+ /**
|
|
+ * Don't schedule rx processing if interrupt
|
|
+ * is already disabled.
|
|
+ */
|
|
+ if (status & enabled & DMA_STATUS_RI) {
|
|
+ /* receive interrupt */
|
|
+ rx_schedule(dev);
|
|
+ }
|
|
+ if (status & DMA_STATUS_TI) {
|
|
+ /* transmit interrupt */
|
|
+ ar231x_tx_int(dev);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* abnormal status */
|
|
+ if (status & (DMA_STATUS_FBE | DMA_STATUS_TPS))
|
|
+ ar231x_restart(dev);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int ar231x_open(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ unsigned int ethsal, ethsah;
|
|
+
|
|
+ /* reset the hardware, in case the MAC address changed */
|
|
+ ethsah = (((u_int) (dev->dev_addr[5]) << 8) & (u_int) 0x0000FF00) |
|
|
+ (((u_int) (dev->dev_addr[4]) << 0) & (u_int) 0x000000FF);
|
|
+
|
|
+ ethsal = (((u_int) (dev->dev_addr[3]) << 24) & (u_int) 0xFF000000) |
|
|
+ (((u_int) (dev->dev_addr[2]) << 16) & (u_int) 0x00FF0000) |
|
|
+ (((u_int) (dev->dev_addr[1]) << 8) & (u_int) 0x0000FF00) |
|
|
+ (((u_int) (dev->dev_addr[0]) << 0) & (u_int) 0x000000FF);
|
|
+
|
|
+ sp->eth_regs->mac_addr[0] = ethsah;
|
|
+ sp->eth_regs->mac_addr[1] = ethsal;
|
|
+
|
|
+ mdelay(10);
|
|
+
|
|
+ dev->mtu = 1500;
|
|
+ netif_start_queue(dev);
|
|
+
|
|
+ sp->eth_regs->mac_control |= MAC_CONTROL_RE;
|
|
+
|
|
+ phy_start(sp->phy_dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void ar231x_tx_timeout(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&sp->lock, flags);
|
|
+ ar231x_restart(dev);
|
|
+ spin_unlock_irqrestore(&sp->lock, flags);
|
|
+}
|
|
+
|
|
+static void ar231x_halt(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ int j;
|
|
+
|
|
+ tasklet_disable(&sp->rx_tasklet);
|
|
+
|
|
+ /* kill the MAC */
|
|
+ sp->eth_regs->mac_control &= ~(MAC_CONTROL_RE | /* disable Receives */
|
|
+ MAC_CONTROL_TE); /* disable Transmits */
|
|
+ /* stop dma */
|
|
+ sp->dma_regs->control = 0;
|
|
+ sp->dma_regs->bus_mode = DMA_BUS_MODE_SWR;
|
|
+
|
|
+ /* place phy and MAC in reset */
|
|
+ sp->cfg->reset_set(sp->cfg->reset_mac);
|
|
+ sp->cfg->reset_set(sp->cfg->reset_phy);
|
|
+
|
|
+ /* free buffers on tx ring */
|
|
+ for (j = 0; j < AR2313_DESCR_ENTRIES; j++) {
|
|
+ struct sk_buff *skb;
|
|
+ ar231x_descr_t *txdesc;
|
|
+
|
|
+ txdesc = &sp->tx_ring[j];
|
|
+ txdesc->descr = 0;
|
|
+
|
|
+ skb = sp->tx_skb[j];
|
|
+ if (skb) {
|
|
+ dev_kfree_skb(skb);
|
|
+ sp->tx_skb[j] = NULL;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * close should do nothing. Here's why. It's called when
|
|
+ * 'ifconfig bond0 down' is run. If it calls free_irq then
|
|
+ * the irq is gone forever ! When bond0 is made 'up' again,
|
|
+ * the ar231x_open () does not call request_irq (). Worse,
|
|
+ * the call to ar231x_halt() generates a WDOG reset due to
|
|
+ * the write to reset register and the box reboots.
|
|
+ * Commenting this out is good since it allows the
|
|
+ * system to resume when bond0 is made up again.
|
|
+ */
|
|
+static int ar231x_close(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+#if 0
|
|
+ /* Disable interrupts */
|
|
+ disable_irq(dev->irq);
|
|
+
|
|
+ /**
|
|
+ * Without (or before) releasing irq and stopping hardware, this
|
|
+ * is an absolute non-sense, by the way. It will be reset instantly
|
|
+ * by the first irq.
|
|
+ */
|
|
+ netif_stop_queue(dev);
|
|
+
|
|
+ /* stop the MAC and DMA engines */
|
|
+ ar231x_halt(dev);
|
|
+
|
|
+ /* release the interrupt */
|
|
+ free_irq(dev->irq, dev);
|
|
+
|
|
+#endif
|
|
+
|
|
+ phy_stop(sp->phy_dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ar231x_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ ar231x_descr_t *td;
|
|
+ u32 idx;
|
|
+
|
|
+ idx = sp->tx_prd;
|
|
+ td = &sp->tx_ring[idx];
|
|
+
|
|
+ if (td->status & DMA_TX_OWN) {
|
|
+ /* free skbuf and lie to the caller that we sent it out */
|
|
+ dev->stats.tx_dropped++;
|
|
+ dev_kfree_skb(skb);
|
|
+
|
|
+ /* restart transmitter in case locked */
|
|
+ sp->dma_regs->xmt_poll = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Setup the transmit descriptor. */
|
|
+ td->devcs = ((skb->len << DMA_TX1_BSIZE_SHIFT) |
|
|
+ (DMA_TX1_LS | DMA_TX1_IC | DMA_TX1_CHAINED));
|
|
+ td->addr = dma_map_single(NULL, skb->data, skb->len, DMA_TO_DEVICE);
|
|
+ td->status = DMA_TX_OWN;
|
|
+
|
|
+ /* kick transmitter last */
|
|
+ sp->dma_regs->xmt_poll = 0;
|
|
+
|
|
+ sp->tx_skb[idx] = skb;
|
|
+ idx = DSC_NEXT(idx);
|
|
+ sp->tx_prd = idx;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ar231x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+
|
|
+ switch (cmd) {
|
|
+ case SIOCGMIIPHY:
|
|
+ case SIOCGMIIREG:
|
|
+ case SIOCSMIIREG:
|
|
+ return phy_mii_ioctl(sp->phy_dev, ifr, cmd);
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return -EOPNOTSUPP;
|
|
+}
|
|
+
|
|
+static void ar231x_adjust_link(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ struct phy_device *phydev = sp->phy_dev;
|
|
+ u32 mc;
|
|
+
|
|
+ if (!phydev->link) {
|
|
+ if (sp->link) {
|
|
+ pr_info("%s: link down\n", dev->name);
|
|
+ sp->link = 0;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ sp->link = 1;
|
|
+
|
|
+ pr_info("%s: link up (%uMbps/%s duplex)\n", dev->name,
|
|
+ phydev->speed, phydev->duplex ? "full" : "half");
|
|
+
|
|
+ mc = sp->eth_regs->mac_control;
|
|
+ if (phydev->duplex)
|
|
+ mc = (mc | MAC_CONTROL_F) & ~MAC_CONTROL_DRO;
|
|
+ else
|
|
+ mc = (mc | MAC_CONTROL_DRO) & ~MAC_CONTROL_F;
|
|
+ sp->eth_regs->mac_control = mc;
|
|
+ sp->duplex = phydev->duplex;
|
|
+}
|
|
+
|
|
+#define MII_ADDR(phy, reg) \
|
|
+ ((reg << MII_ADDR_REG_SHIFT) | (phy << MII_ADDR_PHY_SHIFT))
|
|
+
|
|
+static int
|
|
+ar231x_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
|
|
+{
|
|
+ struct net_device *const dev = bus->priv;
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ volatile MII *ethernet = sp->phy_regs;
|
|
+
|
|
+ ethernet->mii_addr = MII_ADDR(phy_addr, regnum);
|
|
+ while (ethernet->mii_addr & MII_ADDR_BUSY)
|
|
+ ;
|
|
+ return ethernet->mii_data >> MII_DATA_SHIFT;
|
|
+}
|
|
+
|
|
+static int
|
|
+ar231x_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, u16 value)
|
|
+{
|
|
+ struct net_device *const dev = bus->priv;
|
|
+ struct ar231x_private *sp = netdev_priv(dev);
|
|
+ volatile MII *ethernet = sp->phy_regs;
|
|
+
|
|
+ while (ethernet->mii_addr & MII_ADDR_BUSY)
|
|
+ ;
|
|
+ ethernet->mii_data = value << MII_DATA_SHIFT;
|
|
+ ethernet->mii_addr = MII_ADDR(phy_addr, regnum) | MII_ADDR_WRITE;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ar231x_mdiobus_reset(struct mii_bus *bus)
|
|
+{
|
|
+ struct net_device *const dev = bus->priv;
|
|
+
|
|
+ ar231x_reset_reg(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ar231x_mdiobus_probe(struct net_device *dev)
|
|
+{
|
|
+ struct ar231x_private *const sp = netdev_priv(dev);
|
|
+ struct phy_device *phydev = NULL;
|
|
+
|
|
+ /* find the first (lowest address) PHY on the current MAC's MII bus */
|
|
+ phydev = phy_find_first(sp->mii_bus);
|
|
+ if (!phydev) {
|
|
+ printk(KERN_ERR "ar231x: %s: no PHY found\n", dev->name);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* now we are supposed to have a proper phydev, to attach to... */
|
|
+ BUG_ON(phydev->attached_dev);
|
|
+
|
|
+ phydev = phy_connect(dev, phydev_name(phydev), &ar231x_adjust_link,
|
|
+ PHY_INTERFACE_MODE_MII);
|
|
+ if (IS_ERR(phydev)) {
|
|
+ printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
|
|
+ return PTR_ERR(phydev);
|
|
+ }
|
|
+
|
|
+ /* mask with MAC supported features */
|
|
+ phydev->supported &= (SUPPORTED_10baseT_Half
|
|
+ | SUPPORTED_10baseT_Full
|
|
+ | SUPPORTED_100baseT_Half
|
|
+ | SUPPORTED_100baseT_Full
|
|
+ | SUPPORTED_Autoneg
|
|
+ /* | SUPPORTED_Pause | SUPPORTED_Asym_Pause */
|
|
+ | SUPPORTED_MII
|
|
+ | SUPPORTED_TP);
|
|
+
|
|
+ phydev->advertising = phydev->supported;
|
|
+
|
|
+ sp->phy_dev = phydev;
|
|
+
|
|
+ printk(KERN_INFO "%s: attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
|
|
+ dev->name, phydev->drv->name, phydev_name(phydev));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/atheros/ar231x/ar231x.h
|
|
@@ -0,0 +1,281 @@
|
|
+/*
|
|
+ * ar231x.h: Linux driver for the Atheros AR231x Ethernet device.
|
|
+ *
|
|
+ * Copyright (C) 2004 by Sameer Dekate <sdekate@arubanetworks.com>
|
|
+ * Copyright (C) 2006 Imre Kaloz <kaloz@openwrt.org>
|
|
+ * Copyright (C) 2006-2009 Felix Fietkau <nbd@nbd.name>
|
|
+ *
|
|
+ * Thanks to Atheros for providing hardware and documentation
|
|
+ * enabling me to write this driver.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#ifndef _AR2313_H_
|
|
+#define _AR2313_H_
|
|
+
|
|
+#include <linux/interrupt.h>
|
|
+#include <generated/autoconf.h>
|
|
+#include <linux/bitops.h>
|
|
+#include <ath25_platform.h>
|
|
+
|
|
+/* probe link timer - 5 secs */
|
|
+#define LINK_TIMER (5*HZ)
|
|
+
|
|
+#define IS_DMA_TX_INT(X) (((X) & (DMA_STATUS_TI)) != 0)
|
|
+#define IS_DMA_RX_INT(X) (((X) & (DMA_STATUS_RI)) != 0)
|
|
+#define IS_DRIVER_OWNED(X) (((X) & (DMA_TX_OWN)) == 0)
|
|
+
|
|
+#define AR2313_TX_TIMEOUT (HZ/4)
|
|
+
|
|
+/* Rings */
|
|
+#define DSC_RING_ENTRIES_SIZE (AR2313_DESCR_ENTRIES * sizeof(struct desc))
|
|
+#define DSC_NEXT(idx) ((idx + 1) & (AR2313_DESCR_ENTRIES - 1))
|
|
+
|
|
+#define AR2313_MBGET 2
|
|
+#define AR2313_MBSET 3
|
|
+#define AR2313_PCI_RECONFIG 4
|
|
+#define AR2313_PCI_DUMP 5
|
|
+#define AR2313_TEST_PANIC 6
|
|
+#define AR2313_TEST_NULLPTR 7
|
|
+#define AR2313_READ_DATA 8
|
|
+#define AR2313_WRITE_DATA 9
|
|
+#define AR2313_GET_VERSION 10
|
|
+#define AR2313_TEST_HANG 11
|
|
+#define AR2313_SYNC 12
|
|
+
|
|
+#define DMA_RX_ERR_CRC BIT(1)
|
|
+#define DMA_RX_ERR_DRIB BIT(2)
|
|
+#define DMA_RX_ERR_MII BIT(3)
|
|
+#define DMA_RX_EV2 BIT(5)
|
|
+#define DMA_RX_ERR_COL BIT(6)
|
|
+#define DMA_RX_LONG BIT(7)
|
|
+#define DMA_RX_LS BIT(8) /* last descriptor */
|
|
+#define DMA_RX_FS BIT(9) /* first descriptor */
|
|
+#define DMA_RX_MF BIT(10) /* multicast frame */
|
|
+#define DMA_RX_ERR_RUNT BIT(11) /* runt frame */
|
|
+#define DMA_RX_ERR_LENGTH BIT(12) /* length error */
|
|
+#define DMA_RX_ERR_DESC BIT(14) /* descriptor error */
|
|
+#define DMA_RX_ERROR BIT(15) /* error summary */
|
|
+#define DMA_RX_LEN_MASK 0x3fff0000
|
|
+#define DMA_RX_LEN_SHIFT 16
|
|
+#define DMA_RX_FILT BIT(30)
|
|
+#define DMA_RX_OWN BIT(31) /* desc owned by DMA controller */
|
|
+
|
|
+#define DMA_RX1_BSIZE_MASK 0x000007ff
|
|
+#define DMA_RX1_BSIZE_SHIFT 0
|
|
+#define DMA_RX1_CHAINED BIT(24)
|
|
+#define DMA_RX1_RER BIT(25)
|
|
+
|
|
+#define DMA_TX_ERR_UNDER BIT(1) /* underflow error */
|
|
+#define DMA_TX_ERR_DEFER BIT(2) /* excessive deferral */
|
|
+#define DMA_TX_COL_MASK 0x78
|
|
+#define DMA_TX_COL_SHIFT 3
|
|
+#define DMA_TX_ERR_HB BIT(7) /* hearbeat failure */
|
|
+#define DMA_TX_ERR_COL BIT(8) /* excessive collisions */
|
|
+#define DMA_TX_ERR_LATE BIT(9) /* late collision */
|
|
+#define DMA_TX_ERR_LINK BIT(10) /* no carrier */
|
|
+#define DMA_TX_ERR_LOSS BIT(11) /* loss of carrier */
|
|
+#define DMA_TX_ERR_JABBER BIT(14) /* transmit jabber timeout */
|
|
+#define DMA_TX_ERROR BIT(15) /* frame aborted */
|
|
+#define DMA_TX_OWN BIT(31) /* descr owned by DMA controller */
|
|
+
|
|
+#define DMA_TX1_BSIZE_MASK 0x000007ff
|
|
+#define DMA_TX1_BSIZE_SHIFT 0
|
|
+#define DMA_TX1_CHAINED BIT(24) /* chained descriptors */
|
|
+#define DMA_TX1_TER BIT(25) /* transmit end of ring */
|
|
+#define DMA_TX1_FS BIT(29) /* first segment */
|
|
+#define DMA_TX1_LS BIT(30) /* last segment */
|
|
+#define DMA_TX1_IC BIT(31) /* interrupt on completion */
|
|
+
|
|
+#define RCVPKT_LENGTH(X) (X >> 16) /* Received pkt Length */
|
|
+
|
|
+#define MAC_CONTROL_RE BIT(2) /* receive enable */
|
|
+#define MAC_CONTROL_TE BIT(3) /* transmit enable */
|
|
+#define MAC_CONTROL_DC BIT(5) /* Deferral check */
|
|
+#define MAC_CONTROL_ASTP BIT(8) /* Auto pad strip */
|
|
+#define MAC_CONTROL_DRTY BIT(10) /* Disable retry */
|
|
+#define MAC_CONTROL_DBF BIT(11) /* Disable bcast frames */
|
|
+#define MAC_CONTROL_LCC BIT(12) /* late collision ctrl */
|
|
+#define MAC_CONTROL_HP BIT(13) /* Hash Perfect filtering */
|
|
+#define MAC_CONTROL_HASH BIT(14) /* Unicast hash filtering */
|
|
+#define MAC_CONTROL_HO BIT(15) /* Hash only filtering */
|
|
+#define MAC_CONTROL_PB BIT(16) /* Pass Bad frames */
|
|
+#define MAC_CONTROL_IF BIT(17) /* Inverse filtering */
|
|
+#define MAC_CONTROL_PR BIT(18) /* promis mode (valid frames only) */
|
|
+#define MAC_CONTROL_PM BIT(19) /* pass multicast */
|
|
+#define MAC_CONTROL_F BIT(20) /* full-duplex */
|
|
+#define MAC_CONTROL_DRO BIT(23) /* Disable Receive Own */
|
|
+#define MAC_CONTROL_HBD BIT(28) /* heart-beat disabled (MUST BE SET) */
|
|
+#define MAC_CONTROL_BLE BIT(30) /* big endian mode */
|
|
+#define MAC_CONTROL_RA BIT(31) /* rcv all (valid and invalid frames) */
|
|
+
|
|
+#define MII_ADDR_BUSY BIT(0)
|
|
+#define MII_ADDR_WRITE BIT(1)
|
|
+#define MII_ADDR_REG_SHIFT 6
|
|
+#define MII_ADDR_PHY_SHIFT 11
|
|
+#define MII_DATA_SHIFT 0
|
|
+
|
|
+#define FLOW_CONTROL_FCE BIT(1)
|
|
+
|
|
+#define DMA_BUS_MODE_SWR BIT(0) /* software reset */
|
|
+#define DMA_BUS_MODE_BLE BIT(7) /* big endian mode */
|
|
+#define DMA_BUS_MODE_PBL_SHIFT 8 /* programmable burst length 32 */
|
|
+#define DMA_BUS_MODE_DBO BIT(20) /* big-endian descriptors */
|
|
+
|
|
+#define DMA_STATUS_TI BIT(0) /* transmit interrupt */
|
|
+#define DMA_STATUS_TPS BIT(1) /* transmit process stopped */
|
|
+#define DMA_STATUS_TU BIT(2) /* transmit buffer unavailable */
|
|
+#define DMA_STATUS_TJT BIT(3) /* transmit buffer timeout */
|
|
+#define DMA_STATUS_UNF BIT(5) /* transmit underflow */
|
|
+#define DMA_STATUS_RI BIT(6) /* receive interrupt */
|
|
+#define DMA_STATUS_RU BIT(7) /* receive buffer unavailable */
|
|
+#define DMA_STATUS_RPS BIT(8) /* receive process stopped */
|
|
+#define DMA_STATUS_ETI BIT(10) /* early transmit interrupt */
|
|
+#define DMA_STATUS_FBE BIT(13) /* fatal bus interrupt */
|
|
+#define DMA_STATUS_ERI BIT(14) /* early receive interrupt */
|
|
+#define DMA_STATUS_AIS BIT(15) /* abnormal interrupt summary */
|
|
+#define DMA_STATUS_NIS BIT(16) /* normal interrupt summary */
|
|
+#define DMA_STATUS_RS_SHIFT 17 /* receive process state */
|
|
+#define DMA_STATUS_TS_SHIFT 20 /* transmit process state */
|
|
+#define DMA_STATUS_EB_SHIFT 23 /* error bits */
|
|
+
|
|
+#define DMA_CONTROL_SR BIT(1) /* start receive */
|
|
+#define DMA_CONTROL_ST BIT(13) /* start transmit */
|
|
+#define DMA_CONTROL_SF BIT(21) /* store and forward */
|
|
+
|
|
+typedef struct {
|
|
+ volatile unsigned int status; /* OWN, Device control and status. */
|
|
+ volatile unsigned int devcs; /* pkt Control bits + Length */
|
|
+ volatile unsigned int addr; /* Current Address. */
|
|
+ volatile unsigned int descr; /* Next descriptor in chain. */
|
|
+} ar231x_descr_t;
|
|
+
|
|
+/**
|
|
+ * New Combo structure for Both Eth0 AND eth1
|
|
+ *
|
|
+ * Don't directly access MII related regs since phy chip could be actually
|
|
+ * connected to another ethernet block.
|
|
+ */
|
|
+typedef struct {
|
|
+ volatile unsigned int mac_control; /* 0x00 */
|
|
+ volatile unsigned int mac_addr[2]; /* 0x04 - 0x08 */
|
|
+ volatile unsigned int mcast_table[2]; /* 0x0c - 0x10 */
|
|
+ volatile unsigned int __mii_addr; /* 0x14 */
|
|
+ volatile unsigned int __mii_data; /* 0x18 */
|
|
+ volatile unsigned int flow_control; /* 0x1c */
|
|
+ volatile unsigned int vlan_tag; /* 0x20 */
|
|
+ volatile unsigned int pad[7]; /* 0x24 - 0x3c */
|
|
+ volatile unsigned int ucast_table[8]; /* 0x40-0x5c */
|
|
+} ETHERNET_STRUCT;
|
|
+
|
|
+typedef struct {
|
|
+ volatile unsigned int mii_addr;
|
|
+ volatile unsigned int mii_data;
|
|
+} MII;
|
|
+
|
|
+/********************************************************************
|
|
+ * Interrupt controller
|
|
+ ********************************************************************/
|
|
+
|
|
+typedef struct {
|
|
+ volatile unsigned int wdog_control; /* 0x08 */
|
|
+ volatile unsigned int wdog_timer; /* 0x0c */
|
|
+ volatile unsigned int misc_status; /* 0x10 */
|
|
+ volatile unsigned int misc_mask; /* 0x14 */
|
|
+ volatile unsigned int global_status; /* 0x18 */
|
|
+ volatile unsigned int reserved; /* 0x1c */
|
|
+ volatile unsigned int reset_control; /* 0x20 */
|
|
+} INTERRUPT;
|
|
+
|
|
+/********************************************************************
|
|
+ * DMA controller
|
|
+ ********************************************************************/
|
|
+typedef struct {
|
|
+ volatile unsigned int bus_mode; /* 0x00 (CSR0) */
|
|
+ volatile unsigned int xmt_poll; /* 0x04 (CSR1) */
|
|
+ volatile unsigned int rcv_poll; /* 0x08 (CSR2) */
|
|
+ volatile unsigned int rcv_base; /* 0x0c (CSR3) */
|
|
+ volatile unsigned int xmt_base; /* 0x10 (CSR4) */
|
|
+ volatile unsigned int status; /* 0x14 (CSR5) */
|
|
+ volatile unsigned int control; /* 0x18 (CSR6) */
|
|
+ volatile unsigned int intr_ena; /* 0x1c (CSR7) */
|
|
+ volatile unsigned int rcv_missed; /* 0x20 (CSR8) */
|
|
+ volatile unsigned int reserved[11]; /* 0x24-0x4c (CSR9-19) */
|
|
+ volatile unsigned int cur_tx_buf_addr; /* 0x50 (CSR20) */
|
|
+ volatile unsigned int cur_rx_buf_addr; /* 0x50 (CSR21) */
|
|
+} DMA;
|
|
+
|
|
+/**
|
|
+ * Struct private for the Sibyte.
|
|
+ *
|
|
+ * Elements are grouped so variables used by the tx handling goes
|
|
+ * together, and will go into the same cache lines etc. in order to
|
|
+ * avoid cache line contention between the rx and tx handling on SMP.
|
|
+ *
|
|
+ * Frequently accessed variables are put at the beginning of the
|
|
+ * struct to help the compiler generate better/shorter code.
|
|
+ */
|
|
+struct ar231x_private {
|
|
+ struct net_device *dev;
|
|
+ int version;
|
|
+ u32 mb[2];
|
|
+
|
|
+ volatile MII *phy_regs;
|
|
+ volatile ETHERNET_STRUCT *eth_regs;
|
|
+ volatile DMA *dma_regs;
|
|
+ struct ar231x_eth *cfg;
|
|
+
|
|
+ spinlock_t lock; /* Serialise access to device */
|
|
+
|
|
+ /* RX and TX descriptors, must be adjacent */
|
|
+ ar231x_descr_t *rx_ring;
|
|
+ ar231x_descr_t *tx_ring;
|
|
+
|
|
+ struct sk_buff **rx_skb;
|
|
+ struct sk_buff **tx_skb;
|
|
+
|
|
+ /* RX elements */
|
|
+ u32 rx_skbprd;
|
|
+ u32 cur_rx;
|
|
+
|
|
+ /* TX elements */
|
|
+ u32 tx_prd;
|
|
+ u32 tx_csm;
|
|
+
|
|
+ /* Misc elements */
|
|
+ char name[48];
|
|
+ struct {
|
|
+ u32 address;
|
|
+ u32 length;
|
|
+ char *mapping;
|
|
+ } desc;
|
|
+
|
|
+ unsigned short link; /* 0 - link down, 1 - link up */
|
|
+ unsigned short duplex; /* 0 - half, 1 - full */
|
|
+
|
|
+ struct tasklet_struct rx_tasklet;
|
|
+ int unloading;
|
|
+
|
|
+ struct phy_device *phy_dev;
|
|
+ struct mii_bus *mii_bus;
|
|
+};
|
|
+
|
|
+/* Prototypes */
|
|
+static int ar231x_init(struct net_device *dev);
|
|
+#ifdef TX_TIMEOUT
|
|
+static void ar231x_tx_timeout(struct net_device *dev);
|
|
+#endif
|
|
+static int ar231x_restart(struct net_device *dev);
|
|
+static void ar231x_load_rx_ring(struct net_device *dev, int bufs);
|
|
+static irqreturn_t ar231x_interrupt(int irq, void *dev_id);
|
|
+static int ar231x_open(struct net_device *dev);
|
|
+static int ar231x_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
|
+static int ar231x_close(struct net_device *dev);
|
|
+static int ar231x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
|
|
+static void ar231x_init_cleanup(struct net_device *dev);
|
|
+
|
|
+#endif /* _AR2313_H_ */
|
|
--- a/arch/mips/ath25/ar2315_regs.h
|
|
+++ b/arch/mips/ath25/ar2315_regs.h
|
|
@@ -57,6 +57,9 @@
|
|
#define AR2315_PCI_EXT_BASE 0x80000000 /* PCI external */
|
|
#define AR2315_PCI_EXT_SIZE 0x40000000
|
|
|
|
+/* MII registers offset inside Ethernet MMR region */
|
|
+#define AR2315_ENET0_MII_BASE (AR2315_ENET0_BASE + 0x14)
|
|
+
|
|
/*
|
|
* Configuration registers
|
|
*/
|
|
--- a/arch/mips/ath25/ar5312_regs.h
|
|
+++ b/arch/mips/ath25/ar5312_regs.h
|
|
@@ -64,6 +64,10 @@
|
|
#define AR5312_AR5312_REV7 0x0057 /* AR5312 WMAC (AP30-040) */
|
|
#define AR5312_AR2313_REV8 0x0058 /* AR2313 WMAC (AP43-030) */
|
|
|
|
+/* MII registers offset inside Ethernet MMR region */
|
|
+#define AR5312_ENET0_MII_BASE (AR5312_ENET0_BASE + 0x14)
|
|
+#define AR5312_ENET1_MII_BASE (AR5312_ENET1_BASE + 0x14)
|
|
+
|
|
/* Reset/Timer Block Address Map */
|
|
#define AR5312_TIMER 0x0000 /* countdown timer */
|
|
#define AR5312_RELOAD 0x0004 /* timer reload value */
|
|
--- a/arch/mips/ath25/ar2315.c
|
|
+++ b/arch/mips/ath25/ar2315.c
|
|
@@ -136,6 +136,8 @@ static void ar2315_irq_dispatch(void)
|
|
|
|
if (pending & CAUSEF_IP3)
|
|
do_IRQ(AR2315_IRQ_WLAN0);
|
|
+ else if (pending & CAUSEF_IP4)
|
|
+ do_IRQ(AR2315_IRQ_ENET0);
|
|
#ifdef CONFIG_PCI_AR2315
|
|
else if (pending & CAUSEF_IP5)
|
|
do_IRQ(AR2315_IRQ_LCBUS_PCI);
|
|
@@ -169,6 +171,29 @@ void __init ar2315_arch_init_irq(void)
|
|
ar2315_misc_irq_domain = domain;
|
|
}
|
|
|
|
+static void ar2315_device_reset_set(u32 mask)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = ar2315_rst_reg_read(AR2315_RESET);
|
|
+ ar2315_rst_reg_write(AR2315_RESET, val | mask);
|
|
+}
|
|
+
|
|
+static void ar2315_device_reset_clear(u32 mask)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = ar2315_rst_reg_read(AR2315_RESET);
|
|
+ ar2315_rst_reg_write(AR2315_RESET, val & ~mask);
|
|
+}
|
|
+
|
|
+static struct ar231x_eth ar2315_eth_data = {
|
|
+ .reset_set = ar2315_device_reset_set,
|
|
+ .reset_clear = ar2315_device_reset_clear,
|
|
+ .reset_mac = AR2315_RESET_ENET0,
|
|
+ .reset_phy = AR2315_RESET_EPHY0,
|
|
+};
|
|
+
|
|
static struct resource ar2315_gpio_res[] = {
|
|
{
|
|
.name = "ar2315-gpio",
|
|
@@ -205,6 +230,11 @@ void __init ar2315_init_devices(void)
|
|
ar2315_gpio_res[1].end = ar2315_gpio_res[1].start;
|
|
platform_device_register(&ar2315_gpio);
|
|
|
|
+ ar2315_eth_data.macaddr = ath25_board.config->enet0_mac;
|
|
+ ath25_add_ethernet(0, AR2315_ENET0_BASE, "eth0_mii",
|
|
+ AR2315_ENET0_MII_BASE, AR2315_IRQ_ENET0,
|
|
+ &ar2315_eth_data);
|
|
+
|
|
ath25_add_wmac(0, AR2315_WLAN0_BASE, AR2315_IRQ_WLAN0);
|
|
}
|
|
|
|
--- a/arch/mips/ath25/ar5312.c
|
|
+++ b/arch/mips/ath25/ar5312.c
|
|
@@ -132,6 +132,10 @@ static void ar5312_irq_dispatch(void)
|
|
|
|
if (pending & CAUSEF_IP2)
|
|
do_IRQ(AR5312_IRQ_WLAN0);
|
|
+ else if (pending & CAUSEF_IP3)
|
|
+ do_IRQ(AR5312_IRQ_ENET0);
|
|
+ else if (pending & CAUSEF_IP4)
|
|
+ do_IRQ(AR5312_IRQ_ENET1);
|
|
else if (pending & CAUSEF_IP5)
|
|
do_IRQ(AR5312_IRQ_WLAN1);
|
|
else if (pending & CAUSEF_IP6)
|
|
@@ -163,6 +167,36 @@ void __init ar5312_arch_init_irq(void)
|
|
ar5312_misc_irq_domain = domain;
|
|
}
|
|
|
|
+static void ar5312_device_reset_set(u32 mask)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = ar5312_rst_reg_read(AR5312_RESET);
|
|
+ ar5312_rst_reg_write(AR5312_RESET, val | mask);
|
|
+}
|
|
+
|
|
+static void ar5312_device_reset_clear(u32 mask)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = ar5312_rst_reg_read(AR5312_RESET);
|
|
+ ar5312_rst_reg_write(AR5312_RESET, val & ~mask);
|
|
+}
|
|
+
|
|
+static struct ar231x_eth ar5312_eth0_data = {
|
|
+ .reset_set = ar5312_device_reset_set,
|
|
+ .reset_clear = ar5312_device_reset_clear,
|
|
+ .reset_mac = AR5312_RESET_ENET0,
|
|
+ .reset_phy = AR5312_RESET_EPHY0,
|
|
+};
|
|
+
|
|
+static struct ar231x_eth ar5312_eth1_data = {
|
|
+ .reset_set = ar5312_device_reset_set,
|
|
+ .reset_clear = ar5312_device_reset_clear,
|
|
+ .reset_mac = AR5312_RESET_ENET1,
|
|
+ .reset_phy = AR5312_RESET_EPHY1,
|
|
+};
|
|
+
|
|
static struct physmap_flash_data ar5312_flash_data = {
|
|
.width = 2,
|
|
};
|
|
@@ -243,6 +277,7 @@ static void __init ar5312_flash_init(voi
|
|
void __init ar5312_init_devices(void)
|
|
{
|
|
struct ath25_boarddata *config;
|
|
+ u8 *c;
|
|
|
|
ar5312_flash_init();
|
|
|
|
@@ -266,8 +301,30 @@ void __init ar5312_init_devices(void)
|
|
|
|
platform_device_register(&ar5312_gpio);
|
|
|
|
+ /* Fix up MAC addresses if necessary */
|
|
+ if (is_broadcast_ether_addr(config->enet0_mac))
|
|
+ ether_addr_copy(config->enet0_mac, config->enet1_mac);
|
|
+
|
|
+ /* If ENET0 and ENET1 have the same mac address,
|
|
+ * increment the one from ENET1 */
|
|
+ if (ether_addr_equal(config->enet0_mac, config->enet1_mac)) {
|
|
+ c = config->enet1_mac + 5;
|
|
+ while ((c >= config->enet1_mac) && !(++(*c)))
|
|
+ c--;
|
|
+ }
|
|
+
|
|
switch (ath25_soc) {
|
|
case ATH25_SOC_AR5312:
|
|
+ ar5312_eth0_data.macaddr = config->enet0_mac;
|
|
+ ath25_add_ethernet(0, AR5312_ENET0_BASE, "eth0_mii",
|
|
+ AR5312_ENET0_MII_BASE, AR5312_IRQ_ENET0,
|
|
+ &ar5312_eth0_data);
|
|
+
|
|
+ ar5312_eth1_data.macaddr = config->enet1_mac;
|
|
+ ath25_add_ethernet(1, AR5312_ENET1_BASE, "eth1_mii",
|
|
+ AR5312_ENET1_MII_BASE, AR5312_IRQ_ENET1,
|
|
+ &ar5312_eth1_data);
|
|
+
|
|
if (!ath25_board.radio)
|
|
return;
|
|
|
|
@@ -276,8 +333,18 @@ void __init ar5312_init_devices(void)
|
|
|
|
ath25_add_wmac(0, AR5312_WLAN0_BASE, AR5312_IRQ_WLAN0);
|
|
break;
|
|
+ /*
|
|
+ * AR2312/3 ethernet uses the PHY of ENET0, but the MAC
|
|
+ * of ENET1. Atheros calls it 'twisted' for a reason :)
|
|
+ */
|
|
case ATH25_SOC_AR2312:
|
|
case ATH25_SOC_AR2313:
|
|
+ ar5312_eth1_data.reset_phy = ar5312_eth0_data.reset_phy;
|
|
+ ar5312_eth1_data.macaddr = config->enet0_mac;
|
|
+ ath25_add_ethernet(1, AR5312_ENET1_BASE, "eth0_mii",
|
|
+ AR5312_ENET0_MII_BASE, AR5312_IRQ_ENET1,
|
|
+ &ar5312_eth1_data);
|
|
+
|
|
if (!ath25_board.radio)
|
|
return;
|
|
break;
|
|
--- a/arch/mips/ath25/devices.h
|
|
+++ b/arch/mips/ath25/devices.h
|
|
@@ -32,6 +32,8 @@ extern struct ar231x_board_config ath25_
|
|
extern void (*ath25_irq_dispatch)(void);
|
|
|
|
int ath25_find_config(phys_addr_t offset, unsigned long size);
|
|
+int ath25_add_ethernet(int nr, u32 base, const char *mii_name, u32 mii_base,
|
|
+ int irq, void *pdata);
|
|
void ath25_serial_setup(u32 mapbase, int irq, unsigned int uartclk);
|
|
int ath25_add_wmac(int nr, u32 base, int irq);
|
|
|
|
--- a/arch/mips/ath25/devices.c
|
|
+++ b/arch/mips/ath25/devices.c
|
|
@@ -12,6 +12,51 @@
|
|
struct ar231x_board_config ath25_board;
|
|
enum ath25_soc_type ath25_soc = ATH25_SOC_UNKNOWN;
|
|
|
|
+static struct resource ath25_eth0_res[] = {
|
|
+ {
|
|
+ .name = "eth0_membase",
|
|
+ .flags = IORESOURCE_MEM,
|
|
+ },
|
|
+ {
|
|
+ .name = "eth0_mii",
|
|
+ .flags = IORESOURCE_MEM,
|
|
+ },
|
|
+ {
|
|
+ .name = "eth0_irq",
|
|
+ .flags = IORESOURCE_IRQ,
|
|
+ }
|
|
+};
|
|
+
|
|
+static struct resource ath25_eth1_res[] = {
|
|
+ {
|
|
+ .name = "eth1_membase",
|
|
+ .flags = IORESOURCE_MEM,
|
|
+ },
|
|
+ {
|
|
+ .name = "eth1_mii",
|
|
+ .flags = IORESOURCE_MEM,
|
|
+ },
|
|
+ {
|
|
+ .name = "eth1_irq",
|
|
+ .flags = IORESOURCE_IRQ,
|
|
+ }
|
|
+};
|
|
+
|
|
+static struct platform_device ath25_eth[] = {
|
|
+ {
|
|
+ .id = 0,
|
|
+ .name = "ar231x-eth",
|
|
+ .resource = ath25_eth0_res,
|
|
+ .num_resources = ARRAY_SIZE(ath25_eth0_res)
|
|
+ },
|
|
+ {
|
|
+ .id = 1,
|
|
+ .name = "ar231x-eth",
|
|
+ .resource = ath25_eth1_res,
|
|
+ .num_resources = ARRAY_SIZE(ath25_eth1_res)
|
|
+ }
|
|
+};
|
|
+
|
|
static struct resource ath25_wmac0_res[] = {
|
|
{
|
|
.name = "wmac0_membase",
|
|
@@ -70,6 +115,25 @@ const char *get_system_type(void)
|
|
return soc_type_strings[ath25_soc];
|
|
}
|
|
|
|
+int __init ath25_add_ethernet(int nr, u32 base, const char *mii_name,
|
|
+ u32 mii_base, int irq, void *pdata)
|
|
+{
|
|
+ struct resource *res;
|
|
+
|
|
+ ath25_eth[nr].dev.platform_data = pdata;
|
|
+ res = &ath25_eth[nr].resource[0];
|
|
+ res->start = base;
|
|
+ res->end = base + 0x2000 - 1;
|
|
+ res++;
|
|
+ res->name = mii_name;
|
|
+ res->start = mii_base;
|
|
+ res->end = mii_base + 8 - 1;
|
|
+ res++;
|
|
+ res->start = irq;
|
|
+ res->end = irq;
|
|
+ return platform_device_register(&ath25_eth[nr]);
|
|
+}
|
|
+
|
|
void __init ath25_serial_setup(u32 mapbase, int irq, unsigned int uartclk)
|
|
{
|
|
struct uart_port s;
|
|
--- a/arch/mips/include/asm/mach-ath25/ath25_platform.h
|
|
+++ b/arch/mips/include/asm/mach-ath25/ath25_platform.h
|
|
@@ -70,4 +70,15 @@ struct ar231x_board_config {
|
|
const char *radio;
|
|
};
|
|
|
|
+/*
|
|
+ * Platform device information for the Ethernet MAC
|
|
+ */
|
|
+struct ar231x_eth {
|
|
+ void (*reset_set)(u32);
|
|
+ void (*reset_clear)(u32);
|
|
+ u32 reset_mac;
|
|
+ u32 reset_phy;
|
|
+ char *macaddr;
|
|
+};
|
|
+
|
|
#endif /* __ASM_MACH_ATH25_PLATFORM_H */
|