mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2025-04-21 12:27:27 +03:00
change the way ./files* and the generic kernel files are applied. ./files now applies to *ALL* kernel versions, and is copied along with ./files-* - this gets rid of quite a bit of redundancy in the extra kernel drivers.
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@13010 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
1 parent
a662d5b130
commit
79670e1e13
125 files changed
+4
-8971
No files matched your search
+1
-1
@@ -31,7 +31,7 @@ else
|
||||
endif
|
||||
|
||||
PATCH_DIR ?= ./patches$(shell [ -d "./patches-$(KERNEL_PATCHVER)" ] && printf -- "-$(KERNEL_PATCHVER)" || true )
|
||||
FILES_DIR ?= ./files$(shell [ -d "./files-$(KERNEL_PATCHVER)" ] && printf -- "-$(KERNEL_PATCHVER)" || true )
|
||||
FILES_DIR ?= $(foreach dir,$(wildcard ./files ./files-$(KERNEL_PATCHVER)),"$(dir)")
|
||||
KERNEL_BUILD_DIR ?= $(BUILD_DIR_BASE)/linux-$(BOARD)$(if $(SUBTARGET),_$(SUBTARGET))$(if $(BUILD_SUFFIX),_$(BUILD_SUFFIX))
|
||||
LINUX_DIR ?= $(KERNEL_BUILD_DIR)/linux-$(LINUX_VERSION)
|
||||
|
||||
|
||||
+2
-5
@@ -68,11 +68,8 @@ endef
|
||||
|
||||
define Kernel/Patch/Default
|
||||
rm -rf $(PKG_BUILD_DIR)/patches; mkdir -p $(PKG_BUILD_DIR)/patches
|
||||
if [ -d $(GENERIC_FILES_DIR) ]; then $(CP) $(GENERIC_FILES_DIR)/* $(LINUX_DIR)/; fi
|
||||
if [ -d $(FILES_DIR) ]; then \
|
||||
$(CP) $(FILES_DIR)/* $(LINUX_DIR)/; \
|
||||
find $(LINUX_DIR)/ -name \*.rej | xargs rm -f; \
|
||||
fi
|
||||
$(CP) $(foreach fdir,$(GENERIC_FILES_DIR) $(FILES_DIR),$(fdir)/.) $(LINUX_DIR)/
|
||||
find $(LINUX_DIR)/ -name \*.rej -or -name \*.orig | $(XARGS) rm -f
|
||||
$(call PatchDir,$(GENERIC_PATCH_DIR),generic/)
|
||||
$(call PatchDir,$(PATCH_DIR),platform/)
|
||||
endef
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ include $(INCLUDE_DIR)/kernel-version.mk
|
||||
|
||||
GENERIC_PLATFORM_DIR := $(TOPDIR)/target/linux/generic-$(KERNEL)
|
||||
GENERIC_PATCH_DIR := $(GENERIC_PLATFORM_DIR)/patches$(shell [ -d "$(GENERIC_PLATFORM_DIR)/patches-$(KERNEL_PATCHVER)" ] && printf -- "-$(KERNEL_PATCHVER)" || true )
|
||||
GENERIC_FILES_DIR := $(GENERIC_PLATFORM_DIR)/files$(shell [ -d "$(GENERIC_PLATFORM_DIR)/files-$(KERNEL_PATCHVER)" ] && printf -- "-$(KERNEL_PATCHVER)" || true )
|
||||
GENERIC_FILES_DIR := $(foreach dir,$(wildcard $(GENERIC_PLATFORM_DIR)/files $(GENERIC_PLATFORM_DIR)/files-$(KERNEL_PATCHVER)),"$(dir)")
|
||||
|
||||
GENERIC_LINUX_CONFIG?=$(firstword $(wildcard $(GENERIC_PLATFORM_DIR)/config-$(KERNEL_PATCHVER) $(GENERIC_PLATFORM_DIR)/config-default))
|
||||
LINUX_CONFIG?=$(firstword $(wildcard $(foreach subdir,$(PLATFORM_DIR) $(PLATFORM_SUBDIR),$(subdir)/config-$(KERNEL_PATCHVER) $(subdir)/config-default)) $(PLATFORM_DIR)/config-$(KERNEL_PATCHVER))
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* ADM6996 switch driver
|
||||
*
|
||||
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "adm6996.h"
|
||||
|
||||
MODULE_DESCRIPTION("Infineon ADM6996 Switch");
|
||||
MODULE_AUTHOR("Felix Fietkau");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct adm6996_priv {
|
||||
/* use abstraction for regops, we want to add gpio support in the future */
|
||||
u16 (*read)(struct phy_device *phydev, enum admreg reg);
|
||||
void (*write)(struct phy_device *phydev, enum admreg reg, u16 val);
|
||||
};
|
||||
|
||||
#define to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv)
|
||||
|
||||
|
||||
static inline u16
|
||||
r16(struct phy_device *pdev, enum admreg reg)
|
||||
{
|
||||
return to_adm(pdev)->read(pdev, reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
w16(struct phy_device *pdev, enum admreg reg, u16 val)
|
||||
{
|
||||
to_adm(pdev)->write(pdev, reg, val);
|
||||
}
|
||||
|
||||
|
||||
static u16
|
||||
adm6996_read_mii_reg(struct phy_device *phydev, enum admreg reg)
|
||||
{
|
||||
return phydev->bus->read(phydev->bus, PHYADDR(reg));
|
||||
}
|
||||
|
||||
static void
|
||||
adm6996_write_mii_reg(struct phy_device *phydev, enum admreg reg, u16 val)
|
||||
{
|
||||
phydev->bus->write(phydev->bus, PHYADDR(reg), val);
|
||||
}
|
||||
|
||||
|
||||
static int adm6996_config_init(struct phy_device *pdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk("%s: ADM6996 PHY driver attached.\n", pdev->attached_dev->name);
|
||||
pdev->supported = ADVERTISED_100baseT_Full;
|
||||
pdev->advertising = ADVERTISED_100baseT_Full;
|
||||
|
||||
/* initialize port and vlan settings */
|
||||
for (i = 0; i < ADM_PHY_PORTS; i++) {
|
||||
w16(pdev, adm_portcfg[i], ADM_PORTCFG_INIT |
|
||||
ADM_PORTCFG_PVID((i == ADM_WAN_PORT) ? 1 : 0));
|
||||
}
|
||||
w16(pdev, adm_portcfg[5], ADM_PORTCFG_CPU);
|
||||
|
||||
/* reset all ports */
|
||||
for (i = 0; i < ADM_PHY_PORTS; i++) {
|
||||
w16(pdev, ADM_PHY_PORT(i), ADM_PHYCFG_INIT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adm6996_read_status(struct phy_device *phydev)
|
||||
{
|
||||
phydev->speed = SPEED_100;
|
||||
phydev->duplex = DUPLEX_FULL;
|
||||
phydev->state = PHY_UP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adm6996_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adm6996_probe(struct phy_device *pdev)
|
||||
{
|
||||
struct adm6996_priv *priv;
|
||||
|
||||
priv = kzalloc(sizeof(struct adm6996_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->read = adm6996_read_mii_reg;
|
||||
priv->write = adm6996_write_mii_reg;
|
||||
pdev->priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adm6996_remove(struct phy_device *pdev)
|
||||
{
|
||||
kfree(pdev->priv);
|
||||
}
|
||||
|
||||
static bool adm6996_detect(struct mii_bus *bus, int addr)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
/* we only attach to phy id 0 */
|
||||
if (addr != 0)
|
||||
return false;
|
||||
|
||||
/* look for the switch on the bus */
|
||||
reg = bus->read(bus, PHYADDR(ADM_SIG0)) & ADM_SIG0_MASK;
|
||||
if (reg != ADM_SIG0_VAL)
|
||||
return false;
|
||||
|
||||
reg = bus->read(bus, PHYADDR(ADM_SIG1)) & ADM_SIG1_MASK;
|
||||
if (reg != ADM_SIG1_VAL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct phy_driver adm6996_driver = {
|
||||
.name = "Infineon ADM6996",
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.detect = adm6996_detect,
|
||||
.probe = adm6996_probe,
|
||||
.remove = adm6996_remove,
|
||||
.config_init = &adm6996_config_init,
|
||||
.config_aneg = &adm6996_config_aneg,
|
||||
.read_status = &adm6996_read_status,
|
||||
.driver = { .owner = THIS_MODULE,},
|
||||
};
|
||||
|
||||
static int __init adm6996_init(void)
|
||||
{
|
||||
return phy_driver_register(&adm6996_driver);
|
||||
}
|
||||
|
||||
static void __exit adm6996_exit(void)
|
||||
{
|
||||
phy_driver_unregister(&adm6996_driver);
|
||||
}
|
||||
|
||||
module_init(adm6996_init);
|
||||
module_exit(adm6996_exit);
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* ADM6996 switch driver
|
||||
*
|
||||
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#ifndef __ADM6996_H
|
||||
#define __ADM6996_H
|
||||
|
||||
#define ADM_PHY_PORTS 5
|
||||
#define ADM_CPU_PORT 5
|
||||
#define ADM_WAN_PORT 0 /* FIXME: dynamic ? */
|
||||
|
||||
enum admreg {
|
||||
ADM_EEPROM_BASE = 0x0,
|
||||
ADM_P0_CFG = ADM_EEPROM_BASE + 1,
|
||||
ADM_P1_CFG = ADM_EEPROM_BASE + 3,
|
||||
ADM_P2_CFG = ADM_EEPROM_BASE + 5,
|
||||
ADM_P3_CFG = ADM_EEPROM_BASE + 7,
|
||||
ADM_P4_CFG = ADM_EEPROM_BASE + 8,
|
||||
ADM_P5_CFG = ADM_EEPROM_BASE + 9,
|
||||
ADM_EEPROM_EXT_BASE = 0x40,
|
||||
ADM_COUNTER_BASE = 0xa0,
|
||||
ADM_SIG0 = ADM_COUNTER_BASE + 0,
|
||||
ADM_SIG1 = ADM_COUNTER_BASE + 1,
|
||||
ADM_PHY_BASE = 0x200,
|
||||
#define ADM_PHY_PORT(n) (ADM_PHY_BASE + (0x20 * n))
|
||||
};
|
||||
|
||||
/* Chip identification patterns */
|
||||
#define ADM_SIG0_MASK 0xfff0
|
||||
#define ADM_SIG0_VAL 0x1020
|
||||
#define ADM_SIG1_MASK 0xffff
|
||||
#define ADM_SIG1_VAL 0x0007
|
||||
|
||||
enum {
|
||||
ADM_PHYCFG_COLTST = (1 << 7), /* Enable collision test */
|
||||
ADM_PHYCFG_DPLX = (1 << 8), /* Enable full duplex */
|
||||
ADM_PHYCFG_ANEN_RST = (1 << 9), /* Restart auto negotiation (self clear) */
|
||||
ADM_PHYCFG_ISO = (1 << 10), /* Isolate PHY */
|
||||
ADM_PHYCFG_PDN = (1 << 11), /* Power down PHY */
|
||||
ADM_PHYCFG_ANEN = (1 << 12), /* Enable auto negotiation */
|
||||
ADM_PHYCFG_SPEED_100 = (1 << 13), /* Enable 100 Mbit/s */
|
||||
ADM_PHYCFG_LPBK = (1 << 14), /* Enable loopback operation */
|
||||
ADM_PHYCFG_RST = (1 << 15), /* Reset the port (self clear) */
|
||||
ADM_PHYCFG_INIT = (
|
||||
ADM_PHYCFG_RST |
|
||||
ADM_PHYCFG_SPEED_100 |
|
||||
ADM_PHYCFG_ANEN |
|
||||
ADM_PHYCFG_ANEN_RST
|
||||
)
|
||||
};
|
||||
|
||||
enum {
|
||||
ADM_PORTCFG_FC = (1 << 0), /* Enable 802.x flow control */
|
||||
ADM_PORTCFG_AN = (1 << 1), /* Enable auto-negotiation */
|
||||
ADM_PORTCFG_SPEED_100 = (1 << 2), /* Enable 100 Mbit/s */
|
||||
ADM_PORTCFG_DPLX = (1 << 3), /* Enable full duplex */
|
||||
ADM_PORTCFG_OT = (1 << 4), /* Output tagged packets */
|
||||
ADM_PORTCFG_PD = (1 << 5), /* Port disable */
|
||||
ADM_PORTCFG_TV_PRIO = (1 << 6), /* 0 = VLAN based priority
|
||||
* 1 = TOS based priority */
|
||||
ADM_PORTCFG_PPE = (1 << 7), /* Port based priority enable */
|
||||
ADM_PORTCFG_PP_S = (1 << 8), /* Port based priority, 2 bits */
|
||||
ADM_PORTCFG_PVID_BASE = (1 << 10), /* Primary VLAN id, 4 bits */
|
||||
ADM_PORTCFG_FSE = (1 << 14), /* Fx select enable */
|
||||
ADM_PORTCFG_CAM = (1 << 15), /* Crossover Auto MDIX */
|
||||
|
||||
ADM_PORTCFG_INIT = (
|
||||
ADM_PORTCFG_FC |
|
||||
ADM_PORTCFG_AN |
|
||||
ADM_PORTCFG_SPEED_100 |
|
||||
ADM_PORTCFG_DPLX |
|
||||
ADM_PORTCFG_CAM
|
||||
),
|
||||
ADM_PORTCFG_CPU = (
|
||||
ADM_PORTCFG_FC |
|
||||
ADM_PORTCFG_SPEED_100 |
|
||||
ADM_PORTCFG_OT |
|
||||
ADM_PORTCFG_DPLX
|
||||
),
|
||||
};
|
||||
|
||||
#define ADM_PORTCFG_PPID(N) ((n & 0x3) << 8)
|
||||
#define ADM_PORTCFG_PVID(n) ((n & 0xf) << 10)
|
||||
|
||||
static const u8 adm_portcfg[] = {
|
||||
[0] = ADM_P0_CFG,
|
||||
[1] = ADM_P1_CFG,
|
||||
[2] = ADM_P2_CFG,
|
||||
[3] = ADM_P3_CFG,
|
||||
[4] = ADM_P4_CFG,
|
||||
[5] = ADM_P5_CFG,
|
||||
};
|
||||
|
||||
/*
|
||||
* Split the register address in phy id and register
|
||||
* it will get combined again by the mdio bus op
|
||||
*/
|
||||
#define PHYADDR(_reg) ((_reg >> 5) & 0xff), (_reg & 0x1f)
|
||||
|
||||
#endif
|
||||
@@ -1,474 +0,0 @@
|
||||
/*
|
||||
* Marvell 88E6060 switch driver
|
||||
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "mvswitch.h"
|
||||
|
||||
/* Undefine this to use trailer mode instead.
|
||||
* I don't know if header mode works with all chips */
|
||||
#define HEADER_MODE 1
|
||||
|
||||
MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
|
||||
MODULE_AUTHOR("Felix Fietkau");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct mvswitch_priv {
|
||||
/* the driver's tx function */
|
||||
int (*hardstart)(struct sk_buff *skb, struct net_device *dev);
|
||||
struct vlan_group *grp;
|
||||
u8 vlans[16];
|
||||
};
|
||||
|
||||
#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
|
||||
|
||||
static inline u16
|
||||
r16(struct phy_device *phydev, int addr, int reg)
|
||||
{
|
||||
return phydev->bus->read(phydev->bus, addr, reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
w16(struct phy_device *phydev, int addr, int reg, u16 val)
|
||||
{
|
||||
phydev->bus->write(phydev->bus, addr, reg, val);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
char *buf = NULL;
|
||||
u16 vid;
|
||||
|
||||
priv = dev->phy_ptr;
|
||||
if (unlikely(!priv))
|
||||
goto error;
|
||||
|
||||
if (unlikely(skb->len < 16))
|
||||
goto error;
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
if (__vlan_hwaccel_get_tag(skb, &vid))
|
||||
goto error;
|
||||
|
||||
if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
|
||||
if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
if (skb->len < 62)
|
||||
skb->len = 62;
|
||||
}
|
||||
buf = skb_push(skb, MV_HEADER_SIZE);
|
||||
#else
|
||||
if (__vlan_get_tag(skb, &vid))
|
||||
goto error;
|
||||
|
||||
if (unlikely((vid > 15 || !priv->vlans[vid])))
|
||||
goto error;
|
||||
|
||||
if (skb->len <= 64) {
|
||||
if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
|
||||
buf = skb->data + 64;
|
||||
skb->len = 64 + MV_TRAILER_SIZE;
|
||||
} else {
|
||||
if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) {
|
||||
if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
}
|
||||
buf = skb_put(skb, 4);
|
||||
}
|
||||
|
||||
/* move the ethernet header 4 bytes forward, overwriting the vlan tag */
|
||||
memmove(skb->data + 4, skb->data, 12);
|
||||
skb->data += 4;
|
||||
skb->len -= 4;
|
||||
skb->mac_header += 4;
|
||||
#endif
|
||||
|
||||
if (!buf)
|
||||
goto error;
|
||||
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
/* prepend the tag */
|
||||
*((__be16 *) buf) = cpu_to_be16(
|
||||
((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) |
|
||||
((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M)
|
||||
);
|
||||
#else
|
||||
/* append the tag */
|
||||
*((__be32 *) buf) = cpu_to_be32((
|
||||
(MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) |
|
||||
((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S)
|
||||
));
|
||||
#endif
|
||||
|
||||
return priv->hardstart(skb, dev);
|
||||
|
||||
error_expand:
|
||||
if (net_ratelimit())
|
||||
printk("%s: failed to expand/update skb for the switch\n", dev->name);
|
||||
|
||||
error:
|
||||
/* any errors? drop the packet! */
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_mangle_rx(struct sk_buff *skb, int napi)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
struct net_device *dev;
|
||||
int vlan = -1;
|
||||
unsigned char *buf;
|
||||
int i;
|
||||
|
||||
dev = skb->dev;
|
||||
if (!dev)
|
||||
goto error;
|
||||
|
||||
priv = dev->phy_ptr;
|
||||
if (!priv)
|
||||
goto error;
|
||||
|
||||
if (!priv->grp)
|
||||
goto error;
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
buf = skb->data;
|
||||
skb_pull(skb, MV_HEADER_SIZE);
|
||||
#else
|
||||
buf = skb->data + skb->len - MV_TRAILER_SIZE;
|
||||
if (buf[0] != 0x80)
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
/* look for the vlan matching the incoming port */
|
||||
for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) {
|
||||
if ((1 << buf[1]) & priv->vlans[i])
|
||||
vlan = i;
|
||||
}
|
||||
|
||||
if (vlan == -1)
|
||||
goto error;
|
||||
|
||||
skb->protocol = eth_type_trans(skb, skb->dev);
|
||||
|
||||
if (napi)
|
||||
return vlan_hwaccel_receive_skb(skb, priv->grp, vlan);
|
||||
else
|
||||
return vlan_hwaccel_rx(skb, priv->grp, vlan);
|
||||
|
||||
error:
|
||||
/* no vlan? eat the packet! */
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mvswitch_netif_rx(struct sk_buff *skb)
|
||||
{
|
||||
return mvswitch_mangle_rx(skb, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_netif_receive_skb(struct sk_buff *skb)
|
||||
{
|
||||
return mvswitch_mangle_rx(skb, 1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mvswitch_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
|
||||
{
|
||||
struct mvswitch_priv *priv = dev->phy_ptr;
|
||||
priv->grp = grp;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val)
|
||||
{
|
||||
int i = 100;
|
||||
u16 r;
|
||||
|
||||
do {
|
||||
r = r16(pdev, addr, reg) & mask;
|
||||
if (r == val)
|
||||
return 0;
|
||||
} while(--i > 0);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_config_init(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||
struct net_device *dev = pdev->attached_dev;
|
||||
u8 vlmap = 0;
|
||||
int i;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name);
|
||||
pdev->supported = ADVERTISED_100baseT_Full;
|
||||
pdev->advertising = ADVERTISED_100baseT_Full;
|
||||
dev->phy_ptr = priv;
|
||||
dev->irq = PHY_POLL;
|
||||
|
||||
/* initialize default vlans */
|
||||
for (i = 0; i < MV_PORTS; i++)
|
||||
priv->vlans[(i == MV_WANPORT ? 1 : 0)] |= (1 << i);
|
||||
|
||||
/* before entering reset, disable all ports */
|
||||
for (i = 0; i < MV_PORTS; i++)
|
||||
w16(pdev, MV_PORTREG(CONTROL, i), 0x00);
|
||||
|
||||
msleep(2); /* wait for the status change to settle in */
|
||||
|
||||
/* put the ATU in reset */
|
||||
w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET);
|
||||
|
||||
i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0);
|
||||
if (i < 0) {
|
||||
printk("%s: Timeout waiting for the switch to reset.\n", dev->name);
|
||||
return i;
|
||||
}
|
||||
|
||||
/* set the ATU flags */
|
||||
w16(pdev, MV_SWITCHREG(ATU_CTRL),
|
||||
MV_ATUCTL_NO_LEARN |
|
||||
MV_ATUCTL_ATU_1K |
|
||||
MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */
|
||||
);
|
||||
|
||||
/* initialize the cpu port */
|
||||
w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
|
||||
#ifdef HEADER_MODE
|
||||
MV_PORTCTRL_HEADER |
|
||||
#else
|
||||
MV_PORTCTRL_RXTR |
|
||||
MV_PORTCTRL_TXTR |
|
||||
#endif
|
||||
MV_PORTCTRL_ENABLED
|
||||
);
|
||||
/* wait for the phy change to settle in */
|
||||
msleep(2);
|
||||
for (i = 0; i < MV_PORTS; i++) {
|
||||
u8 pvid = 0;
|
||||
int j;
|
||||
|
||||
vlmap = 0;
|
||||
|
||||
/* look for the matching vlan */
|
||||
for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) {
|
||||
if (priv->vlans[j] & (1 << i)) {
|
||||
vlmap = priv->vlans[j];
|
||||
pvid = j;
|
||||
}
|
||||
}
|
||||
/* leave port unconfigured if it's not part of a vlan */
|
||||
if (!vlmap)
|
||||
continue;
|
||||
|
||||
/* add the cpu port to the allowed destinations list */
|
||||
vlmap |= (1 << MV_CPUPORT);
|
||||
|
||||
/* take port out of its own vlan destination map */
|
||||
vlmap &= ~(1 << i);
|
||||
|
||||
/* apply vlan settings */
|
||||
w16(pdev, MV_PORTREG(VLANMAP, i),
|
||||
MV_PORTVLAN_PORTS(vlmap) |
|
||||
MV_PORTVLAN_ID(i)
|
||||
);
|
||||
|
||||
/* re-enable port */
|
||||
w16(pdev, MV_PORTREG(CONTROL, i),
|
||||
MV_PORTCTRL_ENABLED
|
||||
);
|
||||
}
|
||||
|
||||
w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
|
||||
MV_PORTVLAN_ID(MV_CPUPORT)
|
||||
);
|
||||
|
||||
/* set the port association vector */
|
||||
for (i = 0; i <= MV_PORTS; i++) {
|
||||
w16(pdev, MV_PORTREG(ASSOC, i),
|
||||
MV_PORTASSOC_PORTS(1 << i)
|
||||
);
|
||||
}
|
||||
|
||||
/* init switch control */
|
||||
w16(pdev, MV_SWITCHREG(CTRL),
|
||||
MV_SWITCHCTL_MSIZE |
|
||||
MV_SWITCHCTL_DROP
|
||||
);
|
||||
|
||||
/* hook into the tx function */
|
||||
priv->hardstart = dev->hard_start_xmit;
|
||||
pdev->netif_receive_skb = mvswitch_netif_receive_skb;
|
||||
pdev->netif_rx = mvswitch_netif_rx;
|
||||
dev->hard_start_xmit = mvswitch_mangle_tx;
|
||||
dev->vlan_rx_register = mvswitch_vlan_rx_register;
|
||||
#ifdef HEADER_MODE
|
||||
dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
|
||||
#else
|
||||
dev->features |= NETIF_F_HW_VLAN_RX;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_read_status(struct phy_device *pdev)
|
||||
{
|
||||
pdev->speed = SPEED_100;
|
||||
pdev->duplex = DUPLEX_FULL;
|
||||
pdev->state = PHY_UP;
|
||||
|
||||
/* XXX ugly workaround: we can't force the switch
|
||||
* to gracefully handle hosts moving from one port to another,
|
||||
* so we have to regularly clear the ATU database */
|
||||
|
||||
/* wait for the ATU to become available */
|
||||
mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
|
||||
|
||||
/* flush the ATU */
|
||||
w16(pdev, MV_SWITCHREG(ATU_OP),
|
||||
MV_ATUOP_INPROGRESS |
|
||||
MV_ATUOP_FLUSH_ALL
|
||||
);
|
||||
|
||||
/* wait for operation to complete */
|
||||
mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mvswitch_remove(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||
struct net_device *dev = pdev->attached_dev;
|
||||
|
||||
/* restore old xmit handler */
|
||||
if (priv->hardstart && dev)
|
||||
dev->hard_start_xmit = priv->hardstart;
|
||||
dev->vlan_rx_register = NULL;
|
||||
dev->vlan_rx_kill_vid = NULL;
|
||||
dev->phy_ptr = NULL;
|
||||
dev->features &= ~NETIF_F_HW_VLAN_RX;
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static bool
|
||||
mvswitch_detect(struct mii_bus *bus, int addr)
|
||||
{
|
||||
u16 reg;
|
||||
int i;
|
||||
|
||||
/* we attach to phy id 31 to make sure that the late probe works */
|
||||
if (addr != 31)
|
||||
return false;
|
||||
|
||||
/* look for the switch on the bus */
|
||||
reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
|
||||
if (reg != MV_IDENT_VALUE)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Now that we've established that the switch actually exists, let's
|
||||
* get rid of the competition :)
|
||||
*/
|
||||
for (i = 0; i < 31; i++) {
|
||||
if (!bus->phy_map[i])
|
||||
continue;
|
||||
|
||||
device_unregister(&bus->phy_map[i]->dev);
|
||||
kfree(bus->phy_map[i]);
|
||||
bus->phy_map[i] = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_probe(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
|
||||
priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pdev->priv = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct phy_driver mvswitch_driver = {
|
||||
.name = "Marvell 88E6060",
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.detect = &mvswitch_detect,
|
||||
.probe = &mvswitch_probe,
|
||||
.remove = &mvswitch_remove,
|
||||
.config_init = &mvswitch_config_init,
|
||||
.config_aneg = &mvswitch_config_aneg,
|
||||
.read_status = &mvswitch_read_status,
|
||||
.driver = { .owner = THIS_MODULE,},
|
||||
};
|
||||
|
||||
static int __init
|
||||
mvswitch_init(void)
|
||||
{
|
||||
return phy_driver_register(&mvswitch_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
mvswitch_exit(void)
|
||||
{
|
||||
phy_driver_unregister(&mvswitch_driver);
|
||||
}
|
||||
|
||||
module_init(mvswitch_init);
|
||||
module_exit(mvswitch_exit);
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Marvell 88E6060 switch driver
|
||||
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#ifndef __MVSWITCH_H
|
||||
#define __MVSWITCH_H
|
||||
|
||||
#define MV_HEADER_SIZE 2
|
||||
#define MV_HEADER_PORTS_M 0x001f
|
||||
#define MV_HEADER_PORTS_S 0
|
||||
#define MV_HEADER_VLAN_M 0xf000
|
||||
#define MV_HEADER_VLAN_S 12
|
||||
|
||||
#define MV_TRAILER_SIZE 4
|
||||
#define MV_TRAILER_PORTS_M 0x1f
|
||||
#define MV_TRAILER_PORTS_S 16
|
||||
#define MV_TRAILER_FLAGS_S 24
|
||||
#define MV_TRAILER_OVERRIDE 0x80
|
||||
|
||||
|
||||
#define MV_PORTS 5
|
||||
#define MV_WANPORT 4
|
||||
#define MV_CPUPORT 5
|
||||
|
||||
#define MV_BASE 0x10
|
||||
|
||||
#define MV_PHYPORT_BASE (MV_BASE + 0x0)
|
||||
#define MV_PHYPORT(_n) (MV_PHYPORT_BASE + (_n))
|
||||
#define MV_SWITCHPORT_BASE (MV_BASE + 0x8)
|
||||
#define MV_SWITCHPORT(_n) (MV_SWITCHPORT_BASE + (_n))
|
||||
#define MV_SWITCHREGS (MV_BASE + 0xf)
|
||||
|
||||
enum {
|
||||
MV_PHY_CONTROL = 0x00,
|
||||
MV_PHY_STATUS = 0x01,
|
||||
MV_PHY_IDENT0 = 0x02,
|
||||
MV_PHY_IDENT1 = 0x03,
|
||||
MV_PHY_ANEG = 0x04,
|
||||
MV_PHY_LINK_ABILITY = 0x05,
|
||||
MV_PHY_ANEG_EXPAND = 0x06,
|
||||
MV_PHY_XMIT_NEXTP = 0x07,
|
||||
MV_PHY_LINK_NEXTP = 0x08,
|
||||
MV_PHY_CONTROL1 = 0x10,
|
||||
MV_PHY_STATUS1 = 0x11,
|
||||
MV_PHY_INTR_EN = 0x12,
|
||||
MV_PHY_INTR_STATUS = 0x13,
|
||||
MV_PHY_INTR_PORT = 0x14,
|
||||
MV_PHY_RECV_COUNTER = 0x16,
|
||||
MV_PHY_LED_PARALLEL = 0x16,
|
||||
MV_PHY_LED_STREAM = 0x17,
|
||||
MV_PHY_LED_CTRL = 0x18,
|
||||
MV_PHY_LED_OVERRIDE = 0x19,
|
||||
MV_PHY_VCT_CTRL = 0x1a,
|
||||
MV_PHY_VCT_STATUS = 0x1b,
|
||||
MV_PHY_CONTROL2 = 0x1e
|
||||
};
|
||||
#define MV_PHYREG(_type, _port) MV_PHYPORT(_port), MV_PHY_##_type
|
||||
|
||||
enum {
|
||||
MV_PORT_STATUS = 0x00,
|
||||
MV_PORT_IDENT = 0x03,
|
||||
MV_PORT_CONTROL = 0x04,
|
||||
MV_PORT_VLANMAP = 0x06,
|
||||
MV_PORT_ASSOC = 0x0b,
|
||||
MV_PORT_RXCOUNT = 0x10,
|
||||
MV_PORT_TXCOUNT = 0x11,
|
||||
};
|
||||
#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
|
||||
|
||||
enum {
|
||||
MV_PORTCTRL_BLOCK = (1 << 0),
|
||||
MV_PORTCTRL_LEARN = (2 << 0),
|
||||
MV_PORTCTRL_ENABLED = (3 << 0),
|
||||
MV_PORTCTRL_VLANTUN = (1 << 7), /* Enforce VLANs on packets */
|
||||
MV_PORTCTRL_RXTR = (1 << 8), /* Enable Marvell packet trailer for ingress */
|
||||
MV_PORTCTRL_HEADER = (1 << 11), /* Enable Marvell packet header mode for port */
|
||||
MV_PORTCTRL_TXTR = (1 << 14), /* Enable Marvell packet trailer for egress */
|
||||
MV_PORTCTRL_FORCEFL = (1 << 15), /* force flow control */
|
||||
};
|
||||
|
||||
#define MV_PORTVLAN_ID(_n) (((_n) & 0xf) << 12)
|
||||
#define MV_PORTVLAN_PORTS(_n) ((_n) & 0x3f)
|
||||
|
||||
#define MV_PORTASSOC_PORTS(_n) ((_n) & 0x1f)
|
||||
#define MV_PORTASSOC_MONITOR (1 << 15)
|
||||
|
||||
enum {
|
||||
MV_SWITCH_MAC0 = 0x01,
|
||||
MV_SWITCH_MAC1 = 0x02,
|
||||
MV_SWITCH_MAC2 = 0x03,
|
||||
MV_SWITCH_CTRL = 0x04,
|
||||
MV_SWITCH_ATU_CTRL = 0x0a,
|
||||
MV_SWITCH_ATU_OP = 0x0b,
|
||||
MV_SWITCH_ATU_DATA = 0x0c,
|
||||
MV_SWITCH_ATU_MAC0 = 0x0d,
|
||||
MV_SWITCH_ATU_MAC1 = 0x0e,
|
||||
MV_SWITCH_ATU_MAC2 = 0x0f,
|
||||
};
|
||||
#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
|
||||
|
||||
enum {
|
||||
MV_SWITCHCTL_EEIE = (1 << 0), /* EEPROM interrupt enable */
|
||||
MV_SWITCHCTL_PHYIE = (1 << 1), /* PHY interrupt enable */
|
||||
MV_SWITCHCTL_ATUDONE= (1 << 2), /* ATU done interrupt enable */
|
||||
MV_SWITCHCTL_ATUIE = (1 << 3), /* ATU interrupt enable */
|
||||
MV_SWITCHCTL_CTRMODE= (1 << 8), /* statistics for rx and tx errors */
|
||||
MV_SWITCHCTL_RELOAD = (1 << 9), /* reload registers from eeprom */
|
||||
MV_SWITCHCTL_MSIZE = (1 << 10), /* increase maximum frame size */
|
||||
MV_SWITCHCTL_DROP = (1 << 13), /* discard frames with excessive collisions */
|
||||
};
|
||||
|
||||
enum {
|
||||
#define MV_ATUCTL_AGETIME_MIN 16
|
||||
#define MV_ATUCTL_AGETIME_MAX 4080
|
||||
#define MV_ATUCTL_AGETIME(_n) ((((_n) / 16) & 0xff) << 4)
|
||||
MV_ATUCTL_ATU_256 = (0 << 12),
|
||||
MV_ATUCTL_ATU_512 = (1 << 12),
|
||||
MV_ATUCTL_ATU_1K = (2 << 12),
|
||||
MV_ATUCTL_ATUMASK = (3 << 12),
|
||||
MV_ATUCTL_NO_LEARN = (1 << 14),
|
||||
MV_ATUCTL_RESET = (1 << 15),
|
||||
};
|
||||
|
||||
enum {
|
||||
#define MV_ATUOP_DBNUM(_n) ((_n) & 0x0f)
|
||||
|
||||
MV_ATUOP_NOOP = (0 << 12),
|
||||
MV_ATUOP_FLUSH_ALL = (1 << 12),
|
||||
MV_ATUOP_FLUSH_U = (2 << 12),
|
||||
MV_ATUOP_LOAD_DB = (3 << 12),
|
||||
MV_ATUOP_GET_NEXT = (4 << 12),
|
||||
MV_ATUOP_FLUSH_DB = (5 << 12),
|
||||
MV_ATUOP_FLUSH_DB_UU= (6 << 12),
|
||||
|
||||
MV_ATUOP_INPROGRESS = (1 << 15),
|
||||
};
|
||||
|
||||
#define MV_IDENT_MASK 0xfff0
|
||||
#define MV_IDENT_VALUE 0x0600
|
||||
|
||||
#endif
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* ADM6996 switch driver
|
||||
*
|
||||
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "adm6996.h"
|
||||
|
||||
MODULE_DESCRIPTION("Infineon ADM6996 Switch");
|
||||
MODULE_AUTHOR("Felix Fietkau");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct adm6996_priv {
|
||||
/* use abstraction for regops, we want to add gpio support in the future */
|
||||
u16 (*read)(struct phy_device *phydev, enum admreg reg);
|
||||
void (*write)(struct phy_device *phydev, enum admreg reg, u16 val);
|
||||
};
|
||||
|
||||
#define to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv)
|
||||
|
||||
|
||||
static inline u16
|
||||
r16(struct phy_device *pdev, enum admreg reg)
|
||||
{
|
||||
return to_adm(pdev)->read(pdev, reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
w16(struct phy_device *pdev, enum admreg reg, u16 val)
|
||||
{
|
||||
to_adm(pdev)->write(pdev, reg, val);
|
||||
}
|
||||
|
||||
|
||||
static u16
|
||||
adm6996_read_mii_reg(struct phy_device *phydev, enum admreg reg)
|
||||
{
|
||||
return phydev->bus->read(phydev->bus, PHYADDR(reg));
|
||||
}
|
||||
|
||||
static void
|
||||
adm6996_write_mii_reg(struct phy_device *phydev, enum admreg reg, u16 val)
|
||||
{
|
||||
phydev->bus->write(phydev->bus, PHYADDR(reg), val);
|
||||
}
|
||||
|
||||
|
||||
static int adm6996_config_init(struct phy_device *pdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk("%s: ADM6996 PHY driver attached.\n", pdev->attached_dev->name);
|
||||
pdev->supported = ADVERTISED_100baseT_Full;
|
||||
pdev->advertising = ADVERTISED_100baseT_Full;
|
||||
|
||||
/* initialize port and vlan settings */
|
||||
for (i = 0; i < ADM_PHY_PORTS; i++) {
|
||||
w16(pdev, adm_portcfg[i], ADM_PORTCFG_INIT |
|
||||
ADM_PORTCFG_PVID((i == ADM_WAN_PORT) ? 1 : 0));
|
||||
}
|
||||
w16(pdev, adm_portcfg[5], ADM_PORTCFG_CPU);
|
||||
|
||||
/* reset all ports */
|
||||
for (i = 0; i < ADM_PHY_PORTS; i++) {
|
||||
w16(pdev, ADM_PHY_PORT(i), ADM_PHYCFG_INIT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adm6996_read_status(struct phy_device *phydev)
|
||||
{
|
||||
phydev->speed = SPEED_100;
|
||||
phydev->duplex = DUPLEX_FULL;
|
||||
phydev->state = PHY_UP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adm6996_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adm6996_probe(struct phy_device *pdev)
|
||||
{
|
||||
struct adm6996_priv *priv;
|
||||
|
||||
priv = kzalloc(sizeof(struct adm6996_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->read = adm6996_read_mii_reg;
|
||||
priv->write = adm6996_write_mii_reg;
|
||||
pdev->priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adm6996_remove(struct phy_device *pdev)
|
||||
{
|
||||
kfree(pdev->priv);
|
||||
}
|
||||
|
||||
static bool adm6996_detect(struct mii_bus *bus, int addr)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
/* we only attach to phy id 0 */
|
||||
if (addr != 0)
|
||||
return false;
|
||||
|
||||
/* look for the switch on the bus */
|
||||
reg = bus->read(bus, PHYADDR(ADM_SIG0)) & ADM_SIG0_MASK;
|
||||
if (reg != ADM_SIG0_VAL)
|
||||
return false;
|
||||
|
||||
reg = bus->read(bus, PHYADDR(ADM_SIG1)) & ADM_SIG1_MASK;
|
||||
if (reg != ADM_SIG1_VAL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct phy_driver adm6996_driver = {
|
||||
.name = "Infineon ADM6996",
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.detect = adm6996_detect,
|
||||
.probe = adm6996_probe,
|
||||
.remove = adm6996_remove,
|
||||
.config_init = &adm6996_config_init,
|
||||
.config_aneg = &adm6996_config_aneg,
|
||||
.read_status = &adm6996_read_status,
|
||||
.driver = { .owner = THIS_MODULE,},
|
||||
};
|
||||
|
||||
static int __init adm6996_init(void)
|
||||
{
|
||||
return phy_driver_register(&adm6996_driver);
|
||||
}
|
||||
|
||||
static void __exit adm6996_exit(void)
|
||||
{
|
||||
phy_driver_unregister(&adm6996_driver);
|
||||
}
|
||||
|
||||
module_init(adm6996_init);
|
||||
module_exit(adm6996_exit);
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* ADM6996 switch driver
|
||||
*
|
||||
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#ifndef __ADM6996_H
|
||||
#define __ADM6996_H
|
||||
|
||||
#define ADM_PHY_PORTS 5
|
||||
#define ADM_CPU_PORT 5
|
||||
#define ADM_WAN_PORT 0 /* FIXME: dynamic ? */
|
||||
|
||||
enum admreg {
|
||||
ADM_EEPROM_BASE = 0x0,
|
||||
ADM_P0_CFG = ADM_EEPROM_BASE + 1,
|
||||
ADM_P1_CFG = ADM_EEPROM_BASE + 3,
|
||||
ADM_P2_CFG = ADM_EEPROM_BASE + 5,
|
||||
ADM_P3_CFG = ADM_EEPROM_BASE + 7,
|
||||
ADM_P4_CFG = ADM_EEPROM_BASE + 8,
|
||||
ADM_P5_CFG = ADM_EEPROM_BASE + 9,
|
||||
ADM_EEPROM_EXT_BASE = 0x40,
|
||||
ADM_COUNTER_BASE = 0xa0,
|
||||
ADM_SIG0 = ADM_COUNTER_BASE + 0,
|
||||
ADM_SIG1 = ADM_COUNTER_BASE + 1,
|
||||
ADM_PHY_BASE = 0x200,
|
||||
#define ADM_PHY_PORT(n) (ADM_PHY_BASE + (0x20 * n))
|
||||
};
|
||||
|
||||
/* Chip identification patterns */
|
||||
#define ADM_SIG0_MASK 0xfff0
|
||||
#define ADM_SIG0_VAL 0x1020
|
||||
#define ADM_SIG1_MASK 0xffff
|
||||
#define ADM_SIG1_VAL 0x0007
|
||||
|
||||
enum {
|
||||
ADM_PHYCFG_COLTST = (1 << 7), /* Enable collision test */
|
||||
ADM_PHYCFG_DPLX = (1 << 8), /* Enable full duplex */
|
||||
ADM_PHYCFG_ANEN_RST = (1 << 9), /* Restart auto negotiation (self clear) */
|
||||
ADM_PHYCFG_ISO = (1 << 10), /* Isolate PHY */
|
||||
ADM_PHYCFG_PDN = (1 << 11), /* Power down PHY */
|
||||
ADM_PHYCFG_ANEN = (1 << 12), /* Enable auto negotiation */
|
||||
ADM_PHYCFG_SPEED_100 = (1 << 13), /* Enable 100 Mbit/s */
|
||||
ADM_PHYCFG_LPBK = (1 << 14), /* Enable loopback operation */
|
||||
ADM_PHYCFG_RST = (1 << 15), /* Reset the port (self clear) */
|
||||
ADM_PHYCFG_INIT = (
|
||||
ADM_PHYCFG_RST |
|
||||
ADM_PHYCFG_SPEED_100 |
|
||||
ADM_PHYCFG_ANEN |
|
||||
ADM_PHYCFG_ANEN_RST
|
||||
)
|
||||
};
|
||||
|
||||
enum {
|
||||
ADM_PORTCFG_FC = (1 << 0), /* Enable 802.x flow control */
|
||||
ADM_PORTCFG_AN = (1 << 1), /* Enable auto-negotiation */
|
||||
ADM_PORTCFG_SPEED_100 = (1 << 2), /* Enable 100 Mbit/s */
|
||||
ADM_PORTCFG_DPLX = (1 << 3), /* Enable full duplex */
|
||||
ADM_PORTCFG_OT = (1 << 4), /* Output tagged packets */
|
||||
ADM_PORTCFG_PD = (1 << 5), /* Port disable */
|
||||
ADM_PORTCFG_TV_PRIO = (1 << 6), /* 0 = VLAN based priority
|
||||
* 1 = TOS based priority */
|
||||
ADM_PORTCFG_PPE = (1 << 7), /* Port based priority enable */
|
||||
ADM_PORTCFG_PP_S = (1 << 8), /* Port based priority, 2 bits */
|
||||
ADM_PORTCFG_PVID_BASE = (1 << 10), /* Primary VLAN id, 4 bits */
|
||||
ADM_PORTCFG_FSE = (1 << 14), /* Fx select enable */
|
||||
ADM_PORTCFG_CAM = (1 << 15), /* Crossover Auto MDIX */
|
||||
|
||||
ADM_PORTCFG_INIT = (
|
||||
ADM_PORTCFG_FC |
|
||||
ADM_PORTCFG_AN |
|
||||
ADM_PORTCFG_SPEED_100 |
|
||||
ADM_PORTCFG_DPLX |
|
||||
ADM_PORTCFG_CAM
|
||||
),
|
||||
ADM_PORTCFG_CPU = (
|
||||
ADM_PORTCFG_FC |
|
||||
ADM_PORTCFG_SPEED_100 |
|
||||
ADM_PORTCFG_OT |
|
||||
ADM_PORTCFG_DPLX
|
||||
),
|
||||
};
|
||||
|
||||
#define ADM_PORTCFG_PPID(N) ((n & 0x3) << 8)
|
||||
#define ADM_PORTCFG_PVID(n) ((n & 0xf) << 10)
|
||||
|
||||
static const u8 adm_portcfg[] = {
|
||||
[0] = ADM_P0_CFG,
|
||||
[1] = ADM_P1_CFG,
|
||||
[2] = ADM_P2_CFG,
|
||||
[3] = ADM_P3_CFG,
|
||||
[4] = ADM_P4_CFG,
|
||||
[5] = ADM_P5_CFG,
|
||||
};
|
||||
|
||||
/*
|
||||
* Split the register address in phy id and register
|
||||
* it will get combined again by the mdio bus op
|
||||
*/
|
||||
#define PHYADDR(_reg) ((_reg >> 5) & 0xff), (_reg & 0x1f)
|
||||
|
||||
#endif
|
||||
@@ -1,474 +0,0 @@
|
||||
/*
|
||||
* Marvell 88E6060 switch driver
|
||||
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "mvswitch.h"
|
||||
|
||||
/* Undefine this to use trailer mode instead.
|
||||
* I don't know if header mode works with all chips */
|
||||
#define HEADER_MODE 1
|
||||
|
||||
MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
|
||||
MODULE_AUTHOR("Felix Fietkau");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct mvswitch_priv {
|
||||
/* the driver's tx function */
|
||||
int (*hardstart)(struct sk_buff *skb, struct net_device *dev);
|
||||
struct vlan_group *grp;
|
||||
u8 vlans[16];
|
||||
};
|
||||
|
||||
#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
|
||||
|
||||
static inline u16
|
||||
r16(struct phy_device *phydev, int addr, int reg)
|
||||
{
|
||||
return phydev->bus->read(phydev->bus, addr, reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
w16(struct phy_device *phydev, int addr, int reg, u16 val)
|
||||
{
|
||||
phydev->bus->write(phydev->bus, addr, reg, val);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
char *buf = NULL;
|
||||
u16 vid;
|
||||
|
||||
priv = dev->phy_ptr;
|
||||
if (unlikely(!priv))
|
||||
goto error;
|
||||
|
||||
if (unlikely(skb->len < 16))
|
||||
goto error;
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
if (__vlan_hwaccel_get_tag(skb, &vid))
|
||||
goto error;
|
||||
|
||||
if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
|
||||
if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
if (skb->len < 62)
|
||||
skb->len = 62;
|
||||
}
|
||||
buf = skb_push(skb, MV_HEADER_SIZE);
|
||||
#else
|
||||
if (__vlan_get_tag(skb, &vid))
|
||||
goto error;
|
||||
|
||||
if (unlikely((vid > 15 || !priv->vlans[vid])))
|
||||
goto error;
|
||||
|
||||
if (skb->len <= 64) {
|
||||
if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
|
||||
buf = skb->data + 64;
|
||||
skb->len = 64 + MV_TRAILER_SIZE;
|
||||
} else {
|
||||
if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) {
|
||||
if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
}
|
||||
buf = skb_put(skb, 4);
|
||||
}
|
||||
|
||||
/* move the ethernet header 4 bytes forward, overwriting the vlan tag */
|
||||
memmove(skb->data + 4, skb->data, 12);
|
||||
skb->data += 4;
|
||||
skb->len -= 4;
|
||||
skb->mac_header += 4;
|
||||
#endif
|
||||
|
||||
if (!buf)
|
||||
goto error;
|
||||
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
/* prepend the tag */
|
||||
*((__be16 *) buf) = cpu_to_be16(
|
||||
((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) |
|
||||
((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M)
|
||||
);
|
||||
#else
|
||||
/* append the tag */
|
||||
*((__be32 *) buf) = cpu_to_be32((
|
||||
(MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) |
|
||||
((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S)
|
||||
));
|
||||
#endif
|
||||
|
||||
return priv->hardstart(skb, dev);
|
||||
|
||||
error_expand:
|
||||
if (net_ratelimit())
|
||||
printk("%s: failed to expand/update skb for the switch\n", dev->name);
|
||||
|
||||
error:
|
||||
/* any errors? drop the packet! */
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_mangle_rx(struct sk_buff *skb, int napi)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
struct net_device *dev;
|
||||
int vlan = -1;
|
||||
unsigned char *buf;
|
||||
int i;
|
||||
|
||||
dev = skb->dev;
|
||||
if (!dev)
|
||||
goto error;
|
||||
|
||||
priv = dev->phy_ptr;
|
||||
if (!priv)
|
||||
goto error;
|
||||
|
||||
if (!priv->grp)
|
||||
goto error;
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
buf = skb->data;
|
||||
skb_pull(skb, MV_HEADER_SIZE);
|
||||
#else
|
||||
buf = skb->data + skb->len - MV_TRAILER_SIZE;
|
||||
if (buf[0] != 0x80)
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
/* look for the vlan matching the incoming port */
|
||||
for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) {
|
||||
if ((1 << buf[1]) & priv->vlans[i])
|
||||
vlan = i;
|
||||
}
|
||||
|
||||
if (vlan == -1)
|
||||
goto error;
|
||||
|
||||
skb->protocol = eth_type_trans(skb, skb->dev);
|
||||
|
||||
if (napi)
|
||||
return vlan_hwaccel_receive_skb(skb, priv->grp, vlan);
|
||||
else
|
||||
return vlan_hwaccel_rx(skb, priv->grp, vlan);
|
||||
|
||||
error:
|
||||
/* no vlan? eat the packet! */
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mvswitch_netif_rx(struct sk_buff *skb)
|
||||
{
|
||||
return mvswitch_mangle_rx(skb, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_netif_receive_skb(struct sk_buff *skb)
|
||||
{
|
||||
return mvswitch_mangle_rx(skb, 1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mvswitch_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
|
||||
{
|
||||
struct mvswitch_priv *priv = dev->phy_ptr;
|
||||
priv->grp = grp;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val)
|
||||
{
|
||||
int i = 100;
|
||||
u16 r;
|
||||
|
||||
do {
|
||||
r = r16(pdev, addr, reg) & mask;
|
||||
if (r == val)
|
||||
return 0;
|
||||
} while(--i > 0);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_config_init(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||
struct net_device *dev = pdev->attached_dev;
|
||||
u8 vlmap = 0;
|
||||
int i;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name);
|
||||
pdev->supported = ADVERTISED_100baseT_Full;
|
||||
pdev->advertising = ADVERTISED_100baseT_Full;
|
||||
dev->phy_ptr = priv;
|
||||
dev->irq = PHY_POLL;
|
||||
|
||||
/* initialize default vlans */
|
||||
for (i = 0; i < MV_PORTS; i++)
|
||||
priv->vlans[(i == MV_WANPORT ? 1 : 0)] |= (1 << i);
|
||||
|
||||
/* before entering reset, disable all ports */
|
||||
for (i = 0; i < MV_PORTS; i++)
|
||||
w16(pdev, MV_PORTREG(CONTROL, i), 0x00);
|
||||
|
||||
msleep(2); /* wait for the status change to settle in */
|
||||
|
||||
/* put the ATU in reset */
|
||||
w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET);
|
||||
|
||||
i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0);
|
||||
if (i < 0) {
|
||||
printk("%s: Timeout waiting for the switch to reset.\n", dev->name);
|
||||
return i;
|
||||
}
|
||||
|
||||
/* set the ATU flags */
|
||||
w16(pdev, MV_SWITCHREG(ATU_CTRL),
|
||||
MV_ATUCTL_NO_LEARN |
|
||||
MV_ATUCTL_ATU_1K |
|
||||
MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */
|
||||
);
|
||||
|
||||
/* initialize the cpu port */
|
||||
w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
|
||||
#ifdef HEADER_MODE
|
||||
MV_PORTCTRL_HEADER |
|
||||
#else
|
||||
MV_PORTCTRL_RXTR |
|
||||
MV_PORTCTRL_TXTR |
|
||||
#endif
|
||||
MV_PORTCTRL_ENABLED
|
||||
);
|
||||
/* wait for the phy change to settle in */
|
||||
msleep(2);
|
||||
for (i = 0; i < MV_PORTS; i++) {
|
||||
u8 pvid = 0;
|
||||
int j;
|
||||
|
||||
vlmap = 0;
|
||||
|
||||
/* look for the matching vlan */
|
||||
for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) {
|
||||
if (priv->vlans[j] & (1 << i)) {
|
||||
vlmap = priv->vlans[j];
|
||||
pvid = j;
|
||||
}
|
||||
}
|
||||
/* leave port unconfigured if it's not part of a vlan */
|
||||
if (!vlmap)
|
||||
continue;
|
||||
|
||||
/* add the cpu port to the allowed destinations list */
|
||||
vlmap |= (1 << MV_CPUPORT);
|
||||
|
||||
/* take port out of its own vlan destination map */
|
||||
vlmap &= ~(1 << i);
|
||||
|
||||
/* apply vlan settings */
|
||||
w16(pdev, MV_PORTREG(VLANMAP, i),
|
||||
MV_PORTVLAN_PORTS(vlmap) |
|
||||
MV_PORTVLAN_ID(i)
|
||||
);
|
||||
|
||||
/* re-enable port */
|
||||
w16(pdev, MV_PORTREG(CONTROL, i),
|
||||
MV_PORTCTRL_ENABLED
|
||||
);
|
||||
}
|
||||
|
||||
w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
|
||||
MV_PORTVLAN_ID(MV_CPUPORT)
|
||||
);
|
||||
|
||||
/* set the port association vector */
|
||||
for (i = 0; i <= MV_PORTS; i++) {
|
||||
w16(pdev, MV_PORTREG(ASSOC, i),
|
||||
MV_PORTASSOC_PORTS(1 << i)
|
||||
);
|
||||
}
|
||||
|
||||
/* init switch control */
|
||||
w16(pdev, MV_SWITCHREG(CTRL),
|
||||
MV_SWITCHCTL_MSIZE |
|
||||
MV_SWITCHCTL_DROP
|
||||
);
|
||||
|
||||
/* hook into the tx function */
|
||||
priv->hardstart = dev->hard_start_xmit;
|
||||
pdev->netif_receive_skb = mvswitch_netif_receive_skb;
|
||||
pdev->netif_rx = mvswitch_netif_rx;
|
||||
dev->hard_start_xmit = mvswitch_mangle_tx;
|
||||
dev->vlan_rx_register = mvswitch_vlan_rx_register;
|
||||
#ifdef HEADER_MODE
|
||||
dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
|
||||
#else
|
||||
dev->features |= NETIF_F_HW_VLAN_RX;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_read_status(struct phy_device *pdev)
|
||||
{
|
||||
pdev->speed = SPEED_100;
|
||||
pdev->duplex = DUPLEX_FULL;
|
||||
pdev->state = PHY_UP;
|
||||
|
||||
/* XXX ugly workaround: we can't force the switch
|
||||
* to gracefully handle hosts moving from one port to another,
|
||||
* so we have to regularly clear the ATU database */
|
||||
|
||||
/* wait for the ATU to become available */
|
||||
mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
|
||||
|
||||
/* flush the ATU */
|
||||
w16(pdev, MV_SWITCHREG(ATU_OP),
|
||||
MV_ATUOP_INPROGRESS |
|
||||
MV_ATUOP_FLUSH_ALL
|
||||
);
|
||||
|
||||
/* wait for operation to complete */
|
||||
mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mvswitch_remove(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||
struct net_device *dev = pdev->attached_dev;
|
||||
|
||||
/* restore old xmit handler */
|
||||
if (priv->hardstart && dev)
|
||||
dev->hard_start_xmit = priv->hardstart;
|
||||
dev->vlan_rx_register = NULL;
|
||||
dev->vlan_rx_kill_vid = NULL;
|
||||
dev->phy_ptr = NULL;
|
||||
dev->features &= ~NETIF_F_HW_VLAN_RX;
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static bool
|
||||
mvswitch_detect(struct mii_bus *bus, int addr)
|
||||
{
|
||||
u16 reg;
|
||||
int i;
|
||||
|
||||
/* we attach to phy id 31 to make sure that the late probe works */
|
||||
if (addr != 31)
|
||||
return false;
|
||||
|
||||
/* look for the switch on the bus */
|
||||
reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
|
||||
if (reg != MV_IDENT_VALUE)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Now that we've established that the switch actually exists, let's
|
||||
* get rid of the competition :)
|
||||
*/
|
||||
for (i = 0; i < 31; i++) {
|
||||
if (!bus->phy_map[i])
|
||||
continue;
|
||||
|
||||
device_unregister(&bus->phy_map[i]->dev);
|
||||
kfree(bus->phy_map[i]);
|
||||
bus->phy_map[i] = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_probe(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
|
||||
priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pdev->priv = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct phy_driver mvswitch_driver = {
|
||||
.name = "Marvell 88E6060",
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.detect = &mvswitch_detect,
|
||||
.probe = &mvswitch_probe,
|
||||
.remove = &mvswitch_remove,
|
||||
.config_init = &mvswitch_config_init,
|
||||
.config_aneg = &mvswitch_config_aneg,
|
||||
.read_status = &mvswitch_read_status,
|
||||
.driver = { .owner = THIS_MODULE,},
|
||||
};
|
||||
|
||||
static int __init
|
||||
mvswitch_init(void)
|
||||
{
|
||||
return phy_driver_register(&mvswitch_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
mvswitch_exit(void)
|
||||
{
|
||||
phy_driver_unregister(&mvswitch_driver);
|
||||
}
|
||||
|
||||
module_init(mvswitch_init);
|
||||
module_exit(mvswitch_exit);
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Marvell 88E6060 switch driver
|
||||
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#ifndef __MVSWITCH_H
|
||||
#define __MVSWITCH_H
|
||||
|
||||
#define MV_HEADER_SIZE 2
|
||||
#define MV_HEADER_PORTS_M 0x001f
|
||||
#define MV_HEADER_PORTS_S 0
|
||||
#define MV_HEADER_VLAN_M 0xf000
|
||||
#define MV_HEADER_VLAN_S 12
|
||||
|
||||
#define MV_TRAILER_SIZE 4
|
||||
#define MV_TRAILER_PORTS_M 0x1f
|
||||
#define MV_TRAILER_PORTS_S 16
|
||||
#define MV_TRAILER_FLAGS_S 24
|
||||
#define MV_TRAILER_OVERRIDE 0x80
|
||||
|
||||
|
||||
#define MV_PORTS 5
|
||||
#define MV_WANPORT 4
|
||||
#define MV_CPUPORT 5
|
||||
|
||||
#define MV_BASE 0x10
|
||||
|
||||
#define MV_PHYPORT_BASE (MV_BASE + 0x0)
|
||||
#define MV_PHYPORT(_n) (MV_PHYPORT_BASE + (_n))
|
||||
#define MV_SWITCHPORT_BASE (MV_BASE + 0x8)
|
||||
#define MV_SWITCHPORT(_n) (MV_SWITCHPORT_BASE + (_n))
|
||||
#define MV_SWITCHREGS (MV_BASE + 0xf)
|
||||
|
||||
enum {
|
||||
MV_PHY_CONTROL = 0x00,
|
||||
MV_PHY_STATUS = 0x01,
|
||||
MV_PHY_IDENT0 = 0x02,
|
||||
MV_PHY_IDENT1 = 0x03,
|
||||
MV_PHY_ANEG = 0x04,
|
||||
MV_PHY_LINK_ABILITY = 0x05,
|
||||
MV_PHY_ANEG_EXPAND = 0x06,
|
||||
MV_PHY_XMIT_NEXTP = 0x07,
|
||||
MV_PHY_LINK_NEXTP = 0x08,
|
||||
MV_PHY_CONTROL1 = 0x10,
|
||||
MV_PHY_STATUS1 = 0x11,
|
||||
MV_PHY_INTR_EN = 0x12,
|
||||
MV_PHY_INTR_STATUS = 0x13,
|
||||
MV_PHY_INTR_PORT = 0x14,
|
||||
MV_PHY_RECV_COUNTER = 0x16,
|
||||
MV_PHY_LED_PARALLEL = 0x16,
|
||||
MV_PHY_LED_STREAM = 0x17,
|
||||
MV_PHY_LED_CTRL = 0x18,
|
||||
MV_PHY_LED_OVERRIDE = 0x19,
|
||||
MV_PHY_VCT_CTRL = 0x1a,
|
||||
MV_PHY_VCT_STATUS = 0x1b,
|
||||
MV_PHY_CONTROL2 = 0x1e
|
||||
};
|
||||
#define MV_PHYREG(_type, _port) MV_PHYPORT(_port), MV_PHY_##_type
|
||||
|
||||
enum {
|
||||
MV_PORT_STATUS = 0x00,
|
||||
MV_PORT_IDENT = 0x03,
|
||||
MV_PORT_CONTROL = 0x04,
|
||||
MV_PORT_VLANMAP = 0x06,
|
||||
MV_PORT_ASSOC = 0x0b,
|
||||
MV_PORT_RXCOUNT = 0x10,
|
||||
MV_PORT_TXCOUNT = 0x11,
|
||||
};
|
||||
#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
|
||||
|
||||
enum {
|
||||
MV_PORTCTRL_BLOCK = (1 << 0),
|
||||
MV_PORTCTRL_LEARN = (2 << 0),
|
||||
MV_PORTCTRL_ENABLED = (3 << 0),
|
||||
MV_PORTCTRL_VLANTUN = (1 << 7), /* Enforce VLANs on packets */
|
||||
MV_PORTCTRL_RXTR = (1 << 8), /* Enable Marvell packet trailer for ingress */
|
||||
MV_PORTCTRL_HEADER = (1 << 11), /* Enable Marvell packet header mode for port */
|
||||
MV_PORTCTRL_TXTR = (1 << 14), /* Enable Marvell packet trailer for egress */
|
||||
MV_PORTCTRL_FORCEFL = (1 << 15), /* force flow control */
|
||||
};
|
||||
|
||||
#define MV_PORTVLAN_ID(_n) (((_n) & 0xf) << 12)
|
||||
#define MV_PORTVLAN_PORTS(_n) ((_n) & 0x3f)
|
||||
|
||||
#define MV_PORTASSOC_PORTS(_n) ((_n) & 0x1f)
|
||||
#define MV_PORTASSOC_MONITOR (1 << 15)
|
||||
|
||||
enum {
|
||||
MV_SWITCH_MAC0 = 0x01,
|
||||
MV_SWITCH_MAC1 = 0x02,
|
||||
MV_SWITCH_MAC2 = 0x03,
|
||||
MV_SWITCH_CTRL = 0x04,
|
||||
MV_SWITCH_ATU_CTRL = 0x0a,
|
||||
MV_SWITCH_ATU_OP = 0x0b,
|
||||
MV_SWITCH_ATU_DATA = 0x0c,
|
||||
MV_SWITCH_ATU_MAC0 = 0x0d,
|
||||
MV_SWITCH_ATU_MAC1 = 0x0e,
|
||||
MV_SWITCH_ATU_MAC2 = 0x0f,
|
||||
};
|
||||
#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
|
||||
|
||||
enum {
|
||||
MV_SWITCHCTL_EEIE = (1 << 0), /* EEPROM interrupt enable */
|
||||
MV_SWITCHCTL_PHYIE = (1 << 1), /* PHY interrupt enable */
|
||||
MV_SWITCHCTL_ATUDONE= (1 << 2), /* ATU done interrupt enable */
|
||||
MV_SWITCHCTL_ATUIE = (1 << 3), /* ATU interrupt enable */
|
||||
MV_SWITCHCTL_CTRMODE= (1 << 8), /* statistics for rx and tx errors */
|
||||
MV_SWITCHCTL_RELOAD = (1 << 9), /* reload registers from eeprom */
|
||||
MV_SWITCHCTL_MSIZE = (1 << 10), /* increase maximum frame size */
|
||||
MV_SWITCHCTL_DROP = (1 << 13), /* discard frames with excessive collisions */
|
||||
};
|
||||
|
||||
enum {
|
||||
#define MV_ATUCTL_AGETIME_MIN 16
|
||||
#define MV_ATUCTL_AGETIME_MAX 4080
|
||||
#define MV_ATUCTL_AGETIME(_n) ((((_n) / 16) & 0xff) << 4)
|
||||
MV_ATUCTL_ATU_256 = (0 << 12),
|
||||
MV_ATUCTL_ATU_512 = (1 << 12),
|
||||
MV_ATUCTL_ATU_1K = (2 << 12),
|
||||
MV_ATUCTL_ATUMASK = (3 << 12),
|
||||
MV_ATUCTL_NO_LEARN = (1 << 14),
|
||||
MV_ATUCTL_RESET = (1 << 15),
|
||||
};
|
||||
|
||||
enum {
|
||||
#define MV_ATUOP_DBNUM(_n) ((_n) & 0x0f)
|
||||
|
||||
MV_ATUOP_NOOP = (0 << 12),
|
||||
MV_ATUOP_FLUSH_ALL = (1 << 12),
|
||||
MV_ATUOP_FLUSH_U = (2 << 12),
|
||||
MV_ATUOP_LOAD_DB = (3 << 12),
|
||||
MV_ATUOP_GET_NEXT = (4 << 12),
|
||||
MV_ATUOP_FLUSH_DB = (5 << 12),
|
||||
MV_ATUOP_FLUSH_DB_UU= (6 << 12),
|
||||
|
||||
MV_ATUOP_INPROGRESS = (1 << 15),
|
||||
};
|
||||
|
||||
#define MV_IDENT_MASK 0xfff0
|
||||
#define MV_IDENT_VALUE 0x0600
|
||||
|
||||
#endif
|
||||
@@ -1,872 +0,0 @@
|
||||
/*
|
||||
* swconfig.c: Switch configuration API
|
||||
*
|
||||
* Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/switch.h>
|
||||
|
||||
//#define DEBUG 1
|
||||
#ifdef DEBUG
|
||||
#define DPRINTF(format, ...) printk("%s: " format, __func__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define DPRINTF(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Felix Fietkau <nbd@openwrt.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int swdev_id = 0;
|
||||
static struct list_head swdevs;
|
||||
static spinlock_t swdevs_lock = SPIN_LOCK_UNLOCKED;
|
||||
struct swconfig_callback;
|
||||
|
||||
struct swconfig_callback
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
struct genlmsghdr *hdr;
|
||||
struct genl_info *info;
|
||||
int cmd;
|
||||
|
||||
/* callback for filling in the message data */
|
||||
int (*fill)(struct swconfig_callback *cb, void *arg);
|
||||
|
||||
/* callback for closing the message before sending it */
|
||||
int (*close)(struct swconfig_callback *cb, void *arg);
|
||||
|
||||
struct nlattr *nest[4];
|
||||
int args[4];
|
||||
};
|
||||
|
||||
/* defaults */
|
||||
|
||||
static int
|
||||
swconfig_get_vlan_ports(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
int ret;
|
||||
if (val->port_vlan >= dev->vlans)
|
||||
return -EINVAL;
|
||||
|
||||
if (!dev->get_vlan_ports)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = dev->get_vlan_ports(dev, val);
|
||||
printk("SET PORTS %d\n", val->len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_set_vlan_ports(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (val->port_vlan >= dev->vlans)
|
||||
return -EINVAL;
|
||||
|
||||
/* validate ports */
|
||||
if (val->len > dev->ports)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < val->len; i++) {
|
||||
if (val->value.ports[i].id >= dev->ports)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!dev->set_vlan_ports)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
printk("SET PORTS %d\n", val->len);
|
||||
return dev->set_vlan_ports(dev, val);
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_apply_config(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
/* don't complain if not supported by the switch driver */
|
||||
if (!dev->apply_config)
|
||||
return 0;
|
||||
|
||||
return dev->apply_config(dev);
|
||||
}
|
||||
|
||||
|
||||
enum global_defaults {
|
||||
GLOBAL_APPLY,
|
||||
};
|
||||
|
||||
enum vlan_defaults {
|
||||
VLAN_PORTS,
|
||||
};
|
||||
|
||||
enum port_defaults {
|
||||
PORT_LINK,
|
||||
};
|
||||
|
||||
static struct switch_attr default_global[] = {
|
||||
[GLOBAL_APPLY] = {
|
||||
.type = SWITCH_TYPE_NOVAL,
|
||||
.name = "apply",
|
||||
.description = "Activate changes in the hardware",
|
||||
.set = swconfig_apply_config,
|
||||
}
|
||||
};
|
||||
|
||||
static struct switch_attr default_port[] = {
|
||||
[PORT_LINK] = {
|
||||
.type = SWITCH_TYPE_INT,
|
||||
.name = "link",
|
||||
.description = "Current link speed",
|
||||
}
|
||||
};
|
||||
|
||||
static struct switch_attr default_vlan[] = {
|
||||
[VLAN_PORTS] = {
|
||||
.type = SWITCH_TYPE_PORTS,
|
||||
.name = "ports",
|
||||
.description = "VLAN port mapping",
|
||||
.set = swconfig_set_vlan_ports,
|
||||
.get = swconfig_get_vlan_ports,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static void swconfig_defaults_init(struct switch_dev *dev)
|
||||
{
|
||||
dev->def_global = 0;
|
||||
dev->def_vlan = 0;
|
||||
dev->def_port = 0;
|
||||
|
||||
if (dev->get_vlan_ports || dev->set_vlan_ports)
|
||||
set_bit(VLAN_PORTS, &dev->def_vlan);
|
||||
|
||||
/* always present, can be no-op */
|
||||
set_bit(GLOBAL_APPLY, &dev->def_global);
|
||||
}
|
||||
|
||||
|
||||
static struct genl_family switch_fam = {
|
||||
.id = GENL_ID_GENERATE,
|
||||
.name = "switch",
|
||||
.hdrsize = 0,
|
||||
.version = 1,
|
||||
.maxattr = SWITCH_ATTR_MAX,
|
||||
};
|
||||
|
||||
static const struct nla_policy switch_policy[SWITCH_ATTR_MAX+1] = {
|
||||
[SWITCH_ATTR_ID] = { .type = NLA_U32 },
|
||||
[SWITCH_ATTR_OP_ID] = { .type = NLA_U32 },
|
||||
[SWITCH_ATTR_OP_PORT] = { .type = NLA_U32 },
|
||||
[SWITCH_ATTR_OP_VLAN] = { .type = NLA_U32 },
|
||||
[SWITCH_ATTR_OP_VALUE_INT] = { .type = NLA_U32 },
|
||||
[SWITCH_ATTR_OP_VALUE_STR] = { .type = NLA_NUL_STRING },
|
||||
[SWITCH_ATTR_OP_VALUE_PORTS] = { .type = NLA_NESTED },
|
||||
[SWITCH_ATTR_TYPE] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static const struct nla_policy port_policy[SWITCH_PORT_ATTR_MAX+1] = {
|
||||
[SWITCH_PORT_ID] = { .type = NLA_U32 },
|
||||
[SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },
|
||||
};
|
||||
|
||||
static inline void
|
||||
swconfig_lock(void)
|
||||
{
|
||||
spin_lock(&swdevs_lock);
|
||||
}
|
||||
|
||||
static inline void
|
||||
swconfig_unlock(void)
|
||||
{
|
||||
spin_unlock(&swdevs_lock);
|
||||
}
|
||||
|
||||
static struct switch_dev *
|
||||
swconfig_get_dev(struct genl_info *info)
|
||||
{
|
||||
struct switch_dev *dev = NULL;
|
||||
struct switch_dev *p;
|
||||
int id;
|
||||
|
||||
if (!info->attrs[SWITCH_ATTR_ID])
|
||||
goto done;
|
||||
|
||||
id = nla_get_u32(info->attrs[SWITCH_ATTR_ID]);
|
||||
swconfig_lock();
|
||||
list_for_each_entry(p, &swdevs, dev_list) {
|
||||
if (id != p->id)
|
||||
continue;
|
||||
|
||||
dev = p;
|
||||
break;
|
||||
}
|
||||
if (dev)
|
||||
spin_lock(&dev->lock);
|
||||
else
|
||||
DPRINTF("device %d not found\n", id);
|
||||
swconfig_unlock();
|
||||
done:
|
||||
return dev;
|
||||
}
|
||||
|
||||
static inline void
|
||||
swconfig_put_dev(struct switch_dev *dev)
|
||||
{
|
||||
spin_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_dump_attr(struct swconfig_callback *cb, void *arg)
|
||||
{
|
||||
struct switch_attr *op = arg;
|
||||
struct genl_info *info = cb->info;
|
||||
struct sk_buff *msg = cb->msg;
|
||||
int id = cb->args[0];
|
||||
void *hdr;
|
||||
|
||||
hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, &switch_fam,
|
||||
NLM_F_MULTI, SWITCH_CMD_NEW_ATTR);
|
||||
if (IS_ERR(hdr))
|
||||
return -1;
|
||||
|
||||
NLA_PUT_U32(msg, SWITCH_ATTR_OP_ID, id);
|
||||
NLA_PUT_U32(msg, SWITCH_ATTR_OP_TYPE, op->type);
|
||||
NLA_PUT_STRING(msg, SWITCH_ATTR_OP_NAME, op->name);
|
||||
if (op->description)
|
||||
NLA_PUT_STRING(msg, SWITCH_ATTR_OP_DESCRIPTION,
|
||||
op->description);
|
||||
|
||||
return genlmsg_end(msg, hdr);
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/* spread multipart messages across multiple message buffers */
|
||||
static int
|
||||
swconfig_send_multipart(struct swconfig_callback *cb, void *arg)
|
||||
{
|
||||
struct genl_info *info = cb->info;
|
||||
int restart = 0;
|
||||
int err;
|
||||
|
||||
do {
|
||||
if (!cb->msg) {
|
||||
cb->msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
||||
if (cb->msg == NULL)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(cb->fill(cb, arg) < 0))
|
||||
break;
|
||||
|
||||
/* fill failed, check if this was already the second attempt */
|
||||
if (restart)
|
||||
goto error;
|
||||
|
||||
/* try again in a new message, send the current one */
|
||||
restart = 1;
|
||||
if (cb->close) {
|
||||
if (cb->close(cb, arg) < 0)
|
||||
goto error;
|
||||
}
|
||||
err = genlmsg_unicast(cb->msg, info->snd_pid);
|
||||
cb->msg = NULL;
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
} while (restart);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (cb->msg)
|
||||
nlmsg_free(cb->msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_list_attrs(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
|
||||
const struct switch_attrlist *alist;
|
||||
struct switch_dev *dev;
|
||||
struct swconfig_callback cb;
|
||||
int err = -EINVAL;
|
||||
int i;
|
||||
|
||||
/* defaults */
|
||||
struct switch_attr *def_list;
|
||||
unsigned long *def_active;
|
||||
int n_def;
|
||||
|
||||
dev = swconfig_get_dev(info);
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
switch(hdr->cmd) {
|
||||
case SWITCH_CMD_LIST_GLOBAL:
|
||||
alist = &dev->attr_global;
|
||||
def_list = default_global;
|
||||
def_active = &dev->def_global;
|
||||
n_def = ARRAY_SIZE(default_global);
|
||||
break;
|
||||
case SWITCH_CMD_LIST_VLAN:
|
||||
alist = &dev->attr_vlan;
|
||||
def_list = default_vlan;
|
||||
def_active = &dev->def_vlan;
|
||||
n_def = ARRAY_SIZE(default_vlan);
|
||||
break;
|
||||
case SWITCH_CMD_LIST_PORT:
|
||||
alist = &dev->attr_port;
|
||||
def_list = default_port;
|
||||
def_active = &dev->def_port;
|
||||
n_def = ARRAY_SIZE(default_port);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.info = info;
|
||||
cb.fill = swconfig_dump_attr;
|
||||
for (i = 0; i < alist->n_attr; i++) {
|
||||
if (alist->attr[i].disabled)
|
||||
continue;
|
||||
cb.args[0] = i;
|
||||
err = swconfig_send_multipart(&cb, &alist->attr[i]);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* defaults */
|
||||
for (i = 0; i < n_def; i++) {
|
||||
if (!test_bit(i, def_active))
|
||||
continue;
|
||||
cb.args[0] = SWITCH_ATTR_DEFAULTS_OFFSET + i;
|
||||
err = swconfig_send_multipart(&cb, &def_list[i]);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
swconfig_put_dev(dev);
|
||||
|
||||
if (!cb.msg)
|
||||
return 0;
|
||||
|
||||
return genlmsg_unicast(cb.msg, info->snd_pid);
|
||||
|
||||
error:
|
||||
if (cb.msg)
|
||||
nlmsg_free(cb.msg);
|
||||
out:
|
||||
swconfig_put_dev(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct switch_attr *
|
||||
swconfig_lookup_attr(struct switch_dev *dev, struct genl_info *info,
|
||||
struct switch_val *val)
|
||||
{
|
||||
struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
|
||||
const struct switch_attrlist *alist;
|
||||
struct switch_attr *attr = NULL;
|
||||
int attr_id;
|
||||
|
||||
/* defaults */
|
||||
struct switch_attr *def_list;
|
||||
unsigned long *def_active;
|
||||
int n_def;
|
||||
|
||||
if (!info->attrs[SWITCH_ATTR_OP_ID])
|
||||
goto done;
|
||||
|
||||
switch(hdr->cmd) {
|
||||
case SWITCH_CMD_SET_GLOBAL:
|
||||
case SWITCH_CMD_GET_GLOBAL:
|
||||
alist = &dev->attr_global;
|
||||
def_list = default_global;
|
||||
def_active = &dev->def_global;
|
||||
n_def = ARRAY_SIZE(default_global);
|
||||
break;
|
||||
case SWITCH_CMD_SET_VLAN:
|
||||
case SWITCH_CMD_GET_VLAN:
|
||||
alist = &dev->attr_vlan;
|
||||
def_list = default_vlan;
|
||||
def_active = &dev->def_vlan;
|
||||
n_def = ARRAY_SIZE(default_vlan);
|
||||
if (!info->attrs[SWITCH_ATTR_OP_VLAN])
|
||||
goto done;
|
||||
val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_VLAN]);
|
||||
break;
|
||||
case SWITCH_CMD_SET_PORT:
|
||||
case SWITCH_CMD_GET_PORT:
|
||||
alist = &dev->attr_port;
|
||||
def_list = default_port;
|
||||
def_active = &dev->def_port;
|
||||
n_def = ARRAY_SIZE(default_port);
|
||||
if (!info->attrs[SWITCH_ATTR_OP_PORT])
|
||||
goto done;
|
||||
val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_PORT]);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!alist)
|
||||
goto done;
|
||||
|
||||
attr_id = nla_get_u32(info->attrs[SWITCH_ATTR_OP_ID]);
|
||||
if (attr_id >= SWITCH_ATTR_DEFAULTS_OFFSET) {
|
||||
attr_id -= SWITCH_ATTR_DEFAULTS_OFFSET;
|
||||
if (attr_id >= n_def)
|
||||
goto done;
|
||||
if (!test_bit(attr_id, def_active))
|
||||
goto done;
|
||||
attr = &def_list[attr_id];
|
||||
} else {
|
||||
if (attr_id >= alist->n_attr)
|
||||
goto done;
|
||||
attr = &alist->attr[attr_id];
|
||||
}
|
||||
|
||||
if (attr->disabled)
|
||||
attr = NULL;
|
||||
|
||||
done:
|
||||
if (!attr)
|
||||
DPRINTF("attribute lookup failed\n");
|
||||
val->attr = attr;
|
||||
return attr;
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_parse_ports(struct sk_buff *msg, struct nlattr *head,
|
||||
struct switch_val *val, int max)
|
||||
{
|
||||
struct nlattr *nla;
|
||||
int rem;
|
||||
|
||||
val->len = 0;
|
||||
nla_for_each_nested(nla, head, rem) {
|
||||
struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1];
|
||||
struct switch_port *port = &val->value.ports[val->len];
|
||||
|
||||
if (val->len >= max)
|
||||
return -EINVAL;
|
||||
|
||||
if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla,
|
||||
port_policy))
|
||||
return -EINVAL;
|
||||
|
||||
if (!tb[SWITCH_PORT_ID])
|
||||
return -EINVAL;
|
||||
|
||||
port->id = nla_get_u32(tb[SWITCH_PORT_ID]);
|
||||
if (tb[SWITCH_PORT_FLAG_TAGGED])
|
||||
port->flags |= (1 << SWITCH_PORT_FLAG_TAGGED);
|
||||
val->len++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_set_attr(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct switch_attr *attr;
|
||||
struct switch_dev *dev;
|
||||
struct switch_val val;
|
||||
int err = -EINVAL;
|
||||
|
||||
dev = swconfig_get_dev(info);
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&val, 0, sizeof(val));
|
||||
attr = swconfig_lookup_attr(dev, info, &val);
|
||||
if (!attr || !attr->set)
|
||||
goto error;
|
||||
|
||||
val.attr = attr;
|
||||
switch(attr->type) {
|
||||
case SWITCH_TYPE_NOVAL:
|
||||
break;
|
||||
case SWITCH_TYPE_INT:
|
||||
if (!info->attrs[SWITCH_ATTR_OP_VALUE_INT])
|
||||
goto error;
|
||||
val.value.i =
|
||||
nla_get_u32(info->attrs[SWITCH_ATTR_OP_VALUE_INT]);
|
||||
break;
|
||||
case SWITCH_TYPE_STRING:
|
||||
if (!info->attrs[SWITCH_ATTR_OP_VALUE_STR])
|
||||
goto error;
|
||||
val.value.s =
|
||||
nla_data(info->attrs[SWITCH_ATTR_OP_VALUE_STR]);
|
||||
break;
|
||||
case SWITCH_TYPE_PORTS:
|
||||
val.value.ports = dev->portbuf;
|
||||
memset(dev->portbuf, 0,
|
||||
sizeof(struct switch_port) * dev->ports);
|
||||
|
||||
/* TODO: implement multipart? */
|
||||
if (info->attrs[SWITCH_ATTR_OP_VALUE_PORTS]) {
|
||||
err = swconfig_parse_ports(skb,
|
||||
info->attrs[SWITCH_ATTR_OP_VALUE_PORTS], &val, dev->ports);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
} else {
|
||||
val.len = 0;
|
||||
err = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = attr->set(dev, attr, &val);
|
||||
error:
|
||||
swconfig_put_dev(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_close_portlist(struct swconfig_callback *cb, void *arg)
|
||||
{
|
||||
if (cb->nest[0])
|
||||
nla_nest_end(cb->msg, cb->nest[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_send_port(struct swconfig_callback *cb, void *arg)
|
||||
{
|
||||
const struct switch_port *port = arg;
|
||||
struct nlattr *p = NULL;
|
||||
|
||||
if (!cb->nest[0]) {
|
||||
cb->nest[0] = nla_nest_start(cb->msg, cb->cmd);
|
||||
if (!cb->nest[0])
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = nla_nest_start(cb->msg, SWITCH_ATTR_PORT);
|
||||
if (!p)
|
||||
goto error;
|
||||
|
||||
NLA_PUT_U32(cb->msg, SWITCH_PORT_ID, port->id);
|
||||
if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
|
||||
NLA_PUT_FLAG(cb->msg, SWITCH_PORT_FLAG_TAGGED);
|
||||
|
||||
nla_nest_end(cb->msg, p);
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
nla_nest_cancel(cb->msg, p);
|
||||
error:
|
||||
nla_nest_cancel(cb->msg, cb->nest[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_send_ports(struct sk_buff **msg, struct genl_info *info, int attr,
|
||||
const struct switch_val *val)
|
||||
{
|
||||
struct swconfig_callback cb;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
if (!val->value.ports)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.cmd = attr;
|
||||
cb.msg = *msg;
|
||||
cb.info = info;
|
||||
cb.fill = swconfig_send_port;
|
||||
cb.close = swconfig_close_portlist;
|
||||
|
||||
cb.nest[0] = nla_nest_start(cb.msg, cb.cmd);
|
||||
for (i = 0; i < val->len; i++) {
|
||||
err = swconfig_send_multipart(&cb, &val->value.ports[i]);
|
||||
if (err)
|
||||
goto done;
|
||||
}
|
||||
err = val->len;
|
||||
swconfig_close_portlist(&cb, NULL);
|
||||
*msg = cb.msg;
|
||||
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_get_attr(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
|
||||
struct switch_attr *attr;
|
||||
struct switch_dev *dev;
|
||||
struct sk_buff *msg = NULL;
|
||||
struct switch_val val;
|
||||
int err = -EINVAL;
|
||||
int cmd = hdr->cmd;
|
||||
|
||||
dev = swconfig_get_dev(info);
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&val, 0, sizeof(val));
|
||||
attr = swconfig_lookup_attr(dev, info, &val);
|
||||
if (!attr || !attr->get)
|
||||
goto error_dev;
|
||||
|
||||
if (attr->type == SWITCH_TYPE_PORTS) {
|
||||
val.value.ports = dev->portbuf;
|
||||
memset(dev->portbuf, 0,
|
||||
sizeof(struct switch_port) * dev->ports);
|
||||
}
|
||||
|
||||
err = attr->get(dev, attr, &val);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
goto error;
|
||||
|
||||
hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, &switch_fam,
|
||||
0, cmd);
|
||||
if (IS_ERR(hdr))
|
||||
goto nla_put_failure;
|
||||
|
||||
switch(attr->type) {
|
||||
case SWITCH_TYPE_INT:
|
||||
NLA_PUT_U32(msg, SWITCH_ATTR_OP_VALUE_INT, val.value.i);
|
||||
break;
|
||||
case SWITCH_TYPE_STRING:
|
||||
NLA_PUT_STRING(msg, SWITCH_ATTR_OP_VALUE_STR, val.value.s);
|
||||
break;
|
||||
case SWITCH_TYPE_PORTS:
|
||||
err = swconfig_send_ports(&msg, info,
|
||||
SWITCH_ATTR_OP_VALUE_PORTS, &val);
|
||||
if (err < 0)
|
||||
goto nla_put_failure;
|
||||
break;
|
||||
default:
|
||||
DPRINTF("invalid type in attribute\n");
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
err = genlmsg_end(msg, hdr);
|
||||
if (err < 0)
|
||||
goto nla_put_failure;
|
||||
|
||||
swconfig_put_dev(dev);
|
||||
return genlmsg_unicast(msg, info->snd_pid);
|
||||
|
||||
nla_put_failure:
|
||||
if (msg)
|
||||
nlmsg_free(msg);
|
||||
error_dev:
|
||||
swconfig_put_dev(dev);
|
||||
error:
|
||||
if (!err)
|
||||
err = -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
||||
const struct switch_dev *dev)
|
||||
{
|
||||
void *hdr;
|
||||
|
||||
hdr = genlmsg_put(msg, pid, seq, &switch_fam, flags,
|
||||
SWITCH_CMD_NEW_ATTR);
|
||||
if (IS_ERR(hdr))
|
||||
return -1;
|
||||
|
||||
NLA_PUT_U32(msg, SWITCH_ATTR_ID, dev->id);
|
||||
NLA_PUT_STRING(msg, SWITCH_ATTR_NAME, dev->name);
|
||||
NLA_PUT_STRING(msg, SWITCH_ATTR_DEV_NAME, dev->devname);
|
||||
NLA_PUT_U32(msg, SWITCH_ATTR_VLANS, dev->vlans);
|
||||
NLA_PUT_U32(msg, SWITCH_ATTR_PORTS, dev->ports);
|
||||
|
||||
return genlmsg_end(msg, hdr);
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int swconfig_dump_switches(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
struct switch_dev *dev;
|
||||
int start = cb->args[0];
|
||||
int idx = 0;
|
||||
|
||||
swconfig_lock();
|
||||
list_for_each_entry(dev, &swdevs, dev_list) {
|
||||
if (++idx <= start)
|
||||
continue;
|
||||
if (swconfig_send_switch(skb, NETLINK_CB(cb->skb).pid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
dev) < 0)
|
||||
break;
|
||||
}
|
||||
swconfig_unlock();
|
||||
cb->args[0] = idx;
|
||||
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
static int
|
||||
swconfig_done(struct netlink_callback *cb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct genl_ops swconfig_ops[] = {
|
||||
{
|
||||
.cmd = SWITCH_CMD_LIST_GLOBAL,
|
||||
.doit = swconfig_list_attrs,
|
||||
.policy = switch_policy,
|
||||
},
|
||||
{
|
||||
.cmd = SWITCH_CMD_LIST_VLAN,
|
||||
.doit = swconfig_list_attrs,
|
||||
.policy = switch_policy,
|
||||
},
|
||||
{
|
||||
.cmd = SWITCH_CMD_LIST_PORT,
|
||||
.doit = swconfig_list_attrs,
|
||||
.policy = switch_policy,
|
||||
},
|
||||
{
|
||||
.cmd = SWITCH_CMD_GET_GLOBAL,
|
||||
.doit = swconfig_get_attr,
|
||||
.policy = switch_policy,
|
||||
},
|
||||
{
|
||||
.cmd = SWITCH_CMD_GET_VLAN,
|
||||
.doit = swconfig_get_attr,
|
||||
.policy = switch_policy,
|
||||
},
|
||||
{
|
||||
.cmd = SWITCH_CMD_GET_PORT,
|
||||
.doit = swconfig_get_attr,
|
||||
.policy = switch_policy,
|
||||
},
|
||||
{
|
||||
.cmd = SWITCH_CMD_SET_GLOBAL,
|
||||
.doit = swconfig_set_attr,
|
||||
.policy = switch_policy,
|
||||
},
|
||||
{
|
||||
.cmd = SWITCH_CMD_SET_VLAN,
|
||||
.doit = swconfig_set_attr,
|
||||
.policy = switch_policy,
|
||||
},
|
||||
{
|
||||
.cmd = SWITCH_CMD_SET_PORT,
|
||||
.doit = swconfig_set_attr,
|
||||
.policy = switch_policy,
|
||||
},
|
||||
{
|
||||
.cmd = SWITCH_CMD_GET_SWITCH,
|
||||
.dumpit = swconfig_dump_switches,
|
||||
.policy = switch_policy,
|
||||
.done = swconfig_done,
|
||||
}
|
||||
};
|
||||
|
||||
int
|
||||
register_switch(struct switch_dev *dev, struct net_device *netdev)
|
||||
{
|
||||
INIT_LIST_HEAD(&dev->dev_list);
|
||||
if (netdev) {
|
||||
dev->netdev = netdev;
|
||||
if (!dev->devname)
|
||||
dev->devname = netdev->name;
|
||||
}
|
||||
BUG_ON(!dev->devname);
|
||||
|
||||
if (dev->ports > 0) {
|
||||
dev->portbuf = kzalloc(sizeof(struct switch_port) * dev->ports,
|
||||
GFP_KERNEL);
|
||||
if (!dev->portbuf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
dev->id = ++swdev_id;
|
||||
swconfig_defaults_init(dev);
|
||||
spin_lock_init(&dev->lock);
|
||||
swconfig_lock();
|
||||
list_add(&dev->dev_list, &swdevs);
|
||||
swconfig_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_switch);
|
||||
|
||||
void
|
||||
unregister_switch(struct switch_dev *dev)
|
||||
{
|
||||
kfree(dev->portbuf);
|
||||
spin_lock(&dev->lock);
|
||||
swconfig_lock();
|
||||
list_del(&dev->dev_list);
|
||||
swconfig_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_switch);
|
||||
|
||||
|
||||
static int __init
|
||||
swconfig_init(void)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
INIT_LIST_HEAD(&swdevs);
|
||||
err = genl_register_family(&switch_fam);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(swconfig_ops); i++) {
|
||||
err = genl_register_ops(&switch_fam, &swconfig_ops[i]);
|
||||
if (err)
|
||||
goto unregister;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister:
|
||||
genl_unregister_family(&switch_fam);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit
|
||||
swconfig_exit(void)
|
||||
{
|
||||
genl_unregister_family(&switch_fam);
|
||||
}
|
||||
|
||||
module_init(swconfig_init);
|
||||
module_exit(swconfig_exit);
|
||||
|
||||
File renamed without changes.
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2001 MontaVista Software Inc.
|
||||
* Author: MontaVista Software, Inc.
|
||||
* stevel@mvista.com or source@mvista.com
|
||||
*
|
||||
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <asm/rc32434/rc32434.h>
|
||||
|
||||
static int __devinitdata irq_map[2][12] = {
|
||||
{ 0, 0, 2, 3, 2, 3, 0, 0, 0, 0, 0, 1 },
|
||||
{ 0, 0, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3 }
|
||||
};
|
||||
|
||||
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
|
||||
{
|
||||
int irq = 0;
|
||||
|
||||
if (dev->bus->number < 2 && PCI_SLOT(dev->devfn) < 12) {
|
||||
irq = irq_map[dev->bus->number][PCI_SLOT(dev->devfn)];
|
||||
}
|
||||
return irq + GROUP4_IRQ_BASE + 4;
|
||||
}
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* BRIEF MODULE DESCRIPTION
|
||||
* pci_ops for IDT EB434 board
|
||||
*
|
||||
* Copyright 2004 IDT Inc. (rischelp@idt.com)
|
||||
* Copyright 2006 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*
|
||||
**************************************************************************
|
||||
* May 2004 rkt, neb
|
||||
*
|
||||
* Initial Release
|
||||
*
|
||||
*
|
||||
*
|
||||
**************************************************************************
|
||||
*/
|
||||
|
||||
#include <linux/autoconf.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <asm/rc32434/rc32434.h>
|
||||
#include <asm/rc32434/pci.h>
|
||||
|
||||
#define PCI_ACCESS_READ 0
|
||||
#define PCI_ACCESS_WRITE 1
|
||||
|
||||
|
||||
#define PCI_CFG_SET(bus,slot,func,off) \
|
||||
(rc32434_pci->pcicfga = (0x80000000 | \
|
||||
((bus) << 16) | ((slot)<<11) | \
|
||||
((func)<<8) | (off)))
|
||||
|
||||
static inline int config_access(unsigned char access_type, struct pci_bus *bus,
|
||||
unsigned int devfn, unsigned char where,
|
||||
u32 * data)
|
||||
{
|
||||
unsigned int slot = PCI_SLOT(devfn);
|
||||
u8 func = PCI_FUNC(devfn);
|
||||
|
||||
/* Setup address */
|
||||
PCI_CFG_SET(bus->number, slot, func, where);
|
||||
rc32434_sync();
|
||||
|
||||
if (access_type == PCI_ACCESS_WRITE)
|
||||
rc32434_pci->pcicfgd = *data;
|
||||
else
|
||||
*data = rc32434_pci->pcicfgd;
|
||||
|
||||
rc32434_sync();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We can't address 8 and 16 bit words directly. Instead we have to
|
||||
* read/write a 32bit word and mask/modify the data we actually want.
|
||||
*/
|
||||
static int read_config_byte(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, u8 * val)
|
||||
{
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data);
|
||||
*val = (data >> ((where & 3) << 3)) & 0xff;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int read_config_word(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, u16 * val)
|
||||
{
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data);
|
||||
*val = (data >> ((where & 3) << 3)) & 0xffff;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int read_config_dword(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, u32 * val)
|
||||
{
|
||||
int ret;
|
||||
int delay = 1;
|
||||
|
||||
if (bus->number == 0 && PCI_SLOT(devfn) > 21)
|
||||
return 0;
|
||||
|
||||
retry:
|
||||
ret = config_access(PCI_ACCESS_READ, bus, devfn, where, val);
|
||||
|
||||
/* PCI scan: check for invalid values, device may not have
|
||||
* finished initializing */
|
||||
|
||||
if (where == PCI_VENDOR_ID) {
|
||||
if (ret == 0xffffffff || ret == 0x00000000 ||
|
||||
ret == 0x0000ffff || ret == 0xffff0000) {
|
||||
|
||||
if (delay > 4)
|
||||
return 0;
|
||||
|
||||
delay *= 2;
|
||||
msleep(delay);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
write_config_byte(struct pci_bus *bus, unsigned int devfn, int where,
|
||||
u8 val)
|
||||
{
|
||||
u32 data = 0;
|
||||
|
||||
if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data))
|
||||
return -1;
|
||||
|
||||
data = (data & ~(0xff << ((where & 3) << 3))) |
|
||||
(val << ((where & 3) << 3));
|
||||
|
||||
if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data))
|
||||
return -1;
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_config_word(struct pci_bus *bus, unsigned int devfn, int where,
|
||||
u16 val)
|
||||
{
|
||||
u32 data = 0;
|
||||
|
||||
if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data))
|
||||
return -1;
|
||||
|
||||
data = (data & ~(0xffff << ((where & 3) << 3))) |
|
||||
(val << ((where & 3) << 3));
|
||||
|
||||
if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data))
|
||||
return -1;
|
||||
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_config_dword(struct pci_bus *bus, unsigned int devfn, int where,
|
||||
u32 val)
|
||||
{
|
||||
if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &val))
|
||||
return -1;
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int pci_config_read(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 * val)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
return read_config_byte(bus, devfn, where, (u8 *) val);
|
||||
case 2:
|
||||
return read_config_word(bus, devfn, where, (u16 *) val);
|
||||
default:
|
||||
return read_config_dword(bus, devfn, where, val);
|
||||
}
|
||||
}
|
||||
|
||||
static int pci_config_write(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 val)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
return write_config_byte(bus, devfn, where, (u8) val);
|
||||
case 2:
|
||||
return write_config_word(bus, devfn, where, (u16) val);
|
||||
default:
|
||||
return write_config_dword(bus, devfn, where, val);
|
||||
}
|
||||
}
|
||||
|
||||
struct pci_ops rc32434_pci_ops = {
|
||||
.read = pci_config_read,
|
||||
.write = pci_config_write,
|
||||
};
|
||||
@@ -1,236 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* BRIEF MODULE DESCRIPTION
|
||||
* PCI initialization for IDT EB434 board
|
||||
*
|
||||
* Copyright 2004 IDT Inc. (rischelp@idt.com)
|
||||
*
|
||||
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*
|
||||
**************************************************************************
|
||||
* May 2004 rkt, neb
|
||||
*
|
||||
* Initial Release
|
||||
*
|
||||
*
|
||||
*
|
||||
**************************************************************************
|
||||
*/
|
||||
|
||||
#include <linux/autoconf.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/rc32434/rc32434.h>
|
||||
#include <asm/rc32434/pci.h>
|
||||
|
||||
#define PCI_ACCESS_READ 0
|
||||
#define PCI_ACCESS_WRITE 1
|
||||
|
||||
/* define an unsigned array for the PCI registers */
|
||||
unsigned int korinaCnfgRegs[25] = {
|
||||
KORINA_CNFG1, KORINA_CNFG2, KORINA_CNFG3, KORINA_CNFG4,
|
||||
KORINA_CNFG5, KORINA_CNFG6, KORINA_CNFG7, KORINA_CNFG8,
|
||||
KORINA_CNFG9, KORINA_CNFG10, KORINA_CNFG11, KORINA_CNFG12,
|
||||
KORINA_CNFG13, KORINA_CNFG14, KORINA_CNFG15, KORINA_CNFG16,
|
||||
KORINA_CNFG17, KORINA_CNFG18, KORINA_CNFG19, KORINA_CNFG20,
|
||||
KORINA_CNFG21, KORINA_CNFG22, KORINA_CNFG23, KORINA_CNFG24
|
||||
};
|
||||
static struct resource rc32434_res_pci_mem1;
|
||||
static struct resource rc32434_res_pci_mem2;
|
||||
|
||||
static struct resource rc32434_res_pci_mem1 = {
|
||||
.name = "PCI MEM1",
|
||||
.start = 0x50000000,
|
||||
.end = 0x5FFFFFFF,
|
||||
.flags = IORESOURCE_MEM,
|
||||
.parent = &rc32434_res_pci_mem1,
|
||||
.sibling = NULL,
|
||||
.child = &rc32434_res_pci_mem2
|
||||
};
|
||||
|
||||
static struct resource rc32434_res_pci_mem2 = {
|
||||
.name = "PCI Mem2",
|
||||
.start = 0x60000000,
|
||||
.end = 0x6FFFFFFF,
|
||||
.flags = IORESOURCE_MEM,
|
||||
.parent = &rc32434_res_pci_mem1,
|
||||
.sibling = NULL,
|
||||
.child = NULL
|
||||
};
|
||||
|
||||
static struct resource rc32434_res_pci_io1 = {
|
||||
.name = "PCI I/O1",
|
||||
.start = 0x18800000,
|
||||
.end = 0x188FFFFF,
|
||||
.flags = IORESOURCE_IO,
|
||||
};
|
||||
|
||||
extern struct pci_ops rc32434_pci_ops;
|
||||
|
||||
#define PCI_MEM1_START PCI_ADDR_START
|
||||
#define PCI_MEM1_END PCI_ADDR_START + CPUTOPCI_MEM_WIN - 1
|
||||
#define PCI_MEM2_START PCI_ADDR_START + CPUTOPCI_MEM_WIN
|
||||
#define PCI_MEM2_END PCI_ADDR_START + ( 2* CPUTOPCI_MEM_WIN) - 1
|
||||
#define PCI_IO1_START PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN)
|
||||
#define PCI_IO1_END PCI_ADDR_START + (2* CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN -1
|
||||
#define PCI_IO2_START PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN
|
||||
#define PCI_IO2_END PCI_ADDR_START + (2* CPUTOPCI_MEM_WIN) + (2 * CPUTOPCI_IO_WIN) -1
|
||||
|
||||
|
||||
struct pci_controller rc32434_controller2;
|
||||
|
||||
struct pci_controller rc32434_controller = {
|
||||
.pci_ops = &rc32434_pci_ops,
|
||||
.mem_resource = &rc32434_res_pci_mem1,
|
||||
.io_resource = &rc32434_res_pci_io1,
|
||||
.mem_offset = 0,
|
||||
.io_offset = 0,
|
||||
|
||||
};
|
||||
|
||||
#ifdef __MIPSEB__
|
||||
#define PCI_ENDIAN_FLAG PCILBAC_sb_m
|
||||
#else
|
||||
#define PCI_ENDIAN_FLAG 0
|
||||
#endif
|
||||
|
||||
static int __init rc32434_pcibridge_init(void)
|
||||
{
|
||||
unsigned int pcicValue, pcicData = 0;
|
||||
unsigned int dummyRead, pciCntlVal;
|
||||
int loopCount;
|
||||
unsigned int pciConfigAddr;
|
||||
|
||||
pcicValue = rc32434_pci->pcic;
|
||||
pcicValue = (pcicValue >> PCIM_SHFT) & PCIM_BIT_LEN;
|
||||
if (!((pcicValue == PCIM_H_EA) ||
|
||||
(pcicValue == PCIM_H_IA_FIX) ||
|
||||
(pcicValue == PCIM_H_IA_RR))) {
|
||||
printk("PCI init error!!!\n");
|
||||
/* Not in Host Mode, return ERROR */
|
||||
return -1;
|
||||
}
|
||||
/* Enables the Idle Grant mode, Arbiter Parking */
|
||||
pcicData |=(PCIC_igm_m|PCIC_eap_m|PCIC_en_m);
|
||||
rc32434_pci->pcic = pcicData; /* Enable the PCI bus Interface */
|
||||
/* Zero out the PCI status & PCI Status Mask */
|
||||
for(;;)
|
||||
{
|
||||
pcicData = rc32434_pci->pcis;
|
||||
if (!(pcicData & PCIS_rip_m))
|
||||
break;
|
||||
}
|
||||
|
||||
rc32434_pci->pcis = 0;
|
||||
rc32434_pci->pcism = 0xFFFFFFFF;
|
||||
/* Zero out the PCI decoupled registers */
|
||||
rc32434_pci->pcidac=0; /* disable PCI decoupled accesses at initialization */
|
||||
rc32434_pci->pcidas=0; /* clear the status */
|
||||
rc32434_pci->pcidasm=0x0000007F; /* Mask all the interrupts */
|
||||
/* Mask PCI Messaging Interrupts */
|
||||
rc32434_pci_msg->pciiic = 0;
|
||||
rc32434_pci_msg->pciiim = 0xFFFFFFFF;
|
||||
rc32434_pci_msg->pciioic = 0;
|
||||
rc32434_pci_msg->pciioim = 0;
|
||||
|
||||
|
||||
/* Setup PCILB0 as Memory Window */
|
||||
rc32434_pci->pcilba[0].a = (unsigned int) (PCI_ADDR_START);
|
||||
|
||||
/* setup the PCI map address as same as the local address */
|
||||
|
||||
rc32434_pci->pcilba[0].m = (unsigned int) (PCI_ADDR_START);
|
||||
|
||||
|
||||
/* Setup PCILBA1 as MEM */
|
||||
rc32434_pci->pcilba[0].c = ( ((SIZE_256MB & 0x1f) << PCILBAC_size_b) | PCI_ENDIAN_FLAG);
|
||||
dummyRead = rc32434_pci->pcilba[0].c; /* flush the CPU write Buffers */
|
||||
rc32434_pci->pcilba[1].a = 0x60000000;
|
||||
rc32434_pci->pcilba[1].m = 0x60000000;
|
||||
|
||||
/* setup PCILBA2 as IO Window*/
|
||||
rc32434_pci->pcilba[1].c = (((SIZE_256MB & 0x1f) << PCILBAC_size_b )| PCI_ENDIAN_FLAG);
|
||||
dummyRead = rc32434_pci->pcilba[1].c; /* flush the CPU write Buffers */
|
||||
rc32434_pci->pcilba[2].a = 0x18C00000;
|
||||
rc32434_pci->pcilba[2].m = 0x18FFFFFF;
|
||||
|
||||
/* setup PCILBA2 as IO Window*/
|
||||
rc32434_pci->pcilba[2].c = (((SIZE_4MB & 0x1f) << PCILBAC_size_b) | PCI_ENDIAN_FLAG );
|
||||
dummyRead = rc32434_pci->pcilba[2].c; /* flush the CPU write Buffers */
|
||||
|
||||
/* Setup PCILBA3 as IO Window */
|
||||
rc32434_pci->pcilba[3].a = 0x18800000;
|
||||
rc32434_pci->pcilba[3].m = 0x18800000;
|
||||
rc32434_pci->pcilba[3].c = ( (((SIZE_1MB & 0x1ff) << PCILBAC_size_b) | PCILBAC_msi_m) | PCI_ENDIAN_FLAG);
|
||||
dummyRead = rc32434_pci->pcilba[3].c; /* flush the CPU write Buffers */
|
||||
|
||||
pciConfigAddr=(unsigned int)(0x80000004);
|
||||
for(loopCount=0;loopCount<24;loopCount++){
|
||||
rc32434_pci->pcicfga=pciConfigAddr;
|
||||
dummyRead=rc32434_pci->pcicfga;
|
||||
rc32434_pci->pcicfgd = korinaCnfgRegs[loopCount];
|
||||
dummyRead=rc32434_pci->pcicfgd;
|
||||
pciConfigAddr += 4;
|
||||
}
|
||||
rc32434_pci->pcitc = (unsigned int)((PCITC_RTIMER_VAL&0xff) << PCITC_rtimer_b)
|
||||
| ((PCITC_DTIMER_VAL&0xff) << PCITC_dtimer_b);
|
||||
|
||||
pciCntlVal=rc32434_pci->pcic;
|
||||
pciCntlVal &=~(PCIC_tnr_m);
|
||||
rc32434_pci->pcic = pciCntlVal;
|
||||
pciCntlVal=rc32434_pci->pcic;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do platform specific device initialization at pci_enable_device() time */
|
||||
int pcibios_plat_dev_init(struct pci_dev *dev)
|
||||
{
|
||||
if (PCI_SLOT(dev->devfn) == 6 && dev->bus->number == 0) {
|
||||
/* disable prefetched memory range */
|
||||
pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, 0);
|
||||
pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, 0x10);
|
||||
|
||||
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 4);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init rc32434_pci_init(void)
|
||||
{
|
||||
printk("PCI: Initializing PCI\n");
|
||||
|
||||
ioport_resource.start = rc32434_res_pci_io1.start;
|
||||
ioport_resource.end = rc32434_res_pci_io1.end;
|
||||
|
||||
rc32434_pcibridge_init();
|
||||
|
||||
register_pci_controller(&rc32434_controller);
|
||||
rc32434_sync();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
arch_initcall(rc32434_pci_init);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#
|
||||
# Makefile for the RB500 board specific parts of the kernel
|
||||
#
|
||||
|
||||
obj-y += irq.o time.o setup.o serial.o prom.o gpio.o devices.o
|
||||
@@ -1,338 +0,0 @@
|
||||
/*
|
||||
* RouterBoard 500 Platform devices
|
||||
*
|
||||
* Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2007 Florian Fainelli <florian@openwrt.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <asm/bootinfo.h>
|
||||
|
||||
#include <asm/rc32434/rc32434.h>
|
||||
#include <asm/rc32434/dma.h>
|
||||
#include <asm/rc32434/dma_v.h>
|
||||
#include <asm/rc32434/eth.h>
|
||||
#include <asm/rc32434/rb.h>
|
||||
|
||||
#define ETH0_DMA_RX_IRQ GROUP1_IRQ_BASE + 0
|
||||
#define ETH0_DMA_TX_IRQ GROUP1_IRQ_BASE + 1
|
||||
#define ETH0_RX_OVR_IRQ GROUP3_IRQ_BASE + 9
|
||||
#define ETH0_TX_UND_IRQ GROUP3_IRQ_BASE + 10
|
||||
|
||||
#define ETH0_RX_DMA_ADDR (DMA0_PhysicalAddress + 0*DMA_CHAN_OFFSET)
|
||||
#define ETH0_TX_DMA_ADDR (DMA0_PhysicalAddress + 1*DMA_CHAN_OFFSET)
|
||||
|
||||
/* NAND definitions */
|
||||
#define MEM32(x) *((volatile unsigned *) (x))
|
||||
|
||||
#define GPIO_RDY (1 << 0x08)
|
||||
#define GPIO_WPX (1 << 0x09)
|
||||
#define GPIO_ALE (1 << 0x0a)
|
||||
#define GPIO_CLE (1 << 0x0b)
|
||||
|
||||
extern char* board_type;
|
||||
|
||||
static struct resource korina_dev0_res[] = {
|
||||
{
|
||||
.name = "korina_regs",
|
||||
.start = ETH0_PhysicalAddress,
|
||||
.end = ETH0_PhysicalAddress + sizeof(ETH_t),
|
||||
.flags = IORESOURCE_MEM,
|
||||
}, {
|
||||
.name = "korina_rx",
|
||||
.start = ETH0_DMA_RX_IRQ,
|
||||
.end = ETH0_DMA_RX_IRQ,
|
||||
.flags = IORESOURCE_IRQ
|
||||
}, {
|
||||
.name = "korina_tx",
|
||||
.start = ETH0_DMA_TX_IRQ,
|
||||
.end = ETH0_DMA_TX_IRQ,
|
||||
.flags = IORESOURCE_IRQ
|
||||
}, {
|
||||
.name = "korina_ovr",
|
||||
.start = ETH0_RX_OVR_IRQ,
|
||||
.end = ETH0_RX_OVR_IRQ,
|
||||
.flags = IORESOURCE_IRQ
|
||||
}, {
|
||||
.name = "korina_und",
|
||||
.start = ETH0_TX_UND_IRQ,
|
||||
.end = ETH0_TX_UND_IRQ,
|
||||
.flags = IORESOURCE_IRQ
|
||||
}, {
|
||||
.name = "korina_dma_rx",
|
||||
.start = ETH0_RX_DMA_ADDR,
|
||||
.end = ETH0_RX_DMA_ADDR + DMA_CHAN_OFFSET - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}, {
|
||||
.name = "korina_dma_tx",
|
||||
.start = ETH0_TX_DMA_ADDR,
|
||||
.end = ETH0_TX_DMA_ADDR + DMA_CHAN_OFFSET - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}
|
||||
};
|
||||
|
||||
static struct korina_device korina_dev0_data = {
|
||||
.name = "korina0",
|
||||
.mac = {0xde, 0xca, 0xff, 0xc0, 0xff, 0xee}
|
||||
};
|
||||
|
||||
static struct platform_device korina_dev0 = {
|
||||
.id = 0,
|
||||
.name = "korina",
|
||||
.dev.platform_data = &korina_dev0_data,
|
||||
.resource = korina_dev0_res,
|
||||
.num_resources = ARRAY_SIZE(korina_dev0_res),
|
||||
};
|
||||
|
||||
#define CF_GPIO_NUM 13
|
||||
|
||||
static struct resource cf_slot0_res[] = {
|
||||
{
|
||||
.name = "cf_membase",
|
||||
.flags = IORESOURCE_MEM
|
||||
}, {
|
||||
.name = "cf_irq",
|
||||
.start = (8 + 4 * 32 + CF_GPIO_NUM), /* 149 */
|
||||
.end = (8 + 4 * 32 + CF_GPIO_NUM),
|
||||
.flags = IORESOURCE_IRQ
|
||||
}
|
||||
};
|
||||
|
||||
static struct cf_device cf_slot0_data = {
|
||||
.gpio_pin = 13
|
||||
};
|
||||
|
||||
static struct platform_device cf_slot0 = {
|
||||
.id = 0,
|
||||
.name = "rb500-cf",
|
||||
.dev.platform_data = &cf_slot0_data,
|
||||
.resource = cf_slot0_res,
|
||||
.num_resources = ARRAY_SIZE(cf_slot0_res),
|
||||
};
|
||||
|
||||
/* Resources and device for NAND. There is no data needed and no irqs, so just define the memory used. */
|
||||
|
||||
/*
|
||||
* We need to use the OLD Yaffs-1 OOB layout, otherwise the RB bootloader
|
||||
* will not be able to find the kernel that we load. So set the oobinfo
|
||||
* when creating the partitions
|
||||
*/
|
||||
static struct nand_ecclayout rb500_nand_ecclayout = {
|
||||
.eccbytes = 6,
|
||||
.eccpos = { 8, 9, 10, 13, 14, 15 },
|
||||
.oobavail = 9,
|
||||
.oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } }
|
||||
};
|
||||
|
||||
int rb500_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return MEM32(IDT434_REG_BASE + GPIOD) & GPIO_RDY;
|
||||
}
|
||||
|
||||
void rb500_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
unsigned char orbits, nandbits;
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
|
||||
orbits = (ctrl & NAND_CLE) << 1;
|
||||
orbits |= (ctrl & NAND_ALE) >> 1;
|
||||
|
||||
nandbits = (~ctrl & NAND_CLE) << 1;
|
||||
nandbits |= (~ctrl & NAND_ALE) >> 1;
|
||||
|
||||
changeLatchU5(orbits, nandbits);
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static struct resource nand_slot0_res[] = {
|
||||
[0] = {
|
||||
.name = "nand_membase",
|
||||
.flags = IORESOURCE_MEM
|
||||
}
|
||||
};
|
||||
|
||||
struct platform_nand_data rb500_nand_data = {
|
||||
.ctrl.dev_ready = rb500_dev_ready,
|
||||
.ctrl.cmd_ctrl = rb500_cmd_ctrl,
|
||||
};
|
||||
|
||||
static struct platform_device nand_slot0 = {
|
||||
.name = "gen_nand",
|
||||
.id = -1,
|
||||
.resource = nand_slot0_res,
|
||||
.num_resources = ARRAY_SIZE(nand_slot0_res),
|
||||
.dev.platform_data = &rb500_nand_data,
|
||||
};
|
||||
|
||||
static struct mtd_partition rb500_partition_info[] = {
|
||||
{
|
||||
.name = "Routerboard NAND boot",
|
||||
.offset = 0,
|
||||
.size = 4 * 1024 * 1024,
|
||||
}, {
|
||||
.name = "rootfs",
|
||||
.offset = MTDPART_OFS_NXTBLK,
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device rb500_led = {
|
||||
.name = "rb500-led",
|
||||
.id = 0,
|
||||
};
|
||||
|
||||
static struct gpio_keys_button rb500_gpio_btn[] = {
|
||||
{
|
||||
.gpio = 1,
|
||||
.code = BTN_0,
|
||||
.desc = "S1",
|
||||
.active_low = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static struct gpio_keys_platform_data rb500_gpio_btn_data = {
|
||||
.buttons = rb500_gpio_btn,
|
||||
.nbuttons = ARRAY_SIZE(rb500_gpio_btn),
|
||||
};
|
||||
|
||||
static struct platform_device rb500_button = {
|
||||
.name = "gpio-keys",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &rb500_gpio_btn_data,
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device *rb500_devs[] = {
|
||||
&korina_dev0,
|
||||
&nand_slot0,
|
||||
&cf_slot0,
|
||||
&rb500_led,
|
||||
&rb500_button
|
||||
};
|
||||
|
||||
static void __init parse_mac_addr(char *macstr)
|
||||
{
|
||||
int i, j;
|
||||
unsigned char result, value;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
result = 0;
|
||||
|
||||
if (i != 5 && *(macstr + 2) != ':')
|
||||
return;
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
if (isxdigit(*macstr)
|
||||
&& (value =
|
||||
isdigit(*macstr) ? *macstr -
|
||||
'0' : toupper(*macstr) - 'A' + 10) < 16) {
|
||||
result = result * 16 + value;
|
||||
macstr++;
|
||||
} else
|
||||
return;
|
||||
}
|
||||
|
||||
macstr++;
|
||||
korina_dev0_data.mac[i] = result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* DEVICE CONTROLLER 1 */
|
||||
#define CFG_DC_DEV1 (void*)0xb8010010
|
||||
#define CFG_DC_DEV2 (void*)0xb8010020
|
||||
#define CFG_DC_DEVBASE 0x0
|
||||
#define CFG_DC_DEVMASK 0x4
|
||||
#define CFG_DC_DEVC 0x8
|
||||
#define CFG_DC_DEVTC 0xC
|
||||
|
||||
/* NAND definitions */
|
||||
#define NAND_CHIP_DELAY 25
|
||||
|
||||
static int rb500_nand_fixup(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if (mtd->writesize == 512)
|
||||
chip->ecc.layout = &rb500_nand_ecclayout;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init rb500_nand_setup(void)
|
||||
{
|
||||
switch (mips_machtype) {
|
||||
case MACH_MIKROTIK_RB532A:
|
||||
changeLatchU5(LO_FOFF | LO_CEX, LO_ULED | LO_ALE | LO_CLE | LO_WPX);
|
||||
break;
|
||||
default:
|
||||
changeLatchU5(LO_WPX | LO_FOFF | LO_CEX, LO_ULED | LO_ALE | LO_CLE);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Setup NAND specific settings */
|
||||
rb500_nand_data.chip.nr_chips = 1;
|
||||
rb500_nand_data.chip.nr_partitions = ARRAY_SIZE(rb500_partition_info);
|
||||
rb500_nand_data.chip.partitions = rb500_partition_info;
|
||||
rb500_nand_data.chip.chip_delay = NAND_CHIP_DELAY;
|
||||
rb500_nand_data.chip.options = NAND_NO_AUTOINCR;
|
||||
|
||||
rb500_nand_data.chip.chip_fixup = &rb500_nand_fixup;
|
||||
}
|
||||
|
||||
|
||||
static int __init plat_setup_devices(void)
|
||||
{
|
||||
/* Look for the CF card reader */
|
||||
if (!readl(CFG_DC_DEV1 + CFG_DC_DEVMASK))
|
||||
rb500_devs[1] = NULL;
|
||||
else {
|
||||
cf_slot0_res[0].start =
|
||||
readl(CFG_DC_DEV1 + CFG_DC_DEVBASE);
|
||||
cf_slot0_res[0].end = cf_slot0_res[0].start + 0x1000;
|
||||
}
|
||||
|
||||
/* Read the NAND resources from the device controller */
|
||||
nand_slot0_res[0].start = readl(CFG_DC_DEV2 + CFG_DC_DEVBASE);
|
||||
nand_slot0_res[0].end = nand_slot0_res[0].start + 0x1000;
|
||||
|
||||
/* Initialise the NAND device */
|
||||
rb500_nand_setup();
|
||||
|
||||
return platform_add_devices(rb500_devs, ARRAY_SIZE(rb500_devs));
|
||||
}
|
||||
|
||||
static int __init setup_kmac(char *s)
|
||||
{
|
||||
printk("korina mac = %s\n", s);
|
||||
parse_mac_addr(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__setup("kmac=", setup_kmac);
|
||||
|
||||
arch_initcall(plat_setup_devices);
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
* Miscellaneous functions for IDT EB434 board
|
||||
*
|
||||
* Copyright 2004 IDT Inc. (rischelp@idt.com)
|
||||
* Copyright 2006 Phil Sutter <n0-1@freewrt.org>
|
||||
* Copyright 2007 Florian Fainelli <florian@openwrt.org>
|
||||
*
|
||||
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/addrspace.h>
|
||||
#include <asm/gpio.h>
|
||||
|
||||
#include <asm/rc32434/rb.h>
|
||||
|
||||
#define GPIO_BADDR 0x18050000
|
||||
|
||||
static volatile unsigned char *devCtl3Base;
|
||||
static unsigned char latchU5State;
|
||||
static spinlock_t clu5Lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
struct rb500_gpio_reg __iomem *rb500_gpio_reg0;
|
||||
EXPORT_SYMBOL(rb500_gpio_reg0);
|
||||
|
||||
static struct resource rb500_gpio_reg0_res[] = {
|
||||
{
|
||||
.name = "gpio_reg0",
|
||||
.start = GPIO_BADDR,
|
||||
.end = GPIO_BADDR + sizeof(struct rb500_gpio_reg),
|
||||
.flags = IORESOURCE_MEM,
|
||||
}
|
||||
};
|
||||
|
||||
void set434Reg(unsigned regOffs, unsigned bit, unsigned len, unsigned val)
|
||||
{
|
||||
unsigned flags, data;
|
||||
unsigned i = 0;
|
||||
|
||||
spin_lock_irqsave(&clu5Lock, flags);
|
||||
data = *(volatile unsigned *) (IDT434_REG_BASE + regOffs);
|
||||
for (i = 0; i != len; ++i) {
|
||||
if (val & (1 << i))
|
||||
data |= (1 << (i + bit));
|
||||
else
|
||||
data &= ~(1 << (i + bit));
|
||||
}
|
||||
*(volatile unsigned *) (IDT434_REG_BASE + regOffs) = data;
|
||||
spin_unlock_irqrestore(&clu5Lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(set434Reg);
|
||||
|
||||
void changeLatchU5(unsigned char orMask, unsigned char nandMask)
|
||||
{
|
||||
unsigned flags;
|
||||
|
||||
spin_lock_irqsave(&clu5Lock, flags);
|
||||
latchU5State = (latchU5State | orMask) & ~nandMask;
|
||||
if (!devCtl3Base)
|
||||
devCtl3Base = (volatile unsigned char *)
|
||||
KSEG1ADDR(*(volatile unsigned *)
|
||||
KSEG1ADDR(0x18010030));
|
||||
*devCtl3Base = latchU5State;
|
||||
spin_unlock_irqrestore(&clu5Lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(changeLatchU5);
|
||||
|
||||
unsigned char getLatchU5State(void)
|
||||
{
|
||||
return latchU5State;
|
||||
}
|
||||
EXPORT_SYMBOL(getLatchU5State);
|
||||
|
||||
int rb500_gpio_get_value(unsigned gpio)
|
||||
{
|
||||
return readl(&rb500_gpio_reg0->gpiod) & (1 << gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(rb500_gpio_get_value);
|
||||
|
||||
void rb500_gpio_set_value(unsigned gpio, int value)
|
||||
{
|
||||
unsigned tmp;
|
||||
|
||||
tmp = readl(&rb500_gpio_reg0->gpiod) & ~(1 << gpio);
|
||||
if (value)
|
||||
tmp |= 1 << gpio;
|
||||
|
||||
writel(tmp, (void *)&rb500_gpio_reg0->gpiod);
|
||||
}
|
||||
EXPORT_SYMBOL(rb500_gpio_set_value);
|
||||
|
||||
int rb500_gpio_direction_input(unsigned gpio)
|
||||
{
|
||||
writel(readl(&rb500_gpio_reg0->gpiocfg) & ~(1 << gpio), (void *)&rb500_gpio_reg0->gpiocfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rb500_gpio_direction_input);
|
||||
|
||||
int rb500_gpio_direction_output(unsigned gpio, int value)
|
||||
{
|
||||
gpio_set_value(gpio, value);
|
||||
writel(readl(&rb500_gpio_reg0->gpiocfg) | (1 << gpio), (void *)&rb500_gpio_reg0->gpiocfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rb500_gpio_direction_output);
|
||||
|
||||
void rb500_gpio_set_int_level(unsigned gpio, int value)
|
||||
{
|
||||
unsigned tmp;
|
||||
|
||||
tmp = readl(&rb500_gpio_reg0->gpioilevel) & ~(1 << gpio);
|
||||
if (value)
|
||||
tmp |= 1 << gpio;
|
||||
writel(tmp, (void *)&rb500_gpio_reg0->gpioilevel);
|
||||
}
|
||||
EXPORT_SYMBOL(rb500_gpio_set_int_level);
|
||||
|
||||
int rb500_gpio_get_int_level(unsigned gpio)
|
||||
{
|
||||
return readl(&rb500_gpio_reg0->gpioilevel) & (1 << gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(rb500_gpio_get_int_level);
|
||||
|
||||
void rb500_gpio_set_int_status(unsigned gpio, int value)
|
||||
{
|
||||
unsigned tmp;
|
||||
|
||||
tmp = readl(&rb500_gpio_reg0->gpioistat);
|
||||
if (value)
|
||||
tmp |= 1 << gpio;
|
||||
writel(tmp, (void *)&rb500_gpio_reg0->gpioistat);
|
||||
}
|
||||
EXPORT_SYMBOL(rb500_gpio_set_int_status);
|
||||
|
||||
int rb500_gpio_get_int_status(unsigned gpio)
|
||||
{
|
||||
return readl(&rb500_gpio_reg0->gpioistat) & (1 << gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(rb500_gpio_get_int_status);
|
||||
|
||||
void rb500_gpio_set_func(unsigned gpio, int value)
|
||||
{
|
||||
unsigned tmp;
|
||||
|
||||
tmp = readl(&rb500_gpio_reg0->gpiofunc);
|
||||
if (value)
|
||||
tmp |= 1 << gpio;
|
||||
writel(tmp, (void *)&rb500_gpio_reg0->gpiofunc);
|
||||
}
|
||||
EXPORT_SYMBOL(rb500_gpio_set_func);
|
||||
|
||||
int rb500_gpio_get_func(unsigned gpio)
|
||||
{
|
||||
return readl(&rb500_gpio_reg0->gpiofunc) & (1 << gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(rb500_gpio_get_func);
|
||||
|
||||
int __init rb500_gpio_init(void)
|
||||
{
|
||||
rb500_gpio_reg0 = ioremap_nocache(rb500_gpio_reg0_res[0].start,
|
||||
rb500_gpio_reg0_res[0].end -
|
||||
rb500_gpio_reg0_res[0].start);
|
||||
|
||||
if (!rb500_gpio_reg0) {
|
||||
printk(KERN_ERR "rb500: cannot remap GPIO register 0\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(rb500_gpio_init);
|
||||
Loaded 100 of 125 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user