switch driver cleanup, 4th phase
SVN-Revision: 9340
This commit is contained in:
parent
dc0a4f8ff8
commit
49b631d730
5 changed files with 229 additions and 156 deletions
|
@ -98,6 +98,9 @@ static int __init adm5120_board_setup(void)
|
||||||
amba_device_register(&adm5120_uart0_device, &iomem_resource);
|
amba_device_register(&adm5120_uart0_device, &iomem_resource);
|
||||||
amba_device_register(&adm5120_uart1_device, &iomem_resource);
|
amba_device_register(&adm5120_uart1_device, &iomem_resource);
|
||||||
|
|
||||||
|
/* register built-in ethernet switch */
|
||||||
|
platform_device_register(&adm5120_switch_device);
|
||||||
|
|
||||||
/* setup PCI irq map */
|
/* setup PCI irq map */
|
||||||
adm5120_pci_set_irq_map(board->pci_nr_irqs, board->pci_irq_map);
|
adm5120_pci_set_irq_map(board->pci_nr_irqs, board->pci_irq_map);
|
||||||
|
|
||||||
|
|
|
@ -61,16 +61,30 @@ unsigned char adm5120_eth_vlans[6] = {
|
||||||
0x41, 0x42, 0x44, 0x48, 0x50, 0x60
|
0x41, 0x42, 0x44, 0x48, 0x50, 0x60
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(adm5120_eth_vlans);
|
EXPORT_SYMBOL_GPL(adm5120_eth_vlans);
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
|
||||||
/* Built-in ethernet switch */
|
/* Built-in ethernet switch */
|
||||||
|
struct resource adm5120_switch_resources[] = {
|
||||||
|
[0] = {
|
||||||
|
.start = ADM5120_SWITCH_BASE,
|
||||||
|
.end = ADM5120_SWITCH_BASE+ADM5120_SWITCH_SIZE-1,
|
||||||
|
.flags = IORESOURCE_MEM,
|
||||||
|
},
|
||||||
|
[1] = {
|
||||||
|
.start = ADM5120_IRQ_SWITCH,
|
||||||
|
.end = ADM5120_IRQ_SWITCH,
|
||||||
|
.flags = IORESOURCE_IRQ,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
struct adm5120_switch_platform_data adm5120_switch_data;
|
struct adm5120_switch_platform_data adm5120_switch_data;
|
||||||
struct platform_device adm5120_switch_device = {
|
struct platform_device adm5120_switch_device = {
|
||||||
.name = "adm5120-switch",
|
.name = "adm5120-switch",
|
||||||
.id = -1,
|
.id = -1,
|
||||||
|
.num_resources = ARRAY_SIZE(adm5120_switch_resources),
|
||||||
|
.resource = adm5120_switch_resources,
|
||||||
.dev.platform_data = &adm5120_switch_data,
|
.dev.platform_data = &adm5120_switch_data,
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
/* USB Host Controller */
|
/* USB Host Controller */
|
||||||
struct resource adm5120_hcd_resources[] = {
|
struct resource adm5120_hcd_resources[] = {
|
||||||
|
|
|
@ -1,23 +1,29 @@
|
||||||
/*
|
/*
|
||||||
* ADM5120 built in ethernet switch driver
|
* ADM5120 built-in ethernet switch driver
|
||||||
*
|
*
|
||||||
* Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005
|
* Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org>
|
||||||
*
|
*
|
||||||
* Inspiration for this driver came from the original ADMtek 2.4
|
* This code was based on a driver for Linux 2.6.xx by Jeroen Vreeken.
|
||||||
* driver, Copyright ADMtek Inc.
|
* Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005
|
||||||
|
* NAPI extension for the Jeroen's driver
|
||||||
|
* Copyright Thomas Langer (Thomas.Langer@infineon.com), 2007
|
||||||
|
* Copyright Friedrich Beckmann (Friedrich.Beckmann@infineon.com), 2007
|
||||||
|
* Inspiration for the Jeroen's driver came from the ADMtek 2.4 driver.
|
||||||
|
* Copyright ADMtek Inc.
|
||||||
*
|
*
|
||||||
* NAPI extensions by Thomas Langer (Thomas.Langer@infineon.com)
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* and Friedrich Beckmann (Friedrich.Beckmann@infineon.com), 2007
|
* under the terms of the GNU General Public License version 2 as published
|
||||||
*
|
* by the Free Software Foundation.
|
||||||
* TODO: Add support of high prio queues (currently disabled)
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/etherdevice.h>
|
#include <linux/etherdevice.h>
|
||||||
|
@ -39,14 +45,13 @@
|
||||||
#define DRV_DESC "ADM5120 built-in ethernet switch driver"
|
#define DRV_DESC "ADM5120 built-in ethernet switch driver"
|
||||||
#define DRV_VERSION "0.1.0"
|
#define DRV_VERSION "0.1.0"
|
||||||
|
|
||||||
MODULE_AUTHOR("Jeroen Vreeken (pe1rxq@amsat.org)");
|
#define CONFIG_ADM5120_SWITCH_NAPI 1
|
||||||
MODULE_DESCRIPTION("ADM5120 ethernet switch driver");
|
#undef CONFIG_ADM5120_SWITCH_DEBUG
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
#if 0 /*def ADM5120_SWITCH_DEBUG*/
|
#ifdef CONFIG_ADM5120_SWITCH_DEBUG
|
||||||
#define SW_DBG(f, a...) printk(KERN_DEBUG "%s: " f, DRV_NAME , ## a)
|
#define SW_DBG(f, a...) printk(KERN_DBG "%s: " f, DRV_NAME , ## a)
|
||||||
#else
|
#else
|
||||||
#define SW_DBG(f, a...) do {} while (0)
|
#define SW_DBG(f, a...) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
@ -83,10 +88,15 @@ MODULE_LICENSE("GPL");
|
||||||
SWITCH_INT_MD | SWITCH_INT_PSC)
|
SWITCH_INT_MD | SWITCH_INT_PSC)
|
||||||
|
|
||||||
#define SWITCH_INTS_USED (SWITCH_INTS_LOW | SWITCH_INT_PSC)
|
#define SWITCH_INTS_USED (SWITCH_INTS_LOW | SWITCH_INT_PSC)
|
||||||
#define SWITCH_INTS_POLL (SWITCH_INT_RLD | SWITCH_INT_LDF)
|
#define SWITCH_INTS_POLL (SWITCH_INT_RLD | SWITCH_INT_LDF | SWITCH_INT_SLD)
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
struct adm5120_if_priv {
|
||||||
|
unsigned int vlan_no;
|
||||||
|
unsigned int port_mask;
|
||||||
|
};
|
||||||
|
|
||||||
struct dma_desc {
|
struct dma_desc {
|
||||||
__u32 buf1;
|
__u32 buf1;
|
||||||
#define DESC_OWN (1UL << 31) /* Owned by the switch */
|
#define DESC_OWN (1UL << 31) /* Owned by the switch */
|
||||||
|
@ -117,28 +127,8 @@ struct dma_desc {
|
||||||
#define DESC_TYPE_PPPoE 0x1 /* PPPoE packet */
|
#define DESC_TYPE_PPPoE 0x1 /* PPPoE packet */
|
||||||
} __attribute__ ((aligned(16)));
|
} __attribute__ ((aligned(16)));
|
||||||
|
|
||||||
static inline u32 desc_get_srcport(struct dma_desc *desc)
|
|
||||||
{
|
|
||||||
return (desc->misc >> DESC_SRCPORT_SHIFT) & DESC_SRCPORT_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 desc_get_pktlen(struct dma_desc *desc)
|
|
||||||
{
|
|
||||||
return (desc->misc >> DESC_PKTLEN_SHIFT) & DESC_PKTLEN_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int desc_ipcsum_fail(struct dma_desc *desc)
|
|
||||||
{
|
|
||||||
return ((desc->misc & DESC_IPCSUM_FAIL) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
/* default settings - unlimited TX and RX on all ports, default shaper mode */
|
|
||||||
static unsigned char bw_matrix[SWITCH_NUM_PORTS] = {
|
|
||||||
0, 0, 0, 0, 0, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
static int adm5120_nrdevs;
|
static int adm5120_nrdevs;
|
||||||
|
|
||||||
static struct net_device *adm5120_devs[SWITCH_NUM_PORTS];
|
static struct net_device *adm5120_devs[SWITCH_NUM_PORTS];
|
||||||
|
@ -159,10 +149,7 @@ static unsigned int cur_txl, dirty_txl;
|
||||||
|
|
||||||
static unsigned int sw_used;
|
static unsigned int sw_used;
|
||||||
|
|
||||||
static spinlock_t sw_lock = SPIN_LOCK_UNLOCKED;
|
static spinlock_t tx_lock = SPIN_LOCK_UNLOCKED;
|
||||||
static spinlock_t poll_lock = SPIN_LOCK_UNLOCKED;
|
|
||||||
|
|
||||||
static struct net_device sw_dev;
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
@ -208,6 +195,21 @@ static inline u32 sw_int_status(void)
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32 desc_get_srcport(struct dma_desc *desc)
|
||||||
|
{
|
||||||
|
return (desc->misc >> DESC_SRCPORT_SHIFT) & DESC_SRCPORT_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 desc_get_pktlen(struct dma_desc *desc)
|
||||||
|
{
|
||||||
|
return (desc->misc >> DESC_PKTLEN_SHIFT) & DESC_PKTLEN_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int desc_ipcsum_fail(struct dma_desc *desc)
|
||||||
|
{
|
||||||
|
return ((desc->misc & DESC_IPCSUM_FAIL) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
static void sw_dump_desc(char *label, struct dma_desc *desc, int tx)
|
static void sw_dump_desc(char *label, struct dma_desc *desc, int tx)
|
||||||
|
@ -386,8 +388,6 @@ static int adm5120_switch_rx(int limit)
|
||||||
SW_DBG("rx start, limit=%d, cur_rxl=%u, dirty_rxl=%u\n",
|
SW_DBG("rx start, limit=%d, cur_rxl=%u, dirty_rxl=%u\n",
|
||||||
limit, cur_rxl, dirty_rxl);
|
limit, cur_rxl, dirty_rxl);
|
||||||
|
|
||||||
sw_int_ack(SWITCH_INTS_POLL);
|
|
||||||
|
|
||||||
while (done < limit) {
|
while (done < limit) {
|
||||||
int entry = cur_rxl % RX_RING_SIZE;
|
int entry = cur_rxl % RX_RING_SIZE;
|
||||||
struct dma_desc *desc = &rxl_descs[entry];
|
struct dma_desc *desc = &rxl_descs[entry];
|
||||||
|
@ -430,7 +430,11 @@ static int adm5120_switch_rx(int limit)
|
||||||
dma_cache_wback_inv((unsigned long)skb->data,
|
dma_cache_wback_inv((unsigned long)skb->data,
|
||||||
skb->len);
|
skb->len);
|
||||||
|
|
||||||
|
#ifdef CONFIG_ADM5120_SWITCH_USE_NAPI
|
||||||
netif_receive_skb(skb);
|
netif_receive_skb(skb);
|
||||||
|
#else
|
||||||
|
netif_rx(skb);
|
||||||
|
#endif
|
||||||
|
|
||||||
rdev->last_rx = jiffies;
|
rdev->last_rx = jiffies;
|
||||||
rdev->stats.rx_packets++;
|
rdev->stats.rx_packets++;
|
||||||
|
@ -456,12 +460,11 @@ static int adm5120_switch_rx(int limit)
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void adm5120_switch_tx(void)
|
static void adm5120_switch_tx(void)
|
||||||
{
|
{
|
||||||
unsigned int entry;
|
unsigned int entry;
|
||||||
|
|
||||||
/* find and cleanup dirty tx descriptors */
|
spin_lock(&tx_lock);
|
||||||
entry = dirty_txl % TX_RING_SIZE;
|
entry = dirty_txl % TX_RING_SIZE;
|
||||||
while (dirty_txl != cur_txl) {
|
while (dirty_txl != cur_txl) {
|
||||||
struct dma_desc *desc = &txl_descs[entry];
|
struct dma_desc *desc = &txl_descs[entry];
|
||||||
|
@ -481,7 +484,6 @@ static void adm5120_switch_tx(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cur_txl - dirty_txl) < TX_QUEUE_LEN - 4) {
|
if ((cur_txl - dirty_txl) < TX_QUEUE_LEN - 4) {
|
||||||
/* wake up queue of all devices */
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < SWITCH_NUM_PORTS; i++) {
|
for (i = 0; i < SWITCH_NUM_PORTS; i++) {
|
||||||
if (!adm5120_devs[i])
|
if (!adm5120_devs[i])
|
||||||
|
@ -489,14 +491,22 @@ static void adm5120_switch_tx(void)
|
||||||
netif_wake_queue(adm5120_devs[i]);
|
netif_wake_queue(adm5120_devs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
spin_unlock(&tx_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ADM5120_SWITCH_NAPI
|
||||||
static int adm5120_if_poll(struct net_device *dev, int *budget)
|
static int adm5120_if_poll(struct net_device *dev, int *budget)
|
||||||
{
|
{
|
||||||
int limit = min(dev->quota, *budget);
|
int limit = min(dev->quota, *budget);
|
||||||
int done;
|
int done;
|
||||||
u32 status;
|
u32 status;
|
||||||
|
|
||||||
|
sw_int_ack(SWITCH_INTS_POLL);
|
||||||
|
|
||||||
|
SW_DBG("%s: processing TX ring\n", dev->name);
|
||||||
|
adm5120_switch_tx();
|
||||||
|
|
||||||
|
SW_DBG("%s: processing RX ring\n", dev->name);
|
||||||
done = adm5120_switch_rx(limit);
|
done = adm5120_switch_rx(limit);
|
||||||
|
|
||||||
*budget -= done;
|
*budget -= done;
|
||||||
|
@ -504,74 +514,53 @@ static int adm5120_if_poll(struct net_device *dev, int *budget)
|
||||||
|
|
||||||
status = sw_int_status() & SWITCH_INTS_POLL;
|
status = sw_int_status() & SWITCH_INTS_POLL;
|
||||||
if ((done < limit) && (!status)) {
|
if ((done < limit) && (!status)) {
|
||||||
SW_DBG("disable polling mode for %s\n", poll_dev->name);
|
SW_DBG("disable polling mode for %s\n", dev->name);
|
||||||
netif_rx_complete(dev);
|
netif_rx_complete(dev);
|
||||||
sw_int_unmask(SWITCH_INTS_POLL);
|
sw_int_unmask(SWITCH_INTS_POLL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SW_DBG("%s still in polling mode, done=%d, status=%x\n",
|
||||||
|
dev->name, done, status);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_ADM5120_SWITCH_USE_NAPI */
|
||||||
|
|
||||||
static irqreturn_t adm5120_poll_irq(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct net_device *dev = dev_id;
|
|
||||||
u32 status;
|
|
||||||
|
|
||||||
status = sw_int_status();
|
|
||||||
status &= SWITCH_INTS_POLL;
|
|
||||||
if (!status)
|
|
||||||
return IRQ_NONE;
|
|
||||||
|
|
||||||
sw_dump_intr_mask("poll ints", status);
|
|
||||||
|
|
||||||
SW_DBG("enable polling mode for %s\n", dev->name);
|
|
||||||
sw_int_mask(SWITCH_INTS_POLL);
|
|
||||||
netif_rx_schedule(dev);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static irqreturn_t adm5120_switch_irq(int irq, void *dev_id)
|
static irqreturn_t adm5120_switch_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
u32 status;
|
u32 status;
|
||||||
|
|
||||||
status = sw_int_status();
|
status = sw_int_status();
|
||||||
status &= SWITCH_INTS_ALL & ~SWITCH_INTS_POLL;
|
status &= SWITCH_INTS_ALL;
|
||||||
if (!status)
|
if (!status)
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
#ifdef CONFIG_ADM5120_SWITCH_NAPI
|
||||||
|
sw_int_ack(status & ~SWITCH_INTS_POLL);
|
||||||
|
|
||||||
|
if (status & SWITCH_INTS_POLL) {
|
||||||
|
struct net_device *dev = dev_id;
|
||||||
|
sw_dump_intr_mask("poll ints", status);
|
||||||
|
SW_DBG("enable polling mode for %s\n", dev->name);
|
||||||
|
sw_int_mask(SWITCH_INTS_POLL);
|
||||||
|
netif_rx_schedule(dev);
|
||||||
|
}
|
||||||
|
#else
|
||||||
sw_int_ack(status);
|
sw_int_ack(status);
|
||||||
|
|
||||||
if (status & SWITCH_INT_SLD) {
|
if (status & (SWITCH_INT_RLD | SWITCH_INT_LDF)) {
|
||||||
spin_lock(&sw_lock);
|
adm5120_switch_rx(RX_RING_SIZE);
|
||||||
adm5120_switch_tx();
|
|
||||||
spin_unlock(&sw_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status & SWITCH_INT_SLD) {
|
||||||
|
adm5120_switch_tx();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void adm5120_set_vlan(char *matrix)
|
|
||||||
{
|
|
||||||
unsigned long val;
|
|
||||||
int vlan_port, port;
|
|
||||||
|
|
||||||
val = matrix[0] + (matrix[1]<<8) + (matrix[2]<<16) + (matrix[3]<<24);
|
|
||||||
sw_write_reg(SWITCH_REG_VLAN_G1, val);
|
|
||||||
val = matrix[4] + (matrix[5]<<8);
|
|
||||||
sw_write_reg(SWITCH_REG_VLAN_G2, val);
|
|
||||||
|
|
||||||
/* Now set/update the port vs. device lookup table */
|
|
||||||
for (port=0; port<SWITCH_NUM_PORTS; port++) {
|
|
||||||
for (vlan_port=0; vlan_port<SWITCH_NUM_PORTS && !(matrix[vlan_port] & (0x00000001 << port)); vlan_port++);
|
|
||||||
if (vlan_port <SWITCH_NUM_PORTS)
|
|
||||||
adm5120_port[port] = adm5120_devs[vlan_port];
|
|
||||||
else
|
|
||||||
adm5120_port[port] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void adm5120_set_bw(char *matrix)
|
static void adm5120_set_bw(char *matrix)
|
||||||
{
|
{
|
||||||
unsigned long val;
|
unsigned long val;
|
||||||
|
@ -713,6 +702,81 @@ static void adm5120_switch_rx_ring_free(void)
|
||||||
rxl_descs_dma);
|
rxl_descs_dma);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void adm5120_write_mac(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct adm5120_if_priv *priv = netdev_priv(dev);
|
||||||
|
unsigned char *mac = dev->dev_addr;
|
||||||
|
u32 t;
|
||||||
|
|
||||||
|
t = mac[2] | (mac[3] << MAC_WT1_MAC3_SHIFT) |
|
||||||
|
(mac[4] << MAC_WT1_MAC4_SHIFT) | (mac[5] << MAC_WT1_MAC4_SHIFT);
|
||||||
|
sw_write_reg(SWITCH_REG_MAC_WT1, t);
|
||||||
|
|
||||||
|
t = (mac[0] << MAC_WT0_MAC0_SHIFT) | (mac[1] << MAC_WT0_MAC1_SHIFT) |
|
||||||
|
MAC_WT0_MAWC | MAC_WT0_WVE | (priv->vlan_no<<3);
|
||||||
|
|
||||||
|
sw_write_reg(SWITCH_REG_MAC_WT0, t);
|
||||||
|
|
||||||
|
while (!(sw_read_reg(SWITCH_REG_MAC_WT0) & MAC_WT0_MWD));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adm5120_set_vlan(char *matrix)
|
||||||
|
{
|
||||||
|
unsigned long val;
|
||||||
|
int vlan_port, port;
|
||||||
|
|
||||||
|
val = matrix[0] + (matrix[1]<<8) + (matrix[2]<<16) + (matrix[3]<<24);
|
||||||
|
sw_write_reg(SWITCH_REG_VLAN_G1, val);
|
||||||
|
val = matrix[4] + (matrix[5]<<8);
|
||||||
|
sw_write_reg(SWITCH_REG_VLAN_G2, val);
|
||||||
|
|
||||||
|
/* Now set/update the port vs. device lookup table */
|
||||||
|
for (port=0; port<SWITCH_NUM_PORTS; port++) {
|
||||||
|
for (vlan_port=0; vlan_port<SWITCH_NUM_PORTS && !(matrix[vlan_port] & (0x00000001 << port)); vlan_port++);
|
||||||
|
if (vlan_port <SWITCH_NUM_PORTS)
|
||||||
|
adm5120_port[port] = adm5120_devs[vlan_port];
|
||||||
|
else
|
||||||
|
adm5120_port[port] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adm5120_switch_set_vlan_mac(unsigned int vlan, unsigned char *mac)
|
||||||
|
{
|
||||||
|
u32 t;
|
||||||
|
|
||||||
|
t = mac[2] | (mac[3] << MAC_WT1_MAC3_SHIFT)
|
||||||
|
| (mac[4] << MAC_WT1_MAC4_SHIFT)
|
||||||
|
| (mac[5] << MAC_WT1_MAC4_SHIFT);
|
||||||
|
sw_write_reg(SWITCH_REG_MAC_WT1, t);
|
||||||
|
|
||||||
|
t = (mac[0] << MAC_WT0_MAC0_SHIFT) | (mac[1] << MAC_WT0_MAC1_SHIFT) |
|
||||||
|
MAC_WT0_MAWC | MAC_WT0_WVE | (vlan << MAC_WT0_WVN_SHIFT) |
|
||||||
|
(MAC_WT0_WAF_STATIC << MAC_WT0_WAF_SHIFT);
|
||||||
|
sw_write_reg(SWITCH_REG_MAC_WT0, t);
|
||||||
|
|
||||||
|
do {
|
||||||
|
t = sw_read_reg(SWITCH_REG_MAC_WT0);
|
||||||
|
} while ((t & MAC_WT0_MWD) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adm5120_switch_set_vlan_ports(unsigned int vlan, u32 ports)
|
||||||
|
{
|
||||||
|
unsigned int reg;
|
||||||
|
u32 t;
|
||||||
|
|
||||||
|
if (vlan < 4)
|
||||||
|
reg = SWITCH_REG_VLAN_G1;
|
||||||
|
else {
|
||||||
|
vlan -= 4;
|
||||||
|
reg = SWITCH_REG_VLAN_G2;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = sw_read_reg(reg);
|
||||||
|
t &= ~(0xFF << (vlan*8));
|
||||||
|
t |= (ports << (vlan*8));
|
||||||
|
sw_write_reg(reg, t);
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
static int adm5120_if_open(struct net_device *dev)
|
static int adm5120_if_open(struct net_device *dev)
|
||||||
|
@ -721,7 +785,7 @@ static int adm5120_if_open(struct net_device *dev)
|
||||||
int err;
|
int err;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
err = request_irq(dev->irq, adm5120_poll_irq,
|
err = request_irq(dev->irq, adm5120_switch_irq,
|
||||||
(IRQF_SHARED | IRQF_DISABLED), dev->name, dev);
|
(IRQF_SHARED | IRQF_DISABLED), dev->name, dev);
|
||||||
if (err) {
|
if (err) {
|
||||||
SW_ERR("unable to get irq for %s\n", dev->name);
|
SW_ERR("unable to get irq for %s\n", dev->name);
|
||||||
|
@ -776,12 +840,12 @@ static int adm5120_if_hard_start_xmit(struct sk_buff *skb,
|
||||||
struct net_device *dev)
|
struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct dma_desc *desc;
|
struct dma_desc *desc;
|
||||||
struct adm5120_sw *priv = netdev_priv(dev);
|
struct adm5120_if_priv *priv = netdev_priv(dev);
|
||||||
unsigned int entry;
|
unsigned int entry;
|
||||||
unsigned long data;
|
unsigned long data;
|
||||||
|
|
||||||
/* lock switch irq */
|
/* lock switch irq */
|
||||||
spin_lock_irq(&sw_lock);
|
spin_lock_irq(&tx_lock);
|
||||||
|
|
||||||
/* calculate the next TX descriptor entry. */
|
/* calculate the next TX descriptor entry. */
|
||||||
entry = cur_txl % TX_RING_SIZE;
|
entry = cur_txl % TX_RING_SIZE;
|
||||||
|
@ -790,6 +854,7 @@ static int adm5120_if_hard_start_xmit(struct sk_buff *skb,
|
||||||
if (desc->buf1 & DESC_OWN) {
|
if (desc->buf1 & DESC_OWN) {
|
||||||
/* We want to write a packet but the TX queue is still
|
/* We want to write a packet but the TX queue is still
|
||||||
* occupied by the DMA. We are faster than the DMA... */
|
* occupied by the DMA. We are faster than the DMA... */
|
||||||
|
SW_DBG("%s unable to transmit, packet dopped\n", dev->name);
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
dev->stats.tx_dropped++;
|
dev->stats.tx_dropped++;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -801,7 +866,7 @@ static int adm5120_if_hard_start_xmit(struct sk_buff *skb,
|
||||||
|
|
||||||
desc->misc =
|
desc->misc =
|
||||||
((skb->len<ETH_ZLEN?ETH_ZLEN:skb->len) << DESC_PKTLEN_SHIFT) |
|
((skb->len<ETH_ZLEN?ETH_ZLEN:skb->len) << DESC_PKTLEN_SHIFT) |
|
||||||
(0x1 << priv->port);
|
(0x1 << priv->vlan_no);
|
||||||
|
|
||||||
desc->buflen = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
|
desc->buflen = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
|
||||||
|
|
||||||
|
@ -816,7 +881,7 @@ static int adm5120_if_hard_start_xmit(struct sk_buff *skb,
|
||||||
|
|
||||||
dev->trans_start = jiffies;
|
dev->trans_start = jiffies;
|
||||||
|
|
||||||
spin_unlock_irq(&sw_lock);
|
spin_unlock_irq(&tx_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -826,13 +891,13 @@ static void adm5120_if_tx_timeout(struct net_device *dev)
|
||||||
SW_INFO("TX timeout on %s\n",dev->name);
|
SW_INFO("TX timeout on %s\n",dev->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void adm5120_set_multicast_list(struct net_device *dev)
|
static void adm5120_if_set_multicast_list(struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct adm5120_sw *priv = netdev_priv(dev);
|
struct adm5120_if_priv *priv = netdev_priv(dev);
|
||||||
u32 ports;
|
u32 ports;
|
||||||
u32 t;
|
u32 t;
|
||||||
|
|
||||||
ports = adm5120_eth_vlans[priv->port] & SWITCH_PORTS_NOCPU;
|
ports = adm5120_eth_vlans[priv->vlan_no] & SWITCH_PORTS_NOCPU;
|
||||||
|
|
||||||
t = sw_read_reg(SWITCH_REG_CPUP_CONF);
|
t = sw_read_reg(SWITCH_REG_CPUP_CONF);
|
||||||
if (dev->flags & IFF_PROMISC)
|
if (dev->flags & IFF_PROMISC)
|
||||||
|
@ -877,24 +942,6 @@ static void adm5120_set_multicast_list(struct net_device *dev)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void adm5120_write_mac(struct net_device *dev)
|
|
||||||
{
|
|
||||||
struct adm5120_sw *priv = netdev_priv(dev);
|
|
||||||
unsigned char *mac = dev->dev_addr;
|
|
||||||
u32 t;
|
|
||||||
|
|
||||||
t = mac[2] | (mac[3] << MAC_WT1_MAC3_SHIFT) |
|
|
||||||
(mac[4] << MAC_WT1_MAC4_SHIFT) | (mac[5] << MAC_WT1_MAC4_SHIFT);
|
|
||||||
sw_write_reg(SWITCH_REG_MAC_WT1, t);
|
|
||||||
|
|
||||||
t = (mac[0] << MAC_WT0_MAC0_SHIFT) | (mac[1] << MAC_WT0_MAC1_SHIFT) |
|
|
||||||
MAC_WT0_MAWC | MAC_WT0_WVE | (priv->port<<3);
|
|
||||||
|
|
||||||
sw_write_reg(SWITCH_REG_MAC_WT0, t);
|
|
||||||
|
|
||||||
while (!(sw_read_reg(SWITCH_REG_MAC_WT0) & MAC_WT0_MWD));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int adm5120_if_set_mac_address(struct net_device *dev, void *p)
|
static int adm5120_if_set_mac_address(struct net_device *dev, void *p)
|
||||||
{
|
{
|
||||||
struct sockaddr *addr = p;
|
struct sockaddr *addr = p;
|
||||||
|
@ -904,17 +951,18 @@ static int adm5120_if_set_mac_address(struct net_device *dev, void *p)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adm5120_if_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
static int adm5120_if_do_ioctl(struct net_device *dev, struct ifreq *rq,
|
||||||
|
int cmd)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct adm5120_sw_info info;
|
struct adm5120_sw_info info;
|
||||||
struct adm5120_sw *priv = netdev_priv(dev);
|
struct adm5120_if_priv *priv = netdev_priv(dev);
|
||||||
|
|
||||||
switch(cmd) {
|
switch(cmd) {
|
||||||
case SIOCGADMINFO:
|
case SIOCGADMINFO:
|
||||||
info.magic = 0x5120;
|
info.magic = 0x5120;
|
||||||
info.ports = adm5120_nrdevs;
|
info.ports = adm5120_nrdevs;
|
||||||
info.vlan = priv->port;
|
info.vlan = priv->vlan_no;
|
||||||
err = copy_to_user(rq->ifr_data, &info, sizeof(info));
|
err = copy_to_user(rq->ifr_data, &info, sizeof(info));
|
||||||
if (err)
|
if (err)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -934,19 +982,6 @@ static int adm5120_if_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd
|
||||||
if (err)
|
if (err)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
break;
|
break;
|
||||||
case SIOCGETBW:
|
|
||||||
err = copy_to_user(rq->ifr_data, bw_matrix, sizeof(bw_matrix));
|
|
||||||
if (err)
|
|
||||||
return -EFAULT;
|
|
||||||
break;
|
|
||||||
case SIOCSETBW:
|
|
||||||
if (!capable(CAP_NET_ADMIN))
|
|
||||||
return -EPERM;
|
|
||||||
err = copy_from_user(bw_matrix, rq->ifr_data, sizeof(bw_matrix));
|
|
||||||
if (err)
|
|
||||||
return -EFAULT;
|
|
||||||
adm5120_set_bw(bw_matrix);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
@ -956,7 +991,7 @@ static int adm5120_if_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd
|
||||||
static struct net_device *adm5120_if_alloc(void)
|
static struct net_device *adm5120_if_alloc(void)
|
||||||
{
|
{
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
struct adm5120_sw *priv;
|
struct adm5120_if_priv *priv;
|
||||||
|
|
||||||
dev = alloc_etherdev(sizeof(*priv));
|
dev = alloc_etherdev(sizeof(*priv));
|
||||||
if (!dev)
|
if (!dev)
|
||||||
|
@ -966,19 +1001,23 @@ static struct net_device *adm5120_if_alloc(void)
|
||||||
dev->open = adm5120_if_open;
|
dev->open = adm5120_if_open;
|
||||||
dev->hard_start_xmit = adm5120_if_hard_start_xmit;
|
dev->hard_start_xmit = adm5120_if_hard_start_xmit;
|
||||||
dev->stop = adm5120_if_stop;
|
dev->stop = adm5120_if_stop;
|
||||||
dev->set_multicast_list = adm5120_set_multicast_list;
|
dev->set_multicast_list = adm5120_if_set_multicast_list;
|
||||||
dev->do_ioctl = adm5120_if_do_ioctl;
|
dev->do_ioctl = adm5120_if_do_ioctl;
|
||||||
dev->tx_timeout = adm5120_if_tx_timeout;
|
dev->tx_timeout = adm5120_if_tx_timeout;
|
||||||
dev->watchdog_timeo = TX_TIMEOUT;
|
dev->watchdog_timeo = TX_TIMEOUT;
|
||||||
dev->set_mac_address = adm5120_if_set_mac_address;
|
dev->set_mac_address = adm5120_if_set_mac_address;
|
||||||
|
#ifdef CONFIG_ADM5120_SWITCH_NAPI
|
||||||
dev->poll = adm5120_if_poll;
|
dev->poll = adm5120_if_poll;
|
||||||
dev->weight = 64;
|
dev->weight = 64;
|
||||||
|
#endif
|
||||||
|
|
||||||
SET_MODULE_OWNER(dev);
|
SET_MODULE_OWNER(dev);
|
||||||
|
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
static void adm5120_switch_cleanup(void)
|
static void adm5120_switch_cleanup(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -996,22 +1035,13 @@ static void adm5120_switch_cleanup(void)
|
||||||
|
|
||||||
adm5120_switch_tx_ring_free();
|
adm5120_switch_tx_ring_free();
|
||||||
adm5120_switch_rx_ring_free();
|
adm5120_switch_rx_ring_free();
|
||||||
|
|
||||||
free_irq(ADM5120_IRQ_SWITCH, &sw_dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init adm5120_switch_init(void)
|
static int __init adm5120_switch_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
u32 t;
|
u32 t;
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
err = request_irq(ADM5120_IRQ_SWITCH, adm5120_switch_irq,
|
|
||||||
(IRQF_SHARED | IRQF_DISABLED), "switch", &sw_dev);
|
|
||||||
if (err) {
|
|
||||||
SW_ERR("request_irq failed with error %d\n", err);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
adm5120_nrdevs = adm5120_eth_num_ports;
|
adm5120_nrdevs = adm5120_eth_num_ports;
|
||||||
|
|
||||||
t = CPUP_CONF_DCPUP | CPUP_CONF_CRCP |
|
t = CPUP_CONF_DCPUP | CPUP_CONF_CRCP |
|
||||||
|
@ -1061,7 +1091,7 @@ static int __init adm5120_switch_init(void)
|
||||||
|
|
||||||
for (i = 0; i < SWITCH_NUM_PORTS; i++) {
|
for (i = 0; i < SWITCH_NUM_PORTS; i++) {
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
struct adm5120_sw *priv;
|
struct adm5120_if_priv *priv;
|
||||||
|
|
||||||
dev = adm5120_if_alloc();
|
dev = adm5120_if_alloc();
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
|
@ -1072,7 +1102,8 @@ static int __init adm5120_switch_init(void)
|
||||||
adm5120_devs[i] = dev;
|
adm5120_devs[i] = dev;
|
||||||
priv = netdev_priv(dev);
|
priv = netdev_priv(dev);
|
||||||
|
|
||||||
priv->port = i;
|
priv->vlan_no = i;
|
||||||
|
priv->port_mask = adm5120_eth_vlans[i];
|
||||||
|
|
||||||
memcpy(dev->dev_addr, adm5120_eth_macs[i], 6);
|
memcpy(dev->dev_addr, adm5120_eth_macs[i], 6);
|
||||||
adm5120_write_mac(dev);
|
adm5120_write_mac(dev);
|
||||||
|
@ -1102,10 +1133,41 @@ err:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit adm5120_switch_exit(void)
|
static int adm5120_switch_remove(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
adm5120_switch_cleanup();
|
adm5120_switch_cleanup();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(adm5120_switch_init);
|
static struct platform_driver adm5120_switch_driver = {
|
||||||
module_exit(adm5120_switch_exit);
|
.probe = adm5120_switch_probe,
|
||||||
|
.remove = adm5120_switch_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static int __init adm5120_switch_mod_init(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
pr_info(DRV_DESC " version " DRV_VERSION "\n");
|
||||||
|
err = platform_driver_register(&adm5120_switch_driver);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit adm5120_switch_mod_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&adm5120_switch_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(adm5120_switch_mod_init);
|
||||||
|
module_exit(adm5120_switch_mod_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>");
|
||||||
|
MODULE_DESCRIPTION(DRV_DESC);
|
||||||
|
MODULE_VERSION(DRV_VERSION);
|
||||||
|
|
|
@ -10,15 +10,9 @@
|
||||||
#ifndef _INCLUDE_ADM5120SW_H_
|
#ifndef _INCLUDE_ADM5120SW_H_
|
||||||
#define _INCLUDE_ADM5120SW_H_
|
#define _INCLUDE_ADM5120SW_H_
|
||||||
|
|
||||||
struct adm5120_sw {
|
|
||||||
int port;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SIOCSMATRIX SIOCDEVPRIVATE
|
#define SIOCSMATRIX SIOCDEVPRIVATE
|
||||||
#define SIOCGMATRIX SIOCDEVPRIVATE+1
|
#define SIOCGMATRIX SIOCDEVPRIVATE+1
|
||||||
#define SIOCGADMINFO SIOCDEVPRIVATE+2
|
#define SIOCGADMINFO SIOCDEVPRIVATE+2
|
||||||
#define SIOCGETBW SIOCDEVPRIVATE+3
|
|
||||||
#define SIOCSETBW SIOCDEVPRIVATE+4
|
|
||||||
|
|
||||||
struct adm5120_sw_info {
|
struct adm5120_sw_info {
|
||||||
u16 magic;
|
u16 magic;
|
||||||
|
|
|
@ -164,12 +164,12 @@
|
||||||
#define MAC_WT0_MWD_SHIFT 1
|
#define MAC_WT0_MWD_SHIFT 1
|
||||||
#define MAC_WT0_MWD BIT(1) /* MAC write done */
|
#define MAC_WT0_MWD BIT(1) /* MAC write done */
|
||||||
#define MAC_WT0_WFB BIT(2) /* Write Filter Bit */
|
#define MAC_WT0_WFB BIT(2) /* Write Filter Bit */
|
||||||
#define MAC_WT0_WVN_SHIFT 3
|
#define MAC_WT0_WVN_SHIFT 3 /* Write Vlan Number shift */
|
||||||
#define MAC_WT0_WVE BIT(6) /* Write VLAN enable */
|
#define MAC_WT0_WVE BIT(6) /* Write VLAN enable */
|
||||||
#define MAC_WT0_WPMN_SHIFT 7
|
#define MAC_WT0_WPMN_SHIFT 7
|
||||||
#define MAC_WT0_WAF_SHIFT 13 /* Write Age Field shift */
|
#define MAC_WT0_WAF_SHIFT 13 /* Write Age Field shift */
|
||||||
#define MAC_WT0_WAF_EMPTY 0
|
#define MAC_WT0_WAF_EMPTY 0
|
||||||
#define MAC_WT0_WAF_STATIC 7
|
#define MAC_WT0_WAF_STATIC 7 /* age: static */
|
||||||
#define MAC_WT0_MAC0_SHIFT 16
|
#define MAC_WT0_MAC0_SHIFT 16
|
||||||
#define MAC_WT0_MAC1_SHIFT 24
|
#define MAC_WT0_MAC1_SHIFT 24
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue