diff options
Diffstat (limited to 'target/linux/generic/files')
31 files changed, 4523 insertions, 563 deletions
diff --git a/target/linux/generic/files/crypto/ocf/ocf-compat.h b/target/linux/generic/files/crypto/ocf/ocf-compat.h index 4ad12232b..831927e88 100644 --- a/target/linux/generic/files/crypto/ocf/ocf-compat.h +++ b/target/linux/generic/files/crypto/ocf/ocf-compat.h @@ -161,7 +161,13 @@ struct ocf_device { sigemptyset(¤t->blocked); \ recalc_sigpending(current); \ spin_unlock_irq(¤t->sigmask_lock); \ - sprintf(current->comm, str); + sprintf(current->comm, str); \ +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) \ + spin_lock_irq(¤t->sigmask_lock); \ + sigemptyset(¤t->blocked); \ + recalc_sigpending(current); \ + spin_unlock_irq(¤t->sigmask_lock); \ + sprintf(current->comm, str); \ #else #define ocf_daemonize(str) daemonize(str); #endif diff --git a/target/linux/generic/files/crypto/ocf/pasemi/pasemi.c b/target/linux/generic/files/crypto/ocf/pasemi/pasemi.c index 1b4333cdd..7bb62bc53 100644 --- a/target/linux/generic/files/crypto/ocf/pasemi/pasemi.c +++ b/target/linux/generic/files/crypto/ocf/pasemi/pasemi.c @@ -38,7 +38,7 @@ #define TIMER_INTERVAL 1000 -static void __devexit pasemi_dma_remove(struct pci_dev *pdev); +static void pasemi_dma_remove(struct pci_dev *pdev); static struct pasdma_status volatile * dma_status; static int debug; @@ -790,7 +790,7 @@ static device_method_t pasemi_methods = { /* Set up the crypto device structure, private data, * and anything else we need before we start */ -static int __devinit +static int pasemi_dma_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct pasemi_softc *sc; @@ -943,7 +943,7 @@ static void pasemi_free_tx_resources(struct pasemi_softc *sc, int chan) del_timer(&ring->crypto_timer); } -static void __devexit pasemi_dma_remove(struct pci_dev *pdev) +static void pasemi_dma_remove(struct pci_dev *pdev) { struct pasemi_softc *sc = pci_get_drvdata(pdev); int i; @@ -984,7 +984,7 @@ static struct pci_driver pasemi_dma_driver = { .name = "pasemi_dma", .id_table = pasemi_dma_pci_tbl, .probe = pasemi_dma_probe, - .remove = __devexit_p(pasemi_dma_remove), + .remove = pasemi_dma_remove, }; static void __exit pasemi_dma_cleanup_module(void) diff --git a/target/linux/generic/files/crypto/ocf/random.c b/target/linux/generic/files/crypto/ocf/random.c index 4bb773ffb..9fc070ed4 100644 --- a/target/linux/generic/files/crypto/ocf/random.c +++ b/target/linux/generic/files/crypto/ocf/random.c @@ -49,6 +49,7 @@ #include <linux/unistd.h> #include <linux/poll.h> #include <linux/random.h> +#include <linux/kthread.h> #include <cryptodev.h> #ifdef CONFIG_OCF_FIPS @@ -79,9 +80,9 @@ struct random_op { void *arg; }; +static struct task_struct *random_thread; static int random_proc(void *arg); -static pid_t randomproc = (pid_t) -1; static spinlock_t random_lock; /* @@ -141,12 +142,10 @@ crypto_rregister( spin_lock_irqsave(&random_lock, flags); list_add_tail(&rops->random_list, &random_ops); if (!started) { - randomproc = kernel_thread(random_proc, NULL, CLONE_FS|CLONE_FILES); - if (randomproc < 0) { - ret = randomproc; - printk("crypto: crypto_rregister cannot start random thread; " - "error %d", ret); - } else + random_thread = kthread_run(random_proc, NULL, "ocf-random"); + if (IS_ERR(random_thread)) + ret = PTR_ERR(random_thread); + else started = 1; } spin_unlock_irqrestore(&random_lock, flags); @@ -172,7 +171,7 @@ crypto_runregister_all(u_int32_t driverid) spin_lock_irqsave(&random_lock, flags); if (list_empty(&random_ops) && started) - kill_proc(randomproc, SIGKILL, 1); + kthread_stop(random_thread); spin_unlock_irqrestore(&random_lock, flags); return(0); } @@ -198,9 +197,11 @@ random_proc(void *arg) recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); sprintf(current->comm, "ocf-random"); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) + recalc_sigpending(); + sprintf(current->comm, "ocf-random"); #else daemonize("ocf-random"); - allow_signal(SIGKILL); #endif (void) get_fs(); @@ -303,12 +304,11 @@ random_proc(void *arg) #endif } } - + kfree(buf); bad_alloc: spin_lock_irq(&random_lock); - randomproc = (pid_t) -1; started = 0; spin_unlock_irq(&random_lock); diff --git a/target/linux/generic/files/crypto/ocf/ubsec_ssb/ubsec_ssb.c b/target/linux/generic/files/crypto/ocf/ubsec_ssb/ubsec_ssb.c index f5d776236..3f3f5135c 100644 --- a/target/linux/generic/files/crypto/ocf/ubsec_ssb/ubsec_ssb.c +++ b/target/linux/generic/files/crypto/ocf/ubsec_ssb/ubsec_ssb.c @@ -104,9 +104,9 @@ * Prototypes */ static irqreturn_t ubsec_ssb_isr(int, void *, struct pt_regs *); -static int __devinit ubsec_ssb_probe(struct ssb_device *sdev, +static int ubsec_ssb_probe(struct ssb_device *sdev, const struct ssb_device_id *ent); -static void __devexit ubsec_ssb_remove(struct ssb_device *sdev); +static void ubsec_ssb_remove(struct ssb_device *sdev); int ubsec_attach(struct ssb_device *sdev, const struct ssb_device_id *ent, struct device *self); static void ubsec_setup_mackey(struct ubsec_session *ses, int algo, @@ -185,7 +185,7 @@ static struct ssb_driver ubsec_ssb_driver = { .name = DRV_MODULE_NAME, .id_table = ubsec_ssb_tbl, .probe = ubsec_ssb_probe, - .remove = __devexit_p(ubsec_ssb_remove), + .remove = ubsec_ssb_remove, /* .suspend = ubsec_ssb_suspend, .resume = ubsec_ssb_resume @@ -482,8 +482,8 @@ ubsec_setup_mackey(struct ubsec_session *ses, int algo, caddr_t key, int klen) } #undef N -static int -__devinit ubsec_ssb_probe(struct ssb_device *sdev, +static int +ubsec_ssb_probe(struct ssb_device *sdev, const struct ssb_device_id *ent) { int err; @@ -553,7 +553,7 @@ err_out: return err; } -static void __devexit ubsec_ssb_remove(struct ssb_device *sdev) { +static void ubsec_ssb_remove(struct ssb_device *sdev) { struct ubsec_softc *sc; unsigned int ctrlflgs; diff --git a/target/linux/generic/files/drivers/input/misc/gpio_buttons.c b/target/linux/generic/files/drivers/input/misc/gpio_buttons.c index 51288a3f9..af5491434 100644 --- a/target/linux/generic/files/drivers/input/misc/gpio_buttons.c +++ b/target/linux/generic/files/drivers/input/misc/gpio_buttons.c @@ -82,7 +82,7 @@ static void gpio_buttons_poll(struct input_polled_dev *dev) } } -static int __devinit gpio_buttons_probe(struct platform_device *pdev) +static int gpio_buttons_probe(struct platform_device *pdev) { struct gpio_buttons_platform_data *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; @@ -186,7 +186,7 @@ err_free_bdev: return error; } -static int __devexit gpio_buttons_remove(struct platform_device *pdev) +static int gpio_buttons_remove(struct platform_device *pdev) { struct gpio_buttons_dev *bdev = platform_get_drvdata(pdev); struct gpio_buttons_platform_data *pdata = bdev->pdata; @@ -207,7 +207,7 @@ static int __devexit gpio_buttons_remove(struct platform_device *pdev) static struct platform_driver gpio_buttons_driver = { .probe = gpio_buttons_probe, - .remove = __devexit_p(gpio_buttons_remove), + .remove = gpio_buttons_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 9cbe3a838..b6df6425a 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -33,6 +33,8 @@ #include <linux/lockdep.h> #include <linux/ar8216_platform.h> #include <linux/workqueue.h> +#include <linux/of_device.h> + #include "ar8216.h" /* size of the vlan table */ @@ -42,7 +44,7 @@ #define AR8XXX_MIB_WORK_DELAY 2000 /* msecs */ -struct ar8216_priv; +struct ar8xxx_priv; #define AR8XXX_CAP_GIGE BIT(0) #define AR8XXX_CAP_MIB_COUNTERS BIT(1) @@ -63,31 +65,40 @@ struct ar8xxx_mib_desc { struct ar8xxx_chip { unsigned long caps; - int (*hw_init)(struct ar8216_priv *priv); - void (*init_globals)(struct ar8216_priv *priv); - void (*init_port)(struct ar8216_priv *priv, int port); - void (*setup_port)(struct ar8216_priv *priv, int port, u32 egress, + int (*hw_init)(struct ar8xxx_priv *priv); + void (*init_globals)(struct ar8xxx_priv *priv); + void (*init_port)(struct ar8xxx_priv *priv, int port); + void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, u32 members, u32 pvid); - u32 (*read_port_status)(struct ar8216_priv *priv, int port); - int (*atu_flush)(struct ar8216_priv *priv); - void (*vtu_flush)(struct ar8216_priv *priv); - void (*vtu_load_vlan)(struct ar8216_priv *priv, u32 vid, u32 port_mask); + u32 (*read_port_status)(struct ar8xxx_priv *priv, int port); + int (*atu_flush)(struct ar8xxx_priv *priv); + void (*vtu_flush)(struct ar8xxx_priv *priv); + void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask); const struct ar8xxx_mib_desc *mib_decs; unsigned num_mibs; }; -struct ar8216_priv { +struct ar8327_data { + u32 port0_status; + u32 port6_status; +}; + +struct ar8xxx_priv { struct switch_dev dev; + struct mii_bus *mii_bus; struct phy_device *phy; - u32 (*read)(struct ar8216_priv *priv, int reg); - void (*write)(struct ar8216_priv *priv, int reg, u32 val); + u32 (*read)(struct ar8xxx_priv *priv, int reg); + void (*write)(struct ar8xxx_priv *priv, int reg, u32 val); const struct net_device_ops *ndo_old; struct net_device_ops ndo; struct mutex reg_mutex; u8 chip_ver; u8 chip_rev; const struct ar8xxx_chip *chip; + union { + struct ar8327_data ar8327; + } chip_data; bool initialized; bool port4_phy; char buf[2048]; @@ -100,12 +111,21 @@ struct ar8216_priv { int mib_next_port; u64 *mib_stats; + struct list_head list; + unsigned int use_count; + /* all fields below are cleared on reset */ bool vlan; u16 vlan_id[AR8X16_MAX_VLANS]; u8 vlan_table[AR8X16_MAX_VLANS]; u8 vlan_tagged; u16 pvid[AR8X16_MAX_PORTS]; + + /* mirroring */ + bool mirror_rx; + bool mirror_tx; + int source_port; + int monitor_port; }; #define MIB_DESC(_s , _o, _n) \ @@ -197,34 +217,41 @@ static const struct ar8xxx_mib_desc ar8236_mibs[] = { MIB_DESC(1, AR8236_STATS_TXLATECOL, "TxLateCol"), }; -#define to_ar8216(_dev) container_of(_dev, struct ar8216_priv, dev) +static DEFINE_MUTEX(ar8xxx_dev_list_lock); +static LIST_HEAD(ar8xxx_dev_list); -static inline bool ar8xxx_has_gige(struct ar8216_priv *priv) +static inline struct ar8xxx_priv * +swdev_to_ar8xxx(struct switch_dev *swdev) +{ + return container_of(swdev, struct ar8xxx_priv, dev); +} + +static inline bool ar8xxx_has_gige(struct ar8xxx_priv *priv) { return priv->chip->caps & AR8XXX_CAP_GIGE; } -static inline bool ar8xxx_has_mib_counters(struct ar8216_priv *priv) +static inline bool ar8xxx_has_mib_counters(struct ar8xxx_priv *priv) { return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS; } -static inline bool chip_is_ar8216(struct ar8216_priv *priv) +static inline bool chip_is_ar8216(struct ar8xxx_priv *priv) { return priv->chip_ver == AR8XXX_VER_AR8216; } -static inline bool chip_is_ar8236(struct ar8216_priv *priv) +static inline bool chip_is_ar8236(struct ar8xxx_priv *priv) { return priv->chip_ver == AR8XXX_VER_AR8236; } -static inline bool chip_is_ar8316(struct ar8216_priv *priv) +static inline bool chip_is_ar8316(struct ar8xxx_priv *priv) { return priv->chip_ver == AR8XXX_VER_AR8316; } -static inline bool chip_is_ar8327(struct ar8216_priv *priv) +static inline bool chip_is_ar8327(struct ar8xxx_priv *priv) { return priv->chip_ver == AR8XXX_VER_AR8327; } @@ -243,10 +270,9 @@ split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) } static u32 -ar8216_mii_read(struct ar8216_priv *priv, int reg) +ar8xxx_mii_read(struct ar8xxx_priv *priv, int reg) { - struct phy_device *phy = priv->phy; - struct mii_bus *bus = phy->bus; + struct mii_bus *bus = priv->mii_bus; u16 r1, r2, page; u16 lo, hi; @@ -265,10 +291,9 @@ ar8216_mii_read(struct ar8216_priv *priv, int reg) } static void -ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val) +ar8xxx_mii_write(struct ar8xxx_priv *priv, int reg, u32 val) { - struct phy_device *phy = priv->phy; - struct mii_bus *bus = phy->bus; + struct mii_bus *bus = priv->mii_bus; u16 r1, r2, r3; u16 lo, hi; @@ -292,10 +317,10 @@ ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val) } static void -ar8216_phy_dbg_write(struct ar8216_priv *priv, int phy_addr, +ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr, u16 dbg_addr, u16 dbg_data) { - struct mii_bus *bus = priv->phy->bus; + struct mii_bus *bus = priv->mii_bus; mutex_lock(&bus->mdio_lock); bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr); @@ -304,9 +329,9 @@ ar8216_phy_dbg_write(struct ar8216_priv *priv, int phy_addr, } static void -ar8216_phy_mmd_write(struct ar8216_priv *priv, int phy_addr, u16 addr, u16 data) +ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 data) { - struct mii_bus *bus = priv->phy->bus; + struct mii_bus *bus = priv->mii_bus; mutex_lock(&bus->mdio_lock); bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr); @@ -315,7 +340,7 @@ ar8216_phy_mmd_write(struct ar8216_priv *priv, int phy_addr, u16 addr, u16 data) } static u32 -ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) +ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) { u32 v; @@ -330,7 +355,7 @@ ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) } static inline void -ar8216_reg_set(struct ar8216_priv *priv, int reg, u32 val) +ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val) { u32 v; @@ -342,7 +367,7 @@ ar8216_reg_set(struct ar8216_priv *priv, int reg, u32 val) } static int -ar8216_reg_wait(struct ar8216_priv *priv, u32 reg, u32 mask, u32 val, +ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val, unsigned timeout) { int i; @@ -361,7 +386,7 @@ ar8216_reg_wait(struct ar8216_priv *priv, u32 reg, u32 mask, u32 val, } static int -ar8216_mib_op(struct ar8216_priv *priv, u32 op) +ar8xxx_mib_op(struct ar8xxx_priv *priv, u32 op) { unsigned mib_func; int ret; @@ -375,11 +400,11 @@ ar8216_mib_op(struct ar8216_priv *priv, u32 op) mutex_lock(&priv->reg_mutex); /* Capture the hardware statistics for all ports */ - ar8216_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S)); + ar8xxx_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S)); mutex_unlock(&priv->reg_mutex); /* Wait for the capturing to complete. */ - ret = ar8216_reg_wait(priv, mib_func, AR8216_MIB_BUSY, 0, 10); + ret = ar8xxx_reg_wait(priv, mib_func, AR8216_MIB_BUSY, 0, 10); if (ret) goto out; @@ -390,19 +415,19 @@ out: } static int -ar8216_mib_capture(struct ar8216_priv *priv) +ar8xxx_mib_capture(struct ar8xxx_priv *priv) { - return ar8216_mib_op(priv, AR8216_MIB_FUNC_CAPTURE); + return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_CAPTURE); } static int -ar8216_mib_flush(struct ar8216_priv *priv) +ar8xxx_mib_flush(struct ar8xxx_priv *priv) { - return ar8216_mib_op(priv, AR8216_MIB_FUNC_FLUSH); + return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_FLUSH); } static void -ar8216_mib_fetch_port_stat(struct ar8216_priv *priv, int port, bool flush) +ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush) { unsigned int base; u64 *mib_stats; @@ -442,7 +467,7 @@ ar8216_mib_fetch_port_stat(struct ar8216_priv *priv, int port, bool flush) } static void -ar8216_read_port_link(struct ar8216_priv *priv, int port, +ar8216_read_port_link(struct ar8xxx_priv *priv, int port, struct switch_port_link *link) { u32 status; @@ -487,7 +512,7 @@ ar8216_read_port_link(struct ar8216_priv *priv, int port, static struct sk_buff * ar8216_mangle_tx(struct net_device *dev, struct sk_buff *skb) { - struct ar8216_priv *priv = dev->phy_ptr; + struct ar8xxx_priv *priv = dev->phy_ptr; unsigned char *buf; if (unlikely(!priv)) @@ -516,7 +541,7 @@ error: static void ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb) { - struct ar8216_priv *priv; + struct ar8xxx_priv *priv; unsigned char *buf; int port, vlan; @@ -551,7 +576,7 @@ ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb) } static int -ar8216_wait_bit(struct ar8216_priv *priv, int reg, u32 mask, u32 val) +ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) { int timeout = 20; u32 t = 0; @@ -573,7 +598,7 @@ ar8216_wait_bit(struct ar8216_priv *priv, int reg, u32 mask, u32 val) } static void -ar8216_vtu_op(struct ar8216_priv *priv, u32 op, u32 val) +ar8216_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val) { if (ar8216_wait_bit(priv, AR8216_REG_VTU, AR8216_VTU_ACTIVE, 0)) return; @@ -587,13 +612,13 @@ ar8216_vtu_op(struct ar8216_priv *priv, u32 op, u32 val) } static void -ar8216_vtu_flush(struct ar8216_priv *priv) +ar8216_vtu_flush(struct ar8xxx_priv *priv) { ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0); } static void -ar8216_vtu_load_vlan(struct ar8216_priv *priv, u32 vid, u32 port_mask) +ar8216_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask) { u32 op; @@ -602,7 +627,7 @@ ar8216_vtu_load_vlan(struct ar8216_priv *priv, u32 vid, u32 port_mask) } static int -ar8216_atu_flush(struct ar8216_priv *priv) +ar8216_atu_flush(struct ar8xxx_priv *priv) { int ret; @@ -614,13 +639,13 @@ ar8216_atu_flush(struct ar8216_priv *priv) } static u32 -ar8216_read_port_status(struct ar8216_priv *priv, int port) +ar8216_read_port_status(struct ar8xxx_priv *priv, int port) { return priv->read(priv, AR8216_REG_PORT_STATUS(port)); } static void -ar8216_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, +ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, u32 members, u32 pvid) { u32 header; @@ -630,7 +655,7 @@ ar8216_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, else header = 0; - ar8216_rmw(priv, AR8216_REG_PORT_CTRL(port), + ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE | AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK, @@ -638,7 +663,7 @@ ar8216_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); - ar8216_rmw(priv, AR8216_REG_PORT_VLAN(port), + ar8xxx_rmw(priv, AR8216_REG_PORT_VLAN(port), AR8216_PORT_VLAN_DEST_PORTS | AR8216_PORT_VLAN_MODE | AR8216_PORT_VLAN_DEFAULT_ID, (members << AR8216_PORT_VLAN_DEST_PORTS_S) | @@ -647,23 +672,23 @@ ar8216_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, } static int -ar8216_hw_init(struct ar8216_priv *priv) +ar8216_hw_init(struct ar8xxx_priv *priv) { return 0; } static void -ar8216_init_globals(struct ar8216_priv *priv) +ar8216_init_globals(struct ar8xxx_priv *priv) { /* standard atheros magic */ priv->write(priv, 0x38, 0xc000050e); - ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, AR8216_GCTRL_MTU, 1518 + 8 + 2); } static void -ar8216_init_port(struct ar8216_priv *priv, int port) +ar8216_init_port(struct ar8xxx_priv *priv, int port) { /* Enable port learning and tx */ priv->write(priv, AR8216_REG_PORT_CTRL(port), @@ -705,10 +730,10 @@ static const struct ar8xxx_chip ar8216_chip = { }; static void -ar8236_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, +ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, u32 members, u32 pvid) { - ar8216_rmw(priv, AR8216_REG_PORT_CTRL(port), + ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE | AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK, @@ -716,11 +741,11 @@ ar8236_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); - ar8216_rmw(priv, AR8236_REG_PORT_VLAN(port), + ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN(port), AR8236_PORT_VLAN_DEFAULT_ID, (pvid << AR8236_PORT_VLAN_DEFAULT_ID_S)); - ar8216_rmw(priv, AR8236_REG_PORT_VLAN2(port), + ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN2(port), AR8236_PORT_VLAN2_VLAN_MODE | AR8236_PORT_VLAN2_MEMBER, (ingress << AR8236_PORT_VLAN2_VLAN_MODE_S) | @@ -728,7 +753,7 @@ ar8236_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, } static int -ar8236_hw_init(struct ar8216_priv *priv) +ar8236_hw_init(struct ar8xxx_priv *priv) { int i; struct mii_bus *bus; @@ -737,7 +762,7 @@ ar8236_hw_init(struct ar8216_priv *priv) return 0; /* Initialize the PHYs */ - bus = priv->phy->bus; + bus = priv->mii_bus; for (i = 0; i < 5; i++) { mdiobus_write(bus, i, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | @@ -751,14 +776,14 @@ ar8236_hw_init(struct ar8216_priv *priv) } static void -ar8236_init_globals(struct ar8216_priv *priv) +ar8236_init_globals(struct ar8xxx_priv *priv) { /* enable jumbo frames */ - ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, AR8316_GCTRL_MTU, 9018 + 8 + 2); /* Enable MIB counters */ - ar8216_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN, + ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN, (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) | AR8236_MIB_EN); } @@ -779,67 +804,68 @@ static const struct ar8xxx_chip ar8236_chip = { }; static int -ar8316_hw_init(struct ar8216_priv *priv) +ar8316_hw_init(struct ar8xxx_priv *priv) { int i; u32 val, newval; struct mii_bus *bus; - val = priv->read(priv, 0x8); + val = priv->read(priv, AR8316_REG_POSTRIP); if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { if (priv->port4_phy) { /* value taken from Ubiquiti RouterStation Pro */ newval = 0x81461bea; - printk(KERN_INFO "ar8316: Using port 4 as PHY\n"); + pr_info("ar8316: Using port 4 as PHY\n"); } else { newval = 0x01261be2; - printk(KERN_INFO "ar8316: Using port 4 as switch port\n"); + pr_info("ar8316: Using port 4 as switch port\n"); } } else if (priv->phy->interface == PHY_INTERFACE_MODE_GMII) { /* value taken from AVM Fritz!Box 7390 sources */ newval = 0x010e5b71; } else { /* no known value for phy interface */ - printk(KERN_ERR "ar8316: unsupported mii mode: %d.\n", - priv->phy->interface); + pr_err("ar8316: unsupported mii mode: %d.\n", + priv->phy->interface); return -EINVAL; } if (val == newval) goto out; - priv->write(priv, 0x8, newval); + priv->write(priv, AR8316_REG_POSTRIP, newval); + + if (priv->port4_phy && + priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { + /* work around for phy4 rgmii mode */ + ar8xxx_phy_dbg_write(priv, 4, 0x12, 0x480c); + /* rx delay */ + ar8xxx_phy_dbg_write(priv, 4, 0x0, 0x824e); + /* tx delay */ + ar8xxx_phy_dbg_write(priv, 4, 0x5, 0x3d47); + msleep(1000); + } /* Initialize the ports */ - bus = priv->phy->bus; + bus = priv->mii_bus; for (i = 0; i < 5; i++) { - if ((i == 4) && priv->port4_phy && - priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { - /* work around for phy4 rgmii mode */ - ar8216_phy_dbg_write(priv, i, 0x12, 0x480c); - /* rx delay */ - ar8216_phy_dbg_write(priv, i, 0x0, 0x824e); - /* tx delay */ - ar8216_phy_dbg_write(priv, i, 0x5, 0x3d47); - msleep(1000); - } - /* initialize the port itself */ mdiobus_write(bus, i, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL); mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); - msleep(1000); } + msleep(1000); + out: priv->initialized = true; return 0; } static void -ar8316_init_globals(struct ar8216_priv *priv) +ar8316_init_globals(struct ar8xxx_priv *priv) { /* standard atheros magic */ priv->write(priv, 0x38, 0xc000050e); @@ -848,11 +874,11 @@ ar8316_init_globals(struct ar8216_priv *priv) priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); /* enable jumbo frames */ - ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, AR8316_GCTRL_MTU, 9018 + 8 + 2); /* Enable MIB counters */ - ar8216_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN, + ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN, (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) | AR8236_MIB_EN); } @@ -917,6 +943,9 @@ ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) if (cfg->txclk_delay_en) t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; + if (cfg->sgmii_delay_en) + t |= AR8327_PAD_SGMII_DELAY_EN; + break; case AR8327_PAD_MAC2PHY_MII: @@ -964,45 +993,76 @@ ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) } static void -ar8327_phy_fixup(struct ar8216_priv *priv, int phy) +ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy) { switch (priv->chip_rev) { case 1: /* For 100M waveform */ - ar8216_phy_dbg_write(priv, phy, 0, 0x02ea); + ar8xxx_phy_dbg_write(priv, phy, 0, 0x02ea); /* Turn on Gigabit clock */ - ar8216_phy_dbg_write(priv, phy, 0x3d, 0x68a0); + ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x68a0); break; case 2: - ar8216_phy_mmd_write(priv, phy, 0x7, 0x3c); - ar8216_phy_mmd_write(priv, phy, 0x4007, 0x0); + ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c); + ar8xxx_phy_mmd_write(priv, phy, 0x4007, 0x0); /* fallthrough */ case 4: - ar8216_phy_mmd_write(priv, phy, 0x3, 0x800d); - ar8216_phy_mmd_write(priv, phy, 0x4003, 0x803f); + ar8xxx_phy_mmd_write(priv, phy, 0x3, 0x800d); + ar8xxx_phy_mmd_write(priv, phy, 0x4003, 0x803f); + + ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x6860); + ar8xxx_phy_dbg_write(priv, phy, 0x5, 0x2c46); + ar8xxx_phy_dbg_write(priv, phy, 0x3c, 0x6000); + break; + } +} + +static u32 +ar8327_get_port_init_status(struct ar8327_port_cfg *cfg) +{ + u32 t; - ar8216_phy_dbg_write(priv, phy, 0x3d, 0x6860); - ar8216_phy_dbg_write(priv, phy, 0x5, 0x2c46); - ar8216_phy_dbg_write(priv, phy, 0x3c, 0x6000); + if (!cfg->force_link) + return AR8216_PORT_STATUS_LINK_AUTO; + + t = AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC; + t |= cfg->duplex ? AR8216_PORT_STATUS_DUPLEX : 0; + t |= cfg->rxpause ? AR8216_PORT_STATUS_RXFLOW : 0; + t |= cfg->txpause ? AR8216_PORT_STATUS_TXFLOW : 0; + + switch (cfg->speed) { + case AR8327_PORT_SPEED_10: + t |= AR8216_PORT_SPEED_10M; + break; + case AR8327_PORT_SPEED_100: + t |= AR8216_PORT_SPEED_100M; + break; + case AR8327_PORT_SPEED_1000: + t |= AR8216_PORT_SPEED_1000M; break; } + + return t; } static int -ar8327_hw_init(struct ar8216_priv *priv) +ar8327_hw_config_pdata(struct ar8xxx_priv *priv, + struct ar8327_platform_data *pdata) { - struct ar8327_platform_data *pdata; struct ar8327_led_cfg *led_cfg; - struct mii_bus *bus; + struct ar8327_data *data; u32 pos, new_pos; u32 t; - int i; - pdata = priv->phy->dev.platform_data; if (!pdata) return -EINVAL; + data = &priv->chip_data.ar8327; + + data->port0_status = ar8327_get_port_init_status(&pdata->port0_cfg); + data->port6_status = ar8327_get_port_init_status(&pdata->port6_cfg); + t = ar8327_get_pad_cfg(pdata->pad0_cfg); priv->write(priv, AR8327_REG_PAD0_MODE, t); t = ar8327_get_pad_cfg(pdata->pad5_cfg); @@ -1031,7 +1091,70 @@ ar8327_hw_init(struct ar8216_priv *priv) priv->write(priv, AR8327_REG_POWER_ON_STRIP, new_pos); } - bus = priv->phy->bus; + return 0; +} + +#ifdef CONFIG_OF +static int +ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np) +{ + const __be32 *paddr; + int len; + int i; + + paddr = of_get_property(np, "qca,ar8327-initvals", &len); + if (!paddr || len < (2 * sizeof(*paddr))) + return -EINVAL; + + len /= sizeof(*paddr); + + for (i = 0; i < len - 1; i += 2) { + u32 reg; + u32 val; + + reg = be32_to_cpup(paddr + i); + val = be32_to_cpup(paddr + i + 1); + + switch (reg) { + case AR8327_REG_PORT_STATUS(0): + priv->chip_data.ar8327.port0_status = val; + break; + case AR8327_REG_PORT_STATUS(6): + priv->chip_data.ar8327.port6_status = val; + break; + default: + priv->write(priv, reg, val); + break; + } + } + + return 0; +} +#else +static inline int +ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np) +{ + return -EINVAL; +} +#endif + +static int +ar8327_hw_init(struct ar8xxx_priv *priv) +{ + struct mii_bus *bus; + int ret; + int i; + + if (priv->phy->dev.of_node) + ret = ar8327_hw_config_of(priv, priv->phy->dev.of_node); + else + ret = ar8327_hw_config_pdata(priv, + priv->phy->dev.platform_data); + + if (ret) + return ret; + + bus = priv->mii_bus; for (i = 0; i < AR8327_NUM_PHYS; i++) { ar8327_phy_fixup(priv, i); @@ -1049,7 +1172,7 @@ ar8327_hw_init(struct ar8216_priv *priv) } static void -ar8327_init_globals(struct ar8216_priv *priv) +ar8327_init_globals(struct ar8xxx_priv *priv) { u32 t; @@ -1065,66 +1188,32 @@ ar8327_init_globals(struct ar8216_priv *priv) priv->write(priv, AR8327_REG_FWD_CTRL1, t); /* setup MTU */ - ar8216_rmw(priv, AR8327_REG_MAX_FRAME_SIZE, + ar8xxx_rmw(priv, AR8327_REG_MAX_FRAME_SIZE, AR8327_MAX_FRAME_SIZE_MTU, 1518 + 8 + 2); /* Enable MIB counters */ - ar8216_reg_set(priv, AR8327_REG_MODULE_EN, + ar8xxx_reg_set(priv, AR8327_REG_MODULE_EN, AR8327_MODULE_EN_MIB); } static void -ar8327_init_cpuport(struct ar8216_priv *priv) +ar8327_init_port(struct ar8xxx_priv *priv, int port) { - struct ar8327_platform_data *pdata; - struct ar8327_port_cfg *cfg; u32 t; - pdata = priv->phy->dev.platform_data; - if (!pdata) - return; - - cfg = &pdata->cpuport_cfg; - if (!cfg->force_link) { - priv->write(priv, AR8327_REG_PORT_STATUS(AR8216_PORT_CPU), - AR8216_PORT_STATUS_LINK_AUTO); - return; - } - - t = AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC; - t |= cfg->duplex ? AR8216_PORT_STATUS_DUPLEX : 0; - t |= cfg->rxpause ? AR8216_PORT_STATUS_RXFLOW : 0; - t |= cfg->txpause ? AR8216_PORT_STATUS_TXFLOW : 0; - switch (cfg->speed) { - case AR8327_PORT_SPEED_10: - t |= AR8216_PORT_SPEED_10M; - break; - case AR8327_PORT_SPEED_100: - t |= AR8216_PORT_SPEED_100M; - break; - case AR8327_PORT_SPEED_1000: - t |= AR8216_PORT_SPEED_1000M; - break; - } - - priv->write(priv, AR8327_REG_PORT_STATUS(AR8216_PORT_CPU), t); -} - -static void -ar8327_init_port(struct ar8216_priv *priv, int port) -{ - u32 t; - - if (port == AR8216_PORT_CPU) { - ar8327_init_cpuport(priv); - } else { + if (port == AR8216_PORT_CPU) + t = priv->chip_data.ar8327.port0_status; + else if (port == 6) + t = priv->chip_data.ar8327.port6_status; + else t = AR8216_PORT_STATUS_LINK_AUTO; - priv->write(priv, AR8327_REG_PORT_STATUS(port), t); - } + priv->write(priv, AR8327_REG_PORT_STATUS(port), t); priv->write(priv, AR8327_REG_PORT_HEADER(port), 0); - priv->write(priv, AR8327_REG_PORT_VLAN0(port), 0); + t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; + t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; + priv->write(priv, AR8327_REG_PORT_VLAN0(port), t); t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S; priv->write(priv, AR8327_REG_PORT_VLAN1(port), t); @@ -1135,13 +1224,13 @@ ar8327_init_port(struct ar8216_priv *priv, int port) } static u32 -ar8327_read_port_status(struct ar8216_priv *priv, int port) +ar8327_read_port_status(struct ar8xxx_priv *priv, int port) { return priv->read(priv, AR8327_REG_PORT_STATUS(port)); } static int -ar8327_atu_flush(struct ar8216_priv *priv) +ar8327_atu_flush(struct ar8xxx_priv *priv) { int ret; @@ -1155,7 +1244,7 @@ ar8327_atu_flush(struct ar8216_priv *priv) } static void -ar8327_vtu_op(struct ar8216_priv *priv, u32 op, u32 val) +ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val) { if (ar8216_wait_bit(priv, AR8327_REG_VTU_FUNC1, AR8327_VTU_FUNC1_BUSY, 0)) @@ -1169,13 +1258,13 @@ ar8327_vtu_op(struct ar8216_priv *priv, u32 op, u32 val) } static void -ar8327_vtu_flush(struct ar8216_priv *priv) +ar8327_vtu_flush(struct ar8xxx_priv *priv) { ar8327_vtu_op(priv, AR8327_VTU_FUNC1_OP_FLUSH, 0); } static void -ar8327_vtu_load_vlan(struct ar8216_priv *priv, u32 vid, u32 port_mask) +ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask) { u32 op; u32 val; @@ -1201,7 +1290,7 @@ ar8327_vtu_load_vlan(struct ar8216_priv *priv, u32 vid, u32 port_mask) } static void -ar8327_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, +ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, u32 members, u32 pvid) { u32 t; @@ -1251,28 +1340,28 @@ static const struct ar8xxx_chip ar8327_chip = { }; static int -ar8216_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, +ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); priv->vlan = !!val->value.i; return 0; } static int -ar8216_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, +ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); val->value.i = priv->vlan; return 0; } static int -ar8216_sw_set_pvid(struct switch_dev *dev, int port, int vlan) +ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); /* make sure no invalid PVIDs get set */ @@ -1284,45 +1373,45 @@ ar8216_sw_set_pvid(struct switch_dev *dev, int port, int vlan) } static int -ar8216_sw_get_pvid(struct switch_dev *dev, int port, int *vlan) +ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); *vlan = priv->pvid[port]; return 0; } static int -ar8216_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr, +ar8xxx_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); priv->vlan_id[val->port_vlan] = val->value.i; return 0; } static int -ar8216_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr, +ar8xxx_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); val->value.i = priv->vlan_id[val->port_vlan]; return 0; } static int -ar8216_sw_get_port_link(struct switch_dev *dev, int port, +ar8xxx_sw_get_port_link(struct switch_dev *dev, int port, struct switch_port_link *link) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); ar8216_read_port_link(priv, port, link); return 0; } static int -ar8216_sw_get_ports(struct switch_dev *dev, struct switch_val *val) +ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); u8 ports = priv->vlan_table[val->port_vlan]; int i; @@ -1344,9 +1433,9 @@ ar8216_sw_get_ports(struct switch_dev *dev, struct switch_val *val) } static int -ar8216_sw_set_ports(struct switch_dev *dev, struct switch_val *val) +ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); u8 *vt = &priv->vlan_table[val->port_vlan]; int i, j; @@ -1374,10 +1463,102 @@ ar8216_sw_set_ports(struct switch_dev *dev, struct switch_val *val) return 0; } +static void +ar8327_set_mirror_regs(struct ar8xxx_priv *priv) +{ + int port; + + /* reset all mirror registers */ + ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0, + AR8327_FWD_CTRL0_MIRROR_PORT, + (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S)); + for (port = 0; port < AR8327_NUM_PORTS; port++) { + ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(port), + AR8327_PORT_LOOKUP_ING_MIRROR_EN, + 0); + + ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(port), + AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN, + 0); + } + + /* now enable mirroring if necessary */ + if (priv->source_port >= AR8327_NUM_PORTS || + priv->monitor_port >= AR8327_NUM_PORTS || + priv->source_port == priv->monitor_port) { + return; + } + + ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0, + AR8327_FWD_CTRL0_MIRROR_PORT, + (priv->monitor_port << AR8327_FWD_CTRL0_MIRROR_PORT_S)); + + if (priv->mirror_rx) + ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(priv->source_port), + AR8327_PORT_LOOKUP_ING_MIRROR_EN, + AR8327_PORT_LOOKUP_ING_MIRROR_EN); + + if (priv->mirror_tx) + ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(priv->source_port), + AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN, + AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN); +} + +static void +ar8216_set_mirror_regs(struct ar8xxx_priv *priv) +{ + int port; + + /* reset all mirror registers */ + ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT, + AR8216_GLOBAL_CPUPORT_MIRROR_PORT, + (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S)); + for (port = 0; port < AR8216_NUM_PORTS; port++) { + ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), + AR8216_PORT_CTRL_MIRROR_RX, + 0); + + ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), + AR8216_PORT_CTRL_MIRROR_TX, + 0); + } + + /* now enable mirroring if necessary */ + if (priv->source_port >= AR8216_NUM_PORTS || + priv->monitor_port >= AR8216_NUM_PORTS || + priv->source_port == priv->monitor_port) { + return; + } + + ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT, + AR8216_GLOBAL_CPUPORT_MIRROR_PORT, + (priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S)); + + if (priv->mirror_rx) + ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port), + AR8216_PORT_CTRL_MIRROR_RX, + AR8216_PORT_CTRL_MIRROR_RX); + + if (priv->mirror_tx) + ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port), + AR8216_PORT_CTRL_MIRROR_TX, + AR8216_PORT_CTRL_MIRROR_TX); +} + +static void +ar8xxx_set_mirror_regs(struct ar8xxx_priv *priv) +{ + if (chip_is_ar8327(priv)) { + ar8327_set_mirror_regs(priv); + } else { + ar8216_set_mirror_regs(priv); + } +} + static int -ar8216_sw_hw_apply(struct switch_dev *dev) +ar8xxx_sw_hw_apply(struct switch_dev *dev) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); u8 portmask[AR8X16_MAX_PORTS]; int i, j; @@ -1437,19 +1618,22 @@ ar8216_sw_hw_apply(struct switch_dev *dev) priv->chip->setup_port(priv, i, egress, ingress, portmask[i], pvid); } + + ar8xxx_set_mirror_regs(priv); + mutex_unlock(&priv->reg_mutex); return 0; } static int -ar8216_sw_reset_switch(struct switch_dev *dev) +ar8xxx_sw_reset_switch(struct switch_dev *dev) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); int i; mutex_lock(&priv->reg_mutex); - memset(&priv->vlan, 0, sizeof(struct ar8216_priv) - - offsetof(struct ar8216_priv, vlan)); + memset(&priv->vlan, 0, sizeof(struct ar8xxx_priv) - + offsetof(struct ar8xxx_priv, vlan)); for (i = 0; i < AR8X16_MAX_VLANS; i++) priv->vlan_id[i] = i; @@ -1458,18 +1642,24 @@ ar8216_sw_reset_switch(struct switch_dev *dev) for (i = 0; i < dev->ports; i++) priv->chip->init_port(priv, i); + priv->mirror_rx = false; + priv->mirror_tx = false; + priv->source_port = 0; + priv->monitor_port = 0; + priv->chip->init_globals(priv); + mutex_unlock(&priv->reg_mutex); - return ar8216_sw_hw_apply(dev); + return ar8xxx_sw_hw_apply(dev); } static int -ar8216_sw_set_reset_mibs(struct switch_dev *dev, +ar8xxx_sw_set_reset_mibs(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); unsigned int len; int ret; @@ -1481,7 +1671,7 @@ ar8216_sw_set_reset_mibs(struct switch_dev *dev, len = priv->dev.ports * priv->chip->num_mibs * sizeof(*priv->mib_stats); memset(priv->mib_stats, '\0', len); - ret = ar8216_mib_flush(priv); + ret = ar8xxx_mib_flush(priv); if (ret) goto unlock; @@ -1493,11 +1683,111 @@ unlock: } static int -ar8216_sw_set_port_reset_mib(struct switch_dev *dev, +ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + + mutex_lock(&priv->reg_mutex); + priv->mirror_rx = !!val->value.i; + ar8xxx_set_mirror_regs(priv); + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static int +ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + val->value.i = priv->mirror_rx; + return 0; +} + +static int +ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + + mutex_lock(&priv->reg_mutex); + priv->mirror_tx = !!val->value.i; + ar8xxx_set_mirror_regs(priv); + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static int +ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + val->value.i = priv->mirror_tx; + return 0; +} + +static int +ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + + mutex_lock(&priv->reg_mutex); + priv->monitor_port = val->value.i; + ar8xxx_set_mirror_regs(priv); + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static int +ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + val->value.i = priv->monitor_port; + return 0; +} + +static int +ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + + mutex_lock(&priv->reg_mutex); + priv->source_port = val->value.i; + ar8xxx_set_mirror_regs(priv); + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static int +ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + val->value.i = priv->source_port; + return 0; +} + +static int +ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); int port; int ret; @@ -1509,11 +1799,11 @@ ar8216_sw_set_port_reset_mib(struct switch_dev *dev, return -EINVAL; mutex_lock(&priv->mib_lock); - ret = ar8216_mib_capture(priv); + ret = ar8xxx_mib_capture(priv); if (ret) goto unlock; - ar8216_mib_fetch_port_stat(priv, port, true); + ar8xxx_mib_fetch_port_stat(priv, port, true); ret = 0; @@ -1523,11 +1813,11 @@ unlock: } static int -ar8216_sw_get_port_mib(struct switch_dev *dev, +ar8xxx_sw_get_port_mib(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); const struct ar8xxx_chip *chip = priv->chip; u64 *mib_stats; int port; @@ -1543,11 +1833,11 @@ ar8216_sw_get_port_mib(struct switch_dev *dev, return -EINVAL; mutex_lock(&priv->mib_lock); - ret = ar8216_mib_capture(priv); + ret = ar8xxx_mib_capture(priv); if (ret) goto unlock; - ar8216_mib_fetch_port_stat(priv, port, false); + ar8xxx_mib_fetch_port_stat(priv, port, false); len += snprintf(buf + len, sizeof(priv->buf) - len, "Port %d MIB counters\n", @@ -1570,81 +1860,183 @@ unlock: return ret; } -static struct switch_attr ar8216_globals[] = { +static struct switch_attr ar8xxx_sw_attr_globals[] = { { .type = SWITCH_TYPE_INT, .name = "enable_vlan", .description = "Enable VLAN mode", - .set = ar8216_sw_set_vlan, - .get = ar8216_sw_get_vlan, + .set = ar8xxx_sw_set_vlan, + .get = ar8xxx_sw_get_vlan, .max = 1 }, { .type = SWITCH_TYPE_NOVAL, .name = "reset_mibs", .description = "Reset all MIB counters", - .set = ar8216_sw_set_reset_mibs, + .set = ar8xxx_sw_set_reset_mibs, }, + { + .type = SWITCH_TYPE_INT, + .name = "enable_mirror_rx", + .description = "Enable mirroring of RX packets", + .set = ar8xxx_sw_set_mirror_rx_enable, + .get = ar8xxx_sw_get_mirror_rx_enable, + .max = 1 + }, + { + .type = SWITCH_TYPE_INT, + .name = "enable_mirror_tx", + .description = "Enable mirroring of TX packets", + .set = ar8xxx_sw_set_mirror_tx_enable, + .get = ar8xxx_sw_get_mirror_tx_enable, + .max = 1 + }, + { + .type = SWITCH_TYPE_INT, + .name = "mirror_monitor_port", + .description = "Mirror monitor port", + .set = ar8xxx_sw_set_mirror_monitor_port, + .get = ar8xxx_sw_get_mirror_monitor_port, + .max = AR8216_NUM_PORTS - 1 + }, + { + .type = SWITCH_TYPE_INT, + .name = "mirror_source_port", + .description = "Mirror source port", + .set = ar8xxx_sw_set_mirror_source_port, + .get = ar8xxx_sw_get_mirror_source_port, + .max = AR8216_NUM_PORTS - 1 + }, +}; +static struct switch_attr ar8327_sw_attr_globals[] = { + { + .type = SWITCH_TYPE_INT, + .name = "enable_vlan", + .description = "Enable VLAN mode", + .set = ar8xxx_sw_set_vlan, + .get = ar8xxx_sw_get_vlan, + .max = 1 + }, + { + .type = SWITCH_TYPE_NOVAL, + .name = "reset_mibs", + .description = "Reset all MIB counters", + .set = ar8xxx_sw_set_reset_mibs, + }, + { + .type = SWITCH_TYPE_INT, + .name = "enable_mirror_rx", + .description = "Enable mirroring of RX packets", + .set = ar8xxx_sw_set_mirror_rx_enable, + .get = ar8xxx_sw_get_mirror_rx_enable, + .max = 1 + }, + { + .type = SWITCH_TYPE_INT, + .name = "enable_mirror_tx", + .description = "Enable mirroring of TX packets", + .set = ar8xxx_sw_set_mirror_tx_enable, + .get = ar8xxx_sw_get_mirror_tx_enable, + .max = 1 + }, + { + .type = SWITCH_TYPE_INT, + .name = "mirror_monitor_port", + .description = "Mirror monitor port", + .set = ar8xxx_sw_set_mirror_monitor_port, + .get = ar8xxx_sw_get_mirror_monitor_port, + .max = AR8327_NUM_PORTS - 1 + }, + { + .type = SWITCH_TYPE_INT, + .name = "mirror_source_port", + .description = "Mirror source port", + .set = ar8xxx_sw_set_mirror_source_port, + .get = ar8xxx_sw_get_mirror_source_port, + .max = AR8327_NUM_PORTS - 1 + }, }; -static struct switch_attr ar8216_port[] = { +static struct switch_attr ar8xxx_sw_attr_port[] = { { .type = SWITCH_TYPE_NOVAL, .name = "reset_mib", .description = "Reset single port MIB counters", - .set = ar8216_sw_set_port_reset_mib, + .set = ar8xxx_sw_set_port_reset_mib, }, { .type = SWITCH_TYPE_STRING, .name = "mib", .description = "Get port's MIB counters", .set = NULL, - .get = ar8216_sw_get_port_mib, + .get = ar8xxx_sw_get_port_mib, }, }; -static struct switch_attr ar8216_vlan[] = { +static struct switch_attr ar8xxx_sw_attr_vlan[] = { { .type = SWITCH_TYPE_INT, .name = "vid", .description = "VLAN ID (0-4094)", - .set = ar8216_sw_set_vid, - .get = ar8216_sw_get_vid, + .set = ar8xxx_sw_set_vid, + .get = ar8xxx_sw_get_vid, .max = 4094, }, }; -static const struct switch_dev_ops ar8216_sw_ops = { +static const struct switch_dev_ops ar8xxx_sw_ops = { .attr_global = { - .attr = ar8216_globals, - .n_attr = ARRAY_SIZE(ar8216_globals), + .attr = ar8xxx_sw_attr_globals, + .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_globals), }, .attr_port = { - .attr = ar8216_port, - .n_attr = ARRAY_SIZE(ar8216_port), + .attr = ar8xxx_sw_attr_port, + .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port), }, .attr_vlan = { - .attr = ar8216_vlan, - .n_attr = ARRAY_SIZE(ar8216_vlan), + .attr = ar8xxx_sw_attr_vlan, + .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan), }, - .get_port_pvid = ar8216_sw_get_pvid, - .set_port_pvid = ar8216_sw_set_pvid, - .get_vlan_ports = ar8216_sw_get_ports, - .set_vlan_ports = ar8216_sw_set_ports, - .apply_config = ar8216_sw_hw_apply, - .reset_switch = ar8216_sw_reset_switch, - .get_port_link = ar8216_sw_get_port_link, + .get_port_pvid = ar8xxx_sw_get_pvid, + .set_port_pvid = ar8xxx_sw_set_pvid, + .get_vlan_ports = ar8xxx_sw_get_ports, + .set_vlan_ports = ar8xxx_sw_set_ports, + .apply_config = ar8xxx_sw_hw_apply, + .reset_switch = ar8xxx_sw_reset_switch, + .get_port_link = ar8xxx_sw_get_port_link, +}; + +static const struct switch_dev_ops ar8327_sw_ops = { + .attr_global = { + .attr = ar8327_sw_attr_globals, + .n_attr = ARRAY_SIZE(ar8327_sw_attr_globals), + }, + .attr_port = { + .attr = ar8xxx_sw_attr_port, + .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port), + }, + .attr_vlan = { + .attr = ar8xxx_sw_attr_vlan, + .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan), + }, + .get_port_pvid = ar8xxx_sw_get_pvid, + .set_port_pvid = ar8xxx_sw_set_pvid, + .get_vlan_ports = ar8xxx_sw_get_ports, + .set_vlan_ports = ar8xxx_sw_set_ports, + .apply_config = ar8xxx_sw_hw_apply, + .reset_switch = ar8xxx_sw_reset_switch, + .get_port_link = ar8xxx_sw_get_port_link, }; static int -ar8216_id_chip(struct ar8216_priv *priv) +ar8xxx_id_chip(struct ar8xxx_priv *priv) { u32 val; u16 id; int i; - val = ar8216_mii_read(priv, AR8216_REG_CTRL); + val = priv->read(priv, AR8216_REG_CTRL); if (val == ~0) return -ENODEV; @@ -1652,7 +2044,7 @@ ar8216_id_chip(struct ar8216_priv *priv) for (i = 0; i < AR8X16_PROBE_RETRIES; i++) { u16 t; - val = ar8216_mii_read(priv, AR8216_REG_CTRL); + val = priv->read(priv, AR8216_REG_CTRL); if (val == ~0) return -ENODEV; @@ -1679,11 +2071,8 @@ ar8216_id_chip(struct ar8216_priv *priv) priv->chip = &ar8327_chip; break; default: - printk(KERN_DEBUG - "ar8216: Unknown Atheros device [ver=%d, rev=%d, phy_id=%04x%04x]\n", - priv->chip_ver, priv->chip_rev, - mdiobus_read(priv->phy->bus, priv->phy->addr, 2), - mdiobus_read(priv->phy->bus, priv->phy->addr, 3)); + pr_err("ar8216: Unknown Atheros device [ver=%d, rev=%d]\n", + priv->chip_ver, priv->chip_rev); return -ENODEV; } @@ -1694,18 +2083,18 @@ ar8216_id_chip(struct ar8216_priv *priv) static void ar8xxx_mib_work_func(struct work_struct *work) { - struct ar8216_priv *priv; + struct ar8xxx_priv *priv; int err; - priv = container_of(work, struct ar8216_priv, mib_work.work); + priv = container_of(work, struct ar8xxx_priv, mib_work.work); mutex_lock(&priv->mib_lock); - err = ar8216_mib_capture(priv); + err = ar8xxx_mib_capture(priv); if (err) goto next_port; - ar8216_mib_fetch_port_stat(priv, priv->mib_next_port, false); + ar8xxx_mib_fetch_port_stat(priv, priv->mib_next_port, false); next_port: priv->mib_next_port++; @@ -1718,7 +2107,7 @@ next_port: } static int -ar8xxx_mib_init(struct ar8216_priv *priv) +ar8xxx_mib_init(struct ar8xxx_priv *priv) { unsigned int len; @@ -1734,14 +2123,11 @@ ar8xxx_mib_init(struct ar8216_priv *priv) if (!priv->mib_stats) return -ENOMEM; - mutex_init(&priv->mib_lock); - INIT_DELAYED_WORK(&priv->mib_work, ar8xxx_mib_work_func); - return 0; } static void -ar8xxx_mib_start(struct ar8216_priv *priv) +ar8xxx_mib_start(struct ar8xxx_priv *priv) { if (!ar8xxx_has_mib_counters(priv)) return; @@ -1751,96 +2137,70 @@ ar8xxx_mib_start(struct ar8216_priv *priv) } static void -ar8xxx_mib_cleanup(struct ar8216_priv *priv) +ar8xxx_mib_stop(struct ar8xxx_priv *priv) { if (!ar8xxx_has_mib_counters(priv)) return; cancel_delayed_work(&priv->mib_work); - kfree(priv->mib_stats); } -static int -ar8216_config_init(struct phy_device *pdev) +static struct ar8xxx_priv * +ar8xxx_create(void) { - struct ar8216_priv *priv = pdev->priv; - struct net_device *dev = pdev->attached_dev; - struct switch_dev *swdev; - int ret; - - if (!priv) { - priv = kzalloc(sizeof(struct ar8216_priv), GFP_KERNEL); - if (priv == NULL) - return -ENOMEM; - } - - priv->phy = pdev; + struct ar8xxx_priv *priv; - ret = ar8216_id_chip(priv); - if (ret) - goto err_free_priv; - - if (pdev->addr != 0) { - if (ar8xxx_has_gige(priv)) { - pdev->supported |= SUPPORTED_1000baseT_Full; - pdev->advertising |= ADVERTISED_1000baseT_Full; - } + priv = kzalloc(sizeof(struct ar8xxx_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; - if (chip_is_ar8316(priv)) { - /* check if we're attaching to the switch twice */ - pdev = pdev->bus->phy_map[0]; - if (!pdev) { - kfree(priv); - return 0; - } + mutex_init(&priv->reg_mutex); + mutex_init(&priv->mib_lock); + INIT_DELAYED_WORK(&priv->mib_work, ar8xxx_mib_work_func); - /* switch device has not been initialized, reuse priv */ - if (!pdev->priv) { - priv->port4_phy = true; - pdev->priv = priv; - return 0; - } + return priv; +} - kfree(priv); +static void +ar8xxx_free(struct ar8xxx_priv *priv) +{ + kfree(priv->mib_stats); + kfree(priv); +} - /* switch device has been initialized, reinit */ - priv = pdev->priv; - priv->dev.ports = (AR8216_NUM_PORTS - 1); - priv->initialized = false; - priv->port4_phy = true; - ar8316_hw_init(priv); - return 0; - } +static struct ar8xxx_priv * +ar8xxx_create_mii(struct mii_bus *bus) +{ + struct ar8xxx_priv *priv; - kfree(priv); - return 0; + priv = ar8xxx_create(); + if (priv) { + priv->mii_bus = bus; + priv->read = ar8xxx_mii_read; + priv->write = ar8xxx_mii_write; } - if (ar8xxx_has_gige(priv)) - pdev->supported = SUPPORTED_1000baseT_Full; - else - pdev->supported = SUPPORTED_100baseT_Full; - pdev->advertising = pdev->supported; + return priv; +} - mutex_init(&priv->reg_mutex); - priv->read = ar8216_mii_read; - priv->write = ar8216_mii_write; +static int +ar8xxx_probe_switch(struct ar8xxx_priv *priv) +{ + struct switch_dev *swdev; + int ret; - pdev->priv = priv; + ret = ar8xxx_id_chip(priv); + if (ret) + return ret; swdev = &priv->dev; swdev->cpu_port = AR8216_PORT_CPU; - swdev->ops = &ar8216_sw_ops; - swdev->ports = AR8216_NUM_PORTS; + swdev->ops = &ar8xxx_sw_ops; if (chip_is_ar8316(priv)) { swdev->name = "Atheros AR8316"; swdev->vlans = AR8X16_MAX_VLANS; - - if (priv->port4_phy) { - /* port 5 connected to the other mac, therefore unusable */ - swdev->ports = (AR8216_NUM_PORTS - 1); - } + swdev->ports = AR8216_NUM_PORTS; } else if (chip_is_ar8236(priv)) { swdev->name = "Atheros AR8236"; swdev->vlans = AR8216_NUM_VLANS; @@ -1849,58 +2209,89 @@ ar8216_config_init(struct phy_device *pdev) swdev->name = "Atheros AR8327"; swdev->vlans = AR8X16_MAX_VLANS; swdev->ports = AR8327_NUM_PORTS; + swdev->ops = &ar8327_sw_ops; } else { swdev->name = "Atheros AR8216"; swdev->vlans = AR8216_NUM_VLANS; + swdev->ports = AR8216_NUM_PORTS; } ret = ar8xxx_mib_init(priv); if (ret) - goto err_free_priv; + return ret; - ret = register_switch(&priv->dev, pdev->attached_dev); - if (ret) - goto err_cleanup_mib; + return 0; +} - printk(KERN_INFO "%s: %s switch driver attached.\n", - pdev->attached_dev->name, swdev->name); +static int +ar8xxx_start(struct ar8xxx_priv *priv) +{ + int ret; priv->init = true; ret = priv->chip->hw_init(priv); if (ret) - goto err_cleanup_mib; + return ret; - ret = ar8216_sw_reset_switch(&priv->dev); + ret = ar8xxx_sw_reset_switch(&priv->dev); if (ret) - goto err_cleanup_mib; + return ret; - dev->phy_ptr = priv; + priv->init = false; + + ar8xxx_mib_start(priv); + + return 0; +} + +static int +ar8xxx_phy_config_init(struct phy_device *phydev) +{ + struct ar8xxx_priv *priv = phydev->priv; + struct net_device *dev = phydev->attached_dev; + int ret; + + if (WARN_ON(!priv)) + return -ENODEV; + + if (chip_is_ar8327(priv)) + return 0; + + priv->phy = phydev; + + if (phydev->addr != 0) { + if (chip_is_ar8316(priv)) { + /* switch device has been initialized, reinit */ + priv->dev.ports = (AR8216_NUM_PORTS - 1); + priv->initialized = false; + priv->port4_phy = true; + ar8316_hw_init(priv); + return 0; + } + + return 0; + } + + ret = ar8xxx_start(priv); + if (ret) + return ret; /* VID fixup only needed on ar8216 */ - if (chip_is_ar8216(priv) && pdev->addr == 0) { + if (chip_is_ar8216(priv)) { + dev->phy_ptr = priv; dev->priv_flags |= IFF_NO_IP_ALIGN; dev->eth_mangle_rx = ar8216_mangle_rx; dev->eth_mangle_tx = ar8216_mangle_tx; } - priv->init = false; - - ar8xxx_mib_start(priv); - return 0; - -err_cleanup_mib: - ar8xxx_mib_cleanup(priv); -err_free_priv: - kfree(priv); - return ret; } static int -ar8216_read_status(struct phy_device *phydev) +ar8xxx_phy_read_status(struct phy_device *phydev) { - struct ar8216_priv *priv = phydev->priv; + struct ar8xxx_priv *priv = phydev->priv; struct switch_port_link link; int ret; @@ -1940,7 +2331,7 @@ ar8216_read_status(struct phy_device *phydev) } static int -ar8216_config_aneg(struct phy_device *phydev) +ar8xxx_phy_config_aneg(struct phy_device *phydev) { if (phydev->addr == 0) return 0; @@ -1948,70 +2339,192 @@ ar8216_config_aneg(struct phy_device *phydev) return genphy_config_aneg(phydev); } +static const u32 ar8xxx_phy_ids[] = { + 0x004dd033, + 0x004dd034, + 0x004dd041, + 0x004dd042, +}; + +static bool +ar8xxx_phy_match(u32 phy_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ar8xxx_phy_ids); i++) + if (phy_id == ar8xxx_phy_ids[i]) + return true; + + return false; +} + +static bool +ar8xxx_is_possible(struct mii_bus *bus) +{ + unsigned i; + + for (i = 0; i < 4; i++) { + u32 phy_id; + + phy_id = mdiobus_read(bus, i, MII_PHYSID1) << 16; + phy_id |= mdiobus_read(bus, i, MII_PHYSID2); + if (!ar8xxx_phy_match(phy_id)) { + pr_debug("ar8xxx: unknown PHY at %s:%02x id:%08x\n", + dev_name(&bus->dev), i, phy_id); + return false; + } + } + + return true; +} + static int -ar8216_probe(struct phy_device *pdev) +ar8xxx_phy_probe(struct phy_device *phydev) { - struct ar8216_priv *priv; + struct ar8xxx_priv *priv; + struct switch_dev *swdev; int ret; - priv = kzalloc(sizeof(struct ar8216_priv), GFP_KERNEL); - if (priv == NULL) - return -ENOMEM; + /* skip PHYs at unused adresses */ + if (phydev->addr != 0 && phydev->addr != 4) + return -ENODEV; - priv->phy = pdev; + if (!ar8xxx_is_possible(phydev->bus)) + return -ENODEV; - ret = ar8216_id_chip(priv); - kfree(priv); + mutex_lock(&ar8xxx_dev_list_lock); + list_for_each_entry(priv, &ar8xxx_dev_list, list) + if (priv->mii_bus == phydev->bus) + goto found; + + priv = ar8xxx_create_mii(phydev->bus); + if (priv == NULL) { + ret = -ENOMEM; + goto unlock; + } + ret = ar8xxx_probe_switch(priv); + if (ret) + goto free_priv; + + swdev = &priv->dev; + swdev->alias = dev_name(&priv->mii_bus->dev); + ret = register_switch(swdev, NULL); + if (ret) + goto free_priv; + + pr_info("%s: %s switch registered on %s\n", + swdev->devname, swdev->name, dev_name(&priv->mii_bus->dev)); + +found: + priv->use_count++; + + if (phydev->addr == 0) { + if (ar8xxx_has_gige(priv)) { + phydev->supported = SUPPORTED_1000baseT_Full; + phydev->advertising = ADVERTISED_1000baseT_Full; + } else { + phydev->supported = SUPPORTED_100baseT_Full; + phydev->advertising = ADVERTISED_100baseT_Full; + } + + if (chip_is_ar8327(priv)) { + priv->phy = phydev; + + ret = ar8xxx_start(priv); + if (ret) + goto err_unregister_switch; + } + } else { + if (ar8xxx_has_gige(priv)) { + phydev->supported |= SUPPORTED_1000baseT_Full; + phydev->advertising |= ADVERTISED_1000baseT_Full; + } + } + + phydev->priv = priv; + + list_add(&priv->list, &ar8xxx_dev_list); + + mutex_unlock(&ar8xxx_dev_list_lock); + + return 0; + +err_unregister_switch: + if (--priv->use_count) + goto unlock; + + unregister_switch(&priv->dev); + +free_priv: + ar8xxx_free(priv); +unlock: + mutex_unlock(&ar8xxx_dev_list_lock); return ret; } static void -ar8216_remove(struct phy_device *pdev) +ar8xxx_phy_detach(struct phy_device *phydev) { - struct ar8216_priv *priv = pdev->priv; - struct net_device *dev = pdev->attached_dev; + struct net_device *dev = phydev->attached_dev; - if (!priv) + if (!dev) return; + dev->phy_ptr = NULL; dev->priv_flags &= ~IFF_NO_IP_ALIGN; dev->eth_mangle_rx = NULL; dev->eth_mangle_tx = NULL; +} + +static void +ar8xxx_phy_remove(struct phy_device *phydev) +{ + struct ar8xxx_priv *priv = phydev->priv; - if (pdev->addr == 0) - unregister_switch(&priv->dev); + if (WARN_ON(!priv)) + return; - ar8xxx_mib_cleanup(priv); - kfree(priv); + phydev->priv = NULL; + if (--priv->use_count > 0) + return; + + mutex_lock(&ar8xxx_dev_list_lock); + list_del(&priv->list); + mutex_unlock(&ar8xxx_dev_list_lock); + + unregister_switch(&priv->dev); + ar8xxx_mib_stop(priv); + ar8xxx_free(priv); } -static struct phy_driver ar8216_driver = { +static struct phy_driver ar8xxx_phy_driver = { .phy_id = 0x004d0000, .name = "Atheros AR8216/AR8236/AR8316", .phy_id_mask = 0xffff0000, .features = PHY_BASIC_FEATURES, - .probe = ar8216_probe, - .remove = ar8216_remove, - .config_init = &ar8216_config_init, - .config_aneg = &ar8216_config_aneg, - .read_status = &ar8216_read_status, + .probe = ar8xxx_phy_probe, + .remove = ar8xxx_phy_remove, + .detach = ar8xxx_phy_detach, + .config_init = ar8xxx_phy_config_init, + .config_aneg = ar8xxx_phy_config_aneg, + .read_status = ar8xxx_phy_read_status, .driver = { .owner = THIS_MODULE }, }; int __init -ar8216_init(void) +ar8xxx_init(void) { - return phy_driver_register(&ar8216_driver); + return phy_driver_register(&ar8xxx_phy_driver); } void __exit -ar8216_exit(void) +ar8xxx_exit(void) { - phy_driver_unregister(&ar8216_driver); + phy_driver_unregister(&ar8xxx_phy_driver); } -module_init(ar8216_init); -module_exit(ar8216_exit); +module_init(ar8xxx_init); +module_exit(ar8xxx_exit); MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h index d464101e8..086d58fba 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.h +++ b/target/linux/generic/files/drivers/net/phy/ar8216.h @@ -105,6 +105,10 @@ #define AR8216_MIB_FUNC_CAPTURE 0x3 #define AR8236_MIB_EN BIT(30) +#define AR8216_REG_GLOBAL_CPUPORT 0x0078 +#define AR8216_GLOBAL_CPUPORT_MIRROR_PORT BITS(4, 4) +#define AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S 4 + #define AR8216_PORT_OFFSET(_i) (0x0100 * (_i + 1)) #define AR8216_REG_PORT_STATUS(_i) (AR8216_PORT_OFFSET(_i) + 0x0000) #define AR8216_PORT_STATUS_SPEED BITS(0,2) @@ -255,6 +259,36 @@ #define AR8236_STATS_TXDEFER 0xa0 #define AR8236_STATS_TXLATECOL 0xa4 +#define AR8316_REG_POSTRIP 0x0008 +#define AR8316_POSTRIP_MAC0_GMII_EN BIT(0) +#define AR8316_POSTRIP_MAC0_RGMII_EN BIT(1) +#define AR8316_POSTRIP_PHY4_GMII_EN BIT(2) +#define AR8316_POSTRIP_PHY4_RGMII_EN BIT(3) +#define AR8316_POSTRIP_MAC0_MAC_MODE BIT(4) +#define AR8316_POSTRIP_RTL_MODE BIT(5) +#define AR8316_POSTRIP_RGMII_RXCLK_DELAY_EN BIT(6) +#define AR8316_POSTRIP_RGMII_TXCLK_DELAY_EN BIT(7) +#define AR8316_POSTRIP_SERDES_EN BIT(8) +#define AR8316_POSTRIP_SEL_ANA_RST BIT(9) +#define AR8316_POSTRIP_GATE_25M_EN BIT(10) +#define AR8316_POSTRIP_SEL_CLK25M BIT(11) +#define AR8316_POSTRIP_HIB_PULSE_HW BIT(12) +#define AR8316_POSTRIP_DBG_MODE_I BIT(13) +#define AR8316_POSTRIP_MAC5_MAC_MODE BIT(14) +#define AR8316_POSTRIP_MAC5_PHY_MODE BIT(15) +#define AR8316_POSTRIP_POWER_DOWN_HW BIT(16) +#define AR8316_POSTRIP_LPW_STATE_EN BIT(17) +#define AR8316_POSTRIP_MAN_EN BIT(18) +#define AR8316_POSTRIP_PHY_PLL_ON BIT(19) +#define AR8316_POSTRIP_LPW_EXIT BIT(20) +#define AR8316_POSTRIP_TXDELAY_S0 BIT(21) +#define AR8316_POSTRIP_TXDELAY_S1 BIT(22) +#define AR8316_POSTRIP_RXDELAY_S0 BIT(23) +#define AR8316_POSTRIP_LED_OPEN_EN BIT(24) +#define AR8316_POSTRIP_SPI_EN BIT(25) +#define AR8316_POSTRIP_RXDELAY_S1 BIT(26) +#define AR8316_POSTRIP_POWER_ON_SEL BIT(31) + #define AR8327_NUM_PORTS 7 #define AR8327_NUM_PHYS 5 #define AR8327_PORTS_ALL 0x7f @@ -281,6 +315,7 @@ #define AR8327_PAD_PHYX_GMII_EN BIT(16) #define AR8327_PAD_PHYX_RGMII_EN BIT(17) #define AR8327_PAD_PHYX_MII_EN BIT(18) +#define AR8327_PAD_SGMII_DELAY_EN BIT(19) #define AR8327_PAD_RGMII_RXCLK_DELAY_SEL BITS(20, 2) #define AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S 20 #define AR8327_PAD_RGMII_TXCLK_DELAY_SEL BITS(22, 2) @@ -402,9 +437,13 @@ #define AR8327_PORT_LOOKUP_STATE BITS(16, 3) #define AR8327_PORT_LOOKUP_STATE_S 16 #define AR8327_PORT_LOOKUP_LEARN BIT(20) +#define AR8327_PORT_LOOKUP_ING_MIRROR_EN BIT(25) #define AR8327_REG_PORT_PRIO(_i) (0x664 + (_i) * 0xc) +#define AR8327_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8) +#define AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16) + #define AR8327_REG_PORT_STATS_BASE(_i) (0x1000 + (_i) * 0x100) /* port speed */ diff --git a/target/linux/generic/files/drivers/net/phy/b53/Kconfig b/target/linux/generic/files/drivers/net/phy/b53/Kconfig new file mode 100644 index 000000000..3b514fb62 --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/b53/Kconfig @@ -0,0 +1,30 @@ +menuconfig B53 + tristate "Broadcom bcm53xx managed switch support" + depends on SWCONFIG + help + This driver adds support for Broadcom managed switch chips. It supports + BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX + integrated switches. + +config B53_SPI_DRIVER + tristate "B53 SPI connected switch driver" + depends on B53 && SPI + help + Select to enable support for registering switches configured through SPI. + +config B53_PHY_DRIVER + tristate "B53 MDIO connected switch driver" + depends on B53 + select B53_PHY_FIXUP + help + Select to enable support for registering switches configured through MDIO. + +config B53_MMAP_DRIVER + tristate "B53 MMAP connected switch driver" + depends on B53 + help + Select to enable support for memory-mapped switches like the BCM63XX + integrated switches. + +config B53_PHY_FIXUP + bool diff --git a/target/linux/generic/files/drivers/net/phy/b53/Makefile b/target/linux/generic/files/drivers/net/phy/b53/Makefile new file mode 100644 index 000000000..146196e08 --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/b53/Makefile @@ -0,0 +1,9 @@ +obj-$(CONFIG_B53) += b53_common.o + +obj-$(CONFIG_B53_PHY_FIXUP) += b53_phy_fixup.o + +obj-$(CONFIG_B53_MMAP_DRIVER) += b53_mmap.o +obj-$(CONFIG_B53_PHY_DRIVER) += b53_mdio.o +obj-$(CONFIG_B53_SPI_DRIVER) += b53_spi.o + +ccflags-y += -Werror diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c new file mode 100644 index 000000000..c74bf7fbc --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c @@ -0,0 +1,1292 @@ +/* + * B53 switch driver main logic + * + * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/switch.h> +#include <linux/platform_data/b53.h> + +#include "b53_regs.h" +#include "b53_priv.h" + +/* buffer size needed for displaying all MIBs with max'd values */ +#define B53_BUF_SIZE 1188 + +struct b53_mib_desc { + u8 size; + u8 offset; + const char *name; +}; + + +/* BCM5365 MIB counters */ +static const struct b53_mib_desc b53_mibs_65[] = { + { 8, 0x00, "TxOctets" }, + { 4, 0x08, "TxDropPkts" }, + { 4, 0x10, "TxBroadcastPkts" }, + { 4, 0x14, "TxMulticastPkts" }, + { 4, 0x18, "TxUnicastPkts" }, + { 4, 0x1c, "TxCollisions" }, + { 4, 0x20, "TxSingleCollision" }, + { 4, 0x24, "TxMultipleCollision" }, + { 4, 0x28, "TxDeferredTransmit" }, + { 4, 0x2c, "TxLateCollision" }, + { 4, 0x30, "TxExcessiveCollision" }, + { 4, 0x38, "TxPausePkts" }, + { 8, 0x44, "RxOctets" }, + { 4, 0x4c, "RxUndersizePkts" }, + { 4, 0x50, "RxPausePkts" }, + { 4, 0x54, "Pkts64Octets" }, + { 4, 0x58, "Pkts65to127Octets" }, + { 4, 0x5c, "Pkts128to255Octets" }, + { 4, 0x60, "Pkts256to511Octets" }, + { 4, 0x64, "Pkts512to1023Octets" }, + { 4, 0x68, "Pkts1024to1522Octets" }, + { 4, 0x6c, "RxOversizePkts" }, + { 4, 0x70, "RxJabbers" }, + { 4, 0x74, "RxAlignmentErrors" }, + { 4, 0x78, "RxFCSErrors" }, + { 8, 0x7c, "RxGoodOctets" }, + { 4, 0x84, "RxDropPkts" }, + { 4, 0x88, "RxUnicastPkts" }, + { 4, 0x8c, "RxMulticastPkts" }, + { 4, 0x90, "RxBroadcastPkts" }, + { 4, 0x94, "RxSAChanges" }, + { 4, 0x98, "RxFragments" }, + { }, +}; + +/* BCM63xx MIB counters */ +static const struct b53_mib_desc b53_mibs_63xx[] = { + { 8, 0x00, "TxOctets" }, + { 4, 0x08, "TxDropPkts" }, + { 4, 0x0c, "TxQoSPkts" }, + { 4, 0x10, "TxBroadcastPkts" }, + { 4, 0x14, "TxMulticastPkts" }, + { 4, 0x18, "TxUnicastPkts" }, + { 4, 0x1c, "TxCollisions" }, + { 4, 0x20, "TxSingleCollision" }, + { 4, 0x24, "TxMultipleCollision" }, + { 4, 0x28, "TxDeferredTransmit" }, + { 4, 0x2c, "TxLateCollision" }, + { 4, 0x30, "TxExcessiveCollision" }, + { 4, 0x38, "TxPausePkts" }, + { 8, 0x3c, "TxQoSOctets" }, + { 8, 0x44, "RxOctets" }, + { 4, 0x4c, "RxUndersizePkts" }, + { 4, 0x50, "RxPausePkts" }, + { 4, 0x54, "Pkts64Octets" }, + { 4, 0x58, "Pkts65to127Octets" }, + { 4, 0x5c, "Pkts128to255Octets" }, + { 4, 0x60, "Pkts256to511Octets" }, + { 4, 0x64, "Pkts512to1023Octets" }, + { 4, 0x68, "Pkts1024to1522Octets" }, + { 4, 0x6c, "RxOversizePkts" }, + { 4, 0x70, "RxJabbers" }, + { 4, 0x74, "RxAlignmentErrors" }, + { 4, 0x78, "RxFCSErrors" }, + { 8, 0x7c, "RxGoodOctets" }, + { 4, 0x84, "RxDropPkts" }, + { 4, 0x88, "RxUnicastPkts" }, + { 4, 0x8c, "RxMulticastPkts" }, + { 4, 0x90, "RxBroadcastPkts" }, + { 4, 0x94, "RxSAChanges" }, + { 4, 0x98, "RxFragments" }, + { 4, 0xa0, "RxSymbolErrors" }, + { 4, 0xa4, "RxQoSPkts" }, + { 8, 0xa8, "RxQoSOctets" }, + { 4, 0xb0, "Pkts1523to2047Octets" }, + { 4, 0xb4, "Pkts2048to4095Octets" }, + { 4, 0xb8, "Pkts4096to8191Octets" }, + { 4, 0xbc, "Pkts8192to9728Octets" }, + { 4, 0xc0, "RxDiscarded" }, + { } +}; + +/* MIB counters */ +static const struct b53_mib_desc b53_mibs[] = { + { 8, 0x00, "TxOctets" }, + { 4, 0x08, "TxDropPkts" }, + { 4, 0x10, "TxBroadcastPkts" }, + { 4, 0x14, "TxMulticastPkts" }, + { 4, 0x18, "TxUnicastPkts" }, + { 4, 0x1c, "TxCollisions" }, + { 4, 0x20, "TxSingleCollision" }, + { 4, 0x24, "TxMultipleCollision" }, + { 4, 0x28, "TxDeferredTransmit" }, + { 4, 0x2c, "TxLateCollision" }, + { 4, 0x30, "TxExcessiveCollision" }, + { 4, 0x38, "TxPausePkts" }, + { 8, 0x50, "RxOctets" }, + { 4, 0x58, "RxUndersizePkts" }, + { 4, 0x5c, "RxPausePkts" }, + { 4, 0x60, "Pkts64Octets" }, + { 4, 0x64, "Pkts65to127Octets" }, + { 4, 0x68, "Pkts128to255Octets" }, + { 4, 0x6c, "Pkts256to511Octets" }, + { 4, 0x70, "Pkts512to1023Octets" }, + { 4, 0x74, "Pkts1024to1522Octets" }, + { 4, 0x78, "RxOversizePkts" }, + { 4, 0x7c, "RxJabbers" }, + { 4, 0x80, "RxAlignmentErrors" }, + { 4, 0x84, "RxFCSErrors" }, + { 8, 0x88, "RxGoodOctets" }, + { 4, 0x90, "RxDropPkts" }, + { 4, 0x94, "RxUnicastPkts" }, + { 4, 0x98, "RxMulticastPkts" }, + { 4, 0x9c, "RxBroadcastPkts" }, + { 4, 0xa0, "RxSAChanges" }, + { 4, 0xa4, "RxFragments" }, + { 4, 0xa8, "RxJumboPkts" }, + { 4, 0xac, "RxSymbolErrors" }, + { 4, 0xc0, "RxDiscarded" }, + { } +}; + +static int b53_do_vlan_op(struct b53_device *dev, u8 op) +{ + unsigned int i; + + b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op); + + for (i = 0; i < 10; i++) { + u8 vta; + + b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta); + if (!(vta & VTA_START_CMD)) + return 0; + + usleep_range(100, 200); + } + + return -EIO; +} + +static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, u16 members, + u16 untag) +{ + if (is5325(dev)) { + u32 entry = 0; + + if (members) + entry = (untag << VA_UNTAG_S) | members | VA_VALID_25; + + b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry); + b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid | + VTA_RW_STATE_WR | VTA_RW_OP_EN); + } else if (is5365(dev)) { + u16 entry = 0; + + if (members) + entry = (untag << VA_UNTAG_S) | members | VA_VALID_65; + + b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry); + b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid | + VTA_RW_STATE_WR | VTA_RW_OP_EN); + } else { + b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid); + b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2], + (untag << VTE_UNTAG_S) | members); + + b53_do_vlan_op(dev, VTA_CMD_WRITE); + } +} + +void b53_set_forwarding(struct b53_device *dev, int enable) +{ + u8 mgmt; + + b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); + + if (enable) + mgmt |= SM_SW_FWD_EN; + else + mgmt &= ~SM_SW_FWD_EN; + + b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); +} + +static void b53_enable_vlan(struct b53_device *dev, int enable) +{ + u8 mgmt, vc0, vc1, vc4 = 0, vc5; + + b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); + b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0); + b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1); + + if (is5325(dev) || is5365(dev)) { + b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4); + b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5); + } else if (is63xx(dev)) { + b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4); + b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5); + } else { + b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4); + b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5); + } + + mgmt &= ~SM_SW_FWD_MODE; + + if (enable) { + vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID; + vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN; + vc4 &= ~VC4_ING_VID_CHECK_MASK; + vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S; + vc5 |= VC5_DROP_VTABLE_MISS; + + if (is5325(dev)) + vc0 &= ~VC0_RESERVED_1; + + if (is5325(dev) || is5365(dev)) + vc1 |= VC1_RX_MCST_TAG_EN; + + if (!is5325(dev) && !is5365(dev)) { + if (dev->allow_vid_4095) + vc5 |= VC5_VID_FFF_EN; + else + vc5 &= ~VC5_VID_FFF_EN; + } + } else { + vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID); + vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN); + vc4 &= ~VC4_ING_VID_CHECK_MASK; + vc5 &= ~VC5_DROP_VTABLE_MISS; + + if (is5325(dev) || is5365(dev)) + vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S; + else + vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S; + + if (is5325(dev) || is5365(dev)) + vc1 &= ~VC1_RX_MCST_TAG_EN; + + if (!is5325(dev) && !is5365(dev)) + vc5 &= ~VC5_VID_FFF_EN; + } + + b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0); + b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1); + + if (is5325(dev) || is5365(dev)) { + /* enable the high 8 bit vid check on 5325 */ + if (is5325(dev) && enable) + b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, + VC3_HIGH_8BIT_EN); + else + b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0); + + b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4); + b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5); + } else if (is63xx(dev)) { + b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0); + b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4); + b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5); + } else { + b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0); + b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4); + b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5); + } + + b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); +} + +static int b53_set_jumbo(struct b53_device *dev, int enable, int allow_10_100) +{ + u32 port_mask = 0; + u16 max_size = JMS_MIN_SIZE; + + if (is5325(dev) || is5365(dev)) + return -EINVAL; + + if (enable) { + port_mask = dev->enabled_ports; + max_size = JMS_MAX_SIZE; + if (allow_10_100) + port_mask |= JPM_10_100_JUMBO_EN; + } + + b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask); + return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size); +} + +static int b53_flush_arl(struct b53_device *dev) +{ + unsigned int i; + + b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, + FAST_AGE_DONE | FAST_AGE_DYNAMIC | FAST_AGE_STATIC); + + for (i = 0; i < 10; i++) { + u8 fast_age_ctrl; + + b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, + &fast_age_ctrl); + + if (!(fast_age_ctrl & FAST_AGE_DONE)) + return 0; + + mdelay(1); + } + + pr_warn("time out while flushing ARL\n"); + + return -EINVAL; +} + +static void b53_enable_ports(struct b53_device *dev) +{ + unsigned i; + + b53_for_each_port(dev, i) { + u8 port_ctrl; + u16 pvlan_mask; + + /* + * prevent leaking packets between wan and lan in unmanaged + * mode through port vlans. + */ + if (dev->enable_vlan || is_cpu_port(dev, i)) + pvlan_mask = 0x1ff; + else if (is531x5(dev)) + /* BCM53115 may use a different port as cpu port */ + pvlan_mask = BIT(dev->sw_dev.cpu_port); + else + pvlan_mask = BIT(B53_CPU_PORT); + + /* BCM5325 CPU port is at 8 */ + if ((is5325(dev) || is5365(dev)) && i == B53_CPU_PORT_25) + i = B53_CPU_PORT; + + if (dev->chip_id == BCM5398_DEVICE_ID && (i == 6 || i == 7)) + /* disable unused ports 6 & 7 */ + port_ctrl = PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE; + else if (i == B53_CPU_PORT) + port_ctrl = PORT_CTRL_RX_BCST_EN | + PORT_CTRL_RX_MCST_EN | + PORT_CTRL_RX_UCST_EN; + else + port_ctrl = 0; + + b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), + pvlan_mask); + + /* port state is handled by bcm63xx_enet driver */ + if (!is63xx(dev)) + b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(i), + port_ctrl); + } +} + +static void b53_enable_mib(struct b53_device *dev) +{ + u8 gc; + + b53_read8(dev, B53_CTRL_PAGE, B53_GLOBAL_CONFIG, &gc); + + gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN); + + b53_write8(dev, B53_CTRL_PAGE, B53_GLOBAL_CONFIG, gc); +} + +static int b53_apply(struct b53_device *dev) +{ + int i; + + /* clear all vlan entries */ + if (is5325(dev) || is5365(dev)) { + for (i = 1; i < dev->sw_dev.vlans; i++) + b53_set_vlan_entry(dev, i, 0, 0); + } else { + b53_do_vlan_op(dev, VTA_CMD_CLEAR); + } + + b53_enable_vlan(dev, dev->enable_vlan); + + /* fill VLAN table */ + if (dev->enable_vlan) { + for (i = 0; i < dev->sw_dev.vlans; i++) { + struct b53_vlan *vlan = &dev->vlans[i]; + + if (!vlan->members) + continue; + + b53_set_vlan_entry(dev, i, vlan->members, vlan->untag); + } + + b53_for_each_port(dev, i) + b53_write16(dev, B53_VLAN_PAGE, + B53_VLAN_PORT_DEF_TAG(i), + dev->ports[i].pvid); + } else { + b53_for_each_port(dev, i) + b53_write16(dev, B53_VLAN_PAGE, + B53_VLAN_PORT_DEF_TAG(i), 1); + + } + + b53_enable_ports(dev); + + if (!is5325(dev) && !is5365(dev)) + b53_set_jumbo(dev, dev->enable_jumbo, 1); + + return 0; +} + +static int b53_switch_reset(struct b53_device *dev) +{ + u8 mgmt; + + b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); + + if (!(mgmt & SM_SW_FWD_EN)) { + mgmt &= ~SM_SW_FWD_MODE; + mgmt |= SM_SW_FWD_EN; + + b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); + b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); + + if (!(mgmt & SM_SW_FWD_EN)) { + pr_err("Failed to enable switch!\n"); + return -EINVAL; + } + } + + /* enable all ports */ + b53_enable_ports(dev); + + /* configure MII port if necessary */ + if (is5325(dev)) { + u8 mii_port_override; + + b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, + &mii_port_override); + /* reverse mii needs to be enabled */ + if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) { + b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, + mii_port_override | PORT_OVERRIDE_RV_MII_25); + b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, + &mii_port_override); + + if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) { + pr_err("Failed to enable reverse MII mode\n"); + return -EINVAL; + } + } + } else if (is531x5(dev) && dev->sw_dev.cpu_port == B53_CPU_PORT) { + u8 mii_port_override; + + b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, + &mii_port_override); + b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, + mii_port_override | PORT_OVERRIDE_EN | + PORT_OVERRIDE_LINK); + } + + b53_enable_mib(dev); + + return b53_flush_arl(dev); +} + +/* + * Swconfig glue functions + */ + +static int b53_global_get_vlan_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct b53_device *priv = sw_to_b53(dev); + + val->value.i = priv->enable_vlan; + + return 0; +} + +static int b53_global_set_vlan_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct b53_device *priv = sw_to_b53(dev); + + priv->enable_vlan = val->value.i; + + return 0; +} + +static int b53_global_get_jumbo_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct b53_device *priv = sw_to_b53(dev); + + val->value.i = priv->enable_jumbo; + + return 0; +} + +static int b53_global_set_jumbo_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct b53_device *priv = sw_to_b53(dev); + + priv->enable_jumbo = val->value.i; + + return 0; +} + +static int b53_global_get_4095_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct b53_device *priv = sw_to_b53(dev); + + val->value.i = priv->allow_vid_4095; + + return 0; +} + +static int b53_global_set_4095_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct b53_device *priv = sw_to_b53(dev); + + priv->allow_vid_4095 = val->value.i; + + return 0; +} + +static int b53_global_get_ports(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct b53_device *priv = sw_to_b53(dev); + + val->len = snprintf(priv->buf, B53_BUF_SIZE, "0x%04x", + priv->enabled_ports); + val->value.s = priv->buf; + + return 0; +} + +static int b53_port_get_pvid(struct switch_dev *dev, int port, int *val) +{ + struct b53_device *priv = sw_to_b53(dev); + + *val = priv->ports[port].pvid; + + return 0; +} + +static int b53_port_set_pvid(struct switch_dev *dev, int port, int val) +{ + struct b53_device *priv = sw_to_b53(dev); + + if (val > 15 && is5325(priv)) + return -EINVAL; + if (val == 4095 && !priv->allow_vid_4095) + return -EINVAL; + + priv->ports[port].pvid = val; + + return 0; +} + +static int b53_vlan_get_ports(struct switch_dev *dev, struct switch_val *val) +{ + struct b53_device *priv = sw_to_b53(dev); + struct switch_port *port = &val->value.ports[0]; + struct b53_vlan *vlan = &priv->vlans[val->port_vlan]; + int i; + + val->len = 0; + + if (!vlan->members) + return 0; + + for (i = 0; i < dev->ports; i++) { + if (!(vlan->members & BIT(i))) + continue; + + + if (!(vlan->untag & BIT(i))) + port->flags = BIT(SWITCH_PORT_FLAG_TAGGED); + else + port->flags = 0; + + port->id = i; + val->len++; + port++; + } + + return 0; +} + +static int b53_vlan_set_ports(struct switch_dev *dev, struct switch_val *val) +{ + struct b53_device *priv = sw_to_b53(dev); + struct switch_port *port; + struct b53_vlan *vlan = &priv->vlans[val->port_vlan]; + int i; + + /* only BCM5325 and BCM5365 supports VID 0 */ + if (val->port_vlan == 0 && !is5325(priv) && !is5365(priv)) + return -EINVAL; + + /* VLAN 4095 needs special handling */ + if (val->port_vlan == 4095 && !priv->allow_vid_4095) + return -EINVAL; + + port = &val->value.ports[0]; + vlan->members = 0; + vlan->untag = 0; + for (i = 0; i < val->len; i++, port++) { + vlan->members |= BIT(port->id); + + if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) { + vlan->untag |= BIT(port->id); + priv->ports[port->id].pvid = val->port_vlan; + }; + } + + /* ignore disabled ports */ + vlan->members &= priv->enabled_ports; + vlan->untag &= priv->enabled_ports; + + return 0; +} + +static int b53_port_get_link(struct switch_dev *dev, int port, + struct switch_port_link *link) +{ + struct b53_device *priv = sw_to_b53(dev); + + if (is_cpu_port(priv, port)) { + link->link = 1; + link->duplex = 1; + link->speed = is5325(priv) || is5365(priv) ? + SWITCH_PORT_SPEED_100 : SWITCH_PORT_SPEED_1000; + link->aneg = 0; + } else if (priv->enabled_ports & BIT(port)) { + u32 speed; + u16 lnk, duplex; + + b53_read16(priv, B53_STAT_PAGE, B53_LINK_STAT, &lnk); + b53_read16(priv, B53_STAT_PAGE, priv->duplex_reg, &duplex); + + lnk = (lnk >> port) & 1; + duplex = (duplex >> port) & 1; + + if (is5325(priv) || is5365(priv)) { + u16 tmp; + + b53_read16(priv, B53_STAT_PAGE, B53_SPEED_STAT, &tmp); + speed = SPEED_PORT_FE(tmp, port); + } else { + b53_read32(priv, B53_STAT_PAGE, B53_SPEED_STAT, &speed); + speed = SPEED_PORT_GE(speed, port); + } + + link->link = lnk; + if (lnk) { + link->duplex = duplex; + switch (speed) { + case SPEED_STAT_10M: + link->speed = SWITCH_PORT_SPEED_10; + break; + case SPEED_STAT_100M: + link->speed = SWITCH_PORT_SPEED_100; + break; + case SPEED_STAT_1000M: + link->speed = SWITCH_PORT_SPEED_1000; + break; + } + } + + link->aneg = 1; + } else { + link->link = 0; + } + + return 0; + +} + +static int b53_global_reset_switch(struct switch_dev *dev) +{ + struct b53_device *priv = sw_to_b53(dev); + + /* reset vlans */ + priv->enable_vlan = 0; + priv->enable_jumbo = 0; + priv->allow_vid_4095 = 0; + + memset(priv->vlans, 0, sizeof(priv->vlans) * dev->vlans); + memset(priv->ports, 0, sizeof(priv->ports) * dev->ports); + + return b53_switch_reset(priv); +} + +static int b53_global_apply_config(struct switch_dev *dev) +{ + struct b53_device *priv = sw_to_b53(dev); + + /* disable switching */ + b53_set_forwarding(priv, 0); + + b53_apply(priv); + + /* enable switching */ + b53_set_forwarding(priv, 1); + + return 0; +} + + +static int b53_global_reset_mib(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct b53_device *priv = sw_to_b53(dev); + u8 gc; + + b53_read8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc); + + b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc | GC_RESET_MIB); + mdelay(1); + b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc & ~GC_RESET_MIB); + mdelay(1); + + return 0; +} + +static int b53_port_get_mib(struct switch_dev *sw_dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct b53_device *dev = sw_to_b53(sw_dev); + const struct b53_mib_desc *mibs; + int port = val->port_vlan; + int len = 0; + + if (!(BIT(port) & dev->enabled_ports)) + return -1; + + if (is5365(dev)) { + if (port == 5) + port = 8; + + mibs = b53_mibs_65; + } else if (is63xx(dev)) { + mibs = b53_mibs_63xx; + } else { + mibs = b53_mibs; + } + + dev->buf[0] = 0; + + for (; mibs->size > 0; mibs++) { + u64 val; + + if (mibs->size == 8) { + b53_read64(dev, B53_MIB_PAGE(port), mibs->offset, &val); + } else { + u32 val32; + + b53_read32(dev, B53_MIB_PAGE(port), mibs->offset, + &val32); + val = val32; + } + + len += snprintf(dev->buf + len, B53_BUF_SIZE - len, + "%-20s: %llu\n", mibs->name, val); + } + + val->len = len; + val->value.s = dev->buf; + + return 0; +} + +static struct switch_attr b53_global_ops_25[] = { + { + .type = SWITCH_TYPE_INT, + .name = "enable_vlan", + .description = "Enable VLAN mode", + .set = b53_global_set_vlan_enable, + .get = b53_global_get_vlan_enable, + .max = 1, + }, + { + .type = SWITCH_TYPE_STRING, + .name = "ports", + .description = "Available ports (as bitmask)", + .get = b53_global_get_ports, + }, +}; + +static struct switch_attr b53_global_ops_65[] = { + { + .type = SWITCH_TYPE_INT, + .name = "enable_vlan", + .description = "Enable VLAN mode", + .set = b53_global_set_vlan_enable, + .get = b53_global_get_vlan_enable, + .max = 1, + }, + { + .type = SWITCH_TYPE_STRING, + .name = "ports", + .description = "Available ports (as bitmask)", + .get = b53_global_get_ports, + }, + { + .type = SWITCH_TYPE_INT, + .name = "reset_mib", + .description = "Reset MIB counters", + .set = b53_global_reset_mib, + }, +}; + +static struct switch_attr b53_global_ops[] = { + { + .type = SWITCH_TYPE_INT, + .name = "enable_vlan", + .description = "Enable VLAN mode", + .set = b53_global_set_vlan_enable, + .get = b53_global_get_vlan_enable, + .max = 1, + }, + { + .type = SWITCH_TYPE_STRING, + .name = "ports", + .description = "Available Ports (as bitmask)", + .get = b53_global_get_ports, + }, + { + .type = SWITCH_TYPE_INT, + .name = "reset_mib", + .description = "Reset MIB counters", + .set = b53_global_reset_mib, + }, + { + .type = SWITCH_TYPE_INT, + .name = "enable_jumbo", + .description = "Enable Jumbo Frames", + .set = b53_global_set_jumbo_enable, + .get = b53_global_get_jumbo_enable, + .max = 1, + }, + { + .type = SWITCH_TYPE_INT, + .name = "allow_vid_4095", + .description = "Allow VID 4095", + .set = b53_global_set_4095_enable, + .get = b53_global_get_4095_enable, + .max = 1, + }, +}; + +static struct switch_attr b53_port_ops[] = { + { + .type = SWITCH_TYPE_STRING, + .name = "mib", + .description = "Get port's MIB counters", + .get = b53_port_get_mib, + }, +}; + +static struct switch_attr b53_no_ops[] = { +}; + +static const struct switch_dev_ops b53_switch_ops_25 = { + .attr_global = { + .attr = b53_global_ops_25, + .n_attr = ARRAY_SIZE(b53_global_ops_25), + }, + .attr_port = { + .attr = b53_no_ops, + .n_attr = ARRAY_SIZE(b53_no_ops), + }, + .attr_vlan = { + .attr = b53_no_ops, + .n_attr = ARRAY_SIZE(b53_no_ops), + }, + + .get_vlan_ports = b53_vlan_get_ports, + .set_vlan_ports = b53_vlan_set_ports, + .get_port_pvid = b53_port_get_pvid, + .set_port_pvid = b53_port_set_pvid, + .apply_config = b53_global_apply_config, + .reset_switch = b53_global_reset_switch, + .get_port_link = b53_port_get_link, +}; + +static const struct switch_dev_ops b53_switch_ops_65 = { + .attr_global = { + .attr = b53_global_ops_65, + .n_attr = ARRAY_SIZE(b53_global_ops_65), + }, + .attr_port = { + .attr = b53_no_ops, + .n_attr = ARRAY_SIZE(b53_port_ops), + }, + .attr_vlan = { + .attr = b53_no_ops, + .n_attr = ARRAY_SIZE(b53_no_ops), + }, + + .get_vlan_ports = b53_vlan_get_ports, + .set_vlan_ports = b53_vlan_set_ports, + .get_port_pvid = b53_port_get_pvid, + .set_port_pvid = b53_port_set_pvid, + .apply_config = b53_global_apply_config, + .reset_switch = b53_global_reset_switch, + .get_port_link = b53_port_get_link, +}; + +static const struct switch_dev_ops b53_switch_ops = { + .attr_global = { + .attr = b53_global_ops, + .n_attr = ARRAY_SIZE(b53_global_ops), + }, + .attr_port = { + .attr = b53_port_ops, + .n_attr = ARRAY_SIZE(b53_port_ops), + }, + .attr_vlan = { + .attr = b53_no_ops, + .n_attr = ARRAY_SIZE(b53_no_ops), + }, + + .get_vlan_ports = b53_vlan_get_ports, + .set_vlan_ports = b53_vlan_set_ports, + .get_port_pvid = b53_port_get_pvid, + .set_port_pvid = b53_port_set_pvid, + .apply_config = b53_global_apply_config, + .reset_switch = b53_global_reset_switch, + .get_port_link = b53_port_get_link, +}; + +struct b53_chip_data { + u32 chip_id; + const char *dev_name; + const char *alias; + u16 vlans; + u16 enabled_ports; + u8 cpu_port; + u8 vta_regs[3]; + u8 duplex_reg; + u8 jumbo_pm_reg; + u8 jumbo_size_reg; + const struct switch_dev_ops *sw_ops; +}; + +#define B53_VTA_REGS \ + { B53_VT_ACCESS, B53_VT_INDEX, B53_VT_ENTRY } +#define B53_VTA_REGS_9798 \ + { B53_VT_ACCESS_9798, B53_VT_INDEX_9798, B53_VT_ENTRY_9798 } +#define B53_VTA_REGS_63XX \ + { B53_VT_ACCESS_63XX, B53_VT_INDEX_63XX, B53_VT_ENTRY_63XX } + +static const struct b53_chip_data b53_switch_chips[] = { + { + .chip_id = BCM5325_DEVICE_ID, + .dev_name = "BCM5325", + .alias = "bcm5325", + .vlans = 16, + .enabled_ports = 0x1f, + .cpu_port = B53_CPU_PORT_25, + .duplex_reg = B53_DUPLEX_STAT_FE, + .sw_ops = &b53_switch_ops_25, + }, + { + .chip_id = BCM5365_DEVICE_ID, + .dev_name = "BCM5365", + .alias = "bcm5365", + .vlans = 256, + .enabled_ports = 0x1f, + .cpu_port = B53_CPU_PORT_25, + .duplex_reg = B53_DUPLEX_STAT_FE, + .sw_ops = &b53_switch_ops_65, + }, + { + .chip_id = BCM5395_DEVICE_ID, + .dev_name = "BCM5395", + .alias = "bcm5395", + .vlans = 4096, + .enabled_ports = 0x1f, + .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, + .sw_ops = &b53_switch_ops, + }, + { + .chip_id = BCM5397_DEVICE_ID, + .dev_name = "BCM5397", + .alias = "bcm5397", + .vlans = 4096, + .enabled_ports = 0x1f, + .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS_9798, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, + .sw_ops = &b53_switch_ops, + }, + { + .chip_id = BCM5398_DEVICE_ID, + .dev_name = "BCM5398", + .alias = "bcm5398", + .vlans = 4096, + .enabled_ports = 0x7f, + .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS_9798, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, + .sw_ops = &b53_switch_ops, + }, + { + .chip_id = BCM53115_DEVICE_ID, + .dev_name = "BCM53115", + .alias = "bcm53115", + .vlans = 4096, + .enabled_ports = 0x1f, + .vta_regs = B53_VTA_REGS, + .cpu_port = B53_CPU_PORT, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, + .sw_ops = &b53_switch_ops, + }, + { + .chip_id = BCM53125_DEVICE_ID, + .dev_name = "BCM53125", + .alias = "bcm53125", + .vlans = 4096, + .enabled_ports = 0x1f, + .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, + .sw_ops = &b53_switch_ops, + }, + { + .chip_id = BCM63XX_DEVICE_ID, + .dev_name = "BCM63xx", + .alias = "bcm63xx", + .vlans = 4096, + .enabled_ports = 0, /* pdata must provide them */ + .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS_63XX, + .duplex_reg = B53_DUPLEX_STAT_63XX, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, + .sw_ops = &b53_switch_ops, + }, +}; + +int b53_switch_init(struct b53_device *dev) +{ + struct switch_dev *sw_dev = &dev->sw_dev; + unsigned i; + + for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) { + const struct b53_chip_data *chip = &b53_switch_chips[i]; + + if (chip->chip_id == dev->chip_id) { + sw_dev->name = chip->dev_name; + if (!sw_dev->alias) + sw_dev->alias = chip->alias; + if (!dev->enabled_ports) + dev->enabled_ports = chip->enabled_ports; + dev->duplex_reg = chip->duplex_reg; + dev->vta_regs[0] = chip->vta_regs[0]; + dev->vta_regs[1] = chip->vta_regs[1]; + dev->vta_regs[2] = chip->vta_regs[2]; + dev->jumbo_pm_reg = chip->jumbo_pm_reg; + sw_dev->ops = chip->sw_ops; + sw_dev->cpu_port = chip->cpu_port; + sw_dev->vlans = chip->vlans; + break; + } + } + + if (!sw_dev->name) + return -EINVAL; + + /* check which BCM5325x version we have */ + if (is5325(dev)) { + u8 vc4; + + b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4); + + /* check reserved bits */ + switch (vc4 & 3) { + case 1: + /* BCM5325E */ + break; + case 3: + /* BCM5325F - do not use port 4 */ + dev->enabled_ports &= ~BIT(4); + break; + default: + /* BCM5325M */ + return -EINVAL; + } + } else if (dev->chip_id == BCM53115_DEVICE_ID) { + u64 strap_value; + + b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value); + /* use second IMP port if GMII is enabled */ + if (strap_value & SV_GMII_CTRL_115) + sw_dev->cpu_port = 5; + } + + /* cpu port is always last */ + sw_dev->ports = sw_dev->cpu_port + 1; + dev->enabled_ports |= BIT(sw_dev->cpu_port); + + dev->ports = devm_kzalloc(dev->dev, + sizeof(struct b53_port) * sw_dev->ports, + GFP_KERNEL); + if (!dev->ports) + return -ENOMEM; + + dev->vlans = devm_kzalloc(dev->dev, + sizeof(struct b53_vlan) * sw_dev->vlans, + GFP_KERNEL); + if (!dev->vlans) + return -ENOMEM; + + dev->buf = devm_kzalloc(dev->dev, B53_BUF_SIZE, GFP_KERNEL); + if (!dev->buf) + return -ENOMEM; + + return b53_switch_reset(dev); +} + +struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops, + void *priv) +{ + struct b53_device *dev; + + dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + dev->dev = base; + dev->ops = ops; + dev->priv = priv; + mutex_init(&dev->reg_mutex); + + return dev; +} +EXPORT_SYMBOL(b53_switch_alloc); + +int b53_switch_detect(struct b53_device *dev) +{ + u32 id32; + u16 tmp; + u8 id8; + int ret; + + ret = b53_read8(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id8); + if (ret) + return ret; + + switch (id8) { + case 0: + /* + * BCM5325 and BCM5365 do not have this register so reads + * return 0. But the read operation did succeed, so assume + * this is one of them. + * + * Next check if we can write to the 5325's VTA register; for + * 5365 it is read only. + */ + + b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf); + b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp); + + if (tmp == 0xf) + dev->chip_id = BCM5325_DEVICE_ID; + else + dev->chip_id = BCM5365_DEVICE_ID; + break; + case BCM5395_DEVICE_ID: + case BCM5397_DEVICE_ID: + case BCM5398_DEVICE_ID: + dev->chip_id = id8; + break; + default: + ret = b53_read32(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id32); + if (ret) + return ret; + + switch (id32) { + case BCM53115_DEVICE_ID: + case BCM53125_DEVICE_ID: + dev->chip_id = id32; + break; + default: + pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n", + id8, id32); + return -ENODEV; + } + } + + return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID, &dev->core_rev); +} +EXPORT_SYMBOL(b53_switch_detect); + +int b53_switch_register(struct b53_device *dev) +{ + int ret; + + if (dev->pdata) { + dev->chip_id = dev->pdata->chip_id; + dev->enabled_ports = dev->pdata->enabled_ports; + dev->sw_dev.alias = dev->pdata->alias; + } + + if (!dev->chip_id && b53_switch_detect(dev)) + return -EINVAL; + + ret = b53_switch_init(dev); + if (ret) + return ret; + + pr_info("found switch: %s, rev %i\n", dev->sw_dev.name, dev->core_rev); + + return register_switch(&dev->sw_dev, NULL); +} +EXPORT_SYMBOL(b53_switch_register); + +MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); +MODULE_DESCRIPTION("B53 switch library"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c b/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c new file mode 100644 index 000000000..1ba05914e --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c @@ -0,0 +1,401 @@ +/* + * B53 register access through MII registers + * + * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/phy.h> +#include <linux/module.h> + +#include "b53_priv.h" + +#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */ + +/* MII registers */ +#define REG_MII_PAGE 0x10 /* MII Page register */ +#define REG_MII_ADDR 0x11 /* MII Address register */ +#define REG_MII_DATA0 0x18 /* MII Data register 0 */ +#define REG_MII_DATA1 0x19 /* MII Data register 1 */ +#define REG_MII_DATA2 0x1a /* MII Data register 2 */ +#define REG_MII_DATA3 0x1b /* MII Data register 3 */ + +#define REG_MII_PAGE_ENABLE BIT(0) +#define REG_MII_ADDR_WRITE BIT(0) +#define REG_MII_ADDR_READ BIT(1) + +static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op) +{ + int i; + u16 v; + int ret; + struct mii_bus *bus = dev->priv; + + if (dev->current_page != page) { + /* set page number */ + v = (page << 8) | REG_MII_PAGE_ENABLE; + ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_PAGE, v); + if (ret) + return ret; + dev->current_page = page; + } + + /* set register address */ + v = (reg << 8) | op; + ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_ADDR, v); + if (ret) + return ret; + + /* check if operation completed */ + for (i = 0; i < 5; ++i) { + v = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_ADDR); + if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ))) + break; + usleep_range(10, 100); + } + + if (WARN_ON(i == 5)) + return -EIO; + + return 0; +} + +static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) +{ + struct mii_bus *bus = dev->priv; + int ret; + + ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); + if (ret) + return ret; + + *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0) & 0xff; + + return 0; +} + +static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) +{ + struct mii_bus *bus = dev->priv; + int ret; + + ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); + if (ret) + return ret; + + *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0); + + return 0; +} + +static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) +{ + struct mii_bus *bus = dev->priv; + int ret; + + ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); + if (ret) + return ret; + + *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0); + *val |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA1) << 16; + + return 0; +} + +static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) +{ + struct mii_bus *bus = dev->priv; + u64 temp = 0; + int i; + int ret; + + ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); + if (ret) + return ret; + + for (i = 2; i >= 0; i--) { + temp <<= 16; + temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i); + } + + *val = temp; + + return 0; +} + +static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) +{ + struct mii_bus *bus = dev->priv; + u64 temp = 0; + int i; + int ret; + + ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); + if (ret) + return ret; + + for (i = 3; i >= 0; i--) { + temp <<= 16; + temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i); + } + + *val = temp; + + return 0; +} + +static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) +{ + struct mii_bus *bus = dev->priv; + int ret; + + ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value); + if (ret) + return ret; + + return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); +} + +static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg, + u16 value) +{ + struct mii_bus *bus = dev->priv; + int ret; + + ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value); + if (ret) + return ret; + + return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); +} + +static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg, + u32 value) +{ + struct mii_bus *bus = dev->priv; + unsigned int i; + u32 temp = value; + + for (i = 0; i < 2; i++) { + int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i, + temp & 0xffff); + if (ret) + return ret; + temp >>= 16; + } + + return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); + +} + +static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg, + u64 value) +{ + struct mii_bus *bus = dev->priv; + unsigned i; + u64 temp = value; + + for (i = 0; i < 3; i++) { + int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i, + temp & 0xffff); + if (ret) + return ret; + temp >>= 16; + } + + return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); + +} + +static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg, + u64 value) +{ + struct mii_bus *bus = dev->priv; + unsigned i; + u64 temp = value; + + for (i = 0; i < 4; i++) { + int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i, + temp & 0xffff); + if (ret) + return ret; + temp >>= 16; + } + + return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); +} + +static struct b53_io_ops b53_mdio_ops = { + .read8 = b53_mdio_read8, + .read16 = b53_mdio_read16, + .read32 = b53_mdio_read32, + .read48 = b53_mdio_read48, + .read64 = b53_mdio_read64, + .write8 = b53_mdio_write8, + .write16 = b53_mdio_write16, + .write32 = b53_mdio_write32, + .write48 = b53_mdio_write48, + .write64 = b53_mdio_write64, +}; + +static int b53_phy_probe(struct phy_device *phydev) +{ + struct b53_device dev; + int ret; + + /* allow the generic phy driver to take over */ + if (phydev->addr != B53_PSEUDO_PHY && phydev->addr != 0) + return -ENODEV; + + dev.current_page = 0xff; + dev.priv = phydev->bus; + dev.ops = &b53_mdio_ops; + dev.pdata = NULL; + mutex_init(&dev.reg_mutex); + + ret = b53_switch_detect(&dev); + if (ret) + return ret; + + if (is5325(&dev) || is5365(&dev)) + phydev->supported = SUPPORTED_100baseT_Full; + else + phydev->supported = SUPPORTED_1000baseT_Full; + + phydev->advertising = phydev->supported; + + return 0; +} + +static int b53_phy_config_init(struct phy_device *phydev) +{ + struct b53_device *dev; + int ret; + + dev = b53_switch_alloc(&phydev->dev, &b53_mdio_ops, phydev->bus); + if (!dev) + return -ENOMEM; + + /* we don't use page 0xff, so force a page set */ + dev->current_page = 0xff; + /* force the ethX as alias */ + dev->sw_dev.alias = phydev->attached_dev->name; + + ret = b53_switch_register(dev); + if (ret) { + pr_info("failed to register switch: %i\n", ret); + return ret; + } + + phydev->priv = dev; + + return 0; +} + +static void b53_phy_remove(struct phy_device *phydev) +{ + struct b53_device *priv = phydev->priv; + + if (!priv) + return; + + b53_switch_remove(priv); + + phydev->priv = NULL; +} + +static int b53_phy_config_aneg(struct phy_device *phydev) +{ + return 0; +} + +static int b53_phy_read_status(struct phy_device *phydev) +{ + struct b53_device *priv = phydev->priv; + + if (is5325(priv) || is5365(priv)) + phydev->speed = 100; + else + phydev->speed = 1000; + + phydev->duplex = DUPLEX_FULL; + phydev->link = 1; + phydev->state = PHY_RUNNING; + + netif_carrier_on(phydev->attached_dev); + phydev->adjust_link(phydev->attached_dev); + + return 0; +} + +/* BCM5325, BCM539x */ +static struct phy_driver b53_phy_driver_id1 = { + .phy_id = 0x0143bc00, + .name = "Broadcom B53 (1)", + .phy_id_mask = 0x1ffffc00, + .features = 0, + .probe = b53_phy_probe, + .remove = b53_phy_remove, + .config_aneg = b53_phy_config_aneg, + .config_init = b53_phy_config_init, + .read_status = b53_phy_read_status, + .driver = { + .owner = THIS_MODULE, + }, +}; + +/* BCM53125 */ +static struct phy_driver b53_phy_driver_id2 = { + .phy_id = 0x03625c00, + .name = "Broadcom B53 (2)", + .phy_id_mask = 0x1ffffc00, + .features = 0, + .probe = b53_phy_probe, + .remove = b53_phy_remove, + .config_aneg = b53_phy_config_aneg, + .config_init = b53_phy_config_init, + .read_status = b53_phy_read_status, + .driver = { + .owner = THIS_MODULE, + }, +}; + +int __init b53_phy_driver_register(void) +{ + int ret; + + ret = phy_driver_register(&b53_phy_driver_id1); + if (ret) + return ret; + + ret = phy_driver_register(&b53_phy_driver_id2); + if (ret) + phy_driver_unregister(&b53_phy_driver_id1); + + return ret; +} + +void __exit b53_phy_driver_unregister(void) +{ + phy_driver_unregister(&b53_phy_driver_id2); + phy_driver_unregister(&b53_phy_driver_id1); +} + +module_init(b53_phy_driver_register); +module_exit(b53_phy_driver_unregister); + +MODULE_DESCRIPTION("B53 MDIO access driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c b/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c new file mode 100644 index 000000000..1fd01581a --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c @@ -0,0 +1,241 @@ +/* + * B53 register access through memory mapped registers + * + * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/platform_data/b53.h> + +#include "b53_priv.h" + +static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) +{ + u8 __iomem *regs = dev->priv; + + *val = readb(regs + (page << 8) + reg); + + return 0; +} + +static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) +{ + u8 __iomem *regs = dev->priv; + + if (WARN_ON(reg % 2)) + return -EINVAL; + + if (dev->pdata && dev->pdata->big_endian) + *val = readw_be(regs + (page << 8) + reg); + else + *val = readw(regs + (page << 8) + reg); + + return 0; +} + +static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) +{ + u8 __iomem *regs = dev->priv; + + if (WARN_ON(reg % 4)) + return -EINVAL; + + if (dev->pdata && dev->pdata->big_endian) + *val = readl_be(regs + (page << 8) + reg); + else + *val = readl(regs + (page << 8) + reg); + + return 0; +} + +static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) +{ + u8 __iomem *regs = dev->priv; + + if (WARN_ON(reg % 4)) + return -EINVAL; + + if (dev->pdata && dev->pdata->big_endian) { + *val = readl_be(regs + (page << 8) + reg); + *val <<= 16; + *val |= readw_be(regs + (page << 8) + reg + 4); + } else { + *val |= readw(regs + (page << 8) + reg + 4); + *val <<= 32; + *val = readl(regs + (page << 8) + reg); + } + + return 0; +} + +static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) +{ + u8 __iomem *regs = dev->priv; + u32 hi, lo; + + if (WARN_ON(reg % 4)) + return -EINVAL; + + if (dev->pdata && dev->pdata->big_endian) { + lo = readl_be(regs + (page << 8) + reg); + hi = readl_be(regs + (page << 8) + reg + 4); + } else { + lo = readl(regs + (page << 8) + reg); + hi = readl(regs + (page << 8) + reg + 4); + } + + *val = ((u64)hi << 32) | lo; + + return 0; +} + +static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) +{ + u8 __iomem *regs = dev->priv; + + writeb(value, regs + (page << 8) + reg); + + return 0; +} + +static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg, + u16 value) +{ + u8 __iomem *regs = dev->priv; + + if (WARN_ON(reg % 2)) + return -EINVAL; + + if (dev->pdata && dev->pdata->big_endian) + writew_be(value, regs + (page << 8) + reg); + else + writew(value, regs + (page << 8) + reg); + + return 0; +} + +static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg, + u32 value) +{ + u8 __iomem *regs = dev->priv; + + if (WARN_ON(reg % 4)) + return -EINVAL; + + if (dev->pdata && dev->pdata->big_endian) + writel_be(value, regs + (page << 8) + reg); + else + writel(value, regs + (page << 8) + reg); + + return 0; +} + +static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg, + u64 value) +{ + u8 __iomem *regs = dev->priv; + + if (WARN_ON(reg % 4)) + return -EINVAL; + + if (dev->pdata && dev->pdata->big_endian) { + writel_be((u32)(value >> 16), regs + (page << 8) + reg); + writew_be((u16)value, regs + (page << 8) + reg + 4); + } else { + writel((u32)value, regs + (page << 8) + reg); + writew((u16)(value >> 32), regs + (page << 8) + reg + 4); + } + + return 0; +} + +static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg, + u64 value) +{ + u8 __iomem *regs = dev->priv; + + if (WARN_ON(reg % 4)) + return -EINVAL; + + if (dev->pdata && dev->pdata->big_endian) { + writel_be((u32)(value >> 32), regs + (page << 8) + reg); + writel_be((u32)value, regs + (page << 8) + reg + 4); + } else { + writel((u32)value, regs + (page << 8) + reg); + writel((u32)(value >> 32), regs + (page << 8) + reg + 4); + } + + return 0; +} + +static struct b53_io_ops b53_mmap_ops = { + .read8 = b53_mmap_read8, + .read16 = b53_mmap_read16, + .read32 = b53_mmap_read32, + .read48 = b53_mmap_read48, + .read64 = b53_mmap_read64, + .write8 = b53_mmap_write8, + .write16 = b53_mmap_write16, + .write32 = b53_mmap_write32, + .write48 = b53_mmap_write48, + .write64 = b53_mmap_write64, +}; + +static int b53_mmap_probe(struct platform_device *pdev) +{ + struct b53_platform_data *pdata = pdev->dev.platform_data; + struct b53_device *dev; + + if (!pdata) + return -EINVAL; + + dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs); + if (!dev) + return -ENOMEM; + + if (pdata) + dev->pdata = pdata; + + pdev->dev.platform_data = dev; + + return b53_switch_register(dev); +} + +static int b53_mmap_remove(struct platform_device *pdev) +{ + struct b53_device *dev = pdev->dev.platform_data; + + if (dev) { + pdev->dev.platform_data = dev->pdata; + b53_switch_remove(dev); + } + + return 0; +} + +static struct platform_driver b53_mmap_driver = { + .probe = b53_mmap_probe, + .remove = b53_mmap_remove, + .driver = { + .name = "b53-switch", + }, +}; + +module_platform_driver(b53_mmap_driver); +MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); +MODULE_DESCRIPTION("B53 MMAP access driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_phy_fixup.c b/target/linux/generic/files/drivers/net/phy/b53/b53_phy_fixup.c new file mode 100644 index 000000000..447f30b64 --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_phy_fixup.c @@ -0,0 +1,53 @@ +/* + * B53 PHY Fixup call + * + * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/phy.h> + +#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */ + +#define B53_BRCM_OUI_1 0x0143bc00 +#define B53_BRCM_OUI_2 0x03625c00 + +static int b53_phy_fixup(struct phy_device *dev) +{ + u32 phy_id; + struct mii_bus *bus = dev->bus; + + if (dev->addr != B53_PSEUDO_PHY) + return 0; + + /* read the first port's id */ + phy_id = mdiobus_read(bus, 0, 2) << 16; + phy_id |= mdiobus_read(bus, 0, 3); + + if ((phy_id & 0xfffffc00) == B53_BRCM_OUI_1 || + (phy_id & 0xfffffc00) == B53_BRCM_OUI_2) { + dev->phy_id = phy_id; + } + + return 0; +} + +int __init b53_phy_fixup_register(void) +{ + return phy_register_fixup_for_id(PHY_ANY_ID, b53_phy_fixup); +} + +subsys_initcall(b53_phy_fixup_register); diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h new file mode 100644 index 000000000..fca74ae4b --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h @@ -0,0 +1,278 @@ +/* + * B53 common definitions + * + * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __B53_PRIV_H +#define __B53_PRIV_H + +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/switch.h> + +struct b53_device; + +struct b53_io_ops { + int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value); + int (*read16)(struct b53_device *dev, u8 page, u8 reg, u16 *value); + int (*read32)(struct b53_device *dev, u8 page, u8 reg, u32 *value); + int (*read48)(struct b53_device *dev, u8 page, u8 reg, u64 *value); + int (*read64)(struct b53_device *dev, u8 page, u8 reg, u64 *value); + int (*write8)(struct b53_device *dev, u8 page, u8 reg, u8 value); + int (*write16)(struct b53_device *dev, u8 page, u8 reg, u16 value); + int (*write32)(struct b53_device *dev, u8 page, u8 reg, u32 value); + int (*write48)(struct b53_device *dev, u8 page, u8 reg, u64 value); + int (*write64)(struct b53_device *dev, u8 page, u8 reg, u64 value); +}; + +enum { + BCM5325_DEVICE_ID = 0x25, + BCM5365_DEVICE_ID = 0x65, + BCM5395_DEVICE_ID = 0x95, + BCM5397_DEVICE_ID = 0x97, + BCM5398_DEVICE_ID = 0x98, + BCM53115_DEVICE_ID = 0x53115, + BCM53125_DEVICE_ID = 0x53125, + BCM63XX_DEVICE_ID = 0x6300, +}; + +#define B53_N_PORTS 9 +#define B53_N_PORTS_25 6 + +struct b53_vlan { + unsigned int members:B53_N_PORTS; + unsigned int untag:B53_N_PORTS; +}; + +struct b53_port { + unsigned int pvid:12; +}; + +struct b53_device { + struct switch_dev sw_dev; + struct b53_platform_data *pdata; + + struct mutex reg_mutex; + const struct b53_io_ops *ops; + + /* chip specific data */ + u32 chip_id; + u8 core_rev; + u8 vta_regs[3]; + u8 duplex_reg; + u8 jumbo_pm_reg; + u8 jumbo_size_reg; + + /* used ports mask */ + u16 enabled_ports; + + /* connect specific data */ + u8 current_page; + struct device *dev; + void *priv; + + /* run time configuration */ + unsigned enable_vlan:1; + unsigned enable_jumbo:1; + unsigned allow_vid_4095:1; + + struct b53_port *ports; + struct b53_vlan *vlans; + + char *buf; +}; + +#define b53_for_each_port(dev, i) \ + for (i = 0; i < B53_N_PORTS; i++) \ + if (dev->enabled_ports & BIT(i)) + + + +static inline int is5325(struct b53_device *dev) +{ + return dev->chip_id == BCM5325_DEVICE_ID; +} + +static inline int is5365(struct b53_device *dev) +{ +#ifdef CONFIG_BCM47XX + return dev->chip_id == BCM5365_DEVICE_ID; +#else + return 0; +#endif +} + +static inline int is5397_98(struct b53_device *dev) +{ + return dev->chip_id == BCM5397_DEVICE_ID || + dev->chip_id == BCM5398_DEVICE_ID; +} + +static inline int is531x5(struct b53_device *dev) +{ + return dev->chip_id == BCM53115_DEVICE_ID || + dev->chip_id == BCM53125_DEVICE_ID; +} + +static inline int is63xx(struct b53_device *dev) +{ +#ifdef CONFIG_BCM63XX + return dev->chip_id == BCM63XX_DEVICE_ID; +#else + return 0; +#endif +} + +#define B53_CPU_PORT_25 5 +#define B53_CPU_PORT 8 + +static inline int is_cpu_port(struct b53_device *dev, int port) +{ + return dev->sw_dev.cpu_port == port; +} + +static inline struct b53_device *sw_to_b53(struct switch_dev *sw) +{ + return container_of(sw, struct b53_device, sw_dev); +} + +struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops, + void *priv); + +int b53_switch_detect(struct b53_device *dev); + +int b53_switch_register(struct b53_device *dev); + +static inline void b53_switch_remove(struct b53_device *dev) +{ + unregister_switch(&dev->sw_dev); +} + +static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) +{ + int ret; + + mutex_lock(&dev->reg_mutex); + ret = dev->ops->read8(dev, page, reg, val); + mutex_unlock(&dev->reg_mutex); + + return ret; +} + +static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) +{ + int ret; + + mutex_lock(&dev->reg_mutex); + ret = dev->ops->read16(dev, page, reg, val); + mutex_unlock(&dev->reg_mutex); + + return ret; +} + +static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) +{ + int ret; + + mutex_lock(&dev->reg_mutex); + ret = dev->ops->read32(dev, page, reg, val); + mutex_unlock(&dev->reg_mutex); + + return ret; +} + +static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) +{ + int ret; + + mutex_lock(&dev->reg_mutex); + ret = dev->ops->read48(dev, page, reg, val); + mutex_unlock(&dev->reg_mutex); + + return ret; +} + +static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) +{ + int ret; + + mutex_lock(&dev->reg_mutex); + ret = dev->ops->read64(dev, page, reg, val); + mutex_unlock(&dev->reg_mutex); + + return ret; +} + +static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) +{ + int ret; + + mutex_lock(&dev->reg_mutex); + ret = dev->ops->write8(dev, page, reg, value); + mutex_unlock(&dev->reg_mutex); + + return ret; +} + +static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg, + u16 value) +{ + int ret; + + mutex_lock(&dev->reg_mutex); + ret = dev->ops->write16(dev, page, reg, value); + mutex_unlock(&dev->reg_mutex); + + return ret; +} + +static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg, + u32 value) +{ + int ret; + + mutex_lock(&dev->reg_mutex); + ret = dev->ops->write32(dev, page, reg, value); + mutex_unlock(&dev->reg_mutex); + + return ret; +} + +static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg, + u64 value) +{ + int ret; + + mutex_lock(&dev->reg_mutex); + ret = dev->ops->write48(dev, page, reg, value); + mutex_unlock(&dev->reg_mutex); + + return ret; +} + +static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg, + u64 value) +{ + int ret; + + mutex_lock(&dev->reg_mutex); + ret = dev->ops->write64(dev, page, reg, value); + mutex_unlock(&dev->reg_mutex); + + return ret; +} + +#endif diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h b/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h new file mode 100644 index 000000000..7b8f539be --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h @@ -0,0 +1,308 @@ +/* + * B53 register definitions + * + * Copyright (C) 2004 Broadcom Corporation + * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __B53_REGS_H +#define __B53_REGS_H + +/* Management Port (SMP) Page offsets */ +#define B53_CTRL_PAGE 0x00 /* Control */ +#define B53_STAT_PAGE 0x01 /* Status */ +#define B53_MGMT_PAGE 0x02 /* Management Mode */ +#define B53_MIB_AC_PAGE 0x03 /* MIB Autocast */ +#define B53_ARLCTRL_PAGE 0x04 /* ARL Control */ +#define B53_ARLIO_PAGE 0x05 /* ARL Access */ +#define B53_FRAMEBUF_PAGE 0x06 /* Management frame access */ +#define B53_MEM_ACCESS_PAGE 0x08 /* Memory access */ + +/* PHY Registers */ +#define B53_PORT_MII_PAGE(i) (0x10 + i) /* Port i MII Registers */ +#define B53_IM_PORT_PAGE 0x18 /* Inverse MII Port (to EMAC) */ +#define B53_ALL_PORT_PAGE 0x19 /* All ports MII (broadcast) */ + +/* MIB registers */ +#define B53_MIB_PAGE(i) (0x20 + i) + +/* Quality of Service (QoS) Registers */ +#define B53_QOS_PAGE 0x30 + +/* Port VLAN Page */ +#define B53_PVLAN_PAGE 0x31 + +/* VLAN Registers */ +#define B53_VLAN_PAGE 0x34 + +/* Jumbo Frame Registers */ +#define B53_JUMBO_PAGE 0x40 + +/************************************************************************* + * Control Page registers + *************************************************************************/ + +/* Port Control Register (8 bit) */ +#define B53_PORT_CTRL(i) (0x00 + i) +#define PORT_CTRL_RX_DISABLE BIT(0) +#define PORT_CTRL_TX_DISABLE BIT(1) +#define PORT_CTRL_RX_BCST_EN BIT(2) /* Broadcast RX (P8 only) */ +#define PORT_CTRL_RX_MCST_EN BIT(3) /* Multicast RX (P8 only) */ +#define PORT_CTRL_RX_UCST_EN BIT(4) /* Unicast RX (P8 only) */ +#define PORT_CTRL_STP_STATE_S 5 +#define PORT_CTRL_STP_STATE_MASK (0x3 << PORT_CTRL_STP_STATE_S) + +/* SMP Control Register (8 bit) */ +#define B53_SMP_CTRL 0x0a + +/* Switch Mode Control Register (8 bit) */ +#define B53_SWITCH_MODE 0x0b +#define SM_SW_FWD_MODE BIT(0) /* 1 = Managed Mode */ +#define SM_SW_FWD_EN BIT(1) /* Forwarding Enable */ + +/* IMP Port state override register (8 bit) */ +#define B53_PORT_OVERRIDE_CTRL 0x0e +#define PORT_OVERRIDE_LINK BIT(0) +#define PORT_OVERRIDE_HALF_DUPLEX BIT(1) /* 0 = Full Duplex */ +#define PORT_OVERRIDE_SPEED_S 2 +#define PORT_OVERRIDE_SPEED_10M (0 << PORT_OVERRIDE_SPEED_S) +#define PORT_OVERRIDE_SPEED_100M (1 << PORT_OVERRIDE_SPEED_S) +#define PORT_OVERRIDE_SPEED_1000M (2 << PORT_OVERRIDE_SPEED_S) +#define PORT_OVERRIDE_RV_MII_25 BIT(4) /* BCM5325 only */ +#define PORT_OVERRIDE_RX_FLOW BIT(4) +#define PORT_OVERRIDE_TX_FLOW BIT(5) +#define PORT_OVERRIDE_EN BIT(7) /* Use the register contents */ + +/* Power-down mode control */ +#define B53_PD_MODE_CTRL_25 0x0f + +/* IP Multicast control (8 bit) */ +#define B53_IP_MULTICAST_CTRL 0x21 +#define B53_IPMC_FWD_EN BIT(1) +#define B53_UC_FWD_EN BIT(6) +#define B53_MC_FWD_EN BIT(7) + +/* (16 bit) */ +#define B53_UC_FLOOD_MASK 0x32 +#define B53_MC_FLOOD_MASK 0x34 +#define B53_IPMC_FLOOD_MASK 0x36 + +/* Software reset register (8 bit) */ +#define B53_SOFTRESET 0x79 + +/* Fast Aging Control register (8 bit) */ +#define B53_FAST_AGE_CTRL 0x88 +#define FAST_AGE_STATIC BIT(0) +#define FAST_AGE_DYNAMIC BIT(1) +#define FAST_AGE_PORT BIT(2) +#define FAST_AGE_VLAN BIT(3) +#define FAST_AGE_STP BIT(4) +#define FAST_AGE_MC BIT(5) +#define FAST_AGE_DONE BIT(7) + +/************************************************************************* + * Status Page registers + *************************************************************************/ + +/* Link Status Summary Register (16bit) */ +#define B53_LINK_STAT 0x00 + +/* Link Status Change Register (16 bit) */ +#define B53_LINK_STAT_CHANGE 0x02 + +/* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */ +#define B53_SPEED_STAT 0x04 +#define SPEED_PORT_FE(reg, port) (((reg) >> (port)) & 1) +#define SPEED_PORT_GE(reg, port) (((reg) >> 2 * (port)) & 3) +#define SPEED_STAT_10M 0 +#define SPEED_STAT_100M 1 +#define SPEED_STAT_1000M 2 + +/* Duplex Status Summary (16 bit) */ +#define B53_DUPLEX_STAT_FE 0x06 +#define B53_DUPLEX_STAT_GE 0x08 +#define B53_DUPLEX_STAT_63XX 0x0c + +/* Strap Value (48 bit) */ +#define B53_STRAP_VALUE 0x70 +#define SV_GMII_CTRL_115 BIT(27) + +/************************************************************************* + * Management Mode Page Registers + *************************************************************************/ + +/* Global Management Config Register (8 bit) */ +#define B53_GLOBAL_CONFIG 0x00 +#define GC_RESET_MIB 0x01 +#define GC_RX_BPDU_EN 0x02 +#define GC_MIB_AC_HDR_EN 0x10 +#define GC_MIB_AC_EN 0x20 +#define GC_FRM_MGMT_PORT_M 0xC0 +#define GC_FRM_MGMT_PORT_04 0x00 +#define GC_FRM_MGMT_PORT_MII 0x80 + +/* Device ID register (8 or 32 bit) */ +#define B53_DEVICE_ID 0x30 + +/* Revision ID register (8 bit) */ +#define B53_REV_ID 0x40 + +/************************************************************************* + * ARL Access Page Registers + *************************************************************************/ + +/* VLAN Table Access Register (8 bit) */ +#define B53_VT_ACCESS 0x80 +#define B53_VT_ACCESS_9798 0x60 /* for BCM5397/BCM5398 */ +#define B53_VT_ACCESS_63XX 0x60 /* for BCM6328/62/68 */ +#define VTA_CMD_WRITE 0 +#define VTA_CMD_READ 1 +#define VTA_CMD_CLEAR 2 +#define VTA_START_CMD BIT(7) + +/* VLAN Table Index Register (16 bit) */ +#define B53_VT_INDEX 0x81 +#define B53_VT_INDEX_9798 0x61 +#define B53_VT_INDEX_63XX 0x62 + +/* VLAN Table Entry Register (32 bit) */ +#define B53_VT_ENTRY 0x83 +#define B53_VT_ENTRY_9798 0x63 +#define B53_VT_ENTRY_63XX 0x64 +#define VTE_MEMBERS 0x1ff +#define VTE_UNTAG_S 9 +#define VTE_UNTAG (0x1ff << 9) + +/************************************************************************* + * Port VLAN Registers + *************************************************************************/ + +/* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */ +#define B53_PVLAN_PORT_MASK(i) ((i) * 2) + +/************************************************************************* + * 802.1Q Page Registers + *************************************************************************/ + +/* Global QoS Control (8 bit) */ +#define B53_QOS_GLOBAL_CTL 0x00 + +/* Enable 802.1Q for individual Ports (16 bit) */ +#define B53_802_1P_EN 0x04 + +/************************************************************************* + * VLAN Page Registers + *************************************************************************/ + +/* VLAN Control 0 (8 bit) */ +#define B53_VLAN_CTRL0 0x00 +#define VC0_8021PF_CTRL_MASK 0x3 +#define VC0_8021PF_CTRL_NONE 0x0 +#define VC0_8021PF_CTRL_CHANGE_PRI 0x1 +#define VC0_8021PF_CTRL_CHANGE_VID 0x2 +#define VC0_8021PF_CTRL_CHANGE_BOTH 0x3 +#define VC0_8021QF_CTRL_MASK 0xc +#define VC0_8021QF_CTRL_CHANGE_PRI 0x1 +#define VC0_8021QF_CTRL_CHANGE_VID 0x2 +#define VC0_8021QF_CTRL_CHANGE_BOTH 0x3 +#define VC0_RESERVED_1 BIT(1) +#define VC0_DROP_VID_MISS BIT(4) +#define VC0_VID_HASH_VID BIT(5) +#define VC0_VID_CHK_EN BIT(6) /* Use VID,DA or VID,SA */ +#define VC0_VLAN_EN BIT(7) /* 802.1Q VLAN Enabled */ + +/* VLAN Control 1 (8 bit) */ +#define B53_VLAN_CTRL1 0x01 +#define VC1_RX_MCST_TAG_EN BIT(1) +#define VC1_RX_MCST_FWD_EN BIT(2) +#define VC1_RX_MCST_UNTAG_EN BIT(3) + +/* VLAN Control 2 (8 bit) */ +#define B53_VLAN_CTRL2 0x02 + +/* VLAN Control 3 (8 bit when BCM5325, 16 bit else) */ +#define B53_VLAN_CTRL3 0x03 +#define B53_VLAN_CTRL3_63XX 0x04 +#define VC3_MAXSIZE_1532 BIT(6) /* 5325 only */ +#define VC3_HIGH_8BIT_EN BIT(7) /* 5325 only */ + +/* VLAN Control 4 (8 bit) */ +#define B53_VLAN_CTRL4 0x05 +#define B53_VLAN_CTRL4_25 0x04 +#define B53_VLAN_CTRL4_63XX 0x06 +#define VC4_ING_VID_CHECK_S 6 +#define VC4_ING_VID_CHECK_MASK (0x3 << VC4_ING_VID_CHECK_S) +#define VC4_ING_VID_VIO_FWD 0 /* forward, but do not learn */ +#define VC4_ING_VID_VIO_DROP 1 /* drop VID violations */ +#define VC4_NO_ING_VID_CHK 2 /* do not check */ +#define VC4_ING_VID_VIO_TO_IMP 3 /* redirect to MII port */ + +/* VLAN Control 5 (8 bit) */ +#define B53_VLAN_CTRL5 0x06 +#define B53_VLAN_CTRL5_25 0x05 +#define B53_VLAN_CTRL5_63XX 0x07 +#define VC5_VID_FFF_EN BIT(2) +#define VC5_DROP_VTABLE_MISS BIT(3) + +/* VLAN Control 6 (8 bit) */ +#define B53_VLAN_CTRL6 0x07 +#define B53_VLAN_CTRL6_63XX 0x08 + +/* VLAN Table Access Register (16 bit) */ +#define B53_VLAN_TABLE_ACCESS_25 0x06 /* BCM5325E/5350 */ +#define B53_VLAN_TABLE_ACCESS_65 0x08 /* BCM5365 */ +#define VTA_VID_LOW_MASK_25 0xf +#define VTA_VID_LOW_MASK_65 0xff +#define VTA_VID_HIGH_S_25 4 +#define VTA_VID_HIGH_S_65 8 +#define VTA_VID_HIGH_MASK_25 (0xff << VTA_VID_HIGH_S_25E) +#define VTA_VID_HIGH_MASK_65 (0xf << VTA_VID_HIGH_S_65) +#define VTA_RW_STATE BIT(12) +#define VTA_RW_STATE_RD 0 +#define VTA_RW_STATE_WR BIT(12) +#define VTA_RW_OP_EN BIT(13) + +/* VLAN Read/Write Registers for (16/32 bit) */ +#define B53_VLAN_WRITE_25 0x08 +#define B53_VLAN_WRITE_65 0x0a +#define B53_VLAN_READ 0x0c +#define VA_MEMBER_MASK 0x3f +#define VA_UNTAG_S 6 +#define VA_UNTAG_MASK (0x3f << VA_UNTAG_S) +#define VA_VID_HIGH_S 12 +#define VA_VID_HIGH_MASK (0xffff << VA_VID_HIGH_S) +#define VA_VALID_25 BIT(20) +#define VA_VALID_25_R4 BIT(24) +#define VA_VALID_65 BIT(14) + +/* VLAN Port Default Tag (16 bit) */ +#define B53_VLAN_PORT_DEF_TAG(i) (0x10 + 2 * (i)) + +/************************************************************************* + * Jumbo Frame Page Registers + *************************************************************************/ + +/* Jumbo Enable Port Mask (bit i == port i enabled) (32 bit) */ +#define B53_JUMBO_PORT_MASK 0x01 +#define B53_JUMBO_PORT_MASK_63XX 0x04 +#define JPM_10_100_JUMBO_EN BIT(24) /* GigE always enabled */ + +/* Good Frame Max Size without 802.1Q TAG (16 bit) */ +#define B53_JUMBO_MAX_SIZE 0x05 +#define B53_JUMBO_MAX_SIZE_63XX 0x08 +#define JMS_MIN_SIZE 1518 +#define JMS_MAX_SIZE 9724 + +#endif /* !__B53_REGS_H */ diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c b/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c new file mode 100644 index 000000000..6050fea32 --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c @@ -0,0 +1,329 @@ +/* + * B53 register access through SPI + * + * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <asm/unaligned.h> + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/platform_data/b53.h> + +#include "b53_priv.h" + +#define B53_SPI_DATA 0xf0 + +#define B53_SPI_STATUS 0xfe +#define B53_SPI_CMD_SPIF BIT(7) +#define B53_SPI_CMD_RACK BIT(5) + +#define B53_SPI_CMD_READ 0x00 +#define B53_SPI_CMD_WRITE 0x01 +#define B53_SPI_CMD_NORMAL 0x60 +#define B53_SPI_CMD_FAST 0x10 + +#define B53_SPI_PAGE_SELECT 0xff + +static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val, + unsigned len) +{ + u8 txbuf[2]; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ; + txbuf[1] = reg; + + return spi_write_then_read(spi, txbuf, 2, val, len); +} + +static inline int b53_spi_clear_status(struct spi_device *spi) +{ + unsigned int i; + u8 rxbuf; + int ret; + + for (i = 0; i < 10; i++) { + ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1); + if (ret) + return ret; + + if (!(rxbuf & B53_SPI_CMD_SPIF)) + break; + + mdelay(1); + } + + if (i == 10) + return -EIO; + + return 0; +} + +static inline int b53_spi_set_page(struct spi_device *spi, u8 page) +{ + u8 txbuf[3]; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = B53_SPI_PAGE_SELECT; + txbuf[2] = page; + + return spi_write(spi, txbuf, sizeof(txbuf)); +} + +static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page) +{ + int ret = b53_spi_clear_status(spi); + if (ret) + return ret; + + return b53_spi_set_page(spi, page); +} + +static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg) +{ + u8 rxbuf; + int retry_count; + int ret; + + ret = b53_spi_read_reg(spi, reg, &rxbuf, 1); + if (ret) + return ret; + + for (retry_count = 0; retry_count < 10; retry_count++) { + ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1); + if (ret) + return ret; + + if (rxbuf & B53_SPI_CMD_RACK) + break; + + mdelay(1); + } + + if (retry_count == 10) + return -EIO; + + return 0; +} + +static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data, + unsigned len) +{ + struct spi_device *spi = dev->priv; + int ret; + + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + ret = b53_spi_prepare_reg_read(spi, reg); + if (ret) + return ret; + + return b53_spi_read_reg(spi, B53_SPI_DATA, data, len); +} + +static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) +{ + return b53_spi_read(dev, page, reg, val, 1); +} + +static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) +{ + int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2); + if (!ret) + *val = le16_to_cpu(*val); + + return ret; +} + +static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) +{ + int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4); + if (!ret) + *val = le32_to_cpu(*val); + + return ret; +} + +static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) +{ + int ret; + + *val = 0; + ret = b53_spi_read(dev, page, reg, (u8 *)val, 6); + if (!ret) + *val = le64_to_cpu(*val); + + return ret; +} + +static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) +{ + int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8); + if (!ret) + *val = le64_to_cpu(*val); + + return ret; +} + +static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) +{ + struct spi_device *spi = dev->priv; + int ret; + u8 txbuf[3]; + + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = reg; + txbuf[2] = value; + + return spi_write(spi, txbuf, sizeof(txbuf)); +} + +static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value) +{ + struct spi_device *spi = dev->priv; + int ret; + u8 txbuf[4]; + + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = reg; + put_unaligned_le16(value, &txbuf[2]); + + return spi_write(spi, txbuf, sizeof(txbuf)); +} + +static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value) +{ + struct spi_device *spi = dev->priv; + int ret; + u8 txbuf[6]; + + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = reg; + put_unaligned_le32(value, &txbuf[2]); + + return spi_write(spi, txbuf, sizeof(txbuf)); +} + +static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value) +{ + struct spi_device *spi = dev->priv; + int ret; + u8 txbuf[10]; + + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = reg; + put_unaligned_le64(value, &txbuf[2]); + + return spi_write(spi, txbuf, sizeof(txbuf) - 2); +} + +static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value) +{ + struct spi_device *spi = dev->priv; + int ret; + u8 txbuf[10]; + + ret = b53_prepare_reg_access(spi, page); + if (ret) + return ret; + + txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; + txbuf[1] = reg; + put_unaligned_le64(value, &txbuf[2]); + + return spi_write(spi, txbuf, sizeof(txbuf)); +} + +static struct b53_io_ops b53_spi_ops = { + .read8 = b53_spi_read8, + .read16 = b53_spi_read16, + .read32 = b53_spi_read32, + .read48 = b53_spi_read48, + .read64 = b53_spi_read64, + .write8 = b53_spi_write8, + .write16 = b53_spi_write16, + .write32 = b53_spi_write32, + .write48 = b53_spi_write48, + .write64 = b53_spi_write64, +}; + +static int b53_spi_probe(struct spi_device *spi) +{ + struct b53_device *dev; + int ret; + + dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi); + if (!dev) + return -ENOMEM; + + if (spi->dev.platform_data) + dev->pdata = spi->dev.platform_data; + + ret = b53_switch_register(dev); + if (ret) + return ret; + + spi->dev.platform_data = dev; + + return 0; +} + +static int b53_spi_remove(struct spi_device *spi) +{ + struct b53_device *dev = spi->dev.platform_data; + + if (dev) { + struct b53_platform_data *pdata = dev->pdata; + b53_switch_remove(dev); + spi->dev.platform_data = pdata; + } + + return 0; +} + +static struct spi_driver b53_spi_driver = { + .driver = { + .name = "b53-switch", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = b53_spi_probe, + .remove = b53_spi_remove, +}; + +module_spi_driver(b53_spi_driver); + +MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); +MODULE_DESCRIPTION("B53 SPI access driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/target/linux/generic/files/drivers/net/phy/mvswitch.c b/target/linux/generic/files/drivers/net/phy/mvswitch.c index d754951ac..af73ec22a 100644 --- a/target/linux/generic/files/drivers/net/phy/mvswitch.c +++ b/target/linux/generic/files/drivers/net/phy/mvswitch.c @@ -348,16 +348,26 @@ mvswitch_config_aneg(struct phy_device *phydev) } static void -mvswitch_remove(struct phy_device *pdev) +mvswitch_detach(struct phy_device *pdev) { struct mvswitch_priv *priv = to_mvsw(pdev); struct net_device *dev = pdev->attached_dev; + if (!dev) + return; + dev->phy_ptr = NULL; dev->eth_mangle_rx = NULL; dev->eth_mangle_tx = NULL; dev->features = priv->orig_features; dev->priv_flags &= ~IFF_NO_IP_ALIGN; +} + +static void +mvswitch_remove(struct phy_device *pdev) +{ + struct mvswitch_priv *priv = to_mvsw(pdev); + kfree(priv); } @@ -399,6 +409,7 @@ static struct phy_driver mvswitch_driver = { .features = PHY_BASIC_FEATURES, .probe = &mvswitch_probe, .remove = &mvswitch_remove, + .detach = &mvswitch_detach, .config_init = &mvswitch_config_init, .config_aneg = &mvswitch_config_aneg, .read_status = &mvswitch_read_status, diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c index be85274e7..e2b4f02ef 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c +++ b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c @@ -15,6 +15,9 @@ #include <linux/gpio.h> #include <linux/spinlock.h> #include <linux/skbuff.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> #include <linux/rtl8366.h> #ifdef CONFIG_RTL8366_SMI_DEBUG_FS @@ -1106,6 +1109,7 @@ int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) port = &val->value.ports[0]; for (i = 0; i < val->len; i++, port++) { + int pvid = 0; member |= BIT(port->id); if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) @@ -1115,9 +1119,14 @@ int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) * To ensure that we have a valid MC entry for this VLAN, * initialize the port VLAN ID here. */ - err = rtl8366_set_pvid(smi, port->id, val->port_vlan); + err = rtl8366_get_pvid(smi, port->id, &pvid); if (err < 0) return err; + if (pvid == 0) { + err = rtl8366_set_pvid(smi, port->id, val->port_vlan); + if (err < 0) + return err; + } } return rtl8366_set_vlan(smi, val->port_vlan, member, untag, 0); @@ -1370,6 +1379,71 @@ void rtl8366_smi_cleanup(struct rtl8366_smi *smi) } EXPORT_SYMBOL_GPL(rtl8366_smi_cleanup); +#ifdef CONFIG_OF +int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi) +{ + int sck = of_get_named_gpio(pdev->dev.of_node, "gpio-sck", 0); + int sda = of_get_named_gpio(pdev->dev.of_node, "gpio-sda", 0); + + if (!gpio_is_valid(sck) || !gpio_is_valid(sda)) { + dev_err(&pdev->dev, "gpios missing in devictree\n"); + return -EINVAL; + } + + smi->gpio_sda = sda; + smi->gpio_sck = sck; + + return 0; +} +#else +static inline int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi) +{ + return -ENODEV; +} +#endif + +int rtl8366_smi_probe_plat(struct platform_device *pdev, struct rtl8366_smi *smi) +{ + struct rtl8366_platform_data *pdata = pdev->dev.platform_data; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data specified\n"); + return -EINVAL; + } + + smi->gpio_sda = pdata->gpio_sda; + smi->gpio_sck = pdata->gpio_sck; + smi->hw_reset = pdata->hw_reset; + + return 0; +} + + +struct rtl8366_smi *rtl8366_smi_probe(struct platform_device *pdev) +{ + struct rtl8366_smi *smi; + int err; + + smi = rtl8366_smi_alloc(&pdev->dev); + if (!smi) + return NULL; + + if (pdev->dev.of_node) + err = rtl8366_smi_probe_of(pdev, smi); + else + err = rtl8366_smi_probe_plat(pdev, smi); + + if (err) + goto free_smi; + + return smi; + +free_smi: + kfree(smi); + return NULL; +} +EXPORT_SYMBOL_GPL(rtl8366_smi_probe); + MODULE_DESCRIPTION("Realtek RTL8366 SMI interface driver"); MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); MODULE_LICENSE("GPL v2"); diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h index 700f83c1a..bd41385be 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h +++ b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h @@ -13,6 +13,7 @@ #include <linux/phy.h> #include <linux/switch.h> +#include <linux/platform_device.h> struct rtl8366_smi_ops; struct rtl8366_vlan_ops; @@ -146,4 +147,6 @@ int rtl8366_sw_set_vlan_enable(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val); +struct rtl8366_smi* rtl8366_smi_probe(struct platform_device *pdev); + #endif /* _RTL8366_SMI_H */ diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366rb.c b/target/linux/generic/files/drivers/net/phy/rtl8366rb.c index 775949792..d3674afcb 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8366rb.c +++ b/target/linux/generic/files/drivers/net/phy/rtl8366rb.c @@ -13,7 +13,9 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/delay.h> #include <linux/skbuff.h> #include <linux/rtl8366.h> @@ -1168,10 +1170,9 @@ static struct rtl8366_smi_ops rtl8366rb_smi_ops = { .enable_port = rtl8366rb_enable_port, }; -static int __devinit rtl8366rb_probe(struct platform_device *pdev) +static int rtl8366rb_probe(struct platform_device *pdev) { static int rtl8366_smi_version_printed; - struct rtl8366_platform_data *pdata; struct rtl8366_smi *smi; int err; @@ -1179,22 +1180,9 @@ static int __devinit rtl8366rb_probe(struct platform_device *pdev) printk(KERN_NOTICE RTL8366RB_DRIVER_DESC " version " RTL8366RB_DRIVER_VER"\n"); - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "no platform data specified\n"); - err = -EINVAL; - goto err_out; - } - - smi = rtl8366_smi_alloc(&pdev->dev); - if (!smi) { - err = -ENOMEM; - goto err_out; - } - - smi->gpio_sda = pdata->gpio_sda; - smi->gpio_sck = pdata->gpio_sck; - smi->hw_reset = pdata->hw_reset; + smi = rtl8366_smi_probe(pdev); + if (!smi) + return -ENODEV; smi->clk_delay = 10; smi->cmd_read = 0xa9; @@ -1223,11 +1211,10 @@ static int __devinit rtl8366rb_probe(struct platform_device *pdev) rtl8366_smi_cleanup(smi); err_free_smi: kfree(smi); - err_out: return err; } -static int __devexit rtl8366rb_remove(struct platform_device *pdev) +static int rtl8366rb_remove(struct platform_device *pdev) { struct rtl8366_smi *smi = platform_get_drvdata(pdev); @@ -1241,13 +1228,22 @@ static int __devexit rtl8366rb_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id rtl8366rb_match[] = { + { .compatible = "rtl8366rb" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rtl8366rb_match); +#endif + static struct platform_driver rtl8366rb_driver = { .driver = { .name = RTL8366RB_DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rtl8366rb_match), }, .probe = rtl8366rb_probe, - .remove = __devexit_p(rtl8366rb_remove), + .remove = rtl8366rb_remove, }; static int __init rtl8366rb_module_init(void) diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366s.c b/target/linux/generic/files/drivers/net/phy/rtl8366s.c index 77427d6c9..3dc2a87ae 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8366s.c +++ b/target/linux/generic/files/drivers/net/phy/rtl8366s.c @@ -12,7 +12,9 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/delay.h> #include <linux/skbuff.h> #include <linux/rtl8366.h> @@ -254,7 +256,7 @@ static int rtl8366s_setup(struct rtl8366_smi *smi) int err; pdata = smi->parent->platform_data; - if (pdata->num_initvals && pdata->initvals) { + if (pdata && pdata->num_initvals && pdata->initvals) { unsigned i; dev_info(smi->parent, "applying initvals\n"); @@ -1048,10 +1050,9 @@ static struct rtl8366_smi_ops rtl8366s_smi_ops = { .enable_port = rtl8366s_enable_port, }; -static int __devinit rtl8366s_probe(struct platform_device *pdev) +static int rtl8366s_probe(struct platform_device *pdev) { static int rtl8366_smi_version_printed; - struct rtl8366_platform_data *pdata; struct rtl8366_smi *smi; int err; @@ -1059,22 +1060,9 @@ static int __devinit rtl8366s_probe(struct platform_device *pdev) printk(KERN_NOTICE RTL8366S_DRIVER_DESC " version " RTL8366S_DRIVER_VER"\n"); - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "no platform data specified\n"); - err = -EINVAL; - goto err_out; - } - - smi = rtl8366_smi_alloc(&pdev->dev); - if (!smi) { - err = -ENOMEM; - goto err_out; - } - - smi->gpio_sda = pdata->gpio_sda; - smi->gpio_sck = pdata->gpio_sck; - smi->hw_reset = pdata->hw_reset; + smi = rtl8366_smi_probe(pdev); + if (!smi) + return -ENODEV; smi->clk_delay = 10; smi->cmd_read = 0xa9; @@ -1103,11 +1091,10 @@ static int __devinit rtl8366s_probe(struct platform_device *pdev) rtl8366_smi_cleanup(smi); err_free_smi: kfree(smi); - err_out: return err; } -static int __devexit rtl8366s_remove(struct platform_device *pdev) +static int rtl8366s_remove(struct platform_device *pdev) { struct rtl8366_smi *smi = platform_get_drvdata(pdev); @@ -1121,13 +1108,24 @@ static int __devexit rtl8366s_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id rtl8366s_match[] = { + { .compatible = "realtek,rtl8366s" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rtl8366s_match); +#endif + static struct platform_driver rtl8366s_driver = { .driver = { .name = RTL8366S_DRIVER_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(rtl8366s_match), +#endif }, .probe = rtl8366s_probe, - .remove = __devexit_p(rtl8366s_remove), + .remove = rtl8366s_remove, }; static int __init rtl8366s_module_init(void) diff --git a/target/linux/generic/files/drivers/net/phy/rtl8367.c b/target/linux/generic/files/drivers/net/phy/rtl8367.c index ee2a04701..3cd811e75 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8367.c +++ b/target/linux/generic/files/drivers/net/phy/rtl8367.c @@ -11,7 +11,9 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/delay.h> #include <linux/skbuff.h> #include <linux/rtl8367.h> @@ -1071,6 +1073,51 @@ static int rtl8367_led_blinkrate_set(struct rtl8366_smi *smi, unsigned int rate) return 0; } +#ifdef CONFIG_OF +static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id, + const char *name) +{ + struct rtl8367_extif_config *cfg; + const __be32 *prop; + int size; + int err; + + prop = of_get_property(smi->parent->of_node, name, &size); + if (!prop) + return rtl8367_extif_init(smi, id, NULL); + + if (size != (9 * sizeof(*prop))) { + dev_err(smi->parent, "%s property is invalid\n", name); + return -EINVAL; + } + + cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->txdelay = be32_to_cpup(prop++); + cfg->rxdelay = be32_to_cpup(prop++); + cfg->mode = be32_to_cpup(prop++); + cfg->ability.force_mode = be32_to_cpup(prop++); + cfg->ability.txpause = be32_to_cpup(prop++); + cfg->ability.rxpause = be32_to_cpup(prop++); + cfg->ability.link = be32_to_cpup(prop++); + cfg->ability.duplex = be32_to_cpup(prop++); + cfg->ability.speed = be32_to_cpup(prop++); + + err = rtl8367_extif_init(smi, id, cfg); + kfree(cfg); + + return err; +} +#else +static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id, + const char *name) +{ + return -EINVAL; +} +#endif + static int rtl8367_setup(struct rtl8366_smi *smi) { struct rtl8367_platform_data *pdata; @@ -1084,13 +1131,23 @@ static int rtl8367_setup(struct rtl8366_smi *smi) return err; /* initialize external interfaces */ - err = rtl8367_extif_init(smi, 0, pdata->extif0_cfg); - if (err) - return err; + if (smi->parent->of_node) { + err = rtl8367_extif_init_of(smi, 0, "realtek,extif0"); + if (err) + return err; - err = rtl8367_extif_init(smi, 1, pdata->extif1_cfg); - if (err) - return err; + err = rtl8367_extif_init_of(smi, 1, "realtek,extif1"); + if (err) + return err; + } else { + err = rtl8367_extif_init(smi, 0, pdata->extif0_cfg); + if (err) + return err; + + err = rtl8367_extif_init(smi, 1, pdata->extif1_cfg); + if (err) + return err; + } /* set maximum packet length to 1536 bytes */ REG_RMW(smi, RTL8367_SWC0_REG, RTL8367_SWC0_MAX_LENGTH_MASK, @@ -1671,28 +1728,14 @@ static struct rtl8366_smi_ops rtl8367_smi_ops = { .enable_port = rtl8367_enable_port, }; -static int __devinit rtl8367_probe(struct platform_device *pdev) +static int rtl8367_probe(struct platform_device *pdev) { - struct rtl8367_platform_data *pdata; struct rtl8366_smi *smi; int err; - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "no platform data specified\n"); - err = -EINVAL; - goto err_out; - } - - smi = rtl8366_smi_alloc(&pdev->dev); - if (!smi) { - err = -ENOMEM; - goto err_out; - } - - smi->gpio_sda = pdata->gpio_sda; - smi->gpio_sck = pdata->gpio_sck; - smi->hw_reset = pdata->hw_reset; + smi = rtl8366_smi_probe(pdev); + if (!smi) + return -ENODEV; smi->clk_delay = 1500; smi->cmd_read = 0xb9; @@ -1721,11 +1764,10 @@ static int __devinit rtl8367_probe(struct platform_device *pdev) rtl8366_smi_cleanup(smi); err_free_smi: kfree(smi); - err_out: return err; } -static int __devexit rtl8367_remove(struct platform_device *pdev) +static int rtl8367_remove(struct platform_device *pdev) { struct rtl8366_smi *smi = platform_get_drvdata(pdev); @@ -1747,13 +1789,24 @@ static void rtl8367_shutdown(struct platform_device *pdev) rtl8367_reset_chip(smi); } +#ifdef CONFIG_OF +static const struct of_device_id rtl8367_match[] = { + { .compatible = "realtek,rtl8367" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rtl8367_match); +#endif + static struct platform_driver rtl8367_driver = { .driver = { .name = RTL8367_DRIVER_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(rtl8367_match), +#endif }, .probe = rtl8367_probe, - .remove = __devexit_p(rtl8367_remove), + .remove = rtl8367_remove, .shutdown = rtl8367_shutdown, }; diff --git a/target/linux/generic/files/drivers/net/phy/rtl8367b.c b/target/linux/generic/files/drivers/net/phy/rtl8367b.c index 7acec7baf..a82f696d7 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8367b.c +++ b/target/linux/generic/files/drivers/net/phy/rtl8367b.c @@ -11,7 +11,9 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/delay.h> #include <linux/skbuff.h> #include <linux/rtl8367.h> @@ -860,6 +862,51 @@ static int rtl8367b_extif_init(struct rtl8366_smi *smi, int id, return 0; } +#ifdef CONFIG_OF +static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id, + const char *name) +{ + struct rtl8367_extif_config *cfg; + const __be32 *prop; + int size; + int err; + + prop = of_get_property(smi->parent->of_node, name, &size); + if (!prop) + return rtl8367b_extif_init(smi, id, NULL); + + if (size != (9 * sizeof(*prop))) { + dev_err(smi->parent, "%s property is invalid\n", name); + return -EINVAL; + } + + cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->txdelay = be32_to_cpup(prop++); + cfg->rxdelay = be32_to_cpup(prop++); + cfg->mode = be32_to_cpup(prop++); + cfg->ability.force_mode = be32_to_cpup(prop++); + cfg->ability.txpause = be32_to_cpup(prop++); + cfg->ability.rxpause = be32_to_cpup(prop++); + cfg->ability.link = be32_to_cpup(prop++); + cfg->ability.duplex = be32_to_cpup(prop++); + cfg->ability.speed = be32_to_cpup(prop++); + + err = rtl8367b_extif_init(smi, id, cfg); + kfree(cfg); + + return err; +} +#else +static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id, + const char *name) +{ + return -EINVAL; +} +#endif + static int rtl8367b_setup(struct rtl8366_smi *smi) { struct rtl8367_platform_data *pdata; @@ -873,13 +920,23 @@ static int rtl8367b_setup(struct rtl8366_smi *smi) return err; /* initialize external interfaces */ - err = rtl8367b_extif_init(smi, 0, pdata->extif0_cfg); - if (err) - return err; + if (smi->parent->of_node) { + err = rtl8367b_extif_init_of(smi, 0, "realtek,extif0"); + if (err) + return err; - err = rtl8367b_extif_init(smi, 1, pdata->extif1_cfg); - if (err) - return err; + err = rtl8367b_extif_init_of(smi, 1, "realtek,extif1"); + if (err) + return err; + } else { + err = rtl8367b_extif_init(smi, 0, pdata->extif0_cfg); + if (err) + return err; + + err = rtl8367b_extif_init(smi, 1, pdata->extif1_cfg); + if (err) + return err; + } /* set maximum packet length to 1536 bytes */ REG_RMW(smi, RTL8367B_SWC0_REG, RTL8367B_SWC0_MAX_LENGTH_MASK, @@ -1380,7 +1437,7 @@ static int rtl8367b_mii_write(struct mii_bus *bus, int addr, int reg, u16 val) return err; } -static int __devinit rtl8367b_detect(struct rtl8366_smi *smi) +static int rtl8367b_detect(struct rtl8366_smi *smi) { const char *chip_name; u32 chip_num; @@ -1453,27 +1510,15 @@ static struct rtl8366_smi_ops rtl8367b_smi_ops = { .enable_port = rtl8367b_enable_port, }; -static int __devinit rtl8367b_probe(struct platform_device *pdev) +static int rtl8367b_probe(struct platform_device *pdev) { - struct rtl8367_platform_data *pdata; struct rtl8366_smi *smi; int err; - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "no platform data specified\n"); - err = -EINVAL; - goto err_out; - } - - smi = rtl8366_smi_alloc(&pdev->dev); - if (!smi) { - err = -ENOMEM; - goto err_out; - } + smi = rtl8366_smi_probe(pdev); + if (!smi) + return -ENODEV; - smi->gpio_sda = pdata->gpio_sda; - smi->gpio_sck = pdata->gpio_sck; smi->clk_delay = 1500; smi->cmd_read = 0xb9; smi->cmd_write = 0xb8; @@ -1501,11 +1546,10 @@ static int __devinit rtl8367b_probe(struct platform_device *pdev) rtl8366_smi_cleanup(smi); err_free_smi: kfree(smi); - err_out: return err; } -static int __devexit rtl8367b_remove(struct platform_device *pdev) +static int rtl8367b_remove(struct platform_device *pdev) { struct rtl8366_smi *smi = platform_get_drvdata(pdev); @@ -1527,13 +1571,25 @@ static void rtl8367b_shutdown(struct platform_device *pdev) rtl8367b_reset_chip(smi); } +#ifdef CONFIG_OF +static const struct of_device_id rtl8367b_match[] = { + { .compatible = "realtek,rtl8367b" }, + { .compatible = "rtl8367b" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rtl8367b_match); +#endif + static struct platform_driver rtl8367b_driver = { .driver = { .name = RTL8367B_DRIVER_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(rtl8367b_match), +#endif }, .probe = rtl8367b_probe, - .remove = __devexit_p(rtl8367b_remove), + .remove = rtl8367b_remove, .shutdown = rtl8367b_shutdown, }; diff --git a/target/linux/generic/files/drivers/net/phy/swconfig.c b/target/linux/generic/files/drivers/net/phy/swconfig.c index 34f43bfd9..4e6d7136f 100644 --- a/target/linux/generic/files/drivers/net/phy/swconfig.c +++ b/target/linux/generic/files/drivers/net/phy/swconfig.c @@ -23,6 +23,7 @@ #include <linux/capability.h> #include <linux/skbuff.h> #include <linux/switch.h> +#include <linux/of.h> //#define DEBUG 1 #ifdef DEBUG @@ -844,7 +845,9 @@ static int swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags, const struct switch_dev *dev) { + struct nlattr *p = NULL, *m = NULL; void *hdr; + int i; hdr = genlmsg_put(msg, pid, seq, &switch_fam, flags, SWITCH_CMD_NEW_ATTR); @@ -866,6 +869,22 @@ swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (nla_put_u32(msg, SWITCH_ATTR_CPU_PORT, dev->cpu_port)) goto nla_put_failure; + m = nla_nest_start(msg, SWITCH_ATTR_PORTMAP); + if (!m) + goto nla_put_failure; + for (i = 0; i < dev->ports; i++) { + p = nla_nest_start(msg, SWITCH_ATTR_PORTS); + if (!p) + continue; + if (dev->portmap[i].s) { + if (nla_put_string(msg, SWITCH_PORTMAP_SEGMENT, dev->portmap[i].s)) + goto nla_put_failure; + if (nla_put_u32(msg, SWITCH_PORTMAP_VIRT, dev->portmap[i].virt)) + goto nla_put_failure; + } + nla_nest_end(msg, p); + } + nla_nest_end(msg, m); return genlmsg_end(msg, hdr); nla_put_failure: genlmsg_cancel(msg, hdr); @@ -954,6 +973,49 @@ static struct genl_ops swconfig_ops[] = { } }; +#ifdef CONFIG_OF +void +of_switch_load_portmap(struct switch_dev *dev) +{ + struct device_node *port; + + if (!dev->of_node) + return; + + for_each_child_of_node(dev->of_node, port) { + const __be32 *prop; + const char *segment; + int size, phys; + + if (!of_device_is_compatible(port, "swconfig,port")) + continue; + + if (of_property_read_string(port, "swconfig,segment", &segment)) + continue; + + prop = of_get_property(port, "swconfig,portmap", &size); + if (!prop) + continue; + + if (size != (2 * sizeof(*prop))) { + pr_err("%s: failed to parse port mapping\n", port->name); + continue; + } + + phys = be32_to_cpup(prop++); + if ((phys < 0) | (phys >= dev->ports)) { + pr_err("%s: physical port index out of range\n", port->name); + continue; + } + + dev->portmap[phys].s = kstrdup(segment, GFP_KERNEL); + dev->portmap[phys].virt = be32_to_cpup(prop); + pr_debug("Found port: %s, physical: %d, virtual: %d\n", + segment, phys, dev->portmap[phys].virt); + } +} +#endif + int register_switch(struct switch_dev *dev, struct net_device *netdev) { @@ -976,6 +1038,12 @@ register_switch(struct switch_dev *dev, struct net_device *netdev) GFP_KERNEL); if (!dev->portbuf) return -ENOMEM; + dev->portmap = kzalloc(sizeof(struct switch_portmap) * dev->ports, + GFP_KERNEL); + if (!dev->portmap) { + kfree(dev->portbuf); + return -ENOMEM; + } } swconfig_defaults_init(dev); mutex_init(&dev->sw_mutex); @@ -997,6 +1065,11 @@ register_switch(struct switch_dev *dev, struct net_device *netdev) return -ENFILE; } +#ifdef CONFIG_OF + if (dev->ports) + of_switch_load_portmap(dev); +#endif + /* fill device name */ snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i); diff --git a/target/linux/generic/files/drivers/pwm/gpio-pwm.c b/target/linux/generic/files/drivers/pwm/gpio-pwm.c index dff5d1d62..58e518fe7 100644 --- a/target/linux/generic/files/drivers/pwm/gpio-pwm.c +++ b/target/linux/generic/files/drivers/pwm/gpio-pwm.c @@ -205,7 +205,7 @@ gpio_pwm_request(struct pwm_channel *p) return 0; } -static int __devinit +static int gpio_pwm_probe(struct platform_device *pdev) { struct gpio_pwm *gp; @@ -256,7 +256,7 @@ err_alloc: return ret; } -static int __devexit +static int gpio_pwm_remove(struct platform_device *pdev) { struct gpio_pwm *gp = platform_get_drvdata(pdev); @@ -277,7 +277,7 @@ static struct platform_driver gpio_pwm_driver = { .owner = THIS_MODULE, }, .probe = gpio_pwm_probe, - .remove = __devexit_p(gpio_pwm_remove), + .remove = gpio_pwm_remove, }; static int __init gpio_pwm_init(void) diff --git a/target/linux/generic/files/include/linux/ar8216_platform.h b/target/linux/generic/files/include/linux/ar8216_platform.h index f5c2ef065..c6c6144e8 100644 --- a/target/linux/generic/files/include/linux/ar8216_platform.h +++ b/target/linux/generic/files/include/linux/ar8216_platform.h @@ -44,6 +44,7 @@ struct ar8327_pad_cfg { bool pipe_rxclk_sel; bool txclk_delay_en; bool rxclk_delay_en; + bool sgmii_delay_en; enum ar8327_clk_delay_sel txclk_delay_sel; enum ar8327_clk_delay_sel rxclk_delay_sel; }; @@ -74,7 +75,8 @@ struct ar8327_platform_data { struct ar8327_pad_cfg *pad0_cfg; struct ar8327_pad_cfg *pad5_cfg; struct ar8327_pad_cfg *pad6_cfg; - struct ar8327_port_cfg cpuport_cfg; + struct ar8327_port_cfg port0_cfg; + struct ar8327_port_cfg port6_cfg; struct ar8327_led_cfg *led_cfg; }; diff --git a/target/linux/generic/files/include/linux/ath9k_platform.h b/target/linux/generic/files/include/linux/ath9k_platform.h index 53b25d229..a0b7531fd 100644 --- a/target/linux/generic/files/include/linux/ath9k_platform.h +++ b/target/linux/generic/files/include/linux/ath9k_platform.h @@ -22,6 +22,8 @@ #define ATH9K_PLAT_EEP_MAX_WORDS 2048 struct ath9k_platform_data { + const char *eeprom_name; + u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS]; u8 *macaddr; diff --git a/target/linux/generic/files/include/linux/platform_data/b53.h b/target/linux/generic/files/include/linux/platform_data/b53.h new file mode 100644 index 000000000..78427417a --- /dev/null +++ b/target/linux/generic/files/include/linux/platform_data/b53.h @@ -0,0 +1,36 @@ +/* + * B53 platform data + * + * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __B53_H +#define __B53_H + +#include <linux/kernel.h> + +struct b53_platform_data { + u32 chip_id; + u16 enabled_ports; + + /* allow to specify an ethX alias */ + const char *alias; + + /* only used by MMAP'd driver */ + unsigned big_endian:1; + void __iomem *regs; +}; + +#endif diff --git a/target/linux/generic/files/include/linux/platform_data/brcmfmac-sdio.h b/target/linux/generic/files/include/linux/platform_data/brcmfmac-sdio.h new file mode 100644 index 000000000..1ade657d5 --- /dev/null +++ b/target/linux/generic/files/include/linux/platform_data/brcmfmac-sdio.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2013 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LINUX_BRCMFMAC_PLATFORM_H +#define _LINUX_BRCMFMAC_PLATFORM_H + +/* + * Platform specific driver functions and data. Through the platform specific + * device data functions can be provided to help the brcmfmac driver to + * operate with the device in combination with the used platform. + * + * Use the platform data in the following (similar) way: + * + * +#include <brcmfmac_platform.h> + + +static void brcmfmac_power_on(void) +{ +} + +static void brcmfmac_power_off(void) +{ +} + +static void brcmfmac_reset(void) +{ +} + +static struct brcmfmac_sdio_platform_data brcmfmac_sdio_pdata = { + .power_on = brcmfmac_power_on, + .power_off = brcmfmac_power_off, + .reset = brcmfmac_reset +}; + +static struct platform_device brcmfmac_device = { + .name = BRCMFMAC_SDIO_PDATA_NAME, + .id = PLATFORM_DEVID_NONE, + .dev.platform_data = &brcmfmac_sdio_pdata +}; + +void __init brcmfmac_init_pdata(void) +{ + brcmfmac_sdio_pdata.oob_irq_supported = true; + brcmfmac_sdio_pdata.oob_irq_nr = gpio_to_irq(GPIO_BRCMF_SDIO_OOB); + brcmfmac_sdio_pdata.oob_irq_flags = IORESOURCE_IRQ | + IORESOURCE_IRQ_HIGHLEVEL; + platform_device_register(&brcmfmac_device); +} + * + * + * Note: the brcmfmac can be loaded as module or be statically built-in into + * the kernel. If built-in then do note that it uses module_init (and + * module_exit) routines which equal device_initcall. So if you intend to + * create a module with the platform specific data for the brcmfmac and have + * it built-in to the kernel then use a higher initcall then device_initcall + * (see init.h). If this is not done then brcmfmac will load without problems + * but will not pickup the platform data. + * + * When the driver does not "detect" platform driver data then it will continue + * without reporting anything and just assume there is no data needed. Which is + * probably true for most platforms. + * + * Explanation of the platform_data fields: + * + * drive_strength: is the preferred drive_strength to be used for the SDIO + * pins. If 0 then a default value will be used. This is the target drive + * strength, the exact drive strength which will be used depends on the + * capabilities of the device. + * + * oob_irq_supported: does the board have support for OOB interrupts. SDIO + * in-band interrupts are relatively slow and for having less overhead on + * interrupt processing an out of band interrupt can be used. If the HW + * supports this then enable this by setting this field to true and configure + * the oob related fields. + * + * oob_irq_nr, oob_irq_flags: the OOB interrupt information. The values are + * used for registering the irq using request_irq function. + * + * power_on: This function is called by the brcmfmac when the module gets + * loaded. This can be particularly useful for low power devices. The platform + * spcific routine may for example decide to power up the complete device. + * If there is no use-case for this function then provide NULL. + * + * power_off: This function is called by the brcmfmac when the module gets + * unloaded. At this point the device can be powered down or otherwise be reset. + * So if an actual power_off is not supported but reset is then reset the device + * when this function gets called. This can be particularly useful for low power + * devices. If there is no use-case for this function (either power-down or + * reset) then provide NULL. + * + * reset: This function can get called if the device communication broke down. + * This functionality is particularly useful in case of SDIO type devices. It is + * possible to reset a dongle via sdio data interface, but it requires that + * this is fully functional. This function is chip/module specific and this + * function should return only after the complete reset has completed. + */ + +#define BRCMFMAC_SDIO_PDATA_NAME "brcmfmac_sdio" + +struct brcmfmac_sdio_platform_data { + unsigned int drive_strength; + bool oob_irq_supported; + unsigned int oob_irq_nr; + unsigned long oob_irq_flags; + void (*power_on)(void); + void (*power_off)(void); + void (*reset)(void); +}; + +#endif /* _LINUX_BRCMFMAC_PLATFORM_H */ diff --git a/target/linux/generic/files/include/linux/switch.h b/target/linux/generic/files/include/linux/switch.h index 4f4085ef8..b53431e0a 100644 --- a/target/linux/generic/files/include/linux/switch.h +++ b/target/linux/generic/files/include/linux/switch.h @@ -13,86 +13,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#ifndef _LINUX_SWITCH_H +#define _LINUX_SWITCH_H -#ifndef __LINUX_SWITCH_H -#define __LINUX_SWITCH_H - -#include <linux/types.h> -#include <linux/netdevice.h> -#include <linux/netlink.h> -#include <linux/genetlink.h> -#ifndef __KERNEL__ -#include <netlink/netlink.h> -#include <netlink/genl/genl.h> -#include <netlink/genl/ctrl.h> -#else #include <net/genetlink.h> -#endif - -/* main attributes */ -enum { - SWITCH_ATTR_UNSPEC, - /* global */ - SWITCH_ATTR_TYPE, - /* device */ - SWITCH_ATTR_ID, - SWITCH_ATTR_DEV_NAME, - SWITCH_ATTR_ALIAS, - SWITCH_ATTR_NAME, - SWITCH_ATTR_VLANS, - SWITCH_ATTR_PORTS, - SWITCH_ATTR_CPU_PORT, - /* attributes */ - SWITCH_ATTR_OP_ID, - SWITCH_ATTR_OP_TYPE, - SWITCH_ATTR_OP_NAME, - SWITCH_ATTR_OP_PORT, - SWITCH_ATTR_OP_VLAN, - SWITCH_ATTR_OP_VALUE_INT, - SWITCH_ATTR_OP_VALUE_STR, - SWITCH_ATTR_OP_VALUE_PORTS, - SWITCH_ATTR_OP_DESCRIPTION, - /* port lists */ - SWITCH_ATTR_PORT, - SWITCH_ATTR_MAX -}; - -/* commands */ -enum { - SWITCH_CMD_UNSPEC, - SWITCH_CMD_GET_SWITCH, - SWITCH_CMD_NEW_ATTR, - SWITCH_CMD_LIST_GLOBAL, - SWITCH_CMD_GET_GLOBAL, - SWITCH_CMD_SET_GLOBAL, - SWITCH_CMD_LIST_PORT, - SWITCH_CMD_GET_PORT, - SWITCH_CMD_SET_PORT, - SWITCH_CMD_LIST_VLAN, - SWITCH_CMD_GET_VLAN, - SWITCH_CMD_SET_VLAN -}; - -/* data types */ -enum switch_val_type { - SWITCH_TYPE_UNSPEC, - SWITCH_TYPE_INT, - SWITCH_TYPE_STRING, - SWITCH_TYPE_PORTS, - SWITCH_TYPE_NOVAL, -}; - -/* port nested attributes */ -enum { - SWITCH_PORT_UNSPEC, - SWITCH_PORT_ID, - SWITCH_PORT_FLAG_TAGGED, - SWITCH_PORT_ATTR_MAX -}; - -#define SWITCH_ATTR_DEFAULTS_OFFSET 0x1000 - -#ifdef __KERNEL__ +#include <uapi/linux/switch.h> struct switch_dev; struct switch_op; @@ -173,6 +98,7 @@ struct switch_dev_ops { }; struct switch_dev { + struct device_node *of_node; const struct switch_dev_ops *ops; /* will be automatically filled */ char devname[IFNAMSIZ]; @@ -193,6 +119,7 @@ struct switch_dev { struct mutex sw_mutex; struct switch_port *portbuf; + struct switch_portmap *portmap; char buf[128]; @@ -206,6 +133,11 @@ struct switch_port { u32 flags; }; +struct switch_portmap { + u32 virt; + const char *s; +}; + struct switch_val { const struct switch_attr *attr; int port_vlan; @@ -232,6 +164,4 @@ struct switch_attr { int max; }; -#endif - -#endif +#endif /* _LINUX_SWITCH_H */ diff --git a/target/linux/generic/files/include/uapi/linux/switch.h b/target/linux/generic/files/include/uapi/linux/switch.h new file mode 100644 index 000000000..a59b23953 --- /dev/null +++ b/target/linux/generic/files/include/uapi/linux/switch.h @@ -0,0 +1,103 @@ +/* + * switch.h: 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. + */ + +#ifndef _UAPI_LINUX_SWITCH_H +#define _UAPI_LINUX_SWITCH_H + +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/netlink.h> +#include <linux/genetlink.h> +#ifndef __KERNEL__ +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#endif + +/* main attributes */ +enum { + SWITCH_ATTR_UNSPEC, + /* global */ + SWITCH_ATTR_TYPE, + /* device */ + SWITCH_ATTR_ID, + SWITCH_ATTR_DEV_NAME, + SWITCH_ATTR_ALIAS, + SWITCH_ATTR_NAME, + SWITCH_ATTR_VLANS, + SWITCH_ATTR_PORTS, + SWITCH_ATTR_PORTMAP, + SWITCH_ATTR_CPU_PORT, + /* attributes */ + SWITCH_ATTR_OP_ID, + SWITCH_ATTR_OP_TYPE, + SWITCH_ATTR_OP_NAME, + SWITCH_ATTR_OP_PORT, + SWITCH_ATTR_OP_VLAN, + SWITCH_ATTR_OP_VALUE_INT, + SWITCH_ATTR_OP_VALUE_STR, + SWITCH_ATTR_OP_VALUE_PORTS, + SWITCH_ATTR_OP_DESCRIPTION, + /* port lists */ + SWITCH_ATTR_PORT, + SWITCH_ATTR_MAX +}; + +enum { + /* port map */ + SWITCH_PORTMAP_PORTS, + SWITCH_PORTMAP_SEGMENT, + SWITCH_PORTMAP_VIRT, + SWITCH_PORTMAP_MAX +}; + +/* commands */ +enum { + SWITCH_CMD_UNSPEC, + SWITCH_CMD_GET_SWITCH, + SWITCH_CMD_NEW_ATTR, + SWITCH_CMD_LIST_GLOBAL, + SWITCH_CMD_GET_GLOBAL, + SWITCH_CMD_SET_GLOBAL, + SWITCH_CMD_LIST_PORT, + SWITCH_CMD_GET_PORT, + SWITCH_CMD_SET_PORT, + SWITCH_CMD_LIST_VLAN, + SWITCH_CMD_GET_VLAN, + SWITCH_CMD_SET_VLAN +}; + +/* data types */ +enum switch_val_type { + SWITCH_TYPE_UNSPEC, + SWITCH_TYPE_INT, + SWITCH_TYPE_STRING, + SWITCH_TYPE_PORTS, + SWITCH_TYPE_NOVAL, +}; + +/* port nested attributes */ +enum { + SWITCH_PORT_UNSPEC, + SWITCH_PORT_ID, + SWITCH_PORT_FLAG_TAGGED, + SWITCH_PORT_ATTR_MAX +}; + +#define SWITCH_ATTR_DEFAULTS_OFFSET 0x1000 + + +#endif /* _UAPI_LINUX_SWITCH_H */ |