162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* niu.c: Neptune ethernet driver. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007, 2008 David S. Miller (davem@davemloft.net) 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1462306a36Sopenharmony_ci#include <linux/netdevice.h> 1562306a36Sopenharmony_ci#include <linux/ethtool.h> 1662306a36Sopenharmony_ci#include <linux/etherdevice.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/bitops.h> 2062306a36Sopenharmony_ci#include <linux/mii.h> 2162306a36Sopenharmony_ci#include <linux/if.h> 2262306a36Sopenharmony_ci#include <linux/if_ether.h> 2362306a36Sopenharmony_ci#include <linux/if_vlan.h> 2462306a36Sopenharmony_ci#include <linux/ip.h> 2562306a36Sopenharmony_ci#include <linux/in.h> 2662306a36Sopenharmony_ci#include <linux/ipv6.h> 2762306a36Sopenharmony_ci#include <linux/log2.h> 2862306a36Sopenharmony_ci#include <linux/jiffies.h> 2962306a36Sopenharmony_ci#include <linux/crc32.h> 3062306a36Sopenharmony_ci#include <linux/list.h> 3162306a36Sopenharmony_ci#include <linux/slab.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <linux/io.h> 3462306a36Sopenharmony_ci#include <linux/of.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "niu.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* This driver wants to store a link to a "next page" within the 3962306a36Sopenharmony_ci * page struct itself by overloading the content of the "mapping" 4062306a36Sopenharmony_ci * member. This is not expected by the page API, but does currently 4162306a36Sopenharmony_ci * work. However, the randstruct plugin gets very bothered by this 4262306a36Sopenharmony_ci * case because "mapping" (struct address_space) is randomized, so 4362306a36Sopenharmony_ci * casts to/from it trigger warnings. Hide this by way of a union, 4462306a36Sopenharmony_ci * to create a typed alias of "mapping", since that's how it is 4562306a36Sopenharmony_ci * actually being used here. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ciunion niu_page { 4862306a36Sopenharmony_ci struct page page; 4962306a36Sopenharmony_ci struct { 5062306a36Sopenharmony_ci unsigned long __flags; /* unused alias of "flags" */ 5162306a36Sopenharmony_ci struct list_head __lru; /* unused alias of "lru" */ 5262306a36Sopenharmony_ci struct page *next; /* alias of "mapping" */ 5362306a36Sopenharmony_ci }; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci#define niu_next_page(p) container_of(p, union niu_page, page)->next 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define DRV_MODULE_NAME "niu" 5862306a36Sopenharmony_ci#define DRV_MODULE_VERSION "1.1" 5962306a36Sopenharmony_ci#define DRV_MODULE_RELDATE "Apr 22, 2010" 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic char version[] = 6262306a36Sopenharmony_ci DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciMODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); 6562306a36Sopenharmony_ciMODULE_DESCRIPTION("NIU ethernet driver"); 6662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 6762306a36Sopenharmony_ciMODULE_VERSION(DRV_MODULE_VERSION); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#ifndef readq 7062306a36Sopenharmony_cistatic u64 readq(void __iomem *reg) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci return ((u64) readl(reg)) | (((u64) readl(reg + 4UL)) << 32); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void writeq(u64 val, void __iomem *reg) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci writel(val & 0xffffffff, reg); 7862306a36Sopenharmony_ci writel(val >> 32, reg + 0x4UL); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci#endif 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic const struct pci_device_id niu_pci_tbl[] = { 8362306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_SUN, 0xabcd)}, 8462306a36Sopenharmony_ci {} 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, niu_pci_tbl); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define NIU_TX_TIMEOUT (5 * HZ) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define nr64(reg) readq(np->regs + (reg)) 9262306a36Sopenharmony_ci#define nw64(reg, val) writeq((val), np->regs + (reg)) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define nr64_mac(reg) readq(np->mac_regs + (reg)) 9562306a36Sopenharmony_ci#define nw64_mac(reg, val) writeq((val), np->mac_regs + (reg)) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define nr64_ipp(reg) readq(np->regs + np->ipp_off + (reg)) 9862306a36Sopenharmony_ci#define nw64_ipp(reg, val) writeq((val), np->regs + np->ipp_off + (reg)) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define nr64_pcs(reg) readq(np->regs + np->pcs_off + (reg)) 10162306a36Sopenharmony_ci#define nw64_pcs(reg, val) writeq((val), np->regs + np->pcs_off + (reg)) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define nr64_xpcs(reg) readq(np->regs + np->xpcs_off + (reg)) 10462306a36Sopenharmony_ci#define nw64_xpcs(reg, val) writeq((val), np->regs + np->xpcs_off + (reg)) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define NIU_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int niu_debug; 10962306a36Sopenharmony_cistatic int debug = -1; 11062306a36Sopenharmony_cimodule_param(debug, int, 0); 11162306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "NIU debug level"); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define niu_lock_parent(np, flags) \ 11462306a36Sopenharmony_ci spin_lock_irqsave(&np->parent->lock, flags) 11562306a36Sopenharmony_ci#define niu_unlock_parent(np, flags) \ 11662306a36Sopenharmony_ci spin_unlock_irqrestore(&np->parent->lock, flags) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int serdes_init_10g_serdes(struct niu *np); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int __niu_wait_bits_clear_mac(struct niu *np, unsigned long reg, 12162306a36Sopenharmony_ci u64 bits, int limit, int delay) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci while (--limit >= 0) { 12462306a36Sopenharmony_ci u64 val = nr64_mac(reg); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!(val & bits)) 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci udelay(delay); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci if (limit < 0) 13162306a36Sopenharmony_ci return -ENODEV; 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int __niu_set_and_wait_clear_mac(struct niu *np, unsigned long reg, 13662306a36Sopenharmony_ci u64 bits, int limit, int delay, 13762306a36Sopenharmony_ci const char *reg_name) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci int err; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci nw64_mac(reg, bits); 14262306a36Sopenharmony_ci err = __niu_wait_bits_clear_mac(np, reg, bits, limit, delay); 14362306a36Sopenharmony_ci if (err) 14462306a36Sopenharmony_ci netdev_err(np->dev, "bits (%llx) of register %s would not clear, val[%llx]\n", 14562306a36Sopenharmony_ci (unsigned long long)bits, reg_name, 14662306a36Sopenharmony_ci (unsigned long long)nr64_mac(reg)); 14762306a36Sopenharmony_ci return err; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#define niu_set_and_wait_clear_mac(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \ 15162306a36Sopenharmony_ci({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \ 15262306a36Sopenharmony_ci __niu_set_and_wait_clear_mac(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \ 15362306a36Sopenharmony_ci}) 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int __niu_wait_bits_clear_ipp(struct niu *np, unsigned long reg, 15662306a36Sopenharmony_ci u64 bits, int limit, int delay) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci while (--limit >= 0) { 15962306a36Sopenharmony_ci u64 val = nr64_ipp(reg); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (!(val & bits)) 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci udelay(delay); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci if (limit < 0) 16662306a36Sopenharmony_ci return -ENODEV; 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int __niu_set_and_wait_clear_ipp(struct niu *np, unsigned long reg, 17162306a36Sopenharmony_ci u64 bits, int limit, int delay, 17262306a36Sopenharmony_ci const char *reg_name) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci int err; 17562306a36Sopenharmony_ci u64 val; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci val = nr64_ipp(reg); 17862306a36Sopenharmony_ci val |= bits; 17962306a36Sopenharmony_ci nw64_ipp(reg, val); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci err = __niu_wait_bits_clear_ipp(np, reg, bits, limit, delay); 18262306a36Sopenharmony_ci if (err) 18362306a36Sopenharmony_ci netdev_err(np->dev, "bits (%llx) of register %s would not clear, val[%llx]\n", 18462306a36Sopenharmony_ci (unsigned long long)bits, reg_name, 18562306a36Sopenharmony_ci (unsigned long long)nr64_ipp(reg)); 18662306a36Sopenharmony_ci return err; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci#define niu_set_and_wait_clear_ipp(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \ 19062306a36Sopenharmony_ci({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \ 19162306a36Sopenharmony_ci __niu_set_and_wait_clear_ipp(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \ 19262306a36Sopenharmony_ci}) 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int __niu_wait_bits_clear(struct niu *np, unsigned long reg, 19562306a36Sopenharmony_ci u64 bits, int limit, int delay) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci while (--limit >= 0) { 19862306a36Sopenharmony_ci u64 val = nr64(reg); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!(val & bits)) 20162306a36Sopenharmony_ci break; 20262306a36Sopenharmony_ci udelay(delay); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci if (limit < 0) 20562306a36Sopenharmony_ci return -ENODEV; 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci#define niu_wait_bits_clear(NP, REG, BITS, LIMIT, DELAY) \ 21062306a36Sopenharmony_ci({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \ 21162306a36Sopenharmony_ci __niu_wait_bits_clear(NP, REG, BITS, LIMIT, DELAY); \ 21262306a36Sopenharmony_ci}) 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int __niu_set_and_wait_clear(struct niu *np, unsigned long reg, 21562306a36Sopenharmony_ci u64 bits, int limit, int delay, 21662306a36Sopenharmony_ci const char *reg_name) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci int err; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci nw64(reg, bits); 22162306a36Sopenharmony_ci err = __niu_wait_bits_clear(np, reg, bits, limit, delay); 22262306a36Sopenharmony_ci if (err) 22362306a36Sopenharmony_ci netdev_err(np->dev, "bits (%llx) of register %s would not clear, val[%llx]\n", 22462306a36Sopenharmony_ci (unsigned long long)bits, reg_name, 22562306a36Sopenharmony_ci (unsigned long long)nr64(reg)); 22662306a36Sopenharmony_ci return err; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci#define niu_set_and_wait_clear(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \ 23062306a36Sopenharmony_ci({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \ 23162306a36Sopenharmony_ci __niu_set_and_wait_clear(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \ 23262306a36Sopenharmony_ci}) 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic void niu_ldg_rearm(struct niu *np, struct niu_ldg *lp, int on) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci u64 val = (u64) lp->timer; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (on) 23962306a36Sopenharmony_ci val |= LDG_IMGMT_ARM; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci nw64(LDG_IMGMT(lp->ldg_num), val); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int niu_ldn_irq_enable(struct niu *np, int ldn, int on) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci unsigned long mask_reg, bits; 24762306a36Sopenharmony_ci u64 val; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (ldn < 0 || ldn > LDN_MAX) 25062306a36Sopenharmony_ci return -EINVAL; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (ldn < 64) { 25362306a36Sopenharmony_ci mask_reg = LD_IM0(ldn); 25462306a36Sopenharmony_ci bits = LD_IM0_MASK; 25562306a36Sopenharmony_ci } else { 25662306a36Sopenharmony_ci mask_reg = LD_IM1(ldn - 64); 25762306a36Sopenharmony_ci bits = LD_IM1_MASK; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci val = nr64(mask_reg); 26162306a36Sopenharmony_ci if (on) 26262306a36Sopenharmony_ci val &= ~bits; 26362306a36Sopenharmony_ci else 26462306a36Sopenharmony_ci val |= bits; 26562306a36Sopenharmony_ci nw64(mask_reg, val); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic int niu_enable_ldn_in_ldg(struct niu *np, struct niu_ldg *lp, int on) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 27362306a36Sopenharmony_ci int i; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci for (i = 0; i <= LDN_MAX; i++) { 27662306a36Sopenharmony_ci int err; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (parent->ldg_map[i] != lp->ldg_num) 27962306a36Sopenharmony_ci continue; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci err = niu_ldn_irq_enable(np, i, on); 28262306a36Sopenharmony_ci if (err) 28362306a36Sopenharmony_ci return err; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int niu_enable_interrupts(struct niu *np, int on) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci int i; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci for (i = 0; i < np->num_ldg; i++) { 29362306a36Sopenharmony_ci struct niu_ldg *lp = &np->ldg[i]; 29462306a36Sopenharmony_ci int err; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci err = niu_enable_ldn_in_ldg(np, lp, on); 29762306a36Sopenharmony_ci if (err) 29862306a36Sopenharmony_ci return err; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci for (i = 0; i < np->num_ldg; i++) 30162306a36Sopenharmony_ci niu_ldg_rearm(np, &np->ldg[i], on); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic u32 phy_encode(u32 type, int port) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci return type << (port * 2); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic u32 phy_decode(u32 val, int port) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci return (val >> (port * 2)) & PORT_TYPE_MASK; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int mdio_wait(struct niu *np) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci int limit = 1000; 31962306a36Sopenharmony_ci u64 val; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci while (--limit > 0) { 32262306a36Sopenharmony_ci val = nr64(MIF_FRAME_OUTPUT); 32362306a36Sopenharmony_ci if ((val >> MIF_FRAME_OUTPUT_TA_SHIFT) & 0x1) 32462306a36Sopenharmony_ci return val & MIF_FRAME_OUTPUT_DATA; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci udelay(10); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return -ENODEV; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int mdio_read(struct niu *np, int port, int dev, int reg) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int err; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci nw64(MIF_FRAME_OUTPUT, MDIO_ADDR_OP(port, dev, reg)); 33762306a36Sopenharmony_ci err = mdio_wait(np); 33862306a36Sopenharmony_ci if (err < 0) 33962306a36Sopenharmony_ci return err; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci nw64(MIF_FRAME_OUTPUT, MDIO_READ_OP(port, dev)); 34262306a36Sopenharmony_ci return mdio_wait(np); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int mdio_write(struct niu *np, int port, int dev, int reg, int data) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci int err; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci nw64(MIF_FRAME_OUTPUT, MDIO_ADDR_OP(port, dev, reg)); 35062306a36Sopenharmony_ci err = mdio_wait(np); 35162306a36Sopenharmony_ci if (err < 0) 35262306a36Sopenharmony_ci return err; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci nw64(MIF_FRAME_OUTPUT, MDIO_WRITE_OP(port, dev, data)); 35562306a36Sopenharmony_ci err = mdio_wait(np); 35662306a36Sopenharmony_ci if (err < 0) 35762306a36Sopenharmony_ci return err; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int mii_read(struct niu *np, int port, int reg) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci nw64(MIF_FRAME_OUTPUT, MII_READ_OP(port, reg)); 36562306a36Sopenharmony_ci return mdio_wait(np); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic int mii_write(struct niu *np, int port, int reg, int data) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci int err; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci nw64(MIF_FRAME_OUTPUT, MII_WRITE_OP(port, reg, data)); 37362306a36Sopenharmony_ci err = mdio_wait(np); 37462306a36Sopenharmony_ci if (err < 0) 37562306a36Sopenharmony_ci return err; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int esr2_set_tx_cfg(struct niu *np, unsigned long channel, u32 val) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci int err; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 38562306a36Sopenharmony_ci ESR2_TI_PLL_TX_CFG_L(channel), 38662306a36Sopenharmony_ci val & 0xffff); 38762306a36Sopenharmony_ci if (!err) 38862306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 38962306a36Sopenharmony_ci ESR2_TI_PLL_TX_CFG_H(channel), 39062306a36Sopenharmony_ci val >> 16); 39162306a36Sopenharmony_ci return err; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int esr2_set_rx_cfg(struct niu *np, unsigned long channel, u32 val) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int err; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 39962306a36Sopenharmony_ci ESR2_TI_PLL_RX_CFG_L(channel), 40062306a36Sopenharmony_ci val & 0xffff); 40162306a36Sopenharmony_ci if (!err) 40262306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 40362306a36Sopenharmony_ci ESR2_TI_PLL_RX_CFG_H(channel), 40462306a36Sopenharmony_ci val >> 16); 40562306a36Sopenharmony_ci return err; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/* Mode is always 10G fiber. */ 40962306a36Sopenharmony_cistatic int serdes_init_niu_10g_fiber(struct niu *np) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 41262306a36Sopenharmony_ci u32 tx_cfg, rx_cfg; 41362306a36Sopenharmony_ci unsigned long i; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci tx_cfg = (PLL_TX_CFG_ENTX | PLL_TX_CFG_SWING_1375MV); 41662306a36Sopenharmony_ci rx_cfg = (PLL_RX_CFG_ENRX | PLL_RX_CFG_TERM_0P8VDDT | 41762306a36Sopenharmony_ci PLL_RX_CFG_ALIGN_ENA | PLL_RX_CFG_LOS_LTHRESH | 41862306a36Sopenharmony_ci PLL_RX_CFG_EQ_LP_ADAPTIVE); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_PHY) { 42162306a36Sopenharmony_ci u16 test_cfg = PLL_TEST_CFG_LOOPBACK_CML_DIS; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 42462306a36Sopenharmony_ci ESR2_TI_PLL_TEST_CFG_L, test_cfg); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci tx_cfg |= PLL_TX_CFG_ENTEST; 42762306a36Sopenharmony_ci rx_cfg |= PLL_RX_CFG_ENTEST; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Initialize all 4 lanes of the SERDES. */ 43162306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 43262306a36Sopenharmony_ci int err = esr2_set_tx_cfg(np, i, tx_cfg); 43362306a36Sopenharmony_ci if (err) 43462306a36Sopenharmony_ci return err; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 43862306a36Sopenharmony_ci int err = esr2_set_rx_cfg(np, i, rx_cfg); 43962306a36Sopenharmony_ci if (err) 44062306a36Sopenharmony_ci return err; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int serdes_init_niu_1g_serdes(struct niu *np) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 44962306a36Sopenharmony_ci u16 pll_cfg, pll_sts; 45062306a36Sopenharmony_ci int max_retry = 100; 45162306a36Sopenharmony_ci u64 sig, mask, val; 45262306a36Sopenharmony_ci u32 tx_cfg, rx_cfg; 45362306a36Sopenharmony_ci unsigned long i; 45462306a36Sopenharmony_ci int err; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci tx_cfg = (PLL_TX_CFG_ENTX | PLL_TX_CFG_SWING_1375MV | 45762306a36Sopenharmony_ci PLL_TX_CFG_RATE_HALF); 45862306a36Sopenharmony_ci rx_cfg = (PLL_RX_CFG_ENRX | PLL_RX_CFG_TERM_0P8VDDT | 45962306a36Sopenharmony_ci PLL_RX_CFG_ALIGN_ENA | PLL_RX_CFG_LOS_LTHRESH | 46062306a36Sopenharmony_ci PLL_RX_CFG_RATE_HALF); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (np->port == 0) 46362306a36Sopenharmony_ci rx_cfg |= PLL_RX_CFG_EQ_LP_ADAPTIVE; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_PHY) { 46662306a36Sopenharmony_ci u16 test_cfg = PLL_TEST_CFG_LOOPBACK_CML_DIS; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 46962306a36Sopenharmony_ci ESR2_TI_PLL_TEST_CFG_L, test_cfg); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci tx_cfg |= PLL_TX_CFG_ENTEST; 47262306a36Sopenharmony_ci rx_cfg |= PLL_RX_CFG_ENTEST; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* Initialize PLL for 1G */ 47662306a36Sopenharmony_ci pll_cfg = (PLL_CFG_ENPLL | PLL_CFG_MPY_8X); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 47962306a36Sopenharmony_ci ESR2_TI_PLL_CFG_L, pll_cfg); 48062306a36Sopenharmony_ci if (err) { 48162306a36Sopenharmony_ci netdev_err(np->dev, "NIU Port %d %s() mdio write to ESR2_TI_PLL_CFG_L failed\n", 48262306a36Sopenharmony_ci np->port, __func__); 48362306a36Sopenharmony_ci return err; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci pll_sts = PLL_CFG_ENPLL; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 48962306a36Sopenharmony_ci ESR2_TI_PLL_STS_L, pll_sts); 49062306a36Sopenharmony_ci if (err) { 49162306a36Sopenharmony_ci netdev_err(np->dev, "NIU Port %d %s() mdio write to ESR2_TI_PLL_STS_L failed\n", 49262306a36Sopenharmony_ci np->port, __func__); 49362306a36Sopenharmony_ci return err; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci udelay(200); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* Initialize all 4 lanes of the SERDES. */ 49962306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 50062306a36Sopenharmony_ci err = esr2_set_tx_cfg(np, i, tx_cfg); 50162306a36Sopenharmony_ci if (err) 50262306a36Sopenharmony_ci return err; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 50662306a36Sopenharmony_ci err = esr2_set_rx_cfg(np, i, rx_cfg); 50762306a36Sopenharmony_ci if (err) 50862306a36Sopenharmony_ci return err; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci switch (np->port) { 51262306a36Sopenharmony_ci case 0: 51362306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P0 | ESR_INT_DET0_P0); 51462306a36Sopenharmony_ci mask = val; 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci case 1: 51862306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P1 | ESR_INT_DET0_P1); 51962306a36Sopenharmony_ci mask = val; 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci default: 52362306a36Sopenharmony_ci return -EINVAL; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci while (max_retry--) { 52762306a36Sopenharmony_ci sig = nr64(ESR_INT_SIGNALS); 52862306a36Sopenharmony_ci if ((sig & mask) == val) 52962306a36Sopenharmony_ci break; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci mdelay(500); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if ((sig & mask) != val) { 53562306a36Sopenharmony_ci netdev_err(np->dev, "Port %u signal bits [%08x] are not [%08x]\n", 53662306a36Sopenharmony_ci np->port, (int)(sig & mask), (int)val); 53762306a36Sopenharmony_ci return -ENODEV; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int serdes_init_niu_10g_serdes(struct niu *np) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 54662306a36Sopenharmony_ci u32 tx_cfg, rx_cfg, pll_cfg, pll_sts; 54762306a36Sopenharmony_ci int max_retry = 100; 54862306a36Sopenharmony_ci u64 sig, mask, val; 54962306a36Sopenharmony_ci unsigned long i; 55062306a36Sopenharmony_ci int err; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci tx_cfg = (PLL_TX_CFG_ENTX | PLL_TX_CFG_SWING_1375MV); 55362306a36Sopenharmony_ci rx_cfg = (PLL_RX_CFG_ENRX | PLL_RX_CFG_TERM_0P8VDDT | 55462306a36Sopenharmony_ci PLL_RX_CFG_ALIGN_ENA | PLL_RX_CFG_LOS_LTHRESH | 55562306a36Sopenharmony_ci PLL_RX_CFG_EQ_LP_ADAPTIVE); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_PHY) { 55862306a36Sopenharmony_ci u16 test_cfg = PLL_TEST_CFG_LOOPBACK_CML_DIS; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 56162306a36Sopenharmony_ci ESR2_TI_PLL_TEST_CFG_L, test_cfg); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci tx_cfg |= PLL_TX_CFG_ENTEST; 56462306a36Sopenharmony_ci rx_cfg |= PLL_RX_CFG_ENTEST; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Initialize PLL for 10G */ 56862306a36Sopenharmony_ci pll_cfg = (PLL_CFG_ENPLL | PLL_CFG_MPY_10X); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 57162306a36Sopenharmony_ci ESR2_TI_PLL_CFG_L, pll_cfg & 0xffff); 57262306a36Sopenharmony_ci if (err) { 57362306a36Sopenharmony_ci netdev_err(np->dev, "NIU Port %d %s() mdio write to ESR2_TI_PLL_CFG_L failed\n", 57462306a36Sopenharmony_ci np->port, __func__); 57562306a36Sopenharmony_ci return err; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci pll_sts = PLL_CFG_ENPLL; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR, 58162306a36Sopenharmony_ci ESR2_TI_PLL_STS_L, pll_sts & 0xffff); 58262306a36Sopenharmony_ci if (err) { 58362306a36Sopenharmony_ci netdev_err(np->dev, "NIU Port %d %s() mdio write to ESR2_TI_PLL_STS_L failed\n", 58462306a36Sopenharmony_ci np->port, __func__); 58562306a36Sopenharmony_ci return err; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci udelay(200); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* Initialize all 4 lanes of the SERDES. */ 59162306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 59262306a36Sopenharmony_ci err = esr2_set_tx_cfg(np, i, tx_cfg); 59362306a36Sopenharmony_ci if (err) 59462306a36Sopenharmony_ci return err; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 59862306a36Sopenharmony_ci err = esr2_set_rx_cfg(np, i, rx_cfg); 59962306a36Sopenharmony_ci if (err) 60062306a36Sopenharmony_ci return err; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* check if serdes is ready */ 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci switch (np->port) { 60662306a36Sopenharmony_ci case 0: 60762306a36Sopenharmony_ci mask = ESR_INT_SIGNALS_P0_BITS; 60862306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P0 | 60962306a36Sopenharmony_ci ESR_INT_DET0_P0 | 61062306a36Sopenharmony_ci ESR_INT_XSRDY_P0 | 61162306a36Sopenharmony_ci ESR_INT_XDP_P0_CH3 | 61262306a36Sopenharmony_ci ESR_INT_XDP_P0_CH2 | 61362306a36Sopenharmony_ci ESR_INT_XDP_P0_CH1 | 61462306a36Sopenharmony_ci ESR_INT_XDP_P0_CH0); 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci case 1: 61862306a36Sopenharmony_ci mask = ESR_INT_SIGNALS_P1_BITS; 61962306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P1 | 62062306a36Sopenharmony_ci ESR_INT_DET0_P1 | 62162306a36Sopenharmony_ci ESR_INT_XSRDY_P1 | 62262306a36Sopenharmony_ci ESR_INT_XDP_P1_CH3 | 62362306a36Sopenharmony_ci ESR_INT_XDP_P1_CH2 | 62462306a36Sopenharmony_ci ESR_INT_XDP_P1_CH1 | 62562306a36Sopenharmony_ci ESR_INT_XDP_P1_CH0); 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci default: 62962306a36Sopenharmony_ci return -EINVAL; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci while (max_retry--) { 63362306a36Sopenharmony_ci sig = nr64(ESR_INT_SIGNALS); 63462306a36Sopenharmony_ci if ((sig & mask) == val) 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci mdelay(500); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if ((sig & mask) != val) { 64162306a36Sopenharmony_ci pr_info("NIU Port %u signal bits [%08x] are not [%08x] for 10G...trying 1G\n", 64262306a36Sopenharmony_ci np->port, (int)(sig & mask), (int)val); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* 10G failed, try initializing at 1G */ 64562306a36Sopenharmony_ci err = serdes_init_niu_1g_serdes(np); 64662306a36Sopenharmony_ci if (!err) { 64762306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_10G; 64862306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_PCS; 64962306a36Sopenharmony_ci } else { 65062306a36Sopenharmony_ci netdev_err(np->dev, "Port %u 10G/1G SERDES Link Failed\n", 65162306a36Sopenharmony_ci np->port); 65262306a36Sopenharmony_ci return -ENODEV; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci return 0; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int esr_read_rxtx_ctrl(struct niu *np, unsigned long chan, u32 *val) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci int err; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR, ESR_RXTX_CTRL_L(chan)); 66362306a36Sopenharmony_ci if (err >= 0) { 66462306a36Sopenharmony_ci *val = (err & 0xffff); 66562306a36Sopenharmony_ci err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR, 66662306a36Sopenharmony_ci ESR_RXTX_CTRL_H(chan)); 66762306a36Sopenharmony_ci if (err >= 0) 66862306a36Sopenharmony_ci *val |= ((err & 0xffff) << 16); 66962306a36Sopenharmony_ci err = 0; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci return err; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic int esr_read_glue0(struct niu *np, unsigned long chan, u32 *val) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci int err; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR, 67962306a36Sopenharmony_ci ESR_GLUE_CTRL0_L(chan)); 68062306a36Sopenharmony_ci if (err >= 0) { 68162306a36Sopenharmony_ci *val = (err & 0xffff); 68262306a36Sopenharmony_ci err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR, 68362306a36Sopenharmony_ci ESR_GLUE_CTRL0_H(chan)); 68462306a36Sopenharmony_ci if (err >= 0) { 68562306a36Sopenharmony_ci *val |= ((err & 0xffff) << 16); 68662306a36Sopenharmony_ci err = 0; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci return err; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int esr_read_reset(struct niu *np, u32 *val) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci int err; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR, 69762306a36Sopenharmony_ci ESR_RXTX_RESET_CTRL_L); 69862306a36Sopenharmony_ci if (err >= 0) { 69962306a36Sopenharmony_ci *val = (err & 0xffff); 70062306a36Sopenharmony_ci err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR, 70162306a36Sopenharmony_ci ESR_RXTX_RESET_CTRL_H); 70262306a36Sopenharmony_ci if (err >= 0) { 70362306a36Sopenharmony_ci *val |= ((err & 0xffff) << 16); 70462306a36Sopenharmony_ci err = 0; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci return err; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int esr_write_rxtx_ctrl(struct niu *np, unsigned long chan, u32 val) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci int err; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR, 71562306a36Sopenharmony_ci ESR_RXTX_CTRL_L(chan), val & 0xffff); 71662306a36Sopenharmony_ci if (!err) 71762306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR, 71862306a36Sopenharmony_ci ESR_RXTX_CTRL_H(chan), (val >> 16)); 71962306a36Sopenharmony_ci return err; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic int esr_write_glue0(struct niu *np, unsigned long chan, u32 val) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci int err; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR, 72762306a36Sopenharmony_ci ESR_GLUE_CTRL0_L(chan), val & 0xffff); 72862306a36Sopenharmony_ci if (!err) 72962306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR, 73062306a36Sopenharmony_ci ESR_GLUE_CTRL0_H(chan), (val >> 16)); 73162306a36Sopenharmony_ci return err; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic int esr_reset(struct niu *np) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci u32 reset; 73762306a36Sopenharmony_ci int err; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR, 74062306a36Sopenharmony_ci ESR_RXTX_RESET_CTRL_L, 0x0000); 74162306a36Sopenharmony_ci if (err) 74262306a36Sopenharmony_ci return err; 74362306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR, 74462306a36Sopenharmony_ci ESR_RXTX_RESET_CTRL_H, 0xffff); 74562306a36Sopenharmony_ci if (err) 74662306a36Sopenharmony_ci return err; 74762306a36Sopenharmony_ci udelay(200); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR, 75062306a36Sopenharmony_ci ESR_RXTX_RESET_CTRL_L, 0xffff); 75162306a36Sopenharmony_ci if (err) 75262306a36Sopenharmony_ci return err; 75362306a36Sopenharmony_ci udelay(200); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR, 75662306a36Sopenharmony_ci ESR_RXTX_RESET_CTRL_H, 0x0000); 75762306a36Sopenharmony_ci if (err) 75862306a36Sopenharmony_ci return err; 75962306a36Sopenharmony_ci udelay(200); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci err = esr_read_reset(np, &reset); 76262306a36Sopenharmony_ci if (err) 76362306a36Sopenharmony_ci return err; 76462306a36Sopenharmony_ci if (reset != 0) { 76562306a36Sopenharmony_ci netdev_err(np->dev, "Port %u ESR_RESET did not clear [%08x]\n", 76662306a36Sopenharmony_ci np->port, reset); 76762306a36Sopenharmony_ci return -ENODEV; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return 0; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic int serdes_init_10g(struct niu *np) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 77662306a36Sopenharmony_ci unsigned long ctrl_reg, test_cfg_reg, i; 77762306a36Sopenharmony_ci u64 ctrl_val, test_cfg_val, sig, mask, val; 77862306a36Sopenharmony_ci int err; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci switch (np->port) { 78162306a36Sopenharmony_ci case 0: 78262306a36Sopenharmony_ci ctrl_reg = ENET_SERDES_0_CTRL_CFG; 78362306a36Sopenharmony_ci test_cfg_reg = ENET_SERDES_0_TEST_CFG; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci case 1: 78662306a36Sopenharmony_ci ctrl_reg = ENET_SERDES_1_CTRL_CFG; 78762306a36Sopenharmony_ci test_cfg_reg = ENET_SERDES_1_TEST_CFG; 78862306a36Sopenharmony_ci break; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci default: 79162306a36Sopenharmony_ci return -EINVAL; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci ctrl_val = (ENET_SERDES_CTRL_SDET_0 | 79462306a36Sopenharmony_ci ENET_SERDES_CTRL_SDET_1 | 79562306a36Sopenharmony_ci ENET_SERDES_CTRL_SDET_2 | 79662306a36Sopenharmony_ci ENET_SERDES_CTRL_SDET_3 | 79762306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_0_SHIFT) | 79862306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_1_SHIFT) | 79962306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_2_SHIFT) | 80062306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_3_SHIFT) | 80162306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_0_SHIFT) | 80262306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_1_SHIFT) | 80362306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_2_SHIFT) | 80462306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_3_SHIFT)); 80562306a36Sopenharmony_ci test_cfg_val = 0; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_PHY) { 80862306a36Sopenharmony_ci test_cfg_val |= ((ENET_TEST_MD_PAD_LOOPBACK << 80962306a36Sopenharmony_ci ENET_SERDES_TEST_MD_0_SHIFT) | 81062306a36Sopenharmony_ci (ENET_TEST_MD_PAD_LOOPBACK << 81162306a36Sopenharmony_ci ENET_SERDES_TEST_MD_1_SHIFT) | 81262306a36Sopenharmony_ci (ENET_TEST_MD_PAD_LOOPBACK << 81362306a36Sopenharmony_ci ENET_SERDES_TEST_MD_2_SHIFT) | 81462306a36Sopenharmony_ci (ENET_TEST_MD_PAD_LOOPBACK << 81562306a36Sopenharmony_ci ENET_SERDES_TEST_MD_3_SHIFT)); 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci nw64(ctrl_reg, ctrl_val); 81962306a36Sopenharmony_ci nw64(test_cfg_reg, test_cfg_val); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* Initialize all 4 lanes of the SERDES. */ 82262306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 82362306a36Sopenharmony_ci u32 rxtx_ctrl, glue0; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci err = esr_read_rxtx_ctrl(np, i, &rxtx_ctrl); 82662306a36Sopenharmony_ci if (err) 82762306a36Sopenharmony_ci return err; 82862306a36Sopenharmony_ci err = esr_read_glue0(np, i, &glue0); 82962306a36Sopenharmony_ci if (err) 83062306a36Sopenharmony_ci return err; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci rxtx_ctrl &= ~(ESR_RXTX_CTRL_VMUXLO); 83362306a36Sopenharmony_ci rxtx_ctrl |= (ESR_RXTX_CTRL_ENSTRETCH | 83462306a36Sopenharmony_ci (2 << ESR_RXTX_CTRL_VMUXLO_SHIFT)); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci glue0 &= ~(ESR_GLUE_CTRL0_SRATE | 83762306a36Sopenharmony_ci ESR_GLUE_CTRL0_THCNT | 83862306a36Sopenharmony_ci ESR_GLUE_CTRL0_BLTIME); 83962306a36Sopenharmony_ci glue0 |= (ESR_GLUE_CTRL0_RXLOSENAB | 84062306a36Sopenharmony_ci (0xf << ESR_GLUE_CTRL0_SRATE_SHIFT) | 84162306a36Sopenharmony_ci (0xff << ESR_GLUE_CTRL0_THCNT_SHIFT) | 84262306a36Sopenharmony_ci (BLTIME_300_CYCLES << 84362306a36Sopenharmony_ci ESR_GLUE_CTRL0_BLTIME_SHIFT)); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci err = esr_write_rxtx_ctrl(np, i, rxtx_ctrl); 84662306a36Sopenharmony_ci if (err) 84762306a36Sopenharmony_ci return err; 84862306a36Sopenharmony_ci err = esr_write_glue0(np, i, glue0); 84962306a36Sopenharmony_ci if (err) 85062306a36Sopenharmony_ci return err; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci err = esr_reset(np); 85462306a36Sopenharmony_ci if (err) 85562306a36Sopenharmony_ci return err; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci sig = nr64(ESR_INT_SIGNALS); 85862306a36Sopenharmony_ci switch (np->port) { 85962306a36Sopenharmony_ci case 0: 86062306a36Sopenharmony_ci mask = ESR_INT_SIGNALS_P0_BITS; 86162306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P0 | 86262306a36Sopenharmony_ci ESR_INT_DET0_P0 | 86362306a36Sopenharmony_ci ESR_INT_XSRDY_P0 | 86462306a36Sopenharmony_ci ESR_INT_XDP_P0_CH3 | 86562306a36Sopenharmony_ci ESR_INT_XDP_P0_CH2 | 86662306a36Sopenharmony_ci ESR_INT_XDP_P0_CH1 | 86762306a36Sopenharmony_ci ESR_INT_XDP_P0_CH0); 86862306a36Sopenharmony_ci break; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci case 1: 87162306a36Sopenharmony_ci mask = ESR_INT_SIGNALS_P1_BITS; 87262306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P1 | 87362306a36Sopenharmony_ci ESR_INT_DET0_P1 | 87462306a36Sopenharmony_ci ESR_INT_XSRDY_P1 | 87562306a36Sopenharmony_ci ESR_INT_XDP_P1_CH3 | 87662306a36Sopenharmony_ci ESR_INT_XDP_P1_CH2 | 87762306a36Sopenharmony_ci ESR_INT_XDP_P1_CH1 | 87862306a36Sopenharmony_ci ESR_INT_XDP_P1_CH0); 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci default: 88262306a36Sopenharmony_ci return -EINVAL; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if ((sig & mask) != val) { 88662306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_HOTPLUG_PHY) { 88762306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT; 88862306a36Sopenharmony_ci return 0; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci netdev_err(np->dev, "Port %u signal bits [%08x] are not [%08x]\n", 89162306a36Sopenharmony_ci np->port, (int)(sig & mask), (int)val); 89262306a36Sopenharmony_ci return -ENODEV; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_HOTPLUG_PHY) 89562306a36Sopenharmony_ci np->flags |= NIU_FLAGS_HOTPLUG_PHY_PRESENT; 89662306a36Sopenharmony_ci return 0; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic int serdes_init_1g(struct niu *np) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci u64 val; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci val = nr64(ENET_SERDES_1_PLL_CFG); 90462306a36Sopenharmony_ci val &= ~ENET_SERDES_PLL_FBDIV2; 90562306a36Sopenharmony_ci switch (np->port) { 90662306a36Sopenharmony_ci case 0: 90762306a36Sopenharmony_ci val |= ENET_SERDES_PLL_HRATE0; 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci case 1: 91062306a36Sopenharmony_ci val |= ENET_SERDES_PLL_HRATE1; 91162306a36Sopenharmony_ci break; 91262306a36Sopenharmony_ci case 2: 91362306a36Sopenharmony_ci val |= ENET_SERDES_PLL_HRATE2; 91462306a36Sopenharmony_ci break; 91562306a36Sopenharmony_ci case 3: 91662306a36Sopenharmony_ci val |= ENET_SERDES_PLL_HRATE3; 91762306a36Sopenharmony_ci break; 91862306a36Sopenharmony_ci default: 91962306a36Sopenharmony_ci return -EINVAL; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci nw64(ENET_SERDES_1_PLL_CFG, val); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci return 0; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cistatic int serdes_init_1g_serdes(struct niu *np) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 92962306a36Sopenharmony_ci unsigned long ctrl_reg, test_cfg_reg, pll_cfg, i; 93062306a36Sopenharmony_ci u64 ctrl_val, test_cfg_val, sig, mask, val; 93162306a36Sopenharmony_ci int err; 93262306a36Sopenharmony_ci u64 reset_val, val_rd; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci val = ENET_SERDES_PLL_HRATE0 | ENET_SERDES_PLL_HRATE1 | 93562306a36Sopenharmony_ci ENET_SERDES_PLL_HRATE2 | ENET_SERDES_PLL_HRATE3 | 93662306a36Sopenharmony_ci ENET_SERDES_PLL_FBDIV0; 93762306a36Sopenharmony_ci switch (np->port) { 93862306a36Sopenharmony_ci case 0: 93962306a36Sopenharmony_ci reset_val = ENET_SERDES_RESET_0; 94062306a36Sopenharmony_ci ctrl_reg = ENET_SERDES_0_CTRL_CFG; 94162306a36Sopenharmony_ci test_cfg_reg = ENET_SERDES_0_TEST_CFG; 94262306a36Sopenharmony_ci pll_cfg = ENET_SERDES_0_PLL_CFG; 94362306a36Sopenharmony_ci break; 94462306a36Sopenharmony_ci case 1: 94562306a36Sopenharmony_ci reset_val = ENET_SERDES_RESET_1; 94662306a36Sopenharmony_ci ctrl_reg = ENET_SERDES_1_CTRL_CFG; 94762306a36Sopenharmony_ci test_cfg_reg = ENET_SERDES_1_TEST_CFG; 94862306a36Sopenharmony_ci pll_cfg = ENET_SERDES_1_PLL_CFG; 94962306a36Sopenharmony_ci break; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci default: 95262306a36Sopenharmony_ci return -EINVAL; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci ctrl_val = (ENET_SERDES_CTRL_SDET_0 | 95562306a36Sopenharmony_ci ENET_SERDES_CTRL_SDET_1 | 95662306a36Sopenharmony_ci ENET_SERDES_CTRL_SDET_2 | 95762306a36Sopenharmony_ci ENET_SERDES_CTRL_SDET_3 | 95862306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_0_SHIFT) | 95962306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_1_SHIFT) | 96062306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_2_SHIFT) | 96162306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_3_SHIFT) | 96262306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_0_SHIFT) | 96362306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_1_SHIFT) | 96462306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_2_SHIFT) | 96562306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_3_SHIFT)); 96662306a36Sopenharmony_ci test_cfg_val = 0; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_PHY) { 96962306a36Sopenharmony_ci test_cfg_val |= ((ENET_TEST_MD_PAD_LOOPBACK << 97062306a36Sopenharmony_ci ENET_SERDES_TEST_MD_0_SHIFT) | 97162306a36Sopenharmony_ci (ENET_TEST_MD_PAD_LOOPBACK << 97262306a36Sopenharmony_ci ENET_SERDES_TEST_MD_1_SHIFT) | 97362306a36Sopenharmony_ci (ENET_TEST_MD_PAD_LOOPBACK << 97462306a36Sopenharmony_ci ENET_SERDES_TEST_MD_2_SHIFT) | 97562306a36Sopenharmony_ci (ENET_TEST_MD_PAD_LOOPBACK << 97662306a36Sopenharmony_ci ENET_SERDES_TEST_MD_3_SHIFT)); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci nw64(ENET_SERDES_RESET, reset_val); 98062306a36Sopenharmony_ci mdelay(20); 98162306a36Sopenharmony_ci val_rd = nr64(ENET_SERDES_RESET); 98262306a36Sopenharmony_ci val_rd &= ~reset_val; 98362306a36Sopenharmony_ci nw64(pll_cfg, val); 98462306a36Sopenharmony_ci nw64(ctrl_reg, ctrl_val); 98562306a36Sopenharmony_ci nw64(test_cfg_reg, test_cfg_val); 98662306a36Sopenharmony_ci nw64(ENET_SERDES_RESET, val_rd); 98762306a36Sopenharmony_ci mdelay(2000); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* Initialize all 4 lanes of the SERDES. */ 99062306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 99162306a36Sopenharmony_ci u32 rxtx_ctrl, glue0; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci err = esr_read_rxtx_ctrl(np, i, &rxtx_ctrl); 99462306a36Sopenharmony_ci if (err) 99562306a36Sopenharmony_ci return err; 99662306a36Sopenharmony_ci err = esr_read_glue0(np, i, &glue0); 99762306a36Sopenharmony_ci if (err) 99862306a36Sopenharmony_ci return err; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci rxtx_ctrl &= ~(ESR_RXTX_CTRL_VMUXLO); 100162306a36Sopenharmony_ci rxtx_ctrl |= (ESR_RXTX_CTRL_ENSTRETCH | 100262306a36Sopenharmony_ci (2 << ESR_RXTX_CTRL_VMUXLO_SHIFT)); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci glue0 &= ~(ESR_GLUE_CTRL0_SRATE | 100562306a36Sopenharmony_ci ESR_GLUE_CTRL0_THCNT | 100662306a36Sopenharmony_ci ESR_GLUE_CTRL0_BLTIME); 100762306a36Sopenharmony_ci glue0 |= (ESR_GLUE_CTRL0_RXLOSENAB | 100862306a36Sopenharmony_ci (0xf << ESR_GLUE_CTRL0_SRATE_SHIFT) | 100962306a36Sopenharmony_ci (0xff << ESR_GLUE_CTRL0_THCNT_SHIFT) | 101062306a36Sopenharmony_ci (BLTIME_300_CYCLES << 101162306a36Sopenharmony_ci ESR_GLUE_CTRL0_BLTIME_SHIFT)); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci err = esr_write_rxtx_ctrl(np, i, rxtx_ctrl); 101462306a36Sopenharmony_ci if (err) 101562306a36Sopenharmony_ci return err; 101662306a36Sopenharmony_ci err = esr_write_glue0(np, i, glue0); 101762306a36Sopenharmony_ci if (err) 101862306a36Sopenharmony_ci return err; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci sig = nr64(ESR_INT_SIGNALS); 102362306a36Sopenharmony_ci switch (np->port) { 102462306a36Sopenharmony_ci case 0: 102562306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P0 | ESR_INT_DET0_P0); 102662306a36Sopenharmony_ci mask = val; 102762306a36Sopenharmony_ci break; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci case 1: 103062306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P1 | ESR_INT_DET0_P1); 103162306a36Sopenharmony_ci mask = val; 103262306a36Sopenharmony_ci break; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci default: 103562306a36Sopenharmony_ci return -EINVAL; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if ((sig & mask) != val) { 103962306a36Sopenharmony_ci netdev_err(np->dev, "Port %u signal bits [%08x] are not [%08x]\n", 104062306a36Sopenharmony_ci np->port, (int)(sig & mask), (int)val); 104162306a36Sopenharmony_ci return -ENODEV; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci return 0; 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic int link_status_1g_serdes(struct niu *np, int *link_up_p) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 105062306a36Sopenharmony_ci int link_up; 105162306a36Sopenharmony_ci u64 val; 105262306a36Sopenharmony_ci u16 current_speed; 105362306a36Sopenharmony_ci unsigned long flags; 105462306a36Sopenharmony_ci u8 current_duplex; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci link_up = 0; 105762306a36Sopenharmony_ci current_speed = SPEED_INVALID; 105862306a36Sopenharmony_ci current_duplex = DUPLEX_INVALID; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci val = nr64_pcs(PCS_MII_STAT); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (val & PCS_MII_STAT_LINK_STATUS) { 106562306a36Sopenharmony_ci link_up = 1; 106662306a36Sopenharmony_ci current_speed = SPEED_1000; 106762306a36Sopenharmony_ci current_duplex = DUPLEX_FULL; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci lp->active_speed = current_speed; 107162306a36Sopenharmony_ci lp->active_duplex = current_duplex; 107262306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci *link_up_p = link_up; 107562306a36Sopenharmony_ci return 0; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cistatic int link_status_10g_serdes(struct niu *np, int *link_up_p) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci unsigned long flags; 108162306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 108262306a36Sopenharmony_ci int link_up = 0; 108362306a36Sopenharmony_ci int link_ok = 1; 108462306a36Sopenharmony_ci u64 val, val2; 108562306a36Sopenharmony_ci u16 current_speed; 108662306a36Sopenharmony_ci u8 current_duplex; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (!(np->flags & NIU_FLAGS_10G)) 108962306a36Sopenharmony_ci return link_status_1g_serdes(np, link_up_p); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci current_speed = SPEED_INVALID; 109262306a36Sopenharmony_ci current_duplex = DUPLEX_INVALID; 109362306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci val = nr64_xpcs(XPCS_STATUS(0)); 109662306a36Sopenharmony_ci val2 = nr64_mac(XMAC_INTER2); 109762306a36Sopenharmony_ci if (val2 & 0x01000000) 109862306a36Sopenharmony_ci link_ok = 0; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if ((val & 0x1000ULL) && link_ok) { 110162306a36Sopenharmony_ci link_up = 1; 110262306a36Sopenharmony_ci current_speed = SPEED_10000; 110362306a36Sopenharmony_ci current_duplex = DUPLEX_FULL; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci lp->active_speed = current_speed; 110662306a36Sopenharmony_ci lp->active_duplex = current_duplex; 110762306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 110862306a36Sopenharmony_ci *link_up_p = link_up; 110962306a36Sopenharmony_ci return 0; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic int link_status_mii(struct niu *np, int *link_up_p) 111362306a36Sopenharmony_ci{ 111462306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 111562306a36Sopenharmony_ci int err; 111662306a36Sopenharmony_ci int bmsr, advert, ctrl1000, stat1000, lpa, bmcr, estatus; 111762306a36Sopenharmony_ci int supported, advertising, active_speed, active_duplex; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_BMCR); 112062306a36Sopenharmony_ci if (unlikely(err < 0)) 112162306a36Sopenharmony_ci return err; 112262306a36Sopenharmony_ci bmcr = err; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_BMSR); 112562306a36Sopenharmony_ci if (unlikely(err < 0)) 112662306a36Sopenharmony_ci return err; 112762306a36Sopenharmony_ci bmsr = err; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_ADVERTISE); 113062306a36Sopenharmony_ci if (unlikely(err < 0)) 113162306a36Sopenharmony_ci return err; 113262306a36Sopenharmony_ci advert = err; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_LPA); 113562306a36Sopenharmony_ci if (unlikely(err < 0)) 113662306a36Sopenharmony_ci return err; 113762306a36Sopenharmony_ci lpa = err; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci if (likely(bmsr & BMSR_ESTATEN)) { 114062306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_ESTATUS); 114162306a36Sopenharmony_ci if (unlikely(err < 0)) 114262306a36Sopenharmony_ci return err; 114362306a36Sopenharmony_ci estatus = err; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_CTRL1000); 114662306a36Sopenharmony_ci if (unlikely(err < 0)) 114762306a36Sopenharmony_ci return err; 114862306a36Sopenharmony_ci ctrl1000 = err; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_STAT1000); 115162306a36Sopenharmony_ci if (unlikely(err < 0)) 115262306a36Sopenharmony_ci return err; 115362306a36Sopenharmony_ci stat1000 = err; 115462306a36Sopenharmony_ci } else 115562306a36Sopenharmony_ci estatus = ctrl1000 = stat1000 = 0; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci supported = 0; 115862306a36Sopenharmony_ci if (bmsr & BMSR_ANEGCAPABLE) 115962306a36Sopenharmony_ci supported |= SUPPORTED_Autoneg; 116062306a36Sopenharmony_ci if (bmsr & BMSR_10HALF) 116162306a36Sopenharmony_ci supported |= SUPPORTED_10baseT_Half; 116262306a36Sopenharmony_ci if (bmsr & BMSR_10FULL) 116362306a36Sopenharmony_ci supported |= SUPPORTED_10baseT_Full; 116462306a36Sopenharmony_ci if (bmsr & BMSR_100HALF) 116562306a36Sopenharmony_ci supported |= SUPPORTED_100baseT_Half; 116662306a36Sopenharmony_ci if (bmsr & BMSR_100FULL) 116762306a36Sopenharmony_ci supported |= SUPPORTED_100baseT_Full; 116862306a36Sopenharmony_ci if (estatus & ESTATUS_1000_THALF) 116962306a36Sopenharmony_ci supported |= SUPPORTED_1000baseT_Half; 117062306a36Sopenharmony_ci if (estatus & ESTATUS_1000_TFULL) 117162306a36Sopenharmony_ci supported |= SUPPORTED_1000baseT_Full; 117262306a36Sopenharmony_ci lp->supported = supported; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci advertising = mii_adv_to_ethtool_adv_t(advert); 117562306a36Sopenharmony_ci advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (bmcr & BMCR_ANENABLE) { 117862306a36Sopenharmony_ci int neg, neg1000; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci lp->active_autoneg = 1; 118162306a36Sopenharmony_ci advertising |= ADVERTISED_Autoneg; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci neg = advert & lpa; 118462306a36Sopenharmony_ci neg1000 = (ctrl1000 << 2) & stat1000; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (neg1000 & (LPA_1000FULL | LPA_1000HALF)) 118762306a36Sopenharmony_ci active_speed = SPEED_1000; 118862306a36Sopenharmony_ci else if (neg & LPA_100) 118962306a36Sopenharmony_ci active_speed = SPEED_100; 119062306a36Sopenharmony_ci else if (neg & (LPA_10HALF | LPA_10FULL)) 119162306a36Sopenharmony_ci active_speed = SPEED_10; 119262306a36Sopenharmony_ci else 119362306a36Sopenharmony_ci active_speed = SPEED_INVALID; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if ((neg1000 & LPA_1000FULL) || (neg & LPA_DUPLEX)) 119662306a36Sopenharmony_ci active_duplex = DUPLEX_FULL; 119762306a36Sopenharmony_ci else if (active_speed != SPEED_INVALID) 119862306a36Sopenharmony_ci active_duplex = DUPLEX_HALF; 119962306a36Sopenharmony_ci else 120062306a36Sopenharmony_ci active_duplex = DUPLEX_INVALID; 120162306a36Sopenharmony_ci } else { 120262306a36Sopenharmony_ci lp->active_autoneg = 0; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if ((bmcr & BMCR_SPEED1000) && !(bmcr & BMCR_SPEED100)) 120562306a36Sopenharmony_ci active_speed = SPEED_1000; 120662306a36Sopenharmony_ci else if (bmcr & BMCR_SPEED100) 120762306a36Sopenharmony_ci active_speed = SPEED_100; 120862306a36Sopenharmony_ci else 120962306a36Sopenharmony_ci active_speed = SPEED_10; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if (bmcr & BMCR_FULLDPLX) 121262306a36Sopenharmony_ci active_duplex = DUPLEX_FULL; 121362306a36Sopenharmony_ci else 121462306a36Sopenharmony_ci active_duplex = DUPLEX_HALF; 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci lp->active_advertising = advertising; 121862306a36Sopenharmony_ci lp->active_speed = active_speed; 121962306a36Sopenharmony_ci lp->active_duplex = active_duplex; 122062306a36Sopenharmony_ci *link_up_p = !!(bmsr & BMSR_LSTATUS); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci return 0; 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cistatic int link_status_1g_rgmii(struct niu *np, int *link_up_p) 122662306a36Sopenharmony_ci{ 122762306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 122862306a36Sopenharmony_ci u16 current_speed, bmsr; 122962306a36Sopenharmony_ci unsigned long flags; 123062306a36Sopenharmony_ci u8 current_duplex; 123162306a36Sopenharmony_ci int err, link_up; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci link_up = 0; 123462306a36Sopenharmony_ci current_speed = SPEED_INVALID; 123562306a36Sopenharmony_ci current_duplex = DUPLEX_INVALID; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_BMSR); 124062306a36Sopenharmony_ci if (err < 0) 124162306a36Sopenharmony_ci goto out; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci bmsr = err; 124462306a36Sopenharmony_ci if (bmsr & BMSR_LSTATUS) { 124562306a36Sopenharmony_ci link_up = 1; 124662306a36Sopenharmony_ci current_speed = SPEED_1000; 124762306a36Sopenharmony_ci current_duplex = DUPLEX_FULL; 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci lp->active_speed = current_speed; 125062306a36Sopenharmony_ci lp->active_duplex = current_duplex; 125162306a36Sopenharmony_ci err = 0; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ciout: 125462306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci *link_up_p = link_up; 125762306a36Sopenharmony_ci return err; 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cistatic int link_status_1g(struct niu *np, int *link_up_p) 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 126362306a36Sopenharmony_ci unsigned long flags; 126462306a36Sopenharmony_ci int err; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci err = link_status_mii(np, link_up_p); 126962306a36Sopenharmony_ci lp->supported |= SUPPORTED_TP; 127062306a36Sopenharmony_ci lp->active_advertising |= ADVERTISED_TP; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 127362306a36Sopenharmony_ci return err; 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic int bcm8704_reset(struct niu *np) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci int err, limit; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, 128162306a36Sopenharmony_ci BCM8704_PHYXS_DEV_ADDR, MII_BMCR); 128262306a36Sopenharmony_ci if (err < 0 || err == 0xffff) 128362306a36Sopenharmony_ci return err; 128462306a36Sopenharmony_ci err |= BMCR_RESET; 128562306a36Sopenharmony_ci err = mdio_write(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR, 128662306a36Sopenharmony_ci MII_BMCR, err); 128762306a36Sopenharmony_ci if (err) 128862306a36Sopenharmony_ci return err; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci limit = 1000; 129162306a36Sopenharmony_ci while (--limit >= 0) { 129262306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, 129362306a36Sopenharmony_ci BCM8704_PHYXS_DEV_ADDR, MII_BMCR); 129462306a36Sopenharmony_ci if (err < 0) 129562306a36Sopenharmony_ci return err; 129662306a36Sopenharmony_ci if (!(err & BMCR_RESET)) 129762306a36Sopenharmony_ci break; 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci if (limit < 0) { 130062306a36Sopenharmony_ci netdev_err(np->dev, "Port %u PHY will not reset (bmcr=%04x)\n", 130162306a36Sopenharmony_ci np->port, (err & 0xffff)); 130262306a36Sopenharmony_ci return -ENODEV; 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci return 0; 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci/* When written, certain PHY registers need to be read back twice 130862306a36Sopenharmony_ci * in order for the bits to settle properly. 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_cistatic int bcm8704_user_dev3_readback(struct niu *np, int reg) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci int err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, reg); 131362306a36Sopenharmony_ci if (err < 0) 131462306a36Sopenharmony_ci return err; 131562306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, reg); 131662306a36Sopenharmony_ci if (err < 0) 131762306a36Sopenharmony_ci return err; 131862306a36Sopenharmony_ci return 0; 131962306a36Sopenharmony_ci} 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_cistatic int bcm8706_init_user_dev3(struct niu *np) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci int err; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 132762306a36Sopenharmony_ci BCM8704_USER_OPT_DIGITAL_CTRL); 132862306a36Sopenharmony_ci if (err < 0) 132962306a36Sopenharmony_ci return err; 133062306a36Sopenharmony_ci err &= ~USER_ODIG_CTRL_GPIOS; 133162306a36Sopenharmony_ci err |= (0x3 << USER_ODIG_CTRL_GPIOS_SHIFT); 133262306a36Sopenharmony_ci err |= USER_ODIG_CTRL_RESV2; 133362306a36Sopenharmony_ci err = mdio_write(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 133462306a36Sopenharmony_ci BCM8704_USER_OPT_DIGITAL_CTRL, err); 133562306a36Sopenharmony_ci if (err) 133662306a36Sopenharmony_ci return err; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci mdelay(1000); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci return 0; 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cistatic int bcm8704_init_user_dev3(struct niu *np) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci int err; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci err = mdio_write(np, np->phy_addr, 134862306a36Sopenharmony_ci BCM8704_USER_DEV3_ADDR, BCM8704_USER_CONTROL, 134962306a36Sopenharmony_ci (USER_CONTROL_OPTXRST_LVL | 135062306a36Sopenharmony_ci USER_CONTROL_OPBIASFLT_LVL | 135162306a36Sopenharmony_ci USER_CONTROL_OBTMPFLT_LVL | 135262306a36Sopenharmony_ci USER_CONTROL_OPPRFLT_LVL | 135362306a36Sopenharmony_ci USER_CONTROL_OPTXFLT_LVL | 135462306a36Sopenharmony_ci USER_CONTROL_OPRXLOS_LVL | 135562306a36Sopenharmony_ci USER_CONTROL_OPRXFLT_LVL | 135662306a36Sopenharmony_ci USER_CONTROL_OPTXON_LVL | 135762306a36Sopenharmony_ci (0x3f << USER_CONTROL_RES1_SHIFT))); 135862306a36Sopenharmony_ci if (err) 135962306a36Sopenharmony_ci return err; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci err = mdio_write(np, np->phy_addr, 136262306a36Sopenharmony_ci BCM8704_USER_DEV3_ADDR, BCM8704_USER_PMD_TX_CONTROL, 136362306a36Sopenharmony_ci (USER_PMD_TX_CTL_XFP_CLKEN | 136462306a36Sopenharmony_ci (1 << USER_PMD_TX_CTL_TX_DAC_TXD_SH) | 136562306a36Sopenharmony_ci (2 << USER_PMD_TX_CTL_TX_DAC_TXCK_SH) | 136662306a36Sopenharmony_ci USER_PMD_TX_CTL_TSCK_LPWREN)); 136762306a36Sopenharmony_ci if (err) 136862306a36Sopenharmony_ci return err; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci err = bcm8704_user_dev3_readback(np, BCM8704_USER_CONTROL); 137162306a36Sopenharmony_ci if (err) 137262306a36Sopenharmony_ci return err; 137362306a36Sopenharmony_ci err = bcm8704_user_dev3_readback(np, BCM8704_USER_PMD_TX_CONTROL); 137462306a36Sopenharmony_ci if (err) 137562306a36Sopenharmony_ci return err; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 137862306a36Sopenharmony_ci BCM8704_USER_OPT_DIGITAL_CTRL); 137962306a36Sopenharmony_ci if (err < 0) 138062306a36Sopenharmony_ci return err; 138162306a36Sopenharmony_ci err &= ~USER_ODIG_CTRL_GPIOS; 138262306a36Sopenharmony_ci err |= (0x3 << USER_ODIG_CTRL_GPIOS_SHIFT); 138362306a36Sopenharmony_ci err = mdio_write(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 138462306a36Sopenharmony_ci BCM8704_USER_OPT_DIGITAL_CTRL, err); 138562306a36Sopenharmony_ci if (err) 138662306a36Sopenharmony_ci return err; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci mdelay(1000); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci return 0; 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic int mrvl88x2011_act_led(struct niu *np, int val) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci int err; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR, 139862306a36Sopenharmony_ci MRVL88X2011_LED_8_TO_11_CTL); 139962306a36Sopenharmony_ci if (err < 0) 140062306a36Sopenharmony_ci return err; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci err &= ~MRVL88X2011_LED(MRVL88X2011_LED_ACT,MRVL88X2011_LED_CTL_MASK); 140362306a36Sopenharmony_ci err |= MRVL88X2011_LED(MRVL88X2011_LED_ACT,val); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci return mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR, 140662306a36Sopenharmony_ci MRVL88X2011_LED_8_TO_11_CTL, err); 140762306a36Sopenharmony_ci} 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_cistatic int mrvl88x2011_led_blink_rate(struct niu *np, int rate) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci int err; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR, 141462306a36Sopenharmony_ci MRVL88X2011_LED_BLINK_CTL); 141562306a36Sopenharmony_ci if (err >= 0) { 141662306a36Sopenharmony_ci err &= ~MRVL88X2011_LED_BLKRATE_MASK; 141762306a36Sopenharmony_ci err |= (rate << 4); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci err = mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR, 142062306a36Sopenharmony_ci MRVL88X2011_LED_BLINK_CTL, err); 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci return err; 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_cistatic int xcvr_init_10g_mrvl88x2011(struct niu *np) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci int err; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci /* Set LED functions */ 143162306a36Sopenharmony_ci err = mrvl88x2011_led_blink_rate(np, MRVL88X2011_LED_BLKRATE_134MS); 143262306a36Sopenharmony_ci if (err) 143362306a36Sopenharmony_ci return err; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci /* led activity */ 143662306a36Sopenharmony_ci err = mrvl88x2011_act_led(np, MRVL88X2011_LED_CTL_OFF); 143762306a36Sopenharmony_ci if (err) 143862306a36Sopenharmony_ci return err; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR, 144162306a36Sopenharmony_ci MRVL88X2011_GENERAL_CTL); 144262306a36Sopenharmony_ci if (err < 0) 144362306a36Sopenharmony_ci return err; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci err |= MRVL88X2011_ENA_XFPREFCLK; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci err = mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR, 144862306a36Sopenharmony_ci MRVL88X2011_GENERAL_CTL, err); 144962306a36Sopenharmony_ci if (err < 0) 145062306a36Sopenharmony_ci return err; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR, 145362306a36Sopenharmony_ci MRVL88X2011_PMA_PMD_CTL_1); 145462306a36Sopenharmony_ci if (err < 0) 145562306a36Sopenharmony_ci return err; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci if (np->link_config.loopback_mode == LOOPBACK_MAC) 145862306a36Sopenharmony_ci err |= MRVL88X2011_LOOPBACK; 145962306a36Sopenharmony_ci else 146062306a36Sopenharmony_ci err &= ~MRVL88X2011_LOOPBACK; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci err = mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR, 146362306a36Sopenharmony_ci MRVL88X2011_PMA_PMD_CTL_1, err); 146462306a36Sopenharmony_ci if (err < 0) 146562306a36Sopenharmony_ci return err; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci /* Enable PMD */ 146862306a36Sopenharmony_ci return mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR, 146962306a36Sopenharmony_ci MRVL88X2011_10G_PMD_TX_DIS, MRVL88X2011_ENA_PMDTX); 147062306a36Sopenharmony_ci} 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic int xcvr_diag_bcm870x(struct niu *np) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci u16 analog_stat0, tx_alarm_status; 147662306a36Sopenharmony_ci int err = 0; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci#if 1 147962306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR, 148062306a36Sopenharmony_ci MII_STAT1000); 148162306a36Sopenharmony_ci if (err < 0) 148262306a36Sopenharmony_ci return err; 148362306a36Sopenharmony_ci pr_info("Port %u PMA_PMD(MII_STAT1000) [%04x]\n", np->port, err); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 0x20); 148662306a36Sopenharmony_ci if (err < 0) 148762306a36Sopenharmony_ci return err; 148862306a36Sopenharmony_ci pr_info("Port %u USER_DEV3(0x20) [%04x]\n", np->port, err); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR, 149162306a36Sopenharmony_ci MII_NWAYTEST); 149262306a36Sopenharmony_ci if (err < 0) 149362306a36Sopenharmony_ci return err; 149462306a36Sopenharmony_ci pr_info("Port %u PHYXS(MII_NWAYTEST) [%04x]\n", np->port, err); 149562306a36Sopenharmony_ci#endif 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci /* XXX dig this out it might not be so useful XXX */ 149862306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 149962306a36Sopenharmony_ci BCM8704_USER_ANALOG_STATUS0); 150062306a36Sopenharmony_ci if (err < 0) 150162306a36Sopenharmony_ci return err; 150262306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 150362306a36Sopenharmony_ci BCM8704_USER_ANALOG_STATUS0); 150462306a36Sopenharmony_ci if (err < 0) 150562306a36Sopenharmony_ci return err; 150662306a36Sopenharmony_ci analog_stat0 = err; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 150962306a36Sopenharmony_ci BCM8704_USER_TX_ALARM_STATUS); 151062306a36Sopenharmony_ci if (err < 0) 151162306a36Sopenharmony_ci return err; 151262306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 151362306a36Sopenharmony_ci BCM8704_USER_TX_ALARM_STATUS); 151462306a36Sopenharmony_ci if (err < 0) 151562306a36Sopenharmony_ci return err; 151662306a36Sopenharmony_ci tx_alarm_status = err; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (analog_stat0 != 0x03fc) { 151962306a36Sopenharmony_ci if ((analog_stat0 == 0x43bc) && (tx_alarm_status != 0)) { 152062306a36Sopenharmony_ci pr_info("Port %u cable not connected or bad cable\n", 152162306a36Sopenharmony_ci np->port); 152262306a36Sopenharmony_ci } else if (analog_stat0 == 0x639c) { 152362306a36Sopenharmony_ci pr_info("Port %u optical module is bad or missing\n", 152462306a36Sopenharmony_ci np->port); 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci return 0; 152962306a36Sopenharmony_ci} 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_cistatic int xcvr_10g_set_lb_bcm870x(struct niu *np) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 153462306a36Sopenharmony_ci int err; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR, 153762306a36Sopenharmony_ci MII_BMCR); 153862306a36Sopenharmony_ci if (err < 0) 153962306a36Sopenharmony_ci return err; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci err &= ~BMCR_LOOPBACK; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_MAC) 154462306a36Sopenharmony_ci err |= BMCR_LOOPBACK; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci err = mdio_write(np, np->phy_addr, BCM8704_PCS_DEV_ADDR, 154762306a36Sopenharmony_ci MII_BMCR, err); 154862306a36Sopenharmony_ci if (err) 154962306a36Sopenharmony_ci return err; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci return 0; 155262306a36Sopenharmony_ci} 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cistatic int xcvr_init_10g_bcm8706(struct niu *np) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci int err = 0; 155762306a36Sopenharmony_ci u64 val; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if ((np->flags & NIU_FLAGS_HOTPLUG_PHY) && 156062306a36Sopenharmony_ci (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) == 0) 156162306a36Sopenharmony_ci return err; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci val = nr64_mac(XMAC_CONFIG); 156462306a36Sopenharmony_ci val &= ~XMAC_CONFIG_LED_POLARITY; 156562306a36Sopenharmony_ci val |= XMAC_CONFIG_FORCE_LED_ON; 156662306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci val = nr64(MIF_CONFIG); 156962306a36Sopenharmony_ci val |= MIF_CONFIG_INDIRECT_MODE; 157062306a36Sopenharmony_ci nw64(MIF_CONFIG, val); 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci err = bcm8704_reset(np); 157362306a36Sopenharmony_ci if (err) 157462306a36Sopenharmony_ci return err; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci err = xcvr_10g_set_lb_bcm870x(np); 157762306a36Sopenharmony_ci if (err) 157862306a36Sopenharmony_ci return err; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci err = bcm8706_init_user_dev3(np); 158162306a36Sopenharmony_ci if (err) 158262306a36Sopenharmony_ci return err; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci err = xcvr_diag_bcm870x(np); 158562306a36Sopenharmony_ci if (err) 158662306a36Sopenharmony_ci return err; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci return 0; 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_cistatic int xcvr_init_10g_bcm8704(struct niu *np) 159262306a36Sopenharmony_ci{ 159362306a36Sopenharmony_ci int err; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci err = bcm8704_reset(np); 159662306a36Sopenharmony_ci if (err) 159762306a36Sopenharmony_ci return err; 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci err = bcm8704_init_user_dev3(np); 160062306a36Sopenharmony_ci if (err) 160162306a36Sopenharmony_ci return err; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci err = xcvr_10g_set_lb_bcm870x(np); 160462306a36Sopenharmony_ci if (err) 160562306a36Sopenharmony_ci return err; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci err = xcvr_diag_bcm870x(np); 160862306a36Sopenharmony_ci if (err) 160962306a36Sopenharmony_ci return err; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci return 0; 161262306a36Sopenharmony_ci} 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_cistatic int xcvr_init_10g(struct niu *np) 161562306a36Sopenharmony_ci{ 161662306a36Sopenharmony_ci int phy_id, err; 161762306a36Sopenharmony_ci u64 val; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci val = nr64_mac(XMAC_CONFIG); 162062306a36Sopenharmony_ci val &= ~XMAC_CONFIG_LED_POLARITY; 162162306a36Sopenharmony_ci val |= XMAC_CONFIG_FORCE_LED_ON; 162262306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci /* XXX shared resource, lock parent XXX */ 162562306a36Sopenharmony_ci val = nr64(MIF_CONFIG); 162662306a36Sopenharmony_ci val |= MIF_CONFIG_INDIRECT_MODE; 162762306a36Sopenharmony_ci nw64(MIF_CONFIG, val); 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci phy_id = phy_decode(np->parent->port_phy, np->port); 163062306a36Sopenharmony_ci phy_id = np->parent->phy_probe_info.phy_id[phy_id][np->port]; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci /* handle different phy types */ 163362306a36Sopenharmony_ci switch (phy_id & NIU_PHY_ID_MASK) { 163462306a36Sopenharmony_ci case NIU_PHY_ID_MRVL88X2011: 163562306a36Sopenharmony_ci err = xcvr_init_10g_mrvl88x2011(np); 163662306a36Sopenharmony_ci break; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci default: /* bcom 8704 */ 163962306a36Sopenharmony_ci err = xcvr_init_10g_bcm8704(np); 164062306a36Sopenharmony_ci break; 164162306a36Sopenharmony_ci } 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci return err; 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_cistatic int mii_reset(struct niu *np) 164762306a36Sopenharmony_ci{ 164862306a36Sopenharmony_ci int limit, err; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci err = mii_write(np, np->phy_addr, MII_BMCR, BMCR_RESET); 165162306a36Sopenharmony_ci if (err) 165262306a36Sopenharmony_ci return err; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci limit = 1000; 165562306a36Sopenharmony_ci while (--limit >= 0) { 165662306a36Sopenharmony_ci udelay(500); 165762306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_BMCR); 165862306a36Sopenharmony_ci if (err < 0) 165962306a36Sopenharmony_ci return err; 166062306a36Sopenharmony_ci if (!(err & BMCR_RESET)) 166162306a36Sopenharmony_ci break; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci if (limit < 0) { 166462306a36Sopenharmony_ci netdev_err(np->dev, "Port %u MII would not reset, bmcr[%04x]\n", 166562306a36Sopenharmony_ci np->port, err); 166662306a36Sopenharmony_ci return -ENODEV; 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci return 0; 167062306a36Sopenharmony_ci} 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_cistatic int xcvr_init_1g_rgmii(struct niu *np) 167362306a36Sopenharmony_ci{ 167462306a36Sopenharmony_ci int err; 167562306a36Sopenharmony_ci u64 val; 167662306a36Sopenharmony_ci u16 bmcr, bmsr, estat; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci val = nr64(MIF_CONFIG); 167962306a36Sopenharmony_ci val &= ~MIF_CONFIG_INDIRECT_MODE; 168062306a36Sopenharmony_ci nw64(MIF_CONFIG, val); 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci err = mii_reset(np); 168362306a36Sopenharmony_ci if (err) 168462306a36Sopenharmony_ci return err; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_BMSR); 168762306a36Sopenharmony_ci if (err < 0) 168862306a36Sopenharmony_ci return err; 168962306a36Sopenharmony_ci bmsr = err; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci estat = 0; 169262306a36Sopenharmony_ci if (bmsr & BMSR_ESTATEN) { 169362306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_ESTATUS); 169462306a36Sopenharmony_ci if (err < 0) 169562306a36Sopenharmony_ci return err; 169662306a36Sopenharmony_ci estat = err; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci bmcr = 0; 170062306a36Sopenharmony_ci err = mii_write(np, np->phy_addr, MII_BMCR, bmcr); 170162306a36Sopenharmony_ci if (err) 170262306a36Sopenharmony_ci return err; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci if (bmsr & BMSR_ESTATEN) { 170562306a36Sopenharmony_ci u16 ctrl1000 = 0; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci if (estat & ESTATUS_1000_TFULL) 170862306a36Sopenharmony_ci ctrl1000 |= ADVERTISE_1000FULL; 170962306a36Sopenharmony_ci err = mii_write(np, np->phy_addr, MII_CTRL1000, ctrl1000); 171062306a36Sopenharmony_ci if (err) 171162306a36Sopenharmony_ci return err; 171262306a36Sopenharmony_ci } 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci bmcr = (BMCR_SPEED1000 | BMCR_FULLDPLX); 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci err = mii_write(np, np->phy_addr, MII_BMCR, bmcr); 171762306a36Sopenharmony_ci if (err) 171862306a36Sopenharmony_ci return err; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_BMCR); 172162306a36Sopenharmony_ci if (err < 0) 172262306a36Sopenharmony_ci return err; 172362306a36Sopenharmony_ci bmcr = mii_read(np, np->phy_addr, MII_BMCR); 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_BMSR); 172662306a36Sopenharmony_ci if (err < 0) 172762306a36Sopenharmony_ci return err; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci return 0; 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cistatic int mii_init_common(struct niu *np) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 173562306a36Sopenharmony_ci u16 bmcr, bmsr, adv, estat; 173662306a36Sopenharmony_ci int err; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci err = mii_reset(np); 173962306a36Sopenharmony_ci if (err) 174062306a36Sopenharmony_ci return err; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_BMSR); 174362306a36Sopenharmony_ci if (err < 0) 174462306a36Sopenharmony_ci return err; 174562306a36Sopenharmony_ci bmsr = err; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci estat = 0; 174862306a36Sopenharmony_ci if (bmsr & BMSR_ESTATEN) { 174962306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_ESTATUS); 175062306a36Sopenharmony_ci if (err < 0) 175162306a36Sopenharmony_ci return err; 175262306a36Sopenharmony_ci estat = err; 175362306a36Sopenharmony_ci } 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci bmcr = 0; 175662306a36Sopenharmony_ci err = mii_write(np, np->phy_addr, MII_BMCR, bmcr); 175762306a36Sopenharmony_ci if (err) 175862306a36Sopenharmony_ci return err; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_MAC) { 176162306a36Sopenharmony_ci bmcr |= BMCR_LOOPBACK; 176262306a36Sopenharmony_ci if (lp->active_speed == SPEED_1000) 176362306a36Sopenharmony_ci bmcr |= BMCR_SPEED1000; 176462306a36Sopenharmony_ci if (lp->active_duplex == DUPLEX_FULL) 176562306a36Sopenharmony_ci bmcr |= BMCR_FULLDPLX; 176662306a36Sopenharmony_ci } 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_PHY) { 176962306a36Sopenharmony_ci u16 aux; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci aux = (BCM5464R_AUX_CTL_EXT_LB | 177262306a36Sopenharmony_ci BCM5464R_AUX_CTL_WRITE_1); 177362306a36Sopenharmony_ci err = mii_write(np, np->phy_addr, BCM5464R_AUX_CTL, aux); 177462306a36Sopenharmony_ci if (err) 177562306a36Sopenharmony_ci return err; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci if (lp->autoneg) { 177962306a36Sopenharmony_ci u16 ctrl1000; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; 178262306a36Sopenharmony_ci if ((bmsr & BMSR_10HALF) && 178362306a36Sopenharmony_ci (lp->advertising & ADVERTISED_10baseT_Half)) 178462306a36Sopenharmony_ci adv |= ADVERTISE_10HALF; 178562306a36Sopenharmony_ci if ((bmsr & BMSR_10FULL) && 178662306a36Sopenharmony_ci (lp->advertising & ADVERTISED_10baseT_Full)) 178762306a36Sopenharmony_ci adv |= ADVERTISE_10FULL; 178862306a36Sopenharmony_ci if ((bmsr & BMSR_100HALF) && 178962306a36Sopenharmony_ci (lp->advertising & ADVERTISED_100baseT_Half)) 179062306a36Sopenharmony_ci adv |= ADVERTISE_100HALF; 179162306a36Sopenharmony_ci if ((bmsr & BMSR_100FULL) && 179262306a36Sopenharmony_ci (lp->advertising & ADVERTISED_100baseT_Full)) 179362306a36Sopenharmony_ci adv |= ADVERTISE_100FULL; 179462306a36Sopenharmony_ci err = mii_write(np, np->phy_addr, MII_ADVERTISE, adv); 179562306a36Sopenharmony_ci if (err) 179662306a36Sopenharmony_ci return err; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci if (likely(bmsr & BMSR_ESTATEN)) { 179962306a36Sopenharmony_ci ctrl1000 = 0; 180062306a36Sopenharmony_ci if ((estat & ESTATUS_1000_THALF) && 180162306a36Sopenharmony_ci (lp->advertising & ADVERTISED_1000baseT_Half)) 180262306a36Sopenharmony_ci ctrl1000 |= ADVERTISE_1000HALF; 180362306a36Sopenharmony_ci if ((estat & ESTATUS_1000_TFULL) && 180462306a36Sopenharmony_ci (lp->advertising & ADVERTISED_1000baseT_Full)) 180562306a36Sopenharmony_ci ctrl1000 |= ADVERTISE_1000FULL; 180662306a36Sopenharmony_ci err = mii_write(np, np->phy_addr, 180762306a36Sopenharmony_ci MII_CTRL1000, ctrl1000); 180862306a36Sopenharmony_ci if (err) 180962306a36Sopenharmony_ci return err; 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); 181362306a36Sopenharmony_ci } else { 181462306a36Sopenharmony_ci /* !lp->autoneg */ 181562306a36Sopenharmony_ci int fulldpx; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci if (lp->duplex == DUPLEX_FULL) { 181862306a36Sopenharmony_ci bmcr |= BMCR_FULLDPLX; 181962306a36Sopenharmony_ci fulldpx = 1; 182062306a36Sopenharmony_ci } else if (lp->duplex == DUPLEX_HALF) 182162306a36Sopenharmony_ci fulldpx = 0; 182262306a36Sopenharmony_ci else 182362306a36Sopenharmony_ci return -EINVAL; 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci if (lp->speed == SPEED_1000) { 182662306a36Sopenharmony_ci /* if X-full requested while not supported, or 182762306a36Sopenharmony_ci X-half requested while not supported... */ 182862306a36Sopenharmony_ci if ((fulldpx && !(estat & ESTATUS_1000_TFULL)) || 182962306a36Sopenharmony_ci (!fulldpx && !(estat & ESTATUS_1000_THALF))) 183062306a36Sopenharmony_ci return -EINVAL; 183162306a36Sopenharmony_ci bmcr |= BMCR_SPEED1000; 183262306a36Sopenharmony_ci } else if (lp->speed == SPEED_100) { 183362306a36Sopenharmony_ci if ((fulldpx && !(bmsr & BMSR_100FULL)) || 183462306a36Sopenharmony_ci (!fulldpx && !(bmsr & BMSR_100HALF))) 183562306a36Sopenharmony_ci return -EINVAL; 183662306a36Sopenharmony_ci bmcr |= BMCR_SPEED100; 183762306a36Sopenharmony_ci } else if (lp->speed == SPEED_10) { 183862306a36Sopenharmony_ci if ((fulldpx && !(bmsr & BMSR_10FULL)) || 183962306a36Sopenharmony_ci (!fulldpx && !(bmsr & BMSR_10HALF))) 184062306a36Sopenharmony_ci return -EINVAL; 184162306a36Sopenharmony_ci } else 184262306a36Sopenharmony_ci return -EINVAL; 184362306a36Sopenharmony_ci } 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci err = mii_write(np, np->phy_addr, MII_BMCR, bmcr); 184662306a36Sopenharmony_ci if (err) 184762306a36Sopenharmony_ci return err; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci#if 0 185062306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_BMCR); 185162306a36Sopenharmony_ci if (err < 0) 185262306a36Sopenharmony_ci return err; 185362306a36Sopenharmony_ci bmcr = err; 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci err = mii_read(np, np->phy_addr, MII_BMSR); 185662306a36Sopenharmony_ci if (err < 0) 185762306a36Sopenharmony_ci return err; 185862306a36Sopenharmony_ci bmsr = err; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci pr_info("Port %u after MII init bmcr[%04x] bmsr[%04x]\n", 186162306a36Sopenharmony_ci np->port, bmcr, bmsr); 186262306a36Sopenharmony_ci#endif 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci return 0; 186562306a36Sopenharmony_ci} 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_cistatic int xcvr_init_1g(struct niu *np) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci u64 val; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci /* XXX shared resource, lock parent XXX */ 187262306a36Sopenharmony_ci val = nr64(MIF_CONFIG); 187362306a36Sopenharmony_ci val &= ~MIF_CONFIG_INDIRECT_MODE; 187462306a36Sopenharmony_ci nw64(MIF_CONFIG, val); 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci return mii_init_common(np); 187762306a36Sopenharmony_ci} 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_cistatic int niu_xcvr_init(struct niu *np) 188062306a36Sopenharmony_ci{ 188162306a36Sopenharmony_ci const struct niu_phy_ops *ops = np->phy_ops; 188262306a36Sopenharmony_ci int err; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci err = 0; 188562306a36Sopenharmony_ci if (ops->xcvr_init) 188662306a36Sopenharmony_ci err = ops->xcvr_init(np); 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci return err; 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_cistatic int niu_serdes_init(struct niu *np) 189262306a36Sopenharmony_ci{ 189362306a36Sopenharmony_ci const struct niu_phy_ops *ops = np->phy_ops; 189462306a36Sopenharmony_ci int err; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci err = 0; 189762306a36Sopenharmony_ci if (ops->serdes_init) 189862306a36Sopenharmony_ci err = ops->serdes_init(np); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci return err; 190162306a36Sopenharmony_ci} 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_cistatic void niu_init_xif(struct niu *); 190462306a36Sopenharmony_cistatic void niu_handle_led(struct niu *, int status); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_cistatic int niu_link_status_common(struct niu *np, int link_up) 190762306a36Sopenharmony_ci{ 190862306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 190962306a36Sopenharmony_ci struct net_device *dev = np->dev; 191062306a36Sopenharmony_ci unsigned long flags; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci if (!netif_carrier_ok(dev) && link_up) { 191362306a36Sopenharmony_ci netif_info(np, link, dev, "Link is up at %s, %s duplex\n", 191462306a36Sopenharmony_ci lp->active_speed == SPEED_10000 ? "10Gb/sec" : 191562306a36Sopenharmony_ci lp->active_speed == SPEED_1000 ? "1Gb/sec" : 191662306a36Sopenharmony_ci lp->active_speed == SPEED_100 ? "100Mbit/sec" : 191762306a36Sopenharmony_ci "10Mbit/sec", 191862306a36Sopenharmony_ci lp->active_duplex == DUPLEX_FULL ? "full" : "half"); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 192162306a36Sopenharmony_ci niu_init_xif(np); 192262306a36Sopenharmony_ci niu_handle_led(np, 1); 192362306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci netif_carrier_on(dev); 192662306a36Sopenharmony_ci } else if (netif_carrier_ok(dev) && !link_up) { 192762306a36Sopenharmony_ci netif_warn(np, link, dev, "Link is down\n"); 192862306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 192962306a36Sopenharmony_ci niu_handle_led(np, 0); 193062306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 193162306a36Sopenharmony_ci netif_carrier_off(dev); 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci return 0; 193562306a36Sopenharmony_ci} 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cistatic int link_status_10g_mrvl(struct niu *np, int *link_up_p) 193862306a36Sopenharmony_ci{ 193962306a36Sopenharmony_ci int err, link_up, pma_status, pcs_status; 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci link_up = 0; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR, 194462306a36Sopenharmony_ci MRVL88X2011_10G_PMD_STATUS_2); 194562306a36Sopenharmony_ci if (err < 0) 194662306a36Sopenharmony_ci goto out; 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci /* Check PMA/PMD Register: 1.0001.2 == 1 */ 194962306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR, 195062306a36Sopenharmony_ci MRVL88X2011_PMA_PMD_STATUS_1); 195162306a36Sopenharmony_ci if (err < 0) 195262306a36Sopenharmony_ci goto out; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci pma_status = ((err & MRVL88X2011_LNK_STATUS_OK) ? 1 : 0); 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci /* Check PMC Register : 3.0001.2 == 1: read twice */ 195762306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR, 195862306a36Sopenharmony_ci MRVL88X2011_PMA_PMD_STATUS_1); 195962306a36Sopenharmony_ci if (err < 0) 196062306a36Sopenharmony_ci goto out; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR, 196362306a36Sopenharmony_ci MRVL88X2011_PMA_PMD_STATUS_1); 196462306a36Sopenharmony_ci if (err < 0) 196562306a36Sopenharmony_ci goto out; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci pcs_status = ((err & MRVL88X2011_LNK_STATUS_OK) ? 1 : 0); 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci /* Check XGXS Register : 4.0018.[0-3,12] */ 197062306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV4_ADDR, 197162306a36Sopenharmony_ci MRVL88X2011_10G_XGXS_LANE_STAT); 197262306a36Sopenharmony_ci if (err < 0) 197362306a36Sopenharmony_ci goto out; 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci if (err == (PHYXS_XGXS_LANE_STAT_ALINGED | PHYXS_XGXS_LANE_STAT_LANE3 | 197662306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_LANE2 | PHYXS_XGXS_LANE_STAT_LANE1 | 197762306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_LANE0 | PHYXS_XGXS_LANE_STAT_MAGIC | 197862306a36Sopenharmony_ci 0x800)) 197962306a36Sopenharmony_ci link_up = (pma_status && pcs_status) ? 1 : 0; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci np->link_config.active_speed = SPEED_10000; 198262306a36Sopenharmony_ci np->link_config.active_duplex = DUPLEX_FULL; 198362306a36Sopenharmony_ci err = 0; 198462306a36Sopenharmony_ciout: 198562306a36Sopenharmony_ci mrvl88x2011_act_led(np, (link_up ? 198662306a36Sopenharmony_ci MRVL88X2011_LED_CTL_PCS_ACT : 198762306a36Sopenharmony_ci MRVL88X2011_LED_CTL_OFF)); 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci *link_up_p = link_up; 199062306a36Sopenharmony_ci return err; 199162306a36Sopenharmony_ci} 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_cistatic int link_status_10g_bcm8706(struct niu *np, int *link_up_p) 199462306a36Sopenharmony_ci{ 199562306a36Sopenharmony_ci int err, link_up; 199662306a36Sopenharmony_ci link_up = 0; 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR, 199962306a36Sopenharmony_ci BCM8704_PMD_RCV_SIGDET); 200062306a36Sopenharmony_ci if (err < 0 || err == 0xffff) 200162306a36Sopenharmony_ci goto out; 200262306a36Sopenharmony_ci if (!(err & PMD_RCV_SIGDET_GLOBAL)) { 200362306a36Sopenharmony_ci err = 0; 200462306a36Sopenharmony_ci goto out; 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR, 200862306a36Sopenharmony_ci BCM8704_PCS_10G_R_STATUS); 200962306a36Sopenharmony_ci if (err < 0) 201062306a36Sopenharmony_ci goto out; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci if (!(err & PCS_10G_R_STATUS_BLK_LOCK)) { 201362306a36Sopenharmony_ci err = 0; 201462306a36Sopenharmony_ci goto out; 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR, 201862306a36Sopenharmony_ci BCM8704_PHYXS_XGXS_LANE_STAT); 201962306a36Sopenharmony_ci if (err < 0) 202062306a36Sopenharmony_ci goto out; 202162306a36Sopenharmony_ci if (err != (PHYXS_XGXS_LANE_STAT_ALINGED | 202262306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_MAGIC | 202362306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_PATTEST | 202462306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_LANE3 | 202562306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_LANE2 | 202662306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_LANE1 | 202762306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_LANE0)) { 202862306a36Sopenharmony_ci err = 0; 202962306a36Sopenharmony_ci np->link_config.active_speed = SPEED_INVALID; 203062306a36Sopenharmony_ci np->link_config.active_duplex = DUPLEX_INVALID; 203162306a36Sopenharmony_ci goto out; 203262306a36Sopenharmony_ci } 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci link_up = 1; 203562306a36Sopenharmony_ci np->link_config.active_speed = SPEED_10000; 203662306a36Sopenharmony_ci np->link_config.active_duplex = DUPLEX_FULL; 203762306a36Sopenharmony_ci err = 0; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ciout: 204062306a36Sopenharmony_ci *link_up_p = link_up; 204162306a36Sopenharmony_ci return err; 204262306a36Sopenharmony_ci} 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_cistatic int link_status_10g_bcom(struct niu *np, int *link_up_p) 204562306a36Sopenharmony_ci{ 204662306a36Sopenharmony_ci int err, link_up; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci link_up = 0; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR, 205162306a36Sopenharmony_ci BCM8704_PMD_RCV_SIGDET); 205262306a36Sopenharmony_ci if (err < 0) 205362306a36Sopenharmony_ci goto out; 205462306a36Sopenharmony_ci if (!(err & PMD_RCV_SIGDET_GLOBAL)) { 205562306a36Sopenharmony_ci err = 0; 205662306a36Sopenharmony_ci goto out; 205762306a36Sopenharmony_ci } 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR, 206062306a36Sopenharmony_ci BCM8704_PCS_10G_R_STATUS); 206162306a36Sopenharmony_ci if (err < 0) 206262306a36Sopenharmony_ci goto out; 206362306a36Sopenharmony_ci if (!(err & PCS_10G_R_STATUS_BLK_LOCK)) { 206462306a36Sopenharmony_ci err = 0; 206562306a36Sopenharmony_ci goto out; 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR, 206962306a36Sopenharmony_ci BCM8704_PHYXS_XGXS_LANE_STAT); 207062306a36Sopenharmony_ci if (err < 0) 207162306a36Sopenharmony_ci goto out; 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci if (err != (PHYXS_XGXS_LANE_STAT_ALINGED | 207462306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_MAGIC | 207562306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_LANE3 | 207662306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_LANE2 | 207762306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_LANE1 | 207862306a36Sopenharmony_ci PHYXS_XGXS_LANE_STAT_LANE0)) { 207962306a36Sopenharmony_ci err = 0; 208062306a36Sopenharmony_ci goto out; 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci link_up = 1; 208462306a36Sopenharmony_ci np->link_config.active_speed = SPEED_10000; 208562306a36Sopenharmony_ci np->link_config.active_duplex = DUPLEX_FULL; 208662306a36Sopenharmony_ci err = 0; 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ciout: 208962306a36Sopenharmony_ci *link_up_p = link_up; 209062306a36Sopenharmony_ci return err; 209162306a36Sopenharmony_ci} 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_cistatic int link_status_10g(struct niu *np, int *link_up_p) 209462306a36Sopenharmony_ci{ 209562306a36Sopenharmony_ci unsigned long flags; 209662306a36Sopenharmony_ci int err = -EINVAL; 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci if (np->link_config.loopback_mode == LOOPBACK_DISABLED) { 210162306a36Sopenharmony_ci int phy_id; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci phy_id = phy_decode(np->parent->port_phy, np->port); 210462306a36Sopenharmony_ci phy_id = np->parent->phy_probe_info.phy_id[phy_id][np->port]; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci /* handle different phy types */ 210762306a36Sopenharmony_ci switch (phy_id & NIU_PHY_ID_MASK) { 210862306a36Sopenharmony_ci case NIU_PHY_ID_MRVL88X2011: 210962306a36Sopenharmony_ci err = link_status_10g_mrvl(np, link_up_p); 211062306a36Sopenharmony_ci break; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci default: /* bcom 8704 */ 211362306a36Sopenharmony_ci err = link_status_10g_bcom(np, link_up_p); 211462306a36Sopenharmony_ci break; 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci } 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci return err; 212162306a36Sopenharmony_ci} 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_cistatic int niu_10g_phy_present(struct niu *np) 212462306a36Sopenharmony_ci{ 212562306a36Sopenharmony_ci u64 sig, mask, val; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci sig = nr64(ESR_INT_SIGNALS); 212862306a36Sopenharmony_ci switch (np->port) { 212962306a36Sopenharmony_ci case 0: 213062306a36Sopenharmony_ci mask = ESR_INT_SIGNALS_P0_BITS; 213162306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P0 | 213262306a36Sopenharmony_ci ESR_INT_DET0_P0 | 213362306a36Sopenharmony_ci ESR_INT_XSRDY_P0 | 213462306a36Sopenharmony_ci ESR_INT_XDP_P0_CH3 | 213562306a36Sopenharmony_ci ESR_INT_XDP_P0_CH2 | 213662306a36Sopenharmony_ci ESR_INT_XDP_P0_CH1 | 213762306a36Sopenharmony_ci ESR_INT_XDP_P0_CH0); 213862306a36Sopenharmony_ci break; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci case 1: 214162306a36Sopenharmony_ci mask = ESR_INT_SIGNALS_P1_BITS; 214262306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P1 | 214362306a36Sopenharmony_ci ESR_INT_DET0_P1 | 214462306a36Sopenharmony_ci ESR_INT_XSRDY_P1 | 214562306a36Sopenharmony_ci ESR_INT_XDP_P1_CH3 | 214662306a36Sopenharmony_ci ESR_INT_XDP_P1_CH2 | 214762306a36Sopenharmony_ci ESR_INT_XDP_P1_CH1 | 214862306a36Sopenharmony_ci ESR_INT_XDP_P1_CH0); 214962306a36Sopenharmony_ci break; 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci default: 215262306a36Sopenharmony_ci return 0; 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci if ((sig & mask) != val) 215662306a36Sopenharmony_ci return 0; 215762306a36Sopenharmony_ci return 1; 215862306a36Sopenharmony_ci} 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_cistatic int link_status_10g_hotplug(struct niu *np, int *link_up_p) 216162306a36Sopenharmony_ci{ 216262306a36Sopenharmony_ci unsigned long flags; 216362306a36Sopenharmony_ci int err = 0; 216462306a36Sopenharmony_ci int phy_present; 216562306a36Sopenharmony_ci int phy_present_prev; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci if (np->link_config.loopback_mode == LOOPBACK_DISABLED) { 217062306a36Sopenharmony_ci phy_present_prev = (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) ? 217162306a36Sopenharmony_ci 1 : 0; 217262306a36Sopenharmony_ci phy_present = niu_10g_phy_present(np); 217362306a36Sopenharmony_ci if (phy_present != phy_present_prev) { 217462306a36Sopenharmony_ci /* state change */ 217562306a36Sopenharmony_ci if (phy_present) { 217662306a36Sopenharmony_ci /* A NEM was just plugged in */ 217762306a36Sopenharmony_ci np->flags |= NIU_FLAGS_HOTPLUG_PHY_PRESENT; 217862306a36Sopenharmony_ci if (np->phy_ops->xcvr_init) 217962306a36Sopenharmony_ci err = np->phy_ops->xcvr_init(np); 218062306a36Sopenharmony_ci if (err) { 218162306a36Sopenharmony_ci err = mdio_read(np, np->phy_addr, 218262306a36Sopenharmony_ci BCM8704_PHYXS_DEV_ADDR, MII_BMCR); 218362306a36Sopenharmony_ci if (err == 0xffff) { 218462306a36Sopenharmony_ci /* No mdio, back-to-back XAUI */ 218562306a36Sopenharmony_ci goto out; 218662306a36Sopenharmony_ci } 218762306a36Sopenharmony_ci /* debounce */ 218862306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT; 218962306a36Sopenharmony_ci } 219062306a36Sopenharmony_ci } else { 219162306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT; 219262306a36Sopenharmony_ci *link_up_p = 0; 219362306a36Sopenharmony_ci netif_warn(np, link, np->dev, 219462306a36Sopenharmony_ci "Hotplug PHY Removed\n"); 219562306a36Sopenharmony_ci } 219662306a36Sopenharmony_ci } 219762306a36Sopenharmony_ciout: 219862306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) { 219962306a36Sopenharmony_ci err = link_status_10g_bcm8706(np, link_up_p); 220062306a36Sopenharmony_ci if (err == 0xffff) { 220162306a36Sopenharmony_ci /* No mdio, back-to-back XAUI: it is C10NEM */ 220262306a36Sopenharmony_ci *link_up_p = 1; 220362306a36Sopenharmony_ci np->link_config.active_speed = SPEED_10000; 220462306a36Sopenharmony_ci np->link_config.active_duplex = DUPLEX_FULL; 220562306a36Sopenharmony_ci } 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci return 0; 221262306a36Sopenharmony_ci} 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_cistatic int niu_link_status(struct niu *np, int *link_up_p) 221562306a36Sopenharmony_ci{ 221662306a36Sopenharmony_ci const struct niu_phy_ops *ops = np->phy_ops; 221762306a36Sopenharmony_ci int err; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci err = 0; 222062306a36Sopenharmony_ci if (ops->link_status) 222162306a36Sopenharmony_ci err = ops->link_status(np, link_up_p); 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci return err; 222462306a36Sopenharmony_ci} 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_cistatic void niu_timer(struct timer_list *t) 222762306a36Sopenharmony_ci{ 222862306a36Sopenharmony_ci struct niu *np = from_timer(np, t, timer); 222962306a36Sopenharmony_ci unsigned long off; 223062306a36Sopenharmony_ci int err, link_up; 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci err = niu_link_status(np, &link_up); 223362306a36Sopenharmony_ci if (!err) 223462306a36Sopenharmony_ci niu_link_status_common(np, link_up); 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci if (netif_carrier_ok(np->dev)) 223762306a36Sopenharmony_ci off = 5 * HZ; 223862306a36Sopenharmony_ci else 223962306a36Sopenharmony_ci off = 1 * HZ; 224062306a36Sopenharmony_ci np->timer.expires = jiffies + off; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci add_timer(&np->timer); 224362306a36Sopenharmony_ci} 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_10g_serdes = { 224662306a36Sopenharmony_ci .serdes_init = serdes_init_10g_serdes, 224762306a36Sopenharmony_ci .link_status = link_status_10g_serdes, 224862306a36Sopenharmony_ci}; 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_10g_serdes_niu = { 225162306a36Sopenharmony_ci .serdes_init = serdes_init_niu_10g_serdes, 225262306a36Sopenharmony_ci .link_status = link_status_10g_serdes, 225362306a36Sopenharmony_ci}; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_1g_serdes_niu = { 225662306a36Sopenharmony_ci .serdes_init = serdes_init_niu_1g_serdes, 225762306a36Sopenharmony_ci .link_status = link_status_1g_serdes, 225862306a36Sopenharmony_ci}; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_1g_rgmii = { 226162306a36Sopenharmony_ci .xcvr_init = xcvr_init_1g_rgmii, 226262306a36Sopenharmony_ci .link_status = link_status_1g_rgmii, 226362306a36Sopenharmony_ci}; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_10g_fiber_niu = { 226662306a36Sopenharmony_ci .serdes_init = serdes_init_niu_10g_fiber, 226762306a36Sopenharmony_ci .xcvr_init = xcvr_init_10g, 226862306a36Sopenharmony_ci .link_status = link_status_10g, 226962306a36Sopenharmony_ci}; 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_10g_fiber = { 227262306a36Sopenharmony_ci .serdes_init = serdes_init_10g, 227362306a36Sopenharmony_ci .xcvr_init = xcvr_init_10g, 227462306a36Sopenharmony_ci .link_status = link_status_10g, 227562306a36Sopenharmony_ci}; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_10g_fiber_hotplug = { 227862306a36Sopenharmony_ci .serdes_init = serdes_init_10g, 227962306a36Sopenharmony_ci .xcvr_init = xcvr_init_10g_bcm8706, 228062306a36Sopenharmony_ci .link_status = link_status_10g_hotplug, 228162306a36Sopenharmony_ci}; 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_niu_10g_hotplug = { 228462306a36Sopenharmony_ci .serdes_init = serdes_init_niu_10g_fiber, 228562306a36Sopenharmony_ci .xcvr_init = xcvr_init_10g_bcm8706, 228662306a36Sopenharmony_ci .link_status = link_status_10g_hotplug, 228762306a36Sopenharmony_ci}; 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_10g_copper = { 229062306a36Sopenharmony_ci .serdes_init = serdes_init_10g, 229162306a36Sopenharmony_ci .link_status = link_status_10g, /* XXX */ 229262306a36Sopenharmony_ci}; 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_1g_fiber = { 229562306a36Sopenharmony_ci .serdes_init = serdes_init_1g, 229662306a36Sopenharmony_ci .xcvr_init = xcvr_init_1g, 229762306a36Sopenharmony_ci .link_status = link_status_1g, 229862306a36Sopenharmony_ci}; 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_cistatic const struct niu_phy_ops phy_ops_1g_copper = { 230162306a36Sopenharmony_ci .xcvr_init = xcvr_init_1g, 230262306a36Sopenharmony_ci .link_status = link_status_1g, 230362306a36Sopenharmony_ci}; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_cistruct niu_phy_template { 230662306a36Sopenharmony_ci const struct niu_phy_ops *ops; 230762306a36Sopenharmony_ci u32 phy_addr_base; 230862306a36Sopenharmony_ci}; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_niu_10g_fiber = { 231162306a36Sopenharmony_ci .ops = &phy_ops_10g_fiber_niu, 231262306a36Sopenharmony_ci .phy_addr_base = 16, 231362306a36Sopenharmony_ci}; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_niu_10g_serdes = { 231662306a36Sopenharmony_ci .ops = &phy_ops_10g_serdes_niu, 231762306a36Sopenharmony_ci .phy_addr_base = 0, 231862306a36Sopenharmony_ci}; 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_niu_1g_serdes = { 232162306a36Sopenharmony_ci .ops = &phy_ops_1g_serdes_niu, 232262306a36Sopenharmony_ci .phy_addr_base = 0, 232362306a36Sopenharmony_ci}; 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_10g_fiber = { 232662306a36Sopenharmony_ci .ops = &phy_ops_10g_fiber, 232762306a36Sopenharmony_ci .phy_addr_base = 8, 232862306a36Sopenharmony_ci}; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_10g_fiber_hotplug = { 233162306a36Sopenharmony_ci .ops = &phy_ops_10g_fiber_hotplug, 233262306a36Sopenharmony_ci .phy_addr_base = 8, 233362306a36Sopenharmony_ci}; 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_niu_10g_hotplug = { 233662306a36Sopenharmony_ci .ops = &phy_ops_niu_10g_hotplug, 233762306a36Sopenharmony_ci .phy_addr_base = 8, 233862306a36Sopenharmony_ci}; 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_10g_copper = { 234162306a36Sopenharmony_ci .ops = &phy_ops_10g_copper, 234262306a36Sopenharmony_ci .phy_addr_base = 10, 234362306a36Sopenharmony_ci}; 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_1g_fiber = { 234662306a36Sopenharmony_ci .ops = &phy_ops_1g_fiber, 234762306a36Sopenharmony_ci .phy_addr_base = 0, 234862306a36Sopenharmony_ci}; 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_1g_copper = { 235162306a36Sopenharmony_ci .ops = &phy_ops_1g_copper, 235262306a36Sopenharmony_ci .phy_addr_base = 0, 235362306a36Sopenharmony_ci}; 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_1g_rgmii = { 235662306a36Sopenharmony_ci .ops = &phy_ops_1g_rgmii, 235762306a36Sopenharmony_ci .phy_addr_base = 0, 235862306a36Sopenharmony_ci}; 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_cistatic const struct niu_phy_template phy_template_10g_serdes = { 236162306a36Sopenharmony_ci .ops = &phy_ops_10g_serdes, 236262306a36Sopenharmony_ci .phy_addr_base = 0, 236362306a36Sopenharmony_ci}; 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_cistatic int niu_atca_port_num[4] = { 236662306a36Sopenharmony_ci 0, 0, 11, 10 236762306a36Sopenharmony_ci}; 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_cistatic int serdes_init_10g_serdes(struct niu *np) 237062306a36Sopenharmony_ci{ 237162306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 237262306a36Sopenharmony_ci unsigned long ctrl_reg, test_cfg_reg, pll_cfg, i; 237362306a36Sopenharmony_ci u64 ctrl_val, test_cfg_val, sig, mask, val; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci switch (np->port) { 237662306a36Sopenharmony_ci case 0: 237762306a36Sopenharmony_ci ctrl_reg = ENET_SERDES_0_CTRL_CFG; 237862306a36Sopenharmony_ci test_cfg_reg = ENET_SERDES_0_TEST_CFG; 237962306a36Sopenharmony_ci pll_cfg = ENET_SERDES_0_PLL_CFG; 238062306a36Sopenharmony_ci break; 238162306a36Sopenharmony_ci case 1: 238262306a36Sopenharmony_ci ctrl_reg = ENET_SERDES_1_CTRL_CFG; 238362306a36Sopenharmony_ci test_cfg_reg = ENET_SERDES_1_TEST_CFG; 238462306a36Sopenharmony_ci pll_cfg = ENET_SERDES_1_PLL_CFG; 238562306a36Sopenharmony_ci break; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci default: 238862306a36Sopenharmony_ci return -EINVAL; 238962306a36Sopenharmony_ci } 239062306a36Sopenharmony_ci ctrl_val = (ENET_SERDES_CTRL_SDET_0 | 239162306a36Sopenharmony_ci ENET_SERDES_CTRL_SDET_1 | 239262306a36Sopenharmony_ci ENET_SERDES_CTRL_SDET_2 | 239362306a36Sopenharmony_ci ENET_SERDES_CTRL_SDET_3 | 239462306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_0_SHIFT) | 239562306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_1_SHIFT) | 239662306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_2_SHIFT) | 239762306a36Sopenharmony_ci (0x5 << ENET_SERDES_CTRL_EMPH_3_SHIFT) | 239862306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_0_SHIFT) | 239962306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_1_SHIFT) | 240062306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_2_SHIFT) | 240162306a36Sopenharmony_ci (0x1 << ENET_SERDES_CTRL_LADJ_3_SHIFT)); 240262306a36Sopenharmony_ci test_cfg_val = 0; 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_PHY) { 240562306a36Sopenharmony_ci test_cfg_val |= ((ENET_TEST_MD_PAD_LOOPBACK << 240662306a36Sopenharmony_ci ENET_SERDES_TEST_MD_0_SHIFT) | 240762306a36Sopenharmony_ci (ENET_TEST_MD_PAD_LOOPBACK << 240862306a36Sopenharmony_ci ENET_SERDES_TEST_MD_1_SHIFT) | 240962306a36Sopenharmony_ci (ENET_TEST_MD_PAD_LOOPBACK << 241062306a36Sopenharmony_ci ENET_SERDES_TEST_MD_2_SHIFT) | 241162306a36Sopenharmony_ci (ENET_TEST_MD_PAD_LOOPBACK << 241262306a36Sopenharmony_ci ENET_SERDES_TEST_MD_3_SHIFT)); 241362306a36Sopenharmony_ci } 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci esr_reset(np); 241662306a36Sopenharmony_ci nw64(pll_cfg, ENET_SERDES_PLL_FBDIV2); 241762306a36Sopenharmony_ci nw64(ctrl_reg, ctrl_val); 241862306a36Sopenharmony_ci nw64(test_cfg_reg, test_cfg_val); 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci /* Initialize all 4 lanes of the SERDES. */ 242162306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 242262306a36Sopenharmony_ci u32 rxtx_ctrl, glue0; 242362306a36Sopenharmony_ci int err; 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci err = esr_read_rxtx_ctrl(np, i, &rxtx_ctrl); 242662306a36Sopenharmony_ci if (err) 242762306a36Sopenharmony_ci return err; 242862306a36Sopenharmony_ci err = esr_read_glue0(np, i, &glue0); 242962306a36Sopenharmony_ci if (err) 243062306a36Sopenharmony_ci return err; 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci rxtx_ctrl &= ~(ESR_RXTX_CTRL_VMUXLO); 243362306a36Sopenharmony_ci rxtx_ctrl |= (ESR_RXTX_CTRL_ENSTRETCH | 243462306a36Sopenharmony_ci (2 << ESR_RXTX_CTRL_VMUXLO_SHIFT)); 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci glue0 &= ~(ESR_GLUE_CTRL0_SRATE | 243762306a36Sopenharmony_ci ESR_GLUE_CTRL0_THCNT | 243862306a36Sopenharmony_ci ESR_GLUE_CTRL0_BLTIME); 243962306a36Sopenharmony_ci glue0 |= (ESR_GLUE_CTRL0_RXLOSENAB | 244062306a36Sopenharmony_ci (0xf << ESR_GLUE_CTRL0_SRATE_SHIFT) | 244162306a36Sopenharmony_ci (0xff << ESR_GLUE_CTRL0_THCNT_SHIFT) | 244262306a36Sopenharmony_ci (BLTIME_300_CYCLES << 244362306a36Sopenharmony_ci ESR_GLUE_CTRL0_BLTIME_SHIFT)); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci err = esr_write_rxtx_ctrl(np, i, rxtx_ctrl); 244662306a36Sopenharmony_ci if (err) 244762306a36Sopenharmony_ci return err; 244862306a36Sopenharmony_ci err = esr_write_glue0(np, i, glue0); 244962306a36Sopenharmony_ci if (err) 245062306a36Sopenharmony_ci return err; 245162306a36Sopenharmony_ci } 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ci sig = nr64(ESR_INT_SIGNALS); 245562306a36Sopenharmony_ci switch (np->port) { 245662306a36Sopenharmony_ci case 0: 245762306a36Sopenharmony_ci mask = ESR_INT_SIGNALS_P0_BITS; 245862306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P0 | 245962306a36Sopenharmony_ci ESR_INT_DET0_P0 | 246062306a36Sopenharmony_ci ESR_INT_XSRDY_P0 | 246162306a36Sopenharmony_ci ESR_INT_XDP_P0_CH3 | 246262306a36Sopenharmony_ci ESR_INT_XDP_P0_CH2 | 246362306a36Sopenharmony_ci ESR_INT_XDP_P0_CH1 | 246462306a36Sopenharmony_ci ESR_INT_XDP_P0_CH0); 246562306a36Sopenharmony_ci break; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci case 1: 246862306a36Sopenharmony_ci mask = ESR_INT_SIGNALS_P1_BITS; 246962306a36Sopenharmony_ci val = (ESR_INT_SRDY0_P1 | 247062306a36Sopenharmony_ci ESR_INT_DET0_P1 | 247162306a36Sopenharmony_ci ESR_INT_XSRDY_P1 | 247262306a36Sopenharmony_ci ESR_INT_XDP_P1_CH3 | 247362306a36Sopenharmony_ci ESR_INT_XDP_P1_CH2 | 247462306a36Sopenharmony_ci ESR_INT_XDP_P1_CH1 | 247562306a36Sopenharmony_ci ESR_INT_XDP_P1_CH0); 247662306a36Sopenharmony_ci break; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci default: 247962306a36Sopenharmony_ci return -EINVAL; 248062306a36Sopenharmony_ci } 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci if ((sig & mask) != val) { 248362306a36Sopenharmony_ci int err; 248462306a36Sopenharmony_ci err = serdes_init_1g_serdes(np); 248562306a36Sopenharmony_ci if (!err) { 248662306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_10G; 248762306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_PCS; 248862306a36Sopenharmony_ci } else { 248962306a36Sopenharmony_ci netdev_err(np->dev, "Port %u 10G/1G SERDES Link Failed\n", 249062306a36Sopenharmony_ci np->port); 249162306a36Sopenharmony_ci return -ENODEV; 249262306a36Sopenharmony_ci } 249362306a36Sopenharmony_ci } 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci return 0; 249662306a36Sopenharmony_ci} 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_cistatic int niu_determine_phy_disposition(struct niu *np) 249962306a36Sopenharmony_ci{ 250062306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 250162306a36Sopenharmony_ci u8 plat_type = parent->plat_type; 250262306a36Sopenharmony_ci const struct niu_phy_template *tp; 250362306a36Sopenharmony_ci u32 phy_addr_off = 0; 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci if (plat_type == PLAT_TYPE_NIU) { 250662306a36Sopenharmony_ci switch (np->flags & 250762306a36Sopenharmony_ci (NIU_FLAGS_10G | 250862306a36Sopenharmony_ci NIU_FLAGS_FIBER | 250962306a36Sopenharmony_ci NIU_FLAGS_XCVR_SERDES)) { 251062306a36Sopenharmony_ci case NIU_FLAGS_10G | NIU_FLAGS_XCVR_SERDES: 251162306a36Sopenharmony_ci /* 10G Serdes */ 251262306a36Sopenharmony_ci tp = &phy_template_niu_10g_serdes; 251362306a36Sopenharmony_ci break; 251462306a36Sopenharmony_ci case NIU_FLAGS_XCVR_SERDES: 251562306a36Sopenharmony_ci /* 1G Serdes */ 251662306a36Sopenharmony_ci tp = &phy_template_niu_1g_serdes; 251762306a36Sopenharmony_ci break; 251862306a36Sopenharmony_ci case NIU_FLAGS_10G | NIU_FLAGS_FIBER: 251962306a36Sopenharmony_ci /* 10G Fiber */ 252062306a36Sopenharmony_ci default: 252162306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_HOTPLUG_PHY) { 252262306a36Sopenharmony_ci tp = &phy_template_niu_10g_hotplug; 252362306a36Sopenharmony_ci if (np->port == 0) 252462306a36Sopenharmony_ci phy_addr_off = 8; 252562306a36Sopenharmony_ci if (np->port == 1) 252662306a36Sopenharmony_ci phy_addr_off = 12; 252762306a36Sopenharmony_ci } else { 252862306a36Sopenharmony_ci tp = &phy_template_niu_10g_fiber; 252962306a36Sopenharmony_ci phy_addr_off += np->port; 253062306a36Sopenharmony_ci } 253162306a36Sopenharmony_ci break; 253262306a36Sopenharmony_ci } 253362306a36Sopenharmony_ci } else { 253462306a36Sopenharmony_ci switch (np->flags & 253562306a36Sopenharmony_ci (NIU_FLAGS_10G | 253662306a36Sopenharmony_ci NIU_FLAGS_FIBER | 253762306a36Sopenharmony_ci NIU_FLAGS_XCVR_SERDES)) { 253862306a36Sopenharmony_ci case 0: 253962306a36Sopenharmony_ci /* 1G copper */ 254062306a36Sopenharmony_ci tp = &phy_template_1g_copper; 254162306a36Sopenharmony_ci if (plat_type == PLAT_TYPE_VF_P0) 254262306a36Sopenharmony_ci phy_addr_off = 10; 254362306a36Sopenharmony_ci else if (plat_type == PLAT_TYPE_VF_P1) 254462306a36Sopenharmony_ci phy_addr_off = 26; 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci phy_addr_off += (np->port ^ 0x3); 254762306a36Sopenharmony_ci break; 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci case NIU_FLAGS_10G: 255062306a36Sopenharmony_ci /* 10G copper */ 255162306a36Sopenharmony_ci tp = &phy_template_10g_copper; 255262306a36Sopenharmony_ci break; 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci case NIU_FLAGS_FIBER: 255562306a36Sopenharmony_ci /* 1G fiber */ 255662306a36Sopenharmony_ci tp = &phy_template_1g_fiber; 255762306a36Sopenharmony_ci break; 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci case NIU_FLAGS_10G | NIU_FLAGS_FIBER: 256062306a36Sopenharmony_ci /* 10G fiber */ 256162306a36Sopenharmony_ci tp = &phy_template_10g_fiber; 256262306a36Sopenharmony_ci if (plat_type == PLAT_TYPE_VF_P0 || 256362306a36Sopenharmony_ci plat_type == PLAT_TYPE_VF_P1) 256462306a36Sopenharmony_ci phy_addr_off = 8; 256562306a36Sopenharmony_ci phy_addr_off += np->port; 256662306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_HOTPLUG_PHY) { 256762306a36Sopenharmony_ci tp = &phy_template_10g_fiber_hotplug; 256862306a36Sopenharmony_ci if (np->port == 0) 256962306a36Sopenharmony_ci phy_addr_off = 8; 257062306a36Sopenharmony_ci if (np->port == 1) 257162306a36Sopenharmony_ci phy_addr_off = 12; 257262306a36Sopenharmony_ci } 257362306a36Sopenharmony_ci break; 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci case NIU_FLAGS_10G | NIU_FLAGS_XCVR_SERDES: 257662306a36Sopenharmony_ci case NIU_FLAGS_XCVR_SERDES | NIU_FLAGS_FIBER: 257762306a36Sopenharmony_ci case NIU_FLAGS_XCVR_SERDES: 257862306a36Sopenharmony_ci switch(np->port) { 257962306a36Sopenharmony_ci case 0: 258062306a36Sopenharmony_ci case 1: 258162306a36Sopenharmony_ci tp = &phy_template_10g_serdes; 258262306a36Sopenharmony_ci break; 258362306a36Sopenharmony_ci case 2: 258462306a36Sopenharmony_ci case 3: 258562306a36Sopenharmony_ci tp = &phy_template_1g_rgmii; 258662306a36Sopenharmony_ci break; 258762306a36Sopenharmony_ci default: 258862306a36Sopenharmony_ci return -EINVAL; 258962306a36Sopenharmony_ci } 259062306a36Sopenharmony_ci phy_addr_off = niu_atca_port_num[np->port]; 259162306a36Sopenharmony_ci break; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci default: 259462306a36Sopenharmony_ci return -EINVAL; 259562306a36Sopenharmony_ci } 259662306a36Sopenharmony_ci } 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ci np->phy_ops = tp->ops; 259962306a36Sopenharmony_ci np->phy_addr = tp->phy_addr_base + phy_addr_off; 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci return 0; 260262306a36Sopenharmony_ci} 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_cistatic int niu_init_link(struct niu *np) 260562306a36Sopenharmony_ci{ 260662306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 260762306a36Sopenharmony_ci int err, ignore; 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci if (parent->plat_type == PLAT_TYPE_NIU) { 261062306a36Sopenharmony_ci err = niu_xcvr_init(np); 261162306a36Sopenharmony_ci if (err) 261262306a36Sopenharmony_ci return err; 261362306a36Sopenharmony_ci msleep(200); 261462306a36Sopenharmony_ci } 261562306a36Sopenharmony_ci err = niu_serdes_init(np); 261662306a36Sopenharmony_ci if (err && !(np->flags & NIU_FLAGS_HOTPLUG_PHY)) 261762306a36Sopenharmony_ci return err; 261862306a36Sopenharmony_ci msleep(200); 261962306a36Sopenharmony_ci err = niu_xcvr_init(np); 262062306a36Sopenharmony_ci if (!err || (np->flags & NIU_FLAGS_HOTPLUG_PHY)) 262162306a36Sopenharmony_ci niu_link_status(np, &ignore); 262262306a36Sopenharmony_ci return 0; 262362306a36Sopenharmony_ci} 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_cistatic void niu_set_primary_mac(struct niu *np, const unsigned char *addr) 262662306a36Sopenharmony_ci{ 262762306a36Sopenharmony_ci u16 reg0 = addr[4] << 8 | addr[5]; 262862306a36Sopenharmony_ci u16 reg1 = addr[2] << 8 | addr[3]; 262962306a36Sopenharmony_ci u16 reg2 = addr[0] << 8 | addr[1]; 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) { 263262306a36Sopenharmony_ci nw64_mac(XMAC_ADDR0, reg0); 263362306a36Sopenharmony_ci nw64_mac(XMAC_ADDR1, reg1); 263462306a36Sopenharmony_ci nw64_mac(XMAC_ADDR2, reg2); 263562306a36Sopenharmony_ci } else { 263662306a36Sopenharmony_ci nw64_mac(BMAC_ADDR0, reg0); 263762306a36Sopenharmony_ci nw64_mac(BMAC_ADDR1, reg1); 263862306a36Sopenharmony_ci nw64_mac(BMAC_ADDR2, reg2); 263962306a36Sopenharmony_ci } 264062306a36Sopenharmony_ci} 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_cistatic int niu_num_alt_addr(struct niu *np) 264362306a36Sopenharmony_ci{ 264462306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 264562306a36Sopenharmony_ci return XMAC_NUM_ALT_ADDR; 264662306a36Sopenharmony_ci else 264762306a36Sopenharmony_ci return BMAC_NUM_ALT_ADDR; 264862306a36Sopenharmony_ci} 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_cistatic int niu_set_alt_mac(struct niu *np, int index, unsigned char *addr) 265162306a36Sopenharmony_ci{ 265262306a36Sopenharmony_ci u16 reg0 = addr[4] << 8 | addr[5]; 265362306a36Sopenharmony_ci u16 reg1 = addr[2] << 8 | addr[3]; 265462306a36Sopenharmony_ci u16 reg2 = addr[0] << 8 | addr[1]; 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci if (index >= niu_num_alt_addr(np)) 265762306a36Sopenharmony_ci return -EINVAL; 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) { 266062306a36Sopenharmony_ci nw64_mac(XMAC_ALT_ADDR0(index), reg0); 266162306a36Sopenharmony_ci nw64_mac(XMAC_ALT_ADDR1(index), reg1); 266262306a36Sopenharmony_ci nw64_mac(XMAC_ALT_ADDR2(index), reg2); 266362306a36Sopenharmony_ci } else { 266462306a36Sopenharmony_ci nw64_mac(BMAC_ALT_ADDR0(index), reg0); 266562306a36Sopenharmony_ci nw64_mac(BMAC_ALT_ADDR1(index), reg1); 266662306a36Sopenharmony_ci nw64_mac(BMAC_ALT_ADDR2(index), reg2); 266762306a36Sopenharmony_ci } 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci return 0; 267062306a36Sopenharmony_ci} 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_cistatic int niu_enable_alt_mac(struct niu *np, int index, int on) 267362306a36Sopenharmony_ci{ 267462306a36Sopenharmony_ci unsigned long reg; 267562306a36Sopenharmony_ci u64 val, mask; 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci if (index >= niu_num_alt_addr(np)) 267862306a36Sopenharmony_ci return -EINVAL; 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) { 268162306a36Sopenharmony_ci reg = XMAC_ADDR_CMPEN; 268262306a36Sopenharmony_ci mask = 1 << index; 268362306a36Sopenharmony_ci } else { 268462306a36Sopenharmony_ci reg = BMAC_ADDR_CMPEN; 268562306a36Sopenharmony_ci mask = 1 << (index + 1); 268662306a36Sopenharmony_ci } 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_ci val = nr64_mac(reg); 268962306a36Sopenharmony_ci if (on) 269062306a36Sopenharmony_ci val |= mask; 269162306a36Sopenharmony_ci else 269262306a36Sopenharmony_ci val &= ~mask; 269362306a36Sopenharmony_ci nw64_mac(reg, val); 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci return 0; 269662306a36Sopenharmony_ci} 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_cistatic void __set_rdc_table_num_hw(struct niu *np, unsigned long reg, 269962306a36Sopenharmony_ci int num, int mac_pref) 270062306a36Sopenharmony_ci{ 270162306a36Sopenharmony_ci u64 val = nr64_mac(reg); 270262306a36Sopenharmony_ci val &= ~(HOST_INFO_MACRDCTBLN | HOST_INFO_MPR); 270362306a36Sopenharmony_ci val |= num; 270462306a36Sopenharmony_ci if (mac_pref) 270562306a36Sopenharmony_ci val |= HOST_INFO_MPR; 270662306a36Sopenharmony_ci nw64_mac(reg, val); 270762306a36Sopenharmony_ci} 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_cistatic int __set_rdc_table_num(struct niu *np, 271062306a36Sopenharmony_ci int xmac_index, int bmac_index, 271162306a36Sopenharmony_ci int rdc_table_num, int mac_pref) 271262306a36Sopenharmony_ci{ 271362306a36Sopenharmony_ci unsigned long reg; 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci if (rdc_table_num & ~HOST_INFO_MACRDCTBLN) 271662306a36Sopenharmony_ci return -EINVAL; 271762306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 271862306a36Sopenharmony_ci reg = XMAC_HOST_INFO(xmac_index); 271962306a36Sopenharmony_ci else 272062306a36Sopenharmony_ci reg = BMAC_HOST_INFO(bmac_index); 272162306a36Sopenharmony_ci __set_rdc_table_num_hw(np, reg, rdc_table_num, mac_pref); 272262306a36Sopenharmony_ci return 0; 272362306a36Sopenharmony_ci} 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_cistatic int niu_set_primary_mac_rdc_table(struct niu *np, int table_num, 272662306a36Sopenharmony_ci int mac_pref) 272762306a36Sopenharmony_ci{ 272862306a36Sopenharmony_ci return __set_rdc_table_num(np, 17, 0, table_num, mac_pref); 272962306a36Sopenharmony_ci} 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_cistatic int niu_set_multicast_mac_rdc_table(struct niu *np, int table_num, 273262306a36Sopenharmony_ci int mac_pref) 273362306a36Sopenharmony_ci{ 273462306a36Sopenharmony_ci return __set_rdc_table_num(np, 16, 8, table_num, mac_pref); 273562306a36Sopenharmony_ci} 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_cistatic int niu_set_alt_mac_rdc_table(struct niu *np, int idx, 273862306a36Sopenharmony_ci int table_num, int mac_pref) 273962306a36Sopenharmony_ci{ 274062306a36Sopenharmony_ci if (idx >= niu_num_alt_addr(np)) 274162306a36Sopenharmony_ci return -EINVAL; 274262306a36Sopenharmony_ci return __set_rdc_table_num(np, idx, idx + 1, table_num, mac_pref); 274362306a36Sopenharmony_ci} 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_cistatic u64 vlan_entry_set_parity(u64 reg_val) 274662306a36Sopenharmony_ci{ 274762306a36Sopenharmony_ci u64 port01_mask; 274862306a36Sopenharmony_ci u64 port23_mask; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci port01_mask = 0x00ff; 275162306a36Sopenharmony_ci port23_mask = 0xff00; 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci if (hweight64(reg_val & port01_mask) & 1) 275462306a36Sopenharmony_ci reg_val |= ENET_VLAN_TBL_PARITY0; 275562306a36Sopenharmony_ci else 275662306a36Sopenharmony_ci reg_val &= ~ENET_VLAN_TBL_PARITY0; 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci if (hweight64(reg_val & port23_mask) & 1) 275962306a36Sopenharmony_ci reg_val |= ENET_VLAN_TBL_PARITY1; 276062306a36Sopenharmony_ci else 276162306a36Sopenharmony_ci reg_val &= ~ENET_VLAN_TBL_PARITY1; 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci return reg_val; 276462306a36Sopenharmony_ci} 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_cistatic void vlan_tbl_write(struct niu *np, unsigned long index, 276762306a36Sopenharmony_ci int port, int vpr, int rdc_table) 276862306a36Sopenharmony_ci{ 276962306a36Sopenharmony_ci u64 reg_val = nr64(ENET_VLAN_TBL(index)); 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ci reg_val &= ~((ENET_VLAN_TBL_VPR | 277262306a36Sopenharmony_ci ENET_VLAN_TBL_VLANRDCTBLN) << 277362306a36Sopenharmony_ci ENET_VLAN_TBL_SHIFT(port)); 277462306a36Sopenharmony_ci if (vpr) 277562306a36Sopenharmony_ci reg_val |= (ENET_VLAN_TBL_VPR << 277662306a36Sopenharmony_ci ENET_VLAN_TBL_SHIFT(port)); 277762306a36Sopenharmony_ci reg_val |= (rdc_table << ENET_VLAN_TBL_SHIFT(port)); 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci reg_val = vlan_entry_set_parity(reg_val); 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci nw64(ENET_VLAN_TBL(index), reg_val); 278262306a36Sopenharmony_ci} 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_cistatic void vlan_tbl_clear(struct niu *np) 278562306a36Sopenharmony_ci{ 278662306a36Sopenharmony_ci int i; 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci for (i = 0; i < ENET_VLAN_TBL_NUM_ENTRIES; i++) 278962306a36Sopenharmony_ci nw64(ENET_VLAN_TBL(i), 0); 279062306a36Sopenharmony_ci} 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_cistatic int tcam_wait_bit(struct niu *np, u64 bit) 279362306a36Sopenharmony_ci{ 279462306a36Sopenharmony_ci int limit = 1000; 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci while (--limit > 0) { 279762306a36Sopenharmony_ci if (nr64(TCAM_CTL) & bit) 279862306a36Sopenharmony_ci break; 279962306a36Sopenharmony_ci udelay(1); 280062306a36Sopenharmony_ci } 280162306a36Sopenharmony_ci if (limit <= 0) 280262306a36Sopenharmony_ci return -ENODEV; 280362306a36Sopenharmony_ci 280462306a36Sopenharmony_ci return 0; 280562306a36Sopenharmony_ci} 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_cistatic int tcam_flush(struct niu *np, int index) 280862306a36Sopenharmony_ci{ 280962306a36Sopenharmony_ci nw64(TCAM_KEY_0, 0x00); 281062306a36Sopenharmony_ci nw64(TCAM_KEY_MASK_0, 0xff); 281162306a36Sopenharmony_ci nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_WRITE | index)); 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_ci return tcam_wait_bit(np, TCAM_CTL_STAT); 281462306a36Sopenharmony_ci} 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_ci#if 0 281762306a36Sopenharmony_cistatic int tcam_read(struct niu *np, int index, 281862306a36Sopenharmony_ci u64 *key, u64 *mask) 281962306a36Sopenharmony_ci{ 282062306a36Sopenharmony_ci int err; 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_READ | index)); 282362306a36Sopenharmony_ci err = tcam_wait_bit(np, TCAM_CTL_STAT); 282462306a36Sopenharmony_ci if (!err) { 282562306a36Sopenharmony_ci key[0] = nr64(TCAM_KEY_0); 282662306a36Sopenharmony_ci key[1] = nr64(TCAM_KEY_1); 282762306a36Sopenharmony_ci key[2] = nr64(TCAM_KEY_2); 282862306a36Sopenharmony_ci key[3] = nr64(TCAM_KEY_3); 282962306a36Sopenharmony_ci mask[0] = nr64(TCAM_KEY_MASK_0); 283062306a36Sopenharmony_ci mask[1] = nr64(TCAM_KEY_MASK_1); 283162306a36Sopenharmony_ci mask[2] = nr64(TCAM_KEY_MASK_2); 283262306a36Sopenharmony_ci mask[3] = nr64(TCAM_KEY_MASK_3); 283362306a36Sopenharmony_ci } 283462306a36Sopenharmony_ci return err; 283562306a36Sopenharmony_ci} 283662306a36Sopenharmony_ci#endif 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_cistatic int tcam_write(struct niu *np, int index, 283962306a36Sopenharmony_ci u64 *key, u64 *mask) 284062306a36Sopenharmony_ci{ 284162306a36Sopenharmony_ci nw64(TCAM_KEY_0, key[0]); 284262306a36Sopenharmony_ci nw64(TCAM_KEY_1, key[1]); 284362306a36Sopenharmony_ci nw64(TCAM_KEY_2, key[2]); 284462306a36Sopenharmony_ci nw64(TCAM_KEY_3, key[3]); 284562306a36Sopenharmony_ci nw64(TCAM_KEY_MASK_0, mask[0]); 284662306a36Sopenharmony_ci nw64(TCAM_KEY_MASK_1, mask[1]); 284762306a36Sopenharmony_ci nw64(TCAM_KEY_MASK_2, mask[2]); 284862306a36Sopenharmony_ci nw64(TCAM_KEY_MASK_3, mask[3]); 284962306a36Sopenharmony_ci nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_WRITE | index)); 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci return tcam_wait_bit(np, TCAM_CTL_STAT); 285262306a36Sopenharmony_ci} 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci#if 0 285562306a36Sopenharmony_cistatic int tcam_assoc_read(struct niu *np, int index, u64 *data) 285662306a36Sopenharmony_ci{ 285762306a36Sopenharmony_ci int err; 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci nw64(TCAM_CTL, (TCAM_CTL_RWC_RAM_READ | index)); 286062306a36Sopenharmony_ci err = tcam_wait_bit(np, TCAM_CTL_STAT); 286162306a36Sopenharmony_ci if (!err) 286262306a36Sopenharmony_ci *data = nr64(TCAM_KEY_1); 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci return err; 286562306a36Sopenharmony_ci} 286662306a36Sopenharmony_ci#endif 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_cistatic int tcam_assoc_write(struct niu *np, int index, u64 assoc_data) 286962306a36Sopenharmony_ci{ 287062306a36Sopenharmony_ci nw64(TCAM_KEY_1, assoc_data); 287162306a36Sopenharmony_ci nw64(TCAM_CTL, (TCAM_CTL_RWC_RAM_WRITE | index)); 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci return tcam_wait_bit(np, TCAM_CTL_STAT); 287462306a36Sopenharmony_ci} 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_cistatic void tcam_enable(struct niu *np, int on) 287762306a36Sopenharmony_ci{ 287862306a36Sopenharmony_ci u64 val = nr64(FFLP_CFG_1); 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_ci if (on) 288162306a36Sopenharmony_ci val &= ~FFLP_CFG_1_TCAM_DIS; 288262306a36Sopenharmony_ci else 288362306a36Sopenharmony_ci val |= FFLP_CFG_1_TCAM_DIS; 288462306a36Sopenharmony_ci nw64(FFLP_CFG_1, val); 288562306a36Sopenharmony_ci} 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_cistatic void tcam_set_lat_and_ratio(struct niu *np, u64 latency, u64 ratio) 288862306a36Sopenharmony_ci{ 288962306a36Sopenharmony_ci u64 val = nr64(FFLP_CFG_1); 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci val &= ~(FFLP_CFG_1_FFLPINITDONE | 289262306a36Sopenharmony_ci FFLP_CFG_1_CAMLAT | 289362306a36Sopenharmony_ci FFLP_CFG_1_CAMRATIO); 289462306a36Sopenharmony_ci val |= (latency << FFLP_CFG_1_CAMLAT_SHIFT); 289562306a36Sopenharmony_ci val |= (ratio << FFLP_CFG_1_CAMRATIO_SHIFT); 289662306a36Sopenharmony_ci nw64(FFLP_CFG_1, val); 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_ci val = nr64(FFLP_CFG_1); 289962306a36Sopenharmony_ci val |= FFLP_CFG_1_FFLPINITDONE; 290062306a36Sopenharmony_ci nw64(FFLP_CFG_1, val); 290162306a36Sopenharmony_ci} 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_cistatic int tcam_user_eth_class_enable(struct niu *np, unsigned long class, 290462306a36Sopenharmony_ci int on) 290562306a36Sopenharmony_ci{ 290662306a36Sopenharmony_ci unsigned long reg; 290762306a36Sopenharmony_ci u64 val; 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci if (class < CLASS_CODE_ETHERTYPE1 || 291062306a36Sopenharmony_ci class > CLASS_CODE_ETHERTYPE2) 291162306a36Sopenharmony_ci return -EINVAL; 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci reg = L2_CLS(class - CLASS_CODE_ETHERTYPE1); 291462306a36Sopenharmony_ci val = nr64(reg); 291562306a36Sopenharmony_ci if (on) 291662306a36Sopenharmony_ci val |= L2_CLS_VLD; 291762306a36Sopenharmony_ci else 291862306a36Sopenharmony_ci val &= ~L2_CLS_VLD; 291962306a36Sopenharmony_ci nw64(reg, val); 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci return 0; 292262306a36Sopenharmony_ci} 292362306a36Sopenharmony_ci 292462306a36Sopenharmony_ci#if 0 292562306a36Sopenharmony_cistatic int tcam_user_eth_class_set(struct niu *np, unsigned long class, 292662306a36Sopenharmony_ci u64 ether_type) 292762306a36Sopenharmony_ci{ 292862306a36Sopenharmony_ci unsigned long reg; 292962306a36Sopenharmony_ci u64 val; 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_ci if (class < CLASS_CODE_ETHERTYPE1 || 293262306a36Sopenharmony_ci class > CLASS_CODE_ETHERTYPE2 || 293362306a36Sopenharmony_ci (ether_type & ~(u64)0xffff) != 0) 293462306a36Sopenharmony_ci return -EINVAL; 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_ci reg = L2_CLS(class - CLASS_CODE_ETHERTYPE1); 293762306a36Sopenharmony_ci val = nr64(reg); 293862306a36Sopenharmony_ci val &= ~L2_CLS_ETYPE; 293962306a36Sopenharmony_ci val |= (ether_type << L2_CLS_ETYPE_SHIFT); 294062306a36Sopenharmony_ci nw64(reg, val); 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_ci return 0; 294362306a36Sopenharmony_ci} 294462306a36Sopenharmony_ci#endif 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_cistatic int tcam_user_ip_class_enable(struct niu *np, unsigned long class, 294762306a36Sopenharmony_ci int on) 294862306a36Sopenharmony_ci{ 294962306a36Sopenharmony_ci unsigned long reg; 295062306a36Sopenharmony_ci u64 val; 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci if (class < CLASS_CODE_USER_PROG1 || 295362306a36Sopenharmony_ci class > CLASS_CODE_USER_PROG4) 295462306a36Sopenharmony_ci return -EINVAL; 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci reg = L3_CLS(class - CLASS_CODE_USER_PROG1); 295762306a36Sopenharmony_ci val = nr64(reg); 295862306a36Sopenharmony_ci if (on) 295962306a36Sopenharmony_ci val |= L3_CLS_VALID; 296062306a36Sopenharmony_ci else 296162306a36Sopenharmony_ci val &= ~L3_CLS_VALID; 296262306a36Sopenharmony_ci nw64(reg, val); 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci return 0; 296562306a36Sopenharmony_ci} 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_cistatic int tcam_user_ip_class_set(struct niu *np, unsigned long class, 296862306a36Sopenharmony_ci int ipv6, u64 protocol_id, 296962306a36Sopenharmony_ci u64 tos_mask, u64 tos_val) 297062306a36Sopenharmony_ci{ 297162306a36Sopenharmony_ci unsigned long reg; 297262306a36Sopenharmony_ci u64 val; 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci if (class < CLASS_CODE_USER_PROG1 || 297562306a36Sopenharmony_ci class > CLASS_CODE_USER_PROG4 || 297662306a36Sopenharmony_ci (protocol_id & ~(u64)0xff) != 0 || 297762306a36Sopenharmony_ci (tos_mask & ~(u64)0xff) != 0 || 297862306a36Sopenharmony_ci (tos_val & ~(u64)0xff) != 0) 297962306a36Sopenharmony_ci return -EINVAL; 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci reg = L3_CLS(class - CLASS_CODE_USER_PROG1); 298262306a36Sopenharmony_ci val = nr64(reg); 298362306a36Sopenharmony_ci val &= ~(L3_CLS_IPVER | L3_CLS_PID | 298462306a36Sopenharmony_ci L3_CLS_TOSMASK | L3_CLS_TOS); 298562306a36Sopenharmony_ci if (ipv6) 298662306a36Sopenharmony_ci val |= L3_CLS_IPVER; 298762306a36Sopenharmony_ci val |= (protocol_id << L3_CLS_PID_SHIFT); 298862306a36Sopenharmony_ci val |= (tos_mask << L3_CLS_TOSMASK_SHIFT); 298962306a36Sopenharmony_ci val |= (tos_val << L3_CLS_TOS_SHIFT); 299062306a36Sopenharmony_ci nw64(reg, val); 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci return 0; 299362306a36Sopenharmony_ci} 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_cistatic int tcam_early_init(struct niu *np) 299662306a36Sopenharmony_ci{ 299762306a36Sopenharmony_ci unsigned long i; 299862306a36Sopenharmony_ci int err; 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_ci tcam_enable(np, 0); 300162306a36Sopenharmony_ci tcam_set_lat_and_ratio(np, 300262306a36Sopenharmony_ci DEFAULT_TCAM_LATENCY, 300362306a36Sopenharmony_ci DEFAULT_TCAM_ACCESS_RATIO); 300462306a36Sopenharmony_ci for (i = CLASS_CODE_ETHERTYPE1; i <= CLASS_CODE_ETHERTYPE2; i++) { 300562306a36Sopenharmony_ci err = tcam_user_eth_class_enable(np, i, 0); 300662306a36Sopenharmony_ci if (err) 300762306a36Sopenharmony_ci return err; 300862306a36Sopenharmony_ci } 300962306a36Sopenharmony_ci for (i = CLASS_CODE_USER_PROG1; i <= CLASS_CODE_USER_PROG4; i++) { 301062306a36Sopenharmony_ci err = tcam_user_ip_class_enable(np, i, 0); 301162306a36Sopenharmony_ci if (err) 301262306a36Sopenharmony_ci return err; 301362306a36Sopenharmony_ci } 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci return 0; 301662306a36Sopenharmony_ci} 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_cistatic int tcam_flush_all(struct niu *np) 301962306a36Sopenharmony_ci{ 302062306a36Sopenharmony_ci unsigned long i; 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_ci for (i = 0; i < np->parent->tcam_num_entries; i++) { 302362306a36Sopenharmony_ci int err = tcam_flush(np, i); 302462306a36Sopenharmony_ci if (err) 302562306a36Sopenharmony_ci return err; 302662306a36Sopenharmony_ci } 302762306a36Sopenharmony_ci return 0; 302862306a36Sopenharmony_ci} 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_cistatic u64 hash_addr_regval(unsigned long index, unsigned long num_entries) 303162306a36Sopenharmony_ci{ 303262306a36Sopenharmony_ci return (u64)index | (num_entries == 1 ? HASH_TBL_ADDR_AUTOINC : 0); 303362306a36Sopenharmony_ci} 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_ci#if 0 303662306a36Sopenharmony_cistatic int hash_read(struct niu *np, unsigned long partition, 303762306a36Sopenharmony_ci unsigned long index, unsigned long num_entries, 303862306a36Sopenharmony_ci u64 *data) 303962306a36Sopenharmony_ci{ 304062306a36Sopenharmony_ci u64 val = hash_addr_regval(index, num_entries); 304162306a36Sopenharmony_ci unsigned long i; 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_ci if (partition >= FCRAM_NUM_PARTITIONS || 304462306a36Sopenharmony_ci index + num_entries > FCRAM_SIZE) 304562306a36Sopenharmony_ci return -EINVAL; 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci nw64(HASH_TBL_ADDR(partition), val); 304862306a36Sopenharmony_ci for (i = 0; i < num_entries; i++) 304962306a36Sopenharmony_ci data[i] = nr64(HASH_TBL_DATA(partition)); 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_ci return 0; 305262306a36Sopenharmony_ci} 305362306a36Sopenharmony_ci#endif 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_cistatic int hash_write(struct niu *np, unsigned long partition, 305662306a36Sopenharmony_ci unsigned long index, unsigned long num_entries, 305762306a36Sopenharmony_ci u64 *data) 305862306a36Sopenharmony_ci{ 305962306a36Sopenharmony_ci u64 val = hash_addr_regval(index, num_entries); 306062306a36Sopenharmony_ci unsigned long i; 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci if (partition >= FCRAM_NUM_PARTITIONS || 306362306a36Sopenharmony_ci index + (num_entries * 8) > FCRAM_SIZE) 306462306a36Sopenharmony_ci return -EINVAL; 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci nw64(HASH_TBL_ADDR(partition), val); 306762306a36Sopenharmony_ci for (i = 0; i < num_entries; i++) 306862306a36Sopenharmony_ci nw64(HASH_TBL_DATA(partition), data[i]); 306962306a36Sopenharmony_ci 307062306a36Sopenharmony_ci return 0; 307162306a36Sopenharmony_ci} 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_cistatic void fflp_reset(struct niu *np) 307462306a36Sopenharmony_ci{ 307562306a36Sopenharmony_ci u64 val; 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci nw64(FFLP_CFG_1, FFLP_CFG_1_PIO_FIO_RST); 307862306a36Sopenharmony_ci udelay(10); 307962306a36Sopenharmony_ci nw64(FFLP_CFG_1, 0); 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci val = FFLP_CFG_1_FCRAMOUTDR_NORMAL | FFLP_CFG_1_FFLPINITDONE; 308262306a36Sopenharmony_ci nw64(FFLP_CFG_1, val); 308362306a36Sopenharmony_ci} 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_cistatic void fflp_set_timings(struct niu *np) 308662306a36Sopenharmony_ci{ 308762306a36Sopenharmony_ci u64 val = nr64(FFLP_CFG_1); 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci val &= ~FFLP_CFG_1_FFLPINITDONE; 309062306a36Sopenharmony_ci val |= (DEFAULT_FCRAMRATIO << FFLP_CFG_1_FCRAMRATIO_SHIFT); 309162306a36Sopenharmony_ci nw64(FFLP_CFG_1, val); 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci val = nr64(FFLP_CFG_1); 309462306a36Sopenharmony_ci val |= FFLP_CFG_1_FFLPINITDONE; 309562306a36Sopenharmony_ci nw64(FFLP_CFG_1, val); 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ci val = nr64(FCRAM_REF_TMR); 309862306a36Sopenharmony_ci val &= ~(FCRAM_REF_TMR_MAX | FCRAM_REF_TMR_MIN); 309962306a36Sopenharmony_ci val |= (DEFAULT_FCRAM_REFRESH_MAX << FCRAM_REF_TMR_MAX_SHIFT); 310062306a36Sopenharmony_ci val |= (DEFAULT_FCRAM_REFRESH_MIN << FCRAM_REF_TMR_MIN_SHIFT); 310162306a36Sopenharmony_ci nw64(FCRAM_REF_TMR, val); 310262306a36Sopenharmony_ci} 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_cistatic int fflp_set_partition(struct niu *np, u64 partition, 310562306a36Sopenharmony_ci u64 mask, u64 base, int enable) 310662306a36Sopenharmony_ci{ 310762306a36Sopenharmony_ci unsigned long reg; 310862306a36Sopenharmony_ci u64 val; 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci if (partition >= FCRAM_NUM_PARTITIONS || 311162306a36Sopenharmony_ci (mask & ~(u64)0x1f) != 0 || 311262306a36Sopenharmony_ci (base & ~(u64)0x1f) != 0) 311362306a36Sopenharmony_ci return -EINVAL; 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci reg = FLW_PRT_SEL(partition); 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_ci val = nr64(reg); 311862306a36Sopenharmony_ci val &= ~(FLW_PRT_SEL_EXT | FLW_PRT_SEL_MASK | FLW_PRT_SEL_BASE); 311962306a36Sopenharmony_ci val |= (mask << FLW_PRT_SEL_MASK_SHIFT); 312062306a36Sopenharmony_ci val |= (base << FLW_PRT_SEL_BASE_SHIFT); 312162306a36Sopenharmony_ci if (enable) 312262306a36Sopenharmony_ci val |= FLW_PRT_SEL_EXT; 312362306a36Sopenharmony_ci nw64(reg, val); 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci return 0; 312662306a36Sopenharmony_ci} 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_cistatic int fflp_disable_all_partitions(struct niu *np) 312962306a36Sopenharmony_ci{ 313062306a36Sopenharmony_ci unsigned long i; 313162306a36Sopenharmony_ci 313262306a36Sopenharmony_ci for (i = 0; i < FCRAM_NUM_PARTITIONS; i++) { 313362306a36Sopenharmony_ci int err = fflp_set_partition(np, 0, 0, 0, 0); 313462306a36Sopenharmony_ci if (err) 313562306a36Sopenharmony_ci return err; 313662306a36Sopenharmony_ci } 313762306a36Sopenharmony_ci return 0; 313862306a36Sopenharmony_ci} 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_cistatic void fflp_llcsnap_enable(struct niu *np, int on) 314162306a36Sopenharmony_ci{ 314262306a36Sopenharmony_ci u64 val = nr64(FFLP_CFG_1); 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci if (on) 314562306a36Sopenharmony_ci val |= FFLP_CFG_1_LLCSNAP; 314662306a36Sopenharmony_ci else 314762306a36Sopenharmony_ci val &= ~FFLP_CFG_1_LLCSNAP; 314862306a36Sopenharmony_ci nw64(FFLP_CFG_1, val); 314962306a36Sopenharmony_ci} 315062306a36Sopenharmony_ci 315162306a36Sopenharmony_cistatic void fflp_errors_enable(struct niu *np, int on) 315262306a36Sopenharmony_ci{ 315362306a36Sopenharmony_ci u64 val = nr64(FFLP_CFG_1); 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci if (on) 315662306a36Sopenharmony_ci val &= ~FFLP_CFG_1_ERRORDIS; 315762306a36Sopenharmony_ci else 315862306a36Sopenharmony_ci val |= FFLP_CFG_1_ERRORDIS; 315962306a36Sopenharmony_ci nw64(FFLP_CFG_1, val); 316062306a36Sopenharmony_ci} 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_cistatic int fflp_hash_clear(struct niu *np) 316362306a36Sopenharmony_ci{ 316462306a36Sopenharmony_ci struct fcram_hash_ipv4 ent; 316562306a36Sopenharmony_ci unsigned long i; 316662306a36Sopenharmony_ci 316762306a36Sopenharmony_ci /* IPV4 hash entry with valid bit clear, rest is don't care. */ 316862306a36Sopenharmony_ci memset(&ent, 0, sizeof(ent)); 316962306a36Sopenharmony_ci ent.header = HASH_HEADER_EXT; 317062306a36Sopenharmony_ci 317162306a36Sopenharmony_ci for (i = 0; i < FCRAM_SIZE; i += sizeof(ent)) { 317262306a36Sopenharmony_ci int err = hash_write(np, 0, i, 1, (u64 *) &ent); 317362306a36Sopenharmony_ci if (err) 317462306a36Sopenharmony_ci return err; 317562306a36Sopenharmony_ci } 317662306a36Sopenharmony_ci return 0; 317762306a36Sopenharmony_ci} 317862306a36Sopenharmony_ci 317962306a36Sopenharmony_cistatic int fflp_early_init(struct niu *np) 318062306a36Sopenharmony_ci{ 318162306a36Sopenharmony_ci struct niu_parent *parent; 318262306a36Sopenharmony_ci unsigned long flags; 318362306a36Sopenharmony_ci int err; 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci niu_lock_parent(np, flags); 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci parent = np->parent; 318862306a36Sopenharmony_ci err = 0; 318962306a36Sopenharmony_ci if (!(parent->flags & PARENT_FLGS_CLS_HWINIT)) { 319062306a36Sopenharmony_ci if (np->parent->plat_type != PLAT_TYPE_NIU) { 319162306a36Sopenharmony_ci fflp_reset(np); 319262306a36Sopenharmony_ci fflp_set_timings(np); 319362306a36Sopenharmony_ci err = fflp_disable_all_partitions(np); 319462306a36Sopenharmony_ci if (err) { 319562306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 319662306a36Sopenharmony_ci "fflp_disable_all_partitions failed, err=%d\n", 319762306a36Sopenharmony_ci err); 319862306a36Sopenharmony_ci goto out; 319962306a36Sopenharmony_ci } 320062306a36Sopenharmony_ci } 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_ci err = tcam_early_init(np); 320362306a36Sopenharmony_ci if (err) { 320462306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 320562306a36Sopenharmony_ci "tcam_early_init failed, err=%d\n", err); 320662306a36Sopenharmony_ci goto out; 320762306a36Sopenharmony_ci } 320862306a36Sopenharmony_ci fflp_llcsnap_enable(np, 1); 320962306a36Sopenharmony_ci fflp_errors_enable(np, 0); 321062306a36Sopenharmony_ci nw64(H1POLY, 0); 321162306a36Sopenharmony_ci nw64(H2POLY, 0); 321262306a36Sopenharmony_ci 321362306a36Sopenharmony_ci err = tcam_flush_all(np); 321462306a36Sopenharmony_ci if (err) { 321562306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 321662306a36Sopenharmony_ci "tcam_flush_all failed, err=%d\n", err); 321762306a36Sopenharmony_ci goto out; 321862306a36Sopenharmony_ci } 321962306a36Sopenharmony_ci if (np->parent->plat_type != PLAT_TYPE_NIU) { 322062306a36Sopenharmony_ci err = fflp_hash_clear(np); 322162306a36Sopenharmony_ci if (err) { 322262306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 322362306a36Sopenharmony_ci "fflp_hash_clear failed, err=%d\n", 322462306a36Sopenharmony_ci err); 322562306a36Sopenharmony_ci goto out; 322662306a36Sopenharmony_ci } 322762306a36Sopenharmony_ci } 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci vlan_tbl_clear(np); 323062306a36Sopenharmony_ci 323162306a36Sopenharmony_ci parent->flags |= PARENT_FLGS_CLS_HWINIT; 323262306a36Sopenharmony_ci } 323362306a36Sopenharmony_ciout: 323462306a36Sopenharmony_ci niu_unlock_parent(np, flags); 323562306a36Sopenharmony_ci return err; 323662306a36Sopenharmony_ci} 323762306a36Sopenharmony_ci 323862306a36Sopenharmony_cistatic int niu_set_flow_key(struct niu *np, unsigned long class_code, u64 key) 323962306a36Sopenharmony_ci{ 324062306a36Sopenharmony_ci if (class_code < CLASS_CODE_USER_PROG1 || 324162306a36Sopenharmony_ci class_code > CLASS_CODE_SCTP_IPV6) 324262306a36Sopenharmony_ci return -EINVAL; 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci nw64(FLOW_KEY(class_code - CLASS_CODE_USER_PROG1), key); 324562306a36Sopenharmony_ci return 0; 324662306a36Sopenharmony_ci} 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_cistatic int niu_set_tcam_key(struct niu *np, unsigned long class_code, u64 key) 324962306a36Sopenharmony_ci{ 325062306a36Sopenharmony_ci if (class_code < CLASS_CODE_USER_PROG1 || 325162306a36Sopenharmony_ci class_code > CLASS_CODE_SCTP_IPV6) 325262306a36Sopenharmony_ci return -EINVAL; 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci nw64(TCAM_KEY(class_code - CLASS_CODE_USER_PROG1), key); 325562306a36Sopenharmony_ci return 0; 325662306a36Sopenharmony_ci} 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci/* Entries for the ports are interleaved in the TCAM */ 325962306a36Sopenharmony_cistatic u16 tcam_get_index(struct niu *np, u16 idx) 326062306a36Sopenharmony_ci{ 326162306a36Sopenharmony_ci /* One entry reserved for IP fragment rule */ 326262306a36Sopenharmony_ci if (idx >= (np->clas.tcam_sz - 1)) 326362306a36Sopenharmony_ci idx = 0; 326462306a36Sopenharmony_ci return np->clas.tcam_top + ((idx+1) * np->parent->num_ports); 326562306a36Sopenharmony_ci} 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_cistatic u16 tcam_get_size(struct niu *np) 326862306a36Sopenharmony_ci{ 326962306a36Sopenharmony_ci /* One entry reserved for IP fragment rule */ 327062306a36Sopenharmony_ci return np->clas.tcam_sz - 1; 327162306a36Sopenharmony_ci} 327262306a36Sopenharmony_ci 327362306a36Sopenharmony_cistatic u16 tcam_get_valid_entry_cnt(struct niu *np) 327462306a36Sopenharmony_ci{ 327562306a36Sopenharmony_ci /* One entry reserved for IP fragment rule */ 327662306a36Sopenharmony_ci return np->clas.tcam_valid_entries - 1; 327762306a36Sopenharmony_ci} 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_cistatic void niu_rx_skb_append(struct sk_buff *skb, struct page *page, 328062306a36Sopenharmony_ci u32 offset, u32 size, u32 truesize) 328162306a36Sopenharmony_ci{ 328262306a36Sopenharmony_ci skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, offset, size); 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci skb->len += size; 328562306a36Sopenharmony_ci skb->data_len += size; 328662306a36Sopenharmony_ci skb->truesize += truesize; 328762306a36Sopenharmony_ci} 328862306a36Sopenharmony_ci 328962306a36Sopenharmony_cistatic unsigned int niu_hash_rxaddr(struct rx_ring_info *rp, u64 a) 329062306a36Sopenharmony_ci{ 329162306a36Sopenharmony_ci a >>= PAGE_SHIFT; 329262306a36Sopenharmony_ci a ^= (a >> ilog2(MAX_RBR_RING_SIZE)); 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci return a & (MAX_RBR_RING_SIZE - 1); 329562306a36Sopenharmony_ci} 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_cistatic struct page *niu_find_rxpage(struct rx_ring_info *rp, u64 addr, 329862306a36Sopenharmony_ci struct page ***link) 329962306a36Sopenharmony_ci{ 330062306a36Sopenharmony_ci unsigned int h = niu_hash_rxaddr(rp, addr); 330162306a36Sopenharmony_ci struct page *p, **pp; 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_ci addr &= PAGE_MASK; 330462306a36Sopenharmony_ci pp = &rp->rxhash[h]; 330562306a36Sopenharmony_ci for (; (p = *pp) != NULL; pp = &niu_next_page(p)) { 330662306a36Sopenharmony_ci if (p->index == addr) { 330762306a36Sopenharmony_ci *link = pp; 330862306a36Sopenharmony_ci goto found; 330962306a36Sopenharmony_ci } 331062306a36Sopenharmony_ci } 331162306a36Sopenharmony_ci BUG(); 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_cifound: 331462306a36Sopenharmony_ci return p; 331562306a36Sopenharmony_ci} 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_cistatic void niu_hash_page(struct rx_ring_info *rp, struct page *page, u64 base) 331862306a36Sopenharmony_ci{ 331962306a36Sopenharmony_ci unsigned int h = niu_hash_rxaddr(rp, base); 332062306a36Sopenharmony_ci 332162306a36Sopenharmony_ci page->index = base; 332262306a36Sopenharmony_ci niu_next_page(page) = rp->rxhash[h]; 332362306a36Sopenharmony_ci rp->rxhash[h] = page; 332462306a36Sopenharmony_ci} 332562306a36Sopenharmony_ci 332662306a36Sopenharmony_cistatic int niu_rbr_add_page(struct niu *np, struct rx_ring_info *rp, 332762306a36Sopenharmony_ci gfp_t mask, int start_index) 332862306a36Sopenharmony_ci{ 332962306a36Sopenharmony_ci struct page *page; 333062306a36Sopenharmony_ci u64 addr; 333162306a36Sopenharmony_ci int i; 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci page = alloc_page(mask); 333462306a36Sopenharmony_ci if (!page) 333562306a36Sopenharmony_ci return -ENOMEM; 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_ci addr = np->ops->map_page(np->device, page, 0, 333862306a36Sopenharmony_ci PAGE_SIZE, DMA_FROM_DEVICE); 333962306a36Sopenharmony_ci if (!addr) { 334062306a36Sopenharmony_ci __free_page(page); 334162306a36Sopenharmony_ci return -ENOMEM; 334262306a36Sopenharmony_ci } 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci niu_hash_page(rp, page, addr); 334562306a36Sopenharmony_ci if (rp->rbr_blocks_per_page > 1) 334662306a36Sopenharmony_ci page_ref_add(page, rp->rbr_blocks_per_page - 1); 334762306a36Sopenharmony_ci 334862306a36Sopenharmony_ci for (i = 0; i < rp->rbr_blocks_per_page; i++) { 334962306a36Sopenharmony_ci __le32 *rbr = &rp->rbr[start_index + i]; 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci *rbr = cpu_to_le32(addr >> RBR_DESCR_ADDR_SHIFT); 335262306a36Sopenharmony_ci addr += rp->rbr_block_size; 335362306a36Sopenharmony_ci } 335462306a36Sopenharmony_ci 335562306a36Sopenharmony_ci return 0; 335662306a36Sopenharmony_ci} 335762306a36Sopenharmony_ci 335862306a36Sopenharmony_cistatic void niu_rbr_refill(struct niu *np, struct rx_ring_info *rp, gfp_t mask) 335962306a36Sopenharmony_ci{ 336062306a36Sopenharmony_ci int index = rp->rbr_index; 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci rp->rbr_pending++; 336362306a36Sopenharmony_ci if ((rp->rbr_pending % rp->rbr_blocks_per_page) == 0) { 336462306a36Sopenharmony_ci int err = niu_rbr_add_page(np, rp, mask, index); 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_ci if (unlikely(err)) { 336762306a36Sopenharmony_ci rp->rbr_pending--; 336862306a36Sopenharmony_ci return; 336962306a36Sopenharmony_ci } 337062306a36Sopenharmony_ci 337162306a36Sopenharmony_ci rp->rbr_index += rp->rbr_blocks_per_page; 337262306a36Sopenharmony_ci BUG_ON(rp->rbr_index > rp->rbr_table_size); 337362306a36Sopenharmony_ci if (rp->rbr_index == rp->rbr_table_size) 337462306a36Sopenharmony_ci rp->rbr_index = 0; 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_ci if (rp->rbr_pending >= rp->rbr_kick_thresh) { 337762306a36Sopenharmony_ci nw64(RBR_KICK(rp->rx_channel), rp->rbr_pending); 337862306a36Sopenharmony_ci rp->rbr_pending = 0; 337962306a36Sopenharmony_ci } 338062306a36Sopenharmony_ci } 338162306a36Sopenharmony_ci} 338262306a36Sopenharmony_ci 338362306a36Sopenharmony_cistatic int niu_rx_pkt_ignore(struct niu *np, struct rx_ring_info *rp) 338462306a36Sopenharmony_ci{ 338562306a36Sopenharmony_ci unsigned int index = rp->rcr_index; 338662306a36Sopenharmony_ci int num_rcr = 0; 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ci rp->rx_dropped++; 338962306a36Sopenharmony_ci while (1) { 339062306a36Sopenharmony_ci struct page *page, **link; 339162306a36Sopenharmony_ci u64 addr, val; 339262306a36Sopenharmony_ci u32 rcr_size; 339362306a36Sopenharmony_ci 339462306a36Sopenharmony_ci num_rcr++; 339562306a36Sopenharmony_ci 339662306a36Sopenharmony_ci val = le64_to_cpup(&rp->rcr[index]); 339762306a36Sopenharmony_ci addr = (val & RCR_ENTRY_PKT_BUF_ADDR) << 339862306a36Sopenharmony_ci RCR_ENTRY_PKT_BUF_ADDR_SHIFT; 339962306a36Sopenharmony_ci page = niu_find_rxpage(rp, addr, &link); 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci rcr_size = rp->rbr_sizes[(val & RCR_ENTRY_PKTBUFSZ) >> 340262306a36Sopenharmony_ci RCR_ENTRY_PKTBUFSZ_SHIFT]; 340362306a36Sopenharmony_ci if ((page->index + PAGE_SIZE) - rcr_size == addr) { 340462306a36Sopenharmony_ci *link = niu_next_page(page); 340562306a36Sopenharmony_ci np->ops->unmap_page(np->device, page->index, 340662306a36Sopenharmony_ci PAGE_SIZE, DMA_FROM_DEVICE); 340762306a36Sopenharmony_ci page->index = 0; 340862306a36Sopenharmony_ci niu_next_page(page) = NULL; 340962306a36Sopenharmony_ci __free_page(page); 341062306a36Sopenharmony_ci rp->rbr_refill_pending++; 341162306a36Sopenharmony_ci } 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_ci index = NEXT_RCR(rp, index); 341462306a36Sopenharmony_ci if (!(val & RCR_ENTRY_MULTI)) 341562306a36Sopenharmony_ci break; 341662306a36Sopenharmony_ci 341762306a36Sopenharmony_ci } 341862306a36Sopenharmony_ci rp->rcr_index = index; 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci return num_rcr; 342162306a36Sopenharmony_ci} 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_cistatic int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np, 342462306a36Sopenharmony_ci struct rx_ring_info *rp) 342562306a36Sopenharmony_ci{ 342662306a36Sopenharmony_ci unsigned int index = rp->rcr_index; 342762306a36Sopenharmony_ci struct rx_pkt_hdr1 *rh; 342862306a36Sopenharmony_ci struct sk_buff *skb; 342962306a36Sopenharmony_ci int len, num_rcr; 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_ci skb = netdev_alloc_skb(np->dev, RX_SKB_ALLOC_SIZE); 343262306a36Sopenharmony_ci if (unlikely(!skb)) 343362306a36Sopenharmony_ci return niu_rx_pkt_ignore(np, rp); 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_ci num_rcr = 0; 343662306a36Sopenharmony_ci while (1) { 343762306a36Sopenharmony_ci struct page *page, **link; 343862306a36Sopenharmony_ci u32 rcr_size, append_size; 343962306a36Sopenharmony_ci u64 addr, val, off; 344062306a36Sopenharmony_ci 344162306a36Sopenharmony_ci num_rcr++; 344262306a36Sopenharmony_ci 344362306a36Sopenharmony_ci val = le64_to_cpup(&rp->rcr[index]); 344462306a36Sopenharmony_ci 344562306a36Sopenharmony_ci len = (val & RCR_ENTRY_L2_LEN) >> 344662306a36Sopenharmony_ci RCR_ENTRY_L2_LEN_SHIFT; 344762306a36Sopenharmony_ci append_size = len + ETH_HLEN + ETH_FCS_LEN; 344862306a36Sopenharmony_ci 344962306a36Sopenharmony_ci addr = (val & RCR_ENTRY_PKT_BUF_ADDR) << 345062306a36Sopenharmony_ci RCR_ENTRY_PKT_BUF_ADDR_SHIFT; 345162306a36Sopenharmony_ci page = niu_find_rxpage(rp, addr, &link); 345262306a36Sopenharmony_ci 345362306a36Sopenharmony_ci rcr_size = rp->rbr_sizes[(val & RCR_ENTRY_PKTBUFSZ) >> 345462306a36Sopenharmony_ci RCR_ENTRY_PKTBUFSZ_SHIFT]; 345562306a36Sopenharmony_ci 345662306a36Sopenharmony_ci off = addr & ~PAGE_MASK; 345762306a36Sopenharmony_ci if (num_rcr == 1) { 345862306a36Sopenharmony_ci int ptype; 345962306a36Sopenharmony_ci 346062306a36Sopenharmony_ci ptype = (val >> RCR_ENTRY_PKT_TYPE_SHIFT); 346162306a36Sopenharmony_ci if ((ptype == RCR_PKT_TYPE_TCP || 346262306a36Sopenharmony_ci ptype == RCR_PKT_TYPE_UDP) && 346362306a36Sopenharmony_ci !(val & (RCR_ENTRY_NOPORT | 346462306a36Sopenharmony_ci RCR_ENTRY_ERROR))) 346562306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 346662306a36Sopenharmony_ci else 346762306a36Sopenharmony_ci skb_checksum_none_assert(skb); 346862306a36Sopenharmony_ci } else if (!(val & RCR_ENTRY_MULTI)) 346962306a36Sopenharmony_ci append_size = append_size - skb->len; 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci niu_rx_skb_append(skb, page, off, append_size, rcr_size); 347262306a36Sopenharmony_ci if ((page->index + rp->rbr_block_size) - rcr_size == addr) { 347362306a36Sopenharmony_ci *link = niu_next_page(page); 347462306a36Sopenharmony_ci np->ops->unmap_page(np->device, page->index, 347562306a36Sopenharmony_ci PAGE_SIZE, DMA_FROM_DEVICE); 347662306a36Sopenharmony_ci page->index = 0; 347762306a36Sopenharmony_ci niu_next_page(page) = NULL; 347862306a36Sopenharmony_ci rp->rbr_refill_pending++; 347962306a36Sopenharmony_ci } else 348062306a36Sopenharmony_ci get_page(page); 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci index = NEXT_RCR(rp, index); 348362306a36Sopenharmony_ci if (!(val & RCR_ENTRY_MULTI)) 348462306a36Sopenharmony_ci break; 348562306a36Sopenharmony_ci 348662306a36Sopenharmony_ci } 348762306a36Sopenharmony_ci rp->rcr_index = index; 348862306a36Sopenharmony_ci 348962306a36Sopenharmony_ci len += sizeof(*rh); 349062306a36Sopenharmony_ci len = min_t(int, len, sizeof(*rh) + VLAN_ETH_HLEN); 349162306a36Sopenharmony_ci __pskb_pull_tail(skb, len); 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_ci rh = (struct rx_pkt_hdr1 *) skb->data; 349462306a36Sopenharmony_ci if (np->dev->features & NETIF_F_RXHASH) 349562306a36Sopenharmony_ci skb_set_hash(skb, 349662306a36Sopenharmony_ci ((u32)rh->hashval2_0 << 24 | 349762306a36Sopenharmony_ci (u32)rh->hashval2_1 << 16 | 349862306a36Sopenharmony_ci (u32)rh->hashval1_1 << 8 | 349962306a36Sopenharmony_ci (u32)rh->hashval1_2 << 0), 350062306a36Sopenharmony_ci PKT_HASH_TYPE_L3); 350162306a36Sopenharmony_ci skb_pull(skb, sizeof(*rh)); 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_ci rp->rx_packets++; 350462306a36Sopenharmony_ci rp->rx_bytes += skb->len; 350562306a36Sopenharmony_ci 350662306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, np->dev); 350762306a36Sopenharmony_ci skb_record_rx_queue(skb, rp->rx_channel); 350862306a36Sopenharmony_ci napi_gro_receive(napi, skb); 350962306a36Sopenharmony_ci 351062306a36Sopenharmony_ci return num_rcr; 351162306a36Sopenharmony_ci} 351262306a36Sopenharmony_ci 351362306a36Sopenharmony_cistatic int niu_rbr_fill(struct niu *np, struct rx_ring_info *rp, gfp_t mask) 351462306a36Sopenharmony_ci{ 351562306a36Sopenharmony_ci int blocks_per_page = rp->rbr_blocks_per_page; 351662306a36Sopenharmony_ci int err, index = rp->rbr_index; 351762306a36Sopenharmony_ci 351862306a36Sopenharmony_ci err = 0; 351962306a36Sopenharmony_ci while (index < (rp->rbr_table_size - blocks_per_page)) { 352062306a36Sopenharmony_ci err = niu_rbr_add_page(np, rp, mask, index); 352162306a36Sopenharmony_ci if (unlikely(err)) 352262306a36Sopenharmony_ci break; 352362306a36Sopenharmony_ci 352462306a36Sopenharmony_ci index += blocks_per_page; 352562306a36Sopenharmony_ci } 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ci rp->rbr_index = index; 352862306a36Sopenharmony_ci return err; 352962306a36Sopenharmony_ci} 353062306a36Sopenharmony_ci 353162306a36Sopenharmony_cistatic void niu_rbr_free(struct niu *np, struct rx_ring_info *rp) 353262306a36Sopenharmony_ci{ 353362306a36Sopenharmony_ci int i; 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_ci for (i = 0; i < MAX_RBR_RING_SIZE; i++) { 353662306a36Sopenharmony_ci struct page *page; 353762306a36Sopenharmony_ci 353862306a36Sopenharmony_ci page = rp->rxhash[i]; 353962306a36Sopenharmony_ci while (page) { 354062306a36Sopenharmony_ci struct page *next = niu_next_page(page); 354162306a36Sopenharmony_ci u64 base = page->index; 354262306a36Sopenharmony_ci 354362306a36Sopenharmony_ci np->ops->unmap_page(np->device, base, PAGE_SIZE, 354462306a36Sopenharmony_ci DMA_FROM_DEVICE); 354562306a36Sopenharmony_ci page->index = 0; 354662306a36Sopenharmony_ci niu_next_page(page) = NULL; 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci __free_page(page); 354962306a36Sopenharmony_ci 355062306a36Sopenharmony_ci page = next; 355162306a36Sopenharmony_ci } 355262306a36Sopenharmony_ci } 355362306a36Sopenharmony_ci 355462306a36Sopenharmony_ci for (i = 0; i < rp->rbr_table_size; i++) 355562306a36Sopenharmony_ci rp->rbr[i] = cpu_to_le32(0); 355662306a36Sopenharmony_ci rp->rbr_index = 0; 355762306a36Sopenharmony_ci} 355862306a36Sopenharmony_ci 355962306a36Sopenharmony_cistatic int release_tx_packet(struct niu *np, struct tx_ring_info *rp, int idx) 356062306a36Sopenharmony_ci{ 356162306a36Sopenharmony_ci struct tx_buff_info *tb = &rp->tx_buffs[idx]; 356262306a36Sopenharmony_ci struct sk_buff *skb = tb->skb; 356362306a36Sopenharmony_ci struct tx_pkt_hdr *tp; 356462306a36Sopenharmony_ci u64 tx_flags; 356562306a36Sopenharmony_ci int i, len; 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci tp = (struct tx_pkt_hdr *) skb->data; 356862306a36Sopenharmony_ci tx_flags = le64_to_cpup(&tp->flags); 356962306a36Sopenharmony_ci 357062306a36Sopenharmony_ci rp->tx_packets++; 357162306a36Sopenharmony_ci rp->tx_bytes += (((tx_flags & TXHDR_LEN) >> TXHDR_LEN_SHIFT) - 357262306a36Sopenharmony_ci ((tx_flags & TXHDR_PAD) / 2)); 357362306a36Sopenharmony_ci 357462306a36Sopenharmony_ci len = skb_headlen(skb); 357562306a36Sopenharmony_ci np->ops->unmap_single(np->device, tb->mapping, 357662306a36Sopenharmony_ci len, DMA_TO_DEVICE); 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_ci if (le64_to_cpu(rp->descr[idx]) & TX_DESC_MARK) 357962306a36Sopenharmony_ci rp->mark_pending--; 358062306a36Sopenharmony_ci 358162306a36Sopenharmony_ci tb->skb = NULL; 358262306a36Sopenharmony_ci do { 358362306a36Sopenharmony_ci idx = NEXT_TX(rp, idx); 358462306a36Sopenharmony_ci len -= MAX_TX_DESC_LEN; 358562306a36Sopenharmony_ci } while (len > 0); 358662306a36Sopenharmony_ci 358762306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 358862306a36Sopenharmony_ci tb = &rp->tx_buffs[idx]; 358962306a36Sopenharmony_ci BUG_ON(tb->skb != NULL); 359062306a36Sopenharmony_ci np->ops->unmap_page(np->device, tb->mapping, 359162306a36Sopenharmony_ci skb_frag_size(&skb_shinfo(skb)->frags[i]), 359262306a36Sopenharmony_ci DMA_TO_DEVICE); 359362306a36Sopenharmony_ci idx = NEXT_TX(rp, idx); 359462306a36Sopenharmony_ci } 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_ci dev_kfree_skb(skb); 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_ci return idx; 359962306a36Sopenharmony_ci} 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci#define NIU_TX_WAKEUP_THRESH(rp) ((rp)->pending / 4) 360262306a36Sopenharmony_ci 360362306a36Sopenharmony_cistatic void niu_tx_work(struct niu *np, struct tx_ring_info *rp) 360462306a36Sopenharmony_ci{ 360562306a36Sopenharmony_ci struct netdev_queue *txq; 360662306a36Sopenharmony_ci u16 pkt_cnt, tmp; 360762306a36Sopenharmony_ci int cons, index; 360862306a36Sopenharmony_ci u64 cs; 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_ci index = (rp - np->tx_rings); 361162306a36Sopenharmony_ci txq = netdev_get_tx_queue(np->dev, index); 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci cs = rp->tx_cs; 361462306a36Sopenharmony_ci if (unlikely(!(cs & (TX_CS_MK | TX_CS_MMK)))) 361562306a36Sopenharmony_ci goto out; 361662306a36Sopenharmony_ci 361762306a36Sopenharmony_ci tmp = pkt_cnt = (cs & TX_CS_PKT_CNT) >> TX_CS_PKT_CNT_SHIFT; 361862306a36Sopenharmony_ci pkt_cnt = (pkt_cnt - rp->last_pkt_cnt) & 361962306a36Sopenharmony_ci (TX_CS_PKT_CNT >> TX_CS_PKT_CNT_SHIFT); 362062306a36Sopenharmony_ci 362162306a36Sopenharmony_ci rp->last_pkt_cnt = tmp; 362262306a36Sopenharmony_ci 362362306a36Sopenharmony_ci cons = rp->cons; 362462306a36Sopenharmony_ci 362562306a36Sopenharmony_ci netif_printk(np, tx_done, KERN_DEBUG, np->dev, 362662306a36Sopenharmony_ci "%s() pkt_cnt[%u] cons[%d]\n", __func__, pkt_cnt, cons); 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_ci while (pkt_cnt--) 362962306a36Sopenharmony_ci cons = release_tx_packet(np, rp, cons); 363062306a36Sopenharmony_ci 363162306a36Sopenharmony_ci rp->cons = cons; 363262306a36Sopenharmony_ci smp_mb(); 363362306a36Sopenharmony_ci 363462306a36Sopenharmony_ciout: 363562306a36Sopenharmony_ci if (unlikely(netif_tx_queue_stopped(txq) && 363662306a36Sopenharmony_ci (niu_tx_avail(rp) > NIU_TX_WAKEUP_THRESH(rp)))) { 363762306a36Sopenharmony_ci __netif_tx_lock(txq, smp_processor_id()); 363862306a36Sopenharmony_ci if (netif_tx_queue_stopped(txq) && 363962306a36Sopenharmony_ci (niu_tx_avail(rp) > NIU_TX_WAKEUP_THRESH(rp))) 364062306a36Sopenharmony_ci netif_tx_wake_queue(txq); 364162306a36Sopenharmony_ci __netif_tx_unlock(txq); 364262306a36Sopenharmony_ci } 364362306a36Sopenharmony_ci} 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_cistatic inline void niu_sync_rx_discard_stats(struct niu *np, 364662306a36Sopenharmony_ci struct rx_ring_info *rp, 364762306a36Sopenharmony_ci const int limit) 364862306a36Sopenharmony_ci{ 364962306a36Sopenharmony_ci /* This elaborate scheme is needed for reading the RX discard 365062306a36Sopenharmony_ci * counters, as they are only 16-bit and can overflow quickly, 365162306a36Sopenharmony_ci * and because the overflow indication bit is not usable as 365262306a36Sopenharmony_ci * the counter value does not wrap, but remains at max value 365362306a36Sopenharmony_ci * 0xFFFF. 365462306a36Sopenharmony_ci * 365562306a36Sopenharmony_ci * In theory and in practice counters can be lost in between 365662306a36Sopenharmony_ci * reading nr64() and clearing the counter nw64(). For this 365762306a36Sopenharmony_ci * reason, the number of counter clearings nw64() is 365862306a36Sopenharmony_ci * limited/reduced though the limit parameter. 365962306a36Sopenharmony_ci */ 366062306a36Sopenharmony_ci int rx_channel = rp->rx_channel; 366162306a36Sopenharmony_ci u32 misc, wred; 366262306a36Sopenharmony_ci 366362306a36Sopenharmony_ci /* RXMISC (Receive Miscellaneous Discard Count), covers the 366462306a36Sopenharmony_ci * following discard events: IPP (Input Port Process), 366562306a36Sopenharmony_ci * FFLP/TCAM, Full RCR (Receive Completion Ring) RBR (Receive 366662306a36Sopenharmony_ci * Block Ring) prefetch buffer is empty. 366762306a36Sopenharmony_ci */ 366862306a36Sopenharmony_ci misc = nr64(RXMISC(rx_channel)); 366962306a36Sopenharmony_ci if (unlikely((misc & RXMISC_COUNT) > limit)) { 367062306a36Sopenharmony_ci nw64(RXMISC(rx_channel), 0); 367162306a36Sopenharmony_ci rp->rx_errors += misc & RXMISC_COUNT; 367262306a36Sopenharmony_ci 367362306a36Sopenharmony_ci if (unlikely(misc & RXMISC_OFLOW)) 367462306a36Sopenharmony_ci dev_err(np->device, "rx-%d: Counter overflow RXMISC discard\n", 367562306a36Sopenharmony_ci rx_channel); 367662306a36Sopenharmony_ci 367762306a36Sopenharmony_ci netif_printk(np, rx_err, KERN_DEBUG, np->dev, 367862306a36Sopenharmony_ci "rx-%d: MISC drop=%u over=%u\n", 367962306a36Sopenharmony_ci rx_channel, misc, misc-limit); 368062306a36Sopenharmony_ci } 368162306a36Sopenharmony_ci 368262306a36Sopenharmony_ci /* WRED (Weighted Random Early Discard) by hardware */ 368362306a36Sopenharmony_ci wred = nr64(RED_DIS_CNT(rx_channel)); 368462306a36Sopenharmony_ci if (unlikely((wred & RED_DIS_CNT_COUNT) > limit)) { 368562306a36Sopenharmony_ci nw64(RED_DIS_CNT(rx_channel), 0); 368662306a36Sopenharmony_ci rp->rx_dropped += wred & RED_DIS_CNT_COUNT; 368762306a36Sopenharmony_ci 368862306a36Sopenharmony_ci if (unlikely(wred & RED_DIS_CNT_OFLOW)) 368962306a36Sopenharmony_ci dev_err(np->device, "rx-%d: Counter overflow WRED discard\n", rx_channel); 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci netif_printk(np, rx_err, KERN_DEBUG, np->dev, 369262306a36Sopenharmony_ci "rx-%d: WRED drop=%u over=%u\n", 369362306a36Sopenharmony_ci rx_channel, wred, wred-limit); 369462306a36Sopenharmony_ci } 369562306a36Sopenharmony_ci} 369662306a36Sopenharmony_ci 369762306a36Sopenharmony_cistatic int niu_rx_work(struct napi_struct *napi, struct niu *np, 369862306a36Sopenharmony_ci struct rx_ring_info *rp, int budget) 369962306a36Sopenharmony_ci{ 370062306a36Sopenharmony_ci int qlen, rcr_done = 0, work_done = 0; 370162306a36Sopenharmony_ci struct rxdma_mailbox *mbox = rp->mbox; 370262306a36Sopenharmony_ci u64 stat; 370362306a36Sopenharmony_ci 370462306a36Sopenharmony_ci#if 1 370562306a36Sopenharmony_ci stat = nr64(RX_DMA_CTL_STAT(rp->rx_channel)); 370662306a36Sopenharmony_ci qlen = nr64(RCRSTAT_A(rp->rx_channel)) & RCRSTAT_A_QLEN; 370762306a36Sopenharmony_ci#else 370862306a36Sopenharmony_ci stat = le64_to_cpup(&mbox->rx_dma_ctl_stat); 370962306a36Sopenharmony_ci qlen = (le64_to_cpup(&mbox->rcrstat_a) & RCRSTAT_A_QLEN); 371062306a36Sopenharmony_ci#endif 371162306a36Sopenharmony_ci mbox->rx_dma_ctl_stat = 0; 371262306a36Sopenharmony_ci mbox->rcrstat_a = 0; 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_ci netif_printk(np, rx_status, KERN_DEBUG, np->dev, 371562306a36Sopenharmony_ci "%s(chan[%d]), stat[%llx] qlen=%d\n", 371662306a36Sopenharmony_ci __func__, rp->rx_channel, (unsigned long long)stat, qlen); 371762306a36Sopenharmony_ci 371862306a36Sopenharmony_ci rcr_done = work_done = 0; 371962306a36Sopenharmony_ci qlen = min(qlen, budget); 372062306a36Sopenharmony_ci while (work_done < qlen) { 372162306a36Sopenharmony_ci rcr_done += niu_process_rx_pkt(napi, np, rp); 372262306a36Sopenharmony_ci work_done++; 372362306a36Sopenharmony_ci } 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_ci if (rp->rbr_refill_pending >= rp->rbr_kick_thresh) { 372662306a36Sopenharmony_ci unsigned int i; 372762306a36Sopenharmony_ci 372862306a36Sopenharmony_ci for (i = 0; i < rp->rbr_refill_pending; i++) 372962306a36Sopenharmony_ci niu_rbr_refill(np, rp, GFP_ATOMIC); 373062306a36Sopenharmony_ci rp->rbr_refill_pending = 0; 373162306a36Sopenharmony_ci } 373262306a36Sopenharmony_ci 373362306a36Sopenharmony_ci stat = (RX_DMA_CTL_STAT_MEX | 373462306a36Sopenharmony_ci ((u64)work_done << RX_DMA_CTL_STAT_PKTREAD_SHIFT) | 373562306a36Sopenharmony_ci ((u64)rcr_done << RX_DMA_CTL_STAT_PTRREAD_SHIFT)); 373662306a36Sopenharmony_ci 373762306a36Sopenharmony_ci nw64(RX_DMA_CTL_STAT(rp->rx_channel), stat); 373862306a36Sopenharmony_ci 373962306a36Sopenharmony_ci /* Only sync discards stats when qlen indicate potential for drops */ 374062306a36Sopenharmony_ci if (qlen > 10) 374162306a36Sopenharmony_ci niu_sync_rx_discard_stats(np, rp, 0x7FFF); 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci return work_done; 374462306a36Sopenharmony_ci} 374562306a36Sopenharmony_ci 374662306a36Sopenharmony_cistatic int niu_poll_core(struct niu *np, struct niu_ldg *lp, int budget) 374762306a36Sopenharmony_ci{ 374862306a36Sopenharmony_ci u64 v0 = lp->v0; 374962306a36Sopenharmony_ci u32 tx_vec = (v0 >> 32); 375062306a36Sopenharmony_ci u32 rx_vec = (v0 & 0xffffffff); 375162306a36Sopenharmony_ci int i, work_done = 0; 375262306a36Sopenharmony_ci 375362306a36Sopenharmony_ci netif_printk(np, intr, KERN_DEBUG, np->dev, 375462306a36Sopenharmony_ci "%s() v0[%016llx]\n", __func__, (unsigned long long)v0); 375562306a36Sopenharmony_ci 375662306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 375762306a36Sopenharmony_ci struct tx_ring_info *rp = &np->tx_rings[i]; 375862306a36Sopenharmony_ci if (tx_vec & (1 << rp->tx_channel)) 375962306a36Sopenharmony_ci niu_tx_work(np, rp); 376062306a36Sopenharmony_ci nw64(LD_IM0(LDN_TXDMA(rp->tx_channel)), 0); 376162306a36Sopenharmony_ci } 376262306a36Sopenharmony_ci 376362306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 376462306a36Sopenharmony_ci struct rx_ring_info *rp = &np->rx_rings[i]; 376562306a36Sopenharmony_ci 376662306a36Sopenharmony_ci if (rx_vec & (1 << rp->rx_channel)) { 376762306a36Sopenharmony_ci int this_work_done; 376862306a36Sopenharmony_ci 376962306a36Sopenharmony_ci this_work_done = niu_rx_work(&lp->napi, np, rp, 377062306a36Sopenharmony_ci budget); 377162306a36Sopenharmony_ci 377262306a36Sopenharmony_ci budget -= this_work_done; 377362306a36Sopenharmony_ci work_done += this_work_done; 377462306a36Sopenharmony_ci } 377562306a36Sopenharmony_ci nw64(LD_IM0(LDN_RXDMA(rp->rx_channel)), 0); 377662306a36Sopenharmony_ci } 377762306a36Sopenharmony_ci 377862306a36Sopenharmony_ci return work_done; 377962306a36Sopenharmony_ci} 378062306a36Sopenharmony_ci 378162306a36Sopenharmony_cistatic int niu_poll(struct napi_struct *napi, int budget) 378262306a36Sopenharmony_ci{ 378362306a36Sopenharmony_ci struct niu_ldg *lp = container_of(napi, struct niu_ldg, napi); 378462306a36Sopenharmony_ci struct niu *np = lp->np; 378562306a36Sopenharmony_ci int work_done; 378662306a36Sopenharmony_ci 378762306a36Sopenharmony_ci work_done = niu_poll_core(np, lp, budget); 378862306a36Sopenharmony_ci 378962306a36Sopenharmony_ci if (work_done < budget) { 379062306a36Sopenharmony_ci napi_complete_done(napi, work_done); 379162306a36Sopenharmony_ci niu_ldg_rearm(np, lp, 1); 379262306a36Sopenharmony_ci } 379362306a36Sopenharmony_ci return work_done; 379462306a36Sopenharmony_ci} 379562306a36Sopenharmony_ci 379662306a36Sopenharmony_cistatic void niu_log_rxchan_errors(struct niu *np, struct rx_ring_info *rp, 379762306a36Sopenharmony_ci u64 stat) 379862306a36Sopenharmony_ci{ 379962306a36Sopenharmony_ci netdev_err(np->dev, "RX channel %u errors ( ", rp->rx_channel); 380062306a36Sopenharmony_ci 380162306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_RBR_TMOUT) 380262306a36Sopenharmony_ci pr_cont("RBR_TMOUT "); 380362306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_RSP_CNT_ERR) 380462306a36Sopenharmony_ci pr_cont("RSP_CNT "); 380562306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_BYTE_EN_BUS) 380662306a36Sopenharmony_ci pr_cont("BYTE_EN_BUS "); 380762306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_RSP_DAT_ERR) 380862306a36Sopenharmony_ci pr_cont("RSP_DAT "); 380962306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_RCR_ACK_ERR) 381062306a36Sopenharmony_ci pr_cont("RCR_ACK "); 381162306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_RCR_SHA_PAR) 381262306a36Sopenharmony_ci pr_cont("RCR_SHA_PAR "); 381362306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_RBR_PRE_PAR) 381462306a36Sopenharmony_ci pr_cont("RBR_PRE_PAR "); 381562306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_CONFIG_ERR) 381662306a36Sopenharmony_ci pr_cont("CONFIG "); 381762306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_RCRINCON) 381862306a36Sopenharmony_ci pr_cont("RCRINCON "); 381962306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_RCRFULL) 382062306a36Sopenharmony_ci pr_cont("RCRFULL "); 382162306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_RBRFULL) 382262306a36Sopenharmony_ci pr_cont("RBRFULL "); 382362306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_RBRLOGPAGE) 382462306a36Sopenharmony_ci pr_cont("RBRLOGPAGE "); 382562306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_CFIGLOGPAGE) 382662306a36Sopenharmony_ci pr_cont("CFIGLOGPAGE "); 382762306a36Sopenharmony_ci if (stat & RX_DMA_CTL_STAT_DC_FIFO_ERR) 382862306a36Sopenharmony_ci pr_cont("DC_FIDO "); 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_ci pr_cont(")\n"); 383162306a36Sopenharmony_ci} 383262306a36Sopenharmony_ci 383362306a36Sopenharmony_cistatic int niu_rx_error(struct niu *np, struct rx_ring_info *rp) 383462306a36Sopenharmony_ci{ 383562306a36Sopenharmony_ci u64 stat = nr64(RX_DMA_CTL_STAT(rp->rx_channel)); 383662306a36Sopenharmony_ci int err = 0; 383762306a36Sopenharmony_ci 383862306a36Sopenharmony_ci 383962306a36Sopenharmony_ci if (stat & (RX_DMA_CTL_STAT_CHAN_FATAL | 384062306a36Sopenharmony_ci RX_DMA_CTL_STAT_PORT_FATAL)) 384162306a36Sopenharmony_ci err = -EINVAL; 384262306a36Sopenharmony_ci 384362306a36Sopenharmony_ci if (err) { 384462306a36Sopenharmony_ci netdev_err(np->dev, "RX channel %u error, stat[%llx]\n", 384562306a36Sopenharmony_ci rp->rx_channel, 384662306a36Sopenharmony_ci (unsigned long long) stat); 384762306a36Sopenharmony_ci 384862306a36Sopenharmony_ci niu_log_rxchan_errors(np, rp, stat); 384962306a36Sopenharmony_ci } 385062306a36Sopenharmony_ci 385162306a36Sopenharmony_ci nw64(RX_DMA_CTL_STAT(rp->rx_channel), 385262306a36Sopenharmony_ci stat & RX_DMA_CTL_WRITE_CLEAR_ERRS); 385362306a36Sopenharmony_ci 385462306a36Sopenharmony_ci return err; 385562306a36Sopenharmony_ci} 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_cistatic void niu_log_txchan_errors(struct niu *np, struct tx_ring_info *rp, 385862306a36Sopenharmony_ci u64 cs) 385962306a36Sopenharmony_ci{ 386062306a36Sopenharmony_ci netdev_err(np->dev, "TX channel %u errors ( ", rp->tx_channel); 386162306a36Sopenharmony_ci 386262306a36Sopenharmony_ci if (cs & TX_CS_MBOX_ERR) 386362306a36Sopenharmony_ci pr_cont("MBOX "); 386462306a36Sopenharmony_ci if (cs & TX_CS_PKT_SIZE_ERR) 386562306a36Sopenharmony_ci pr_cont("PKT_SIZE "); 386662306a36Sopenharmony_ci if (cs & TX_CS_TX_RING_OFLOW) 386762306a36Sopenharmony_ci pr_cont("TX_RING_OFLOW "); 386862306a36Sopenharmony_ci if (cs & TX_CS_PREF_BUF_PAR_ERR) 386962306a36Sopenharmony_ci pr_cont("PREF_BUF_PAR "); 387062306a36Sopenharmony_ci if (cs & TX_CS_NACK_PREF) 387162306a36Sopenharmony_ci pr_cont("NACK_PREF "); 387262306a36Sopenharmony_ci if (cs & TX_CS_NACK_PKT_RD) 387362306a36Sopenharmony_ci pr_cont("NACK_PKT_RD "); 387462306a36Sopenharmony_ci if (cs & TX_CS_CONF_PART_ERR) 387562306a36Sopenharmony_ci pr_cont("CONF_PART "); 387662306a36Sopenharmony_ci if (cs & TX_CS_PKT_PRT_ERR) 387762306a36Sopenharmony_ci pr_cont("PKT_PTR "); 387862306a36Sopenharmony_ci 387962306a36Sopenharmony_ci pr_cont(")\n"); 388062306a36Sopenharmony_ci} 388162306a36Sopenharmony_ci 388262306a36Sopenharmony_cistatic int niu_tx_error(struct niu *np, struct tx_ring_info *rp) 388362306a36Sopenharmony_ci{ 388462306a36Sopenharmony_ci u64 cs, logh, logl; 388562306a36Sopenharmony_ci 388662306a36Sopenharmony_ci cs = nr64(TX_CS(rp->tx_channel)); 388762306a36Sopenharmony_ci logh = nr64(TX_RNG_ERR_LOGH(rp->tx_channel)); 388862306a36Sopenharmony_ci logl = nr64(TX_RNG_ERR_LOGL(rp->tx_channel)); 388962306a36Sopenharmony_ci 389062306a36Sopenharmony_ci netdev_err(np->dev, "TX channel %u error, cs[%llx] logh[%llx] logl[%llx]\n", 389162306a36Sopenharmony_ci rp->tx_channel, 389262306a36Sopenharmony_ci (unsigned long long)cs, 389362306a36Sopenharmony_ci (unsigned long long)logh, 389462306a36Sopenharmony_ci (unsigned long long)logl); 389562306a36Sopenharmony_ci 389662306a36Sopenharmony_ci niu_log_txchan_errors(np, rp, cs); 389762306a36Sopenharmony_ci 389862306a36Sopenharmony_ci return -ENODEV; 389962306a36Sopenharmony_ci} 390062306a36Sopenharmony_ci 390162306a36Sopenharmony_cistatic int niu_mif_interrupt(struct niu *np) 390262306a36Sopenharmony_ci{ 390362306a36Sopenharmony_ci u64 mif_status = nr64(MIF_STATUS); 390462306a36Sopenharmony_ci int phy_mdint = 0; 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) { 390762306a36Sopenharmony_ci u64 xrxmac_stat = nr64_mac(XRXMAC_STATUS); 390862306a36Sopenharmony_ci 390962306a36Sopenharmony_ci if (xrxmac_stat & XRXMAC_STATUS_PHY_MDINT) 391062306a36Sopenharmony_ci phy_mdint = 1; 391162306a36Sopenharmony_ci } 391262306a36Sopenharmony_ci 391362306a36Sopenharmony_ci netdev_err(np->dev, "MIF interrupt, stat[%llx] phy_mdint(%d)\n", 391462306a36Sopenharmony_ci (unsigned long long)mif_status, phy_mdint); 391562306a36Sopenharmony_ci 391662306a36Sopenharmony_ci return -ENODEV; 391762306a36Sopenharmony_ci} 391862306a36Sopenharmony_ci 391962306a36Sopenharmony_cistatic void niu_xmac_interrupt(struct niu *np) 392062306a36Sopenharmony_ci{ 392162306a36Sopenharmony_ci struct niu_xmac_stats *mp = &np->mac_stats.xmac; 392262306a36Sopenharmony_ci u64 val; 392362306a36Sopenharmony_ci 392462306a36Sopenharmony_ci val = nr64_mac(XTXMAC_STATUS); 392562306a36Sopenharmony_ci if (val & XTXMAC_STATUS_FRAME_CNT_EXP) 392662306a36Sopenharmony_ci mp->tx_frames += TXMAC_FRM_CNT_COUNT; 392762306a36Sopenharmony_ci if (val & XTXMAC_STATUS_BYTE_CNT_EXP) 392862306a36Sopenharmony_ci mp->tx_bytes += TXMAC_BYTE_CNT_COUNT; 392962306a36Sopenharmony_ci if (val & XTXMAC_STATUS_TXFIFO_XFR_ERR) 393062306a36Sopenharmony_ci mp->tx_fifo_errors++; 393162306a36Sopenharmony_ci if (val & XTXMAC_STATUS_TXMAC_OFLOW) 393262306a36Sopenharmony_ci mp->tx_overflow_errors++; 393362306a36Sopenharmony_ci if (val & XTXMAC_STATUS_MAX_PSIZE_ERR) 393462306a36Sopenharmony_ci mp->tx_max_pkt_size_errors++; 393562306a36Sopenharmony_ci if (val & XTXMAC_STATUS_TXMAC_UFLOW) 393662306a36Sopenharmony_ci mp->tx_underflow_errors++; 393762306a36Sopenharmony_ci 393862306a36Sopenharmony_ci val = nr64_mac(XRXMAC_STATUS); 393962306a36Sopenharmony_ci if (val & XRXMAC_STATUS_LCL_FLT_STATUS) 394062306a36Sopenharmony_ci mp->rx_local_faults++; 394162306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RFLT_DET) 394262306a36Sopenharmony_ci mp->rx_remote_faults++; 394362306a36Sopenharmony_ci if (val & XRXMAC_STATUS_LFLT_CNT_EXP) 394462306a36Sopenharmony_ci mp->rx_link_faults += LINK_FAULT_CNT_COUNT; 394562306a36Sopenharmony_ci if (val & XRXMAC_STATUS_ALIGNERR_CNT_EXP) 394662306a36Sopenharmony_ci mp->rx_align_errors += RXMAC_ALIGN_ERR_CNT_COUNT; 394762306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXFRAG_CNT_EXP) 394862306a36Sopenharmony_ci mp->rx_frags += RXMAC_FRAG_CNT_COUNT; 394962306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXMULTF_CNT_EXP) 395062306a36Sopenharmony_ci mp->rx_mcasts += RXMAC_MC_FRM_CNT_COUNT; 395162306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXBCAST_CNT_EXP) 395262306a36Sopenharmony_ci mp->rx_bcasts += RXMAC_BC_FRM_CNT_COUNT; 395362306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXHIST1_CNT_EXP) 395462306a36Sopenharmony_ci mp->rx_hist_cnt1 += RXMAC_HIST_CNT1_COUNT; 395562306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXHIST2_CNT_EXP) 395662306a36Sopenharmony_ci mp->rx_hist_cnt2 += RXMAC_HIST_CNT2_COUNT; 395762306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXHIST3_CNT_EXP) 395862306a36Sopenharmony_ci mp->rx_hist_cnt3 += RXMAC_HIST_CNT3_COUNT; 395962306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXHIST4_CNT_EXP) 396062306a36Sopenharmony_ci mp->rx_hist_cnt4 += RXMAC_HIST_CNT4_COUNT; 396162306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXHIST5_CNT_EXP) 396262306a36Sopenharmony_ci mp->rx_hist_cnt5 += RXMAC_HIST_CNT5_COUNT; 396362306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXHIST6_CNT_EXP) 396462306a36Sopenharmony_ci mp->rx_hist_cnt6 += RXMAC_HIST_CNT6_COUNT; 396562306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXHIST7_CNT_EXP) 396662306a36Sopenharmony_ci mp->rx_hist_cnt7 += RXMAC_HIST_CNT7_COUNT; 396762306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXOCTET_CNT_EXP) 396862306a36Sopenharmony_ci mp->rx_octets += RXMAC_BT_CNT_COUNT; 396962306a36Sopenharmony_ci if (val & XRXMAC_STATUS_CVIOLERR_CNT_EXP) 397062306a36Sopenharmony_ci mp->rx_code_violations += RXMAC_CD_VIO_CNT_COUNT; 397162306a36Sopenharmony_ci if (val & XRXMAC_STATUS_LENERR_CNT_EXP) 397262306a36Sopenharmony_ci mp->rx_len_errors += RXMAC_MPSZER_CNT_COUNT; 397362306a36Sopenharmony_ci if (val & XRXMAC_STATUS_CRCERR_CNT_EXP) 397462306a36Sopenharmony_ci mp->rx_crc_errors += RXMAC_CRC_ER_CNT_COUNT; 397562306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXUFLOW) 397662306a36Sopenharmony_ci mp->rx_underflows++; 397762306a36Sopenharmony_ci if (val & XRXMAC_STATUS_RXOFLOW) 397862306a36Sopenharmony_ci mp->rx_overflows++; 397962306a36Sopenharmony_ci 398062306a36Sopenharmony_ci val = nr64_mac(XMAC_FC_STAT); 398162306a36Sopenharmony_ci if (val & XMAC_FC_STAT_TX_MAC_NPAUSE) 398262306a36Sopenharmony_ci mp->pause_off_state++; 398362306a36Sopenharmony_ci if (val & XMAC_FC_STAT_TX_MAC_PAUSE) 398462306a36Sopenharmony_ci mp->pause_on_state++; 398562306a36Sopenharmony_ci if (val & XMAC_FC_STAT_RX_MAC_RPAUSE) 398662306a36Sopenharmony_ci mp->pause_received++; 398762306a36Sopenharmony_ci} 398862306a36Sopenharmony_ci 398962306a36Sopenharmony_cistatic void niu_bmac_interrupt(struct niu *np) 399062306a36Sopenharmony_ci{ 399162306a36Sopenharmony_ci struct niu_bmac_stats *mp = &np->mac_stats.bmac; 399262306a36Sopenharmony_ci u64 val; 399362306a36Sopenharmony_ci 399462306a36Sopenharmony_ci val = nr64_mac(BTXMAC_STATUS); 399562306a36Sopenharmony_ci if (val & BTXMAC_STATUS_UNDERRUN) 399662306a36Sopenharmony_ci mp->tx_underflow_errors++; 399762306a36Sopenharmony_ci if (val & BTXMAC_STATUS_MAX_PKT_ERR) 399862306a36Sopenharmony_ci mp->tx_max_pkt_size_errors++; 399962306a36Sopenharmony_ci if (val & BTXMAC_STATUS_BYTE_CNT_EXP) 400062306a36Sopenharmony_ci mp->tx_bytes += BTXMAC_BYTE_CNT_COUNT; 400162306a36Sopenharmony_ci if (val & BTXMAC_STATUS_FRAME_CNT_EXP) 400262306a36Sopenharmony_ci mp->tx_frames += BTXMAC_FRM_CNT_COUNT; 400362306a36Sopenharmony_ci 400462306a36Sopenharmony_ci val = nr64_mac(BRXMAC_STATUS); 400562306a36Sopenharmony_ci if (val & BRXMAC_STATUS_OVERFLOW) 400662306a36Sopenharmony_ci mp->rx_overflows++; 400762306a36Sopenharmony_ci if (val & BRXMAC_STATUS_FRAME_CNT_EXP) 400862306a36Sopenharmony_ci mp->rx_frames += BRXMAC_FRAME_CNT_COUNT; 400962306a36Sopenharmony_ci if (val & BRXMAC_STATUS_ALIGN_ERR_EXP) 401062306a36Sopenharmony_ci mp->rx_align_errors += BRXMAC_ALIGN_ERR_CNT_COUNT; 401162306a36Sopenharmony_ci if (val & BRXMAC_STATUS_CRC_ERR_EXP) 401262306a36Sopenharmony_ci mp->rx_crc_errors += BRXMAC_ALIGN_ERR_CNT_COUNT; 401362306a36Sopenharmony_ci if (val & BRXMAC_STATUS_LEN_ERR_EXP) 401462306a36Sopenharmony_ci mp->rx_len_errors += BRXMAC_CODE_VIOL_ERR_CNT_COUNT; 401562306a36Sopenharmony_ci 401662306a36Sopenharmony_ci val = nr64_mac(BMAC_CTRL_STATUS); 401762306a36Sopenharmony_ci if (val & BMAC_CTRL_STATUS_NOPAUSE) 401862306a36Sopenharmony_ci mp->pause_off_state++; 401962306a36Sopenharmony_ci if (val & BMAC_CTRL_STATUS_PAUSE) 402062306a36Sopenharmony_ci mp->pause_on_state++; 402162306a36Sopenharmony_ci if (val & BMAC_CTRL_STATUS_PAUSE_RECV) 402262306a36Sopenharmony_ci mp->pause_received++; 402362306a36Sopenharmony_ci} 402462306a36Sopenharmony_ci 402562306a36Sopenharmony_cistatic int niu_mac_interrupt(struct niu *np) 402662306a36Sopenharmony_ci{ 402762306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 402862306a36Sopenharmony_ci niu_xmac_interrupt(np); 402962306a36Sopenharmony_ci else 403062306a36Sopenharmony_ci niu_bmac_interrupt(np); 403162306a36Sopenharmony_ci 403262306a36Sopenharmony_ci return 0; 403362306a36Sopenharmony_ci} 403462306a36Sopenharmony_ci 403562306a36Sopenharmony_cistatic void niu_log_device_error(struct niu *np, u64 stat) 403662306a36Sopenharmony_ci{ 403762306a36Sopenharmony_ci netdev_err(np->dev, "Core device errors ( "); 403862306a36Sopenharmony_ci 403962306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_META2) 404062306a36Sopenharmony_ci pr_cont("META2 "); 404162306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_META1) 404262306a36Sopenharmony_ci pr_cont("META1 "); 404362306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_PEU) 404462306a36Sopenharmony_ci pr_cont("PEU "); 404562306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_TXC) 404662306a36Sopenharmony_ci pr_cont("TXC "); 404762306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_RDMC) 404862306a36Sopenharmony_ci pr_cont("RDMC "); 404962306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_TDMC) 405062306a36Sopenharmony_ci pr_cont("TDMC "); 405162306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_ZCP) 405262306a36Sopenharmony_ci pr_cont("ZCP "); 405362306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_FFLP) 405462306a36Sopenharmony_ci pr_cont("FFLP "); 405562306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_IPP) 405662306a36Sopenharmony_ci pr_cont("IPP "); 405762306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_MAC) 405862306a36Sopenharmony_ci pr_cont("MAC "); 405962306a36Sopenharmony_ci if (stat & SYS_ERR_MASK_SMX) 406062306a36Sopenharmony_ci pr_cont("SMX "); 406162306a36Sopenharmony_ci 406262306a36Sopenharmony_ci pr_cont(")\n"); 406362306a36Sopenharmony_ci} 406462306a36Sopenharmony_ci 406562306a36Sopenharmony_cistatic int niu_device_error(struct niu *np) 406662306a36Sopenharmony_ci{ 406762306a36Sopenharmony_ci u64 stat = nr64(SYS_ERR_STAT); 406862306a36Sopenharmony_ci 406962306a36Sopenharmony_ci netdev_err(np->dev, "Core device error, stat[%llx]\n", 407062306a36Sopenharmony_ci (unsigned long long)stat); 407162306a36Sopenharmony_ci 407262306a36Sopenharmony_ci niu_log_device_error(np, stat); 407362306a36Sopenharmony_ci 407462306a36Sopenharmony_ci return -ENODEV; 407562306a36Sopenharmony_ci} 407662306a36Sopenharmony_ci 407762306a36Sopenharmony_cistatic int niu_slowpath_interrupt(struct niu *np, struct niu_ldg *lp, 407862306a36Sopenharmony_ci u64 v0, u64 v1, u64 v2) 407962306a36Sopenharmony_ci{ 408062306a36Sopenharmony_ci 408162306a36Sopenharmony_ci int i, err = 0; 408262306a36Sopenharmony_ci 408362306a36Sopenharmony_ci lp->v0 = v0; 408462306a36Sopenharmony_ci lp->v1 = v1; 408562306a36Sopenharmony_ci lp->v2 = v2; 408662306a36Sopenharmony_ci 408762306a36Sopenharmony_ci if (v1 & 0x00000000ffffffffULL) { 408862306a36Sopenharmony_ci u32 rx_vec = (v1 & 0xffffffff); 408962306a36Sopenharmony_ci 409062306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 409162306a36Sopenharmony_ci struct rx_ring_info *rp = &np->rx_rings[i]; 409262306a36Sopenharmony_ci 409362306a36Sopenharmony_ci if (rx_vec & (1 << rp->rx_channel)) { 409462306a36Sopenharmony_ci int r = niu_rx_error(np, rp); 409562306a36Sopenharmony_ci if (r) { 409662306a36Sopenharmony_ci err = r; 409762306a36Sopenharmony_ci } else { 409862306a36Sopenharmony_ci if (!v0) 409962306a36Sopenharmony_ci nw64(RX_DMA_CTL_STAT(rp->rx_channel), 410062306a36Sopenharmony_ci RX_DMA_CTL_STAT_MEX); 410162306a36Sopenharmony_ci } 410262306a36Sopenharmony_ci } 410362306a36Sopenharmony_ci } 410462306a36Sopenharmony_ci } 410562306a36Sopenharmony_ci if (v1 & 0x7fffffff00000000ULL) { 410662306a36Sopenharmony_ci u32 tx_vec = (v1 >> 32) & 0x7fffffff; 410762306a36Sopenharmony_ci 410862306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 410962306a36Sopenharmony_ci struct tx_ring_info *rp = &np->tx_rings[i]; 411062306a36Sopenharmony_ci 411162306a36Sopenharmony_ci if (tx_vec & (1 << rp->tx_channel)) { 411262306a36Sopenharmony_ci int r = niu_tx_error(np, rp); 411362306a36Sopenharmony_ci if (r) 411462306a36Sopenharmony_ci err = r; 411562306a36Sopenharmony_ci } 411662306a36Sopenharmony_ci } 411762306a36Sopenharmony_ci } 411862306a36Sopenharmony_ci if ((v0 | v1) & 0x8000000000000000ULL) { 411962306a36Sopenharmony_ci int r = niu_mif_interrupt(np); 412062306a36Sopenharmony_ci if (r) 412162306a36Sopenharmony_ci err = r; 412262306a36Sopenharmony_ci } 412362306a36Sopenharmony_ci if (v2) { 412462306a36Sopenharmony_ci if (v2 & 0x01ef) { 412562306a36Sopenharmony_ci int r = niu_mac_interrupt(np); 412662306a36Sopenharmony_ci if (r) 412762306a36Sopenharmony_ci err = r; 412862306a36Sopenharmony_ci } 412962306a36Sopenharmony_ci if (v2 & 0x0210) { 413062306a36Sopenharmony_ci int r = niu_device_error(np); 413162306a36Sopenharmony_ci if (r) 413262306a36Sopenharmony_ci err = r; 413362306a36Sopenharmony_ci } 413462306a36Sopenharmony_ci } 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_ci if (err) 413762306a36Sopenharmony_ci niu_enable_interrupts(np, 0); 413862306a36Sopenharmony_ci 413962306a36Sopenharmony_ci return err; 414062306a36Sopenharmony_ci} 414162306a36Sopenharmony_ci 414262306a36Sopenharmony_cistatic void niu_rxchan_intr(struct niu *np, struct rx_ring_info *rp, 414362306a36Sopenharmony_ci int ldn) 414462306a36Sopenharmony_ci{ 414562306a36Sopenharmony_ci struct rxdma_mailbox *mbox = rp->mbox; 414662306a36Sopenharmony_ci u64 stat_write, stat = le64_to_cpup(&mbox->rx_dma_ctl_stat); 414762306a36Sopenharmony_ci 414862306a36Sopenharmony_ci stat_write = (RX_DMA_CTL_STAT_RCRTHRES | 414962306a36Sopenharmony_ci RX_DMA_CTL_STAT_RCRTO); 415062306a36Sopenharmony_ci nw64(RX_DMA_CTL_STAT(rp->rx_channel), stat_write); 415162306a36Sopenharmony_ci 415262306a36Sopenharmony_ci netif_printk(np, intr, KERN_DEBUG, np->dev, 415362306a36Sopenharmony_ci "%s() stat[%llx]\n", __func__, (unsigned long long)stat); 415462306a36Sopenharmony_ci} 415562306a36Sopenharmony_ci 415662306a36Sopenharmony_cistatic void niu_txchan_intr(struct niu *np, struct tx_ring_info *rp, 415762306a36Sopenharmony_ci int ldn) 415862306a36Sopenharmony_ci{ 415962306a36Sopenharmony_ci rp->tx_cs = nr64(TX_CS(rp->tx_channel)); 416062306a36Sopenharmony_ci 416162306a36Sopenharmony_ci netif_printk(np, intr, KERN_DEBUG, np->dev, 416262306a36Sopenharmony_ci "%s() cs[%llx]\n", __func__, (unsigned long long)rp->tx_cs); 416362306a36Sopenharmony_ci} 416462306a36Sopenharmony_ci 416562306a36Sopenharmony_cistatic void __niu_fastpath_interrupt(struct niu *np, int ldg, u64 v0) 416662306a36Sopenharmony_ci{ 416762306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 416862306a36Sopenharmony_ci u32 rx_vec, tx_vec; 416962306a36Sopenharmony_ci int i; 417062306a36Sopenharmony_ci 417162306a36Sopenharmony_ci tx_vec = (v0 >> 32); 417262306a36Sopenharmony_ci rx_vec = (v0 & 0xffffffff); 417362306a36Sopenharmony_ci 417462306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 417562306a36Sopenharmony_ci struct rx_ring_info *rp = &np->rx_rings[i]; 417662306a36Sopenharmony_ci int ldn = LDN_RXDMA(rp->rx_channel); 417762306a36Sopenharmony_ci 417862306a36Sopenharmony_ci if (parent->ldg_map[ldn] != ldg) 417962306a36Sopenharmony_ci continue; 418062306a36Sopenharmony_ci 418162306a36Sopenharmony_ci nw64(LD_IM0(ldn), LD_IM0_MASK); 418262306a36Sopenharmony_ci if (rx_vec & (1 << rp->rx_channel)) 418362306a36Sopenharmony_ci niu_rxchan_intr(np, rp, ldn); 418462306a36Sopenharmony_ci } 418562306a36Sopenharmony_ci 418662306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 418762306a36Sopenharmony_ci struct tx_ring_info *rp = &np->tx_rings[i]; 418862306a36Sopenharmony_ci int ldn = LDN_TXDMA(rp->tx_channel); 418962306a36Sopenharmony_ci 419062306a36Sopenharmony_ci if (parent->ldg_map[ldn] != ldg) 419162306a36Sopenharmony_ci continue; 419262306a36Sopenharmony_ci 419362306a36Sopenharmony_ci nw64(LD_IM0(ldn), LD_IM0_MASK); 419462306a36Sopenharmony_ci if (tx_vec & (1 << rp->tx_channel)) 419562306a36Sopenharmony_ci niu_txchan_intr(np, rp, ldn); 419662306a36Sopenharmony_ci } 419762306a36Sopenharmony_ci} 419862306a36Sopenharmony_ci 419962306a36Sopenharmony_cistatic void niu_schedule_napi(struct niu *np, struct niu_ldg *lp, 420062306a36Sopenharmony_ci u64 v0, u64 v1, u64 v2) 420162306a36Sopenharmony_ci{ 420262306a36Sopenharmony_ci if (likely(napi_schedule_prep(&lp->napi))) { 420362306a36Sopenharmony_ci lp->v0 = v0; 420462306a36Sopenharmony_ci lp->v1 = v1; 420562306a36Sopenharmony_ci lp->v2 = v2; 420662306a36Sopenharmony_ci __niu_fastpath_interrupt(np, lp->ldg_num, v0); 420762306a36Sopenharmony_ci __napi_schedule(&lp->napi); 420862306a36Sopenharmony_ci } 420962306a36Sopenharmony_ci} 421062306a36Sopenharmony_ci 421162306a36Sopenharmony_cistatic irqreturn_t niu_interrupt(int irq, void *dev_id) 421262306a36Sopenharmony_ci{ 421362306a36Sopenharmony_ci struct niu_ldg *lp = dev_id; 421462306a36Sopenharmony_ci struct niu *np = lp->np; 421562306a36Sopenharmony_ci int ldg = lp->ldg_num; 421662306a36Sopenharmony_ci unsigned long flags; 421762306a36Sopenharmony_ci u64 v0, v1, v2; 421862306a36Sopenharmony_ci 421962306a36Sopenharmony_ci if (netif_msg_intr(np)) 422062306a36Sopenharmony_ci printk(KERN_DEBUG KBUILD_MODNAME ": " "%s() ldg[%p](%d)", 422162306a36Sopenharmony_ci __func__, lp, ldg); 422262306a36Sopenharmony_ci 422362306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_ci v0 = nr64(LDSV0(ldg)); 422662306a36Sopenharmony_ci v1 = nr64(LDSV1(ldg)); 422762306a36Sopenharmony_ci v2 = nr64(LDSV2(ldg)); 422862306a36Sopenharmony_ci 422962306a36Sopenharmony_ci if (netif_msg_intr(np)) 423062306a36Sopenharmony_ci pr_cont(" v0[%llx] v1[%llx] v2[%llx]\n", 423162306a36Sopenharmony_ci (unsigned long long) v0, 423262306a36Sopenharmony_ci (unsigned long long) v1, 423362306a36Sopenharmony_ci (unsigned long long) v2); 423462306a36Sopenharmony_ci 423562306a36Sopenharmony_ci if (unlikely(!v0 && !v1 && !v2)) { 423662306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 423762306a36Sopenharmony_ci return IRQ_NONE; 423862306a36Sopenharmony_ci } 423962306a36Sopenharmony_ci 424062306a36Sopenharmony_ci if (unlikely((v0 & ((u64)1 << LDN_MIF)) || v1 || v2)) { 424162306a36Sopenharmony_ci int err = niu_slowpath_interrupt(np, lp, v0, v1, v2); 424262306a36Sopenharmony_ci if (err) 424362306a36Sopenharmony_ci goto out; 424462306a36Sopenharmony_ci } 424562306a36Sopenharmony_ci if (likely(v0 & ~((u64)1 << LDN_MIF))) 424662306a36Sopenharmony_ci niu_schedule_napi(np, lp, v0, v1, v2); 424762306a36Sopenharmony_ci else 424862306a36Sopenharmony_ci niu_ldg_rearm(np, lp, 1); 424962306a36Sopenharmony_ciout: 425062306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 425162306a36Sopenharmony_ci 425262306a36Sopenharmony_ci return IRQ_HANDLED; 425362306a36Sopenharmony_ci} 425462306a36Sopenharmony_ci 425562306a36Sopenharmony_cistatic void niu_free_rx_ring_info(struct niu *np, struct rx_ring_info *rp) 425662306a36Sopenharmony_ci{ 425762306a36Sopenharmony_ci if (rp->mbox) { 425862306a36Sopenharmony_ci np->ops->free_coherent(np->device, 425962306a36Sopenharmony_ci sizeof(struct rxdma_mailbox), 426062306a36Sopenharmony_ci rp->mbox, rp->mbox_dma); 426162306a36Sopenharmony_ci rp->mbox = NULL; 426262306a36Sopenharmony_ci } 426362306a36Sopenharmony_ci if (rp->rcr) { 426462306a36Sopenharmony_ci np->ops->free_coherent(np->device, 426562306a36Sopenharmony_ci MAX_RCR_RING_SIZE * sizeof(__le64), 426662306a36Sopenharmony_ci rp->rcr, rp->rcr_dma); 426762306a36Sopenharmony_ci rp->rcr = NULL; 426862306a36Sopenharmony_ci rp->rcr_table_size = 0; 426962306a36Sopenharmony_ci rp->rcr_index = 0; 427062306a36Sopenharmony_ci } 427162306a36Sopenharmony_ci if (rp->rbr) { 427262306a36Sopenharmony_ci niu_rbr_free(np, rp); 427362306a36Sopenharmony_ci 427462306a36Sopenharmony_ci np->ops->free_coherent(np->device, 427562306a36Sopenharmony_ci MAX_RBR_RING_SIZE * sizeof(__le32), 427662306a36Sopenharmony_ci rp->rbr, rp->rbr_dma); 427762306a36Sopenharmony_ci rp->rbr = NULL; 427862306a36Sopenharmony_ci rp->rbr_table_size = 0; 427962306a36Sopenharmony_ci rp->rbr_index = 0; 428062306a36Sopenharmony_ci } 428162306a36Sopenharmony_ci kfree(rp->rxhash); 428262306a36Sopenharmony_ci rp->rxhash = NULL; 428362306a36Sopenharmony_ci} 428462306a36Sopenharmony_ci 428562306a36Sopenharmony_cistatic void niu_free_tx_ring_info(struct niu *np, struct tx_ring_info *rp) 428662306a36Sopenharmony_ci{ 428762306a36Sopenharmony_ci if (rp->mbox) { 428862306a36Sopenharmony_ci np->ops->free_coherent(np->device, 428962306a36Sopenharmony_ci sizeof(struct txdma_mailbox), 429062306a36Sopenharmony_ci rp->mbox, rp->mbox_dma); 429162306a36Sopenharmony_ci rp->mbox = NULL; 429262306a36Sopenharmony_ci } 429362306a36Sopenharmony_ci if (rp->descr) { 429462306a36Sopenharmony_ci int i; 429562306a36Sopenharmony_ci 429662306a36Sopenharmony_ci for (i = 0; i < MAX_TX_RING_SIZE; i++) { 429762306a36Sopenharmony_ci if (rp->tx_buffs[i].skb) 429862306a36Sopenharmony_ci (void) release_tx_packet(np, rp, i); 429962306a36Sopenharmony_ci } 430062306a36Sopenharmony_ci 430162306a36Sopenharmony_ci np->ops->free_coherent(np->device, 430262306a36Sopenharmony_ci MAX_TX_RING_SIZE * sizeof(__le64), 430362306a36Sopenharmony_ci rp->descr, rp->descr_dma); 430462306a36Sopenharmony_ci rp->descr = NULL; 430562306a36Sopenharmony_ci rp->pending = 0; 430662306a36Sopenharmony_ci rp->prod = 0; 430762306a36Sopenharmony_ci rp->cons = 0; 430862306a36Sopenharmony_ci rp->wrap_bit = 0; 430962306a36Sopenharmony_ci } 431062306a36Sopenharmony_ci} 431162306a36Sopenharmony_ci 431262306a36Sopenharmony_cistatic void niu_free_channels(struct niu *np) 431362306a36Sopenharmony_ci{ 431462306a36Sopenharmony_ci int i; 431562306a36Sopenharmony_ci 431662306a36Sopenharmony_ci if (np->rx_rings) { 431762306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 431862306a36Sopenharmony_ci struct rx_ring_info *rp = &np->rx_rings[i]; 431962306a36Sopenharmony_ci 432062306a36Sopenharmony_ci niu_free_rx_ring_info(np, rp); 432162306a36Sopenharmony_ci } 432262306a36Sopenharmony_ci kfree(np->rx_rings); 432362306a36Sopenharmony_ci np->rx_rings = NULL; 432462306a36Sopenharmony_ci np->num_rx_rings = 0; 432562306a36Sopenharmony_ci } 432662306a36Sopenharmony_ci 432762306a36Sopenharmony_ci if (np->tx_rings) { 432862306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 432962306a36Sopenharmony_ci struct tx_ring_info *rp = &np->tx_rings[i]; 433062306a36Sopenharmony_ci 433162306a36Sopenharmony_ci niu_free_tx_ring_info(np, rp); 433262306a36Sopenharmony_ci } 433362306a36Sopenharmony_ci kfree(np->tx_rings); 433462306a36Sopenharmony_ci np->tx_rings = NULL; 433562306a36Sopenharmony_ci np->num_tx_rings = 0; 433662306a36Sopenharmony_ci } 433762306a36Sopenharmony_ci} 433862306a36Sopenharmony_ci 433962306a36Sopenharmony_cistatic int niu_alloc_rx_ring_info(struct niu *np, 434062306a36Sopenharmony_ci struct rx_ring_info *rp) 434162306a36Sopenharmony_ci{ 434262306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct rxdma_mailbox) != 64); 434362306a36Sopenharmony_ci 434462306a36Sopenharmony_ci rp->rxhash = kcalloc(MAX_RBR_RING_SIZE, sizeof(struct page *), 434562306a36Sopenharmony_ci GFP_KERNEL); 434662306a36Sopenharmony_ci if (!rp->rxhash) 434762306a36Sopenharmony_ci return -ENOMEM; 434862306a36Sopenharmony_ci 434962306a36Sopenharmony_ci rp->mbox = np->ops->alloc_coherent(np->device, 435062306a36Sopenharmony_ci sizeof(struct rxdma_mailbox), 435162306a36Sopenharmony_ci &rp->mbox_dma, GFP_KERNEL); 435262306a36Sopenharmony_ci if (!rp->mbox) 435362306a36Sopenharmony_ci return -ENOMEM; 435462306a36Sopenharmony_ci if ((unsigned long)rp->mbox & (64UL - 1)) { 435562306a36Sopenharmony_ci netdev_err(np->dev, "Coherent alloc gives misaligned RXDMA mailbox %p\n", 435662306a36Sopenharmony_ci rp->mbox); 435762306a36Sopenharmony_ci return -EINVAL; 435862306a36Sopenharmony_ci } 435962306a36Sopenharmony_ci 436062306a36Sopenharmony_ci rp->rcr = np->ops->alloc_coherent(np->device, 436162306a36Sopenharmony_ci MAX_RCR_RING_SIZE * sizeof(__le64), 436262306a36Sopenharmony_ci &rp->rcr_dma, GFP_KERNEL); 436362306a36Sopenharmony_ci if (!rp->rcr) 436462306a36Sopenharmony_ci return -ENOMEM; 436562306a36Sopenharmony_ci if ((unsigned long)rp->rcr & (64UL - 1)) { 436662306a36Sopenharmony_ci netdev_err(np->dev, "Coherent alloc gives misaligned RXDMA RCR table %p\n", 436762306a36Sopenharmony_ci rp->rcr); 436862306a36Sopenharmony_ci return -EINVAL; 436962306a36Sopenharmony_ci } 437062306a36Sopenharmony_ci rp->rcr_table_size = MAX_RCR_RING_SIZE; 437162306a36Sopenharmony_ci rp->rcr_index = 0; 437262306a36Sopenharmony_ci 437362306a36Sopenharmony_ci rp->rbr = np->ops->alloc_coherent(np->device, 437462306a36Sopenharmony_ci MAX_RBR_RING_SIZE * sizeof(__le32), 437562306a36Sopenharmony_ci &rp->rbr_dma, GFP_KERNEL); 437662306a36Sopenharmony_ci if (!rp->rbr) 437762306a36Sopenharmony_ci return -ENOMEM; 437862306a36Sopenharmony_ci if ((unsigned long)rp->rbr & (64UL - 1)) { 437962306a36Sopenharmony_ci netdev_err(np->dev, "Coherent alloc gives misaligned RXDMA RBR table %p\n", 438062306a36Sopenharmony_ci rp->rbr); 438162306a36Sopenharmony_ci return -EINVAL; 438262306a36Sopenharmony_ci } 438362306a36Sopenharmony_ci rp->rbr_table_size = MAX_RBR_RING_SIZE; 438462306a36Sopenharmony_ci rp->rbr_index = 0; 438562306a36Sopenharmony_ci rp->rbr_pending = 0; 438662306a36Sopenharmony_ci 438762306a36Sopenharmony_ci return 0; 438862306a36Sopenharmony_ci} 438962306a36Sopenharmony_ci 439062306a36Sopenharmony_cistatic void niu_set_max_burst(struct niu *np, struct tx_ring_info *rp) 439162306a36Sopenharmony_ci{ 439262306a36Sopenharmony_ci int mtu = np->dev->mtu; 439362306a36Sopenharmony_ci 439462306a36Sopenharmony_ci /* These values are recommended by the HW designers for fair 439562306a36Sopenharmony_ci * utilization of DRR amongst the rings. 439662306a36Sopenharmony_ci */ 439762306a36Sopenharmony_ci rp->max_burst = mtu + 32; 439862306a36Sopenharmony_ci if (rp->max_burst > 4096) 439962306a36Sopenharmony_ci rp->max_burst = 4096; 440062306a36Sopenharmony_ci} 440162306a36Sopenharmony_ci 440262306a36Sopenharmony_cistatic int niu_alloc_tx_ring_info(struct niu *np, 440362306a36Sopenharmony_ci struct tx_ring_info *rp) 440462306a36Sopenharmony_ci{ 440562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct txdma_mailbox) != 64); 440662306a36Sopenharmony_ci 440762306a36Sopenharmony_ci rp->mbox = np->ops->alloc_coherent(np->device, 440862306a36Sopenharmony_ci sizeof(struct txdma_mailbox), 440962306a36Sopenharmony_ci &rp->mbox_dma, GFP_KERNEL); 441062306a36Sopenharmony_ci if (!rp->mbox) 441162306a36Sopenharmony_ci return -ENOMEM; 441262306a36Sopenharmony_ci if ((unsigned long)rp->mbox & (64UL - 1)) { 441362306a36Sopenharmony_ci netdev_err(np->dev, "Coherent alloc gives misaligned TXDMA mailbox %p\n", 441462306a36Sopenharmony_ci rp->mbox); 441562306a36Sopenharmony_ci return -EINVAL; 441662306a36Sopenharmony_ci } 441762306a36Sopenharmony_ci 441862306a36Sopenharmony_ci rp->descr = np->ops->alloc_coherent(np->device, 441962306a36Sopenharmony_ci MAX_TX_RING_SIZE * sizeof(__le64), 442062306a36Sopenharmony_ci &rp->descr_dma, GFP_KERNEL); 442162306a36Sopenharmony_ci if (!rp->descr) 442262306a36Sopenharmony_ci return -ENOMEM; 442362306a36Sopenharmony_ci if ((unsigned long)rp->descr & (64UL - 1)) { 442462306a36Sopenharmony_ci netdev_err(np->dev, "Coherent alloc gives misaligned TXDMA descr table %p\n", 442562306a36Sopenharmony_ci rp->descr); 442662306a36Sopenharmony_ci return -EINVAL; 442762306a36Sopenharmony_ci } 442862306a36Sopenharmony_ci 442962306a36Sopenharmony_ci rp->pending = MAX_TX_RING_SIZE; 443062306a36Sopenharmony_ci rp->prod = 0; 443162306a36Sopenharmony_ci rp->cons = 0; 443262306a36Sopenharmony_ci rp->wrap_bit = 0; 443362306a36Sopenharmony_ci 443462306a36Sopenharmony_ci /* XXX make these configurable... XXX */ 443562306a36Sopenharmony_ci rp->mark_freq = rp->pending / 4; 443662306a36Sopenharmony_ci 443762306a36Sopenharmony_ci niu_set_max_burst(np, rp); 443862306a36Sopenharmony_ci 443962306a36Sopenharmony_ci return 0; 444062306a36Sopenharmony_ci} 444162306a36Sopenharmony_ci 444262306a36Sopenharmony_cistatic void niu_size_rbr(struct niu *np, struct rx_ring_info *rp) 444362306a36Sopenharmony_ci{ 444462306a36Sopenharmony_ci u16 bss; 444562306a36Sopenharmony_ci 444662306a36Sopenharmony_ci bss = min(PAGE_SHIFT, 15); 444762306a36Sopenharmony_ci 444862306a36Sopenharmony_ci rp->rbr_block_size = 1 << bss; 444962306a36Sopenharmony_ci rp->rbr_blocks_per_page = 1 << (PAGE_SHIFT-bss); 445062306a36Sopenharmony_ci 445162306a36Sopenharmony_ci rp->rbr_sizes[0] = 256; 445262306a36Sopenharmony_ci rp->rbr_sizes[1] = 1024; 445362306a36Sopenharmony_ci if (np->dev->mtu > ETH_DATA_LEN) { 445462306a36Sopenharmony_ci switch (PAGE_SIZE) { 445562306a36Sopenharmony_ci case 4 * 1024: 445662306a36Sopenharmony_ci rp->rbr_sizes[2] = 4096; 445762306a36Sopenharmony_ci break; 445862306a36Sopenharmony_ci 445962306a36Sopenharmony_ci default: 446062306a36Sopenharmony_ci rp->rbr_sizes[2] = 8192; 446162306a36Sopenharmony_ci break; 446262306a36Sopenharmony_ci } 446362306a36Sopenharmony_ci } else { 446462306a36Sopenharmony_ci rp->rbr_sizes[2] = 2048; 446562306a36Sopenharmony_ci } 446662306a36Sopenharmony_ci rp->rbr_sizes[3] = rp->rbr_block_size; 446762306a36Sopenharmony_ci} 446862306a36Sopenharmony_ci 446962306a36Sopenharmony_cistatic int niu_alloc_channels(struct niu *np) 447062306a36Sopenharmony_ci{ 447162306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 447262306a36Sopenharmony_ci int first_rx_channel, first_tx_channel; 447362306a36Sopenharmony_ci int num_rx_rings, num_tx_rings; 447462306a36Sopenharmony_ci struct rx_ring_info *rx_rings; 447562306a36Sopenharmony_ci struct tx_ring_info *tx_rings; 447662306a36Sopenharmony_ci int i, port, err; 447762306a36Sopenharmony_ci 447862306a36Sopenharmony_ci port = np->port; 447962306a36Sopenharmony_ci first_rx_channel = first_tx_channel = 0; 448062306a36Sopenharmony_ci for (i = 0; i < port; i++) { 448162306a36Sopenharmony_ci first_rx_channel += parent->rxchan_per_port[i]; 448262306a36Sopenharmony_ci first_tx_channel += parent->txchan_per_port[i]; 448362306a36Sopenharmony_ci } 448462306a36Sopenharmony_ci 448562306a36Sopenharmony_ci num_rx_rings = parent->rxchan_per_port[port]; 448662306a36Sopenharmony_ci num_tx_rings = parent->txchan_per_port[port]; 448762306a36Sopenharmony_ci 448862306a36Sopenharmony_ci rx_rings = kcalloc(num_rx_rings, sizeof(struct rx_ring_info), 448962306a36Sopenharmony_ci GFP_KERNEL); 449062306a36Sopenharmony_ci err = -ENOMEM; 449162306a36Sopenharmony_ci if (!rx_rings) 449262306a36Sopenharmony_ci goto out_err; 449362306a36Sopenharmony_ci 449462306a36Sopenharmony_ci np->num_rx_rings = num_rx_rings; 449562306a36Sopenharmony_ci smp_wmb(); 449662306a36Sopenharmony_ci np->rx_rings = rx_rings; 449762306a36Sopenharmony_ci 449862306a36Sopenharmony_ci netif_set_real_num_rx_queues(np->dev, num_rx_rings); 449962306a36Sopenharmony_ci 450062306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 450162306a36Sopenharmony_ci struct rx_ring_info *rp = &np->rx_rings[i]; 450262306a36Sopenharmony_ci 450362306a36Sopenharmony_ci rp->np = np; 450462306a36Sopenharmony_ci rp->rx_channel = first_rx_channel + i; 450562306a36Sopenharmony_ci 450662306a36Sopenharmony_ci err = niu_alloc_rx_ring_info(np, rp); 450762306a36Sopenharmony_ci if (err) 450862306a36Sopenharmony_ci goto out_err; 450962306a36Sopenharmony_ci 451062306a36Sopenharmony_ci niu_size_rbr(np, rp); 451162306a36Sopenharmony_ci 451262306a36Sopenharmony_ci /* XXX better defaults, configurable, etc... XXX */ 451362306a36Sopenharmony_ci rp->nonsyn_window = 64; 451462306a36Sopenharmony_ci rp->nonsyn_threshold = rp->rcr_table_size - 64; 451562306a36Sopenharmony_ci rp->syn_window = 64; 451662306a36Sopenharmony_ci rp->syn_threshold = rp->rcr_table_size - 64; 451762306a36Sopenharmony_ci rp->rcr_pkt_threshold = 16; 451862306a36Sopenharmony_ci rp->rcr_timeout = 8; 451962306a36Sopenharmony_ci rp->rbr_kick_thresh = RBR_REFILL_MIN; 452062306a36Sopenharmony_ci if (rp->rbr_kick_thresh < rp->rbr_blocks_per_page) 452162306a36Sopenharmony_ci rp->rbr_kick_thresh = rp->rbr_blocks_per_page; 452262306a36Sopenharmony_ci 452362306a36Sopenharmony_ci err = niu_rbr_fill(np, rp, GFP_KERNEL); 452462306a36Sopenharmony_ci if (err) 452562306a36Sopenharmony_ci goto out_err; 452662306a36Sopenharmony_ci } 452762306a36Sopenharmony_ci 452862306a36Sopenharmony_ci tx_rings = kcalloc(num_tx_rings, sizeof(struct tx_ring_info), 452962306a36Sopenharmony_ci GFP_KERNEL); 453062306a36Sopenharmony_ci err = -ENOMEM; 453162306a36Sopenharmony_ci if (!tx_rings) 453262306a36Sopenharmony_ci goto out_err; 453362306a36Sopenharmony_ci 453462306a36Sopenharmony_ci np->num_tx_rings = num_tx_rings; 453562306a36Sopenharmony_ci smp_wmb(); 453662306a36Sopenharmony_ci np->tx_rings = tx_rings; 453762306a36Sopenharmony_ci 453862306a36Sopenharmony_ci netif_set_real_num_tx_queues(np->dev, num_tx_rings); 453962306a36Sopenharmony_ci 454062306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 454162306a36Sopenharmony_ci struct tx_ring_info *rp = &np->tx_rings[i]; 454262306a36Sopenharmony_ci 454362306a36Sopenharmony_ci rp->np = np; 454462306a36Sopenharmony_ci rp->tx_channel = first_tx_channel + i; 454562306a36Sopenharmony_ci 454662306a36Sopenharmony_ci err = niu_alloc_tx_ring_info(np, rp); 454762306a36Sopenharmony_ci if (err) 454862306a36Sopenharmony_ci goto out_err; 454962306a36Sopenharmony_ci } 455062306a36Sopenharmony_ci 455162306a36Sopenharmony_ci return 0; 455262306a36Sopenharmony_ci 455362306a36Sopenharmony_ciout_err: 455462306a36Sopenharmony_ci niu_free_channels(np); 455562306a36Sopenharmony_ci return err; 455662306a36Sopenharmony_ci} 455762306a36Sopenharmony_ci 455862306a36Sopenharmony_cistatic int niu_tx_cs_sng_poll(struct niu *np, int channel) 455962306a36Sopenharmony_ci{ 456062306a36Sopenharmony_ci int limit = 1000; 456162306a36Sopenharmony_ci 456262306a36Sopenharmony_ci while (--limit > 0) { 456362306a36Sopenharmony_ci u64 val = nr64(TX_CS(channel)); 456462306a36Sopenharmony_ci if (val & TX_CS_SNG_STATE) 456562306a36Sopenharmony_ci return 0; 456662306a36Sopenharmony_ci } 456762306a36Sopenharmony_ci return -ENODEV; 456862306a36Sopenharmony_ci} 456962306a36Sopenharmony_ci 457062306a36Sopenharmony_cistatic int niu_tx_channel_stop(struct niu *np, int channel) 457162306a36Sopenharmony_ci{ 457262306a36Sopenharmony_ci u64 val = nr64(TX_CS(channel)); 457362306a36Sopenharmony_ci 457462306a36Sopenharmony_ci val |= TX_CS_STOP_N_GO; 457562306a36Sopenharmony_ci nw64(TX_CS(channel), val); 457662306a36Sopenharmony_ci 457762306a36Sopenharmony_ci return niu_tx_cs_sng_poll(np, channel); 457862306a36Sopenharmony_ci} 457962306a36Sopenharmony_ci 458062306a36Sopenharmony_cistatic int niu_tx_cs_reset_poll(struct niu *np, int channel) 458162306a36Sopenharmony_ci{ 458262306a36Sopenharmony_ci int limit = 1000; 458362306a36Sopenharmony_ci 458462306a36Sopenharmony_ci while (--limit > 0) { 458562306a36Sopenharmony_ci u64 val = nr64(TX_CS(channel)); 458662306a36Sopenharmony_ci if (!(val & TX_CS_RST)) 458762306a36Sopenharmony_ci return 0; 458862306a36Sopenharmony_ci } 458962306a36Sopenharmony_ci return -ENODEV; 459062306a36Sopenharmony_ci} 459162306a36Sopenharmony_ci 459262306a36Sopenharmony_cistatic int niu_tx_channel_reset(struct niu *np, int channel) 459362306a36Sopenharmony_ci{ 459462306a36Sopenharmony_ci u64 val = nr64(TX_CS(channel)); 459562306a36Sopenharmony_ci int err; 459662306a36Sopenharmony_ci 459762306a36Sopenharmony_ci val |= TX_CS_RST; 459862306a36Sopenharmony_ci nw64(TX_CS(channel), val); 459962306a36Sopenharmony_ci 460062306a36Sopenharmony_ci err = niu_tx_cs_reset_poll(np, channel); 460162306a36Sopenharmony_ci if (!err) 460262306a36Sopenharmony_ci nw64(TX_RING_KICK(channel), 0); 460362306a36Sopenharmony_ci 460462306a36Sopenharmony_ci return err; 460562306a36Sopenharmony_ci} 460662306a36Sopenharmony_ci 460762306a36Sopenharmony_cistatic int niu_tx_channel_lpage_init(struct niu *np, int channel) 460862306a36Sopenharmony_ci{ 460962306a36Sopenharmony_ci u64 val; 461062306a36Sopenharmony_ci 461162306a36Sopenharmony_ci nw64(TX_LOG_MASK1(channel), 0); 461262306a36Sopenharmony_ci nw64(TX_LOG_VAL1(channel), 0); 461362306a36Sopenharmony_ci nw64(TX_LOG_MASK2(channel), 0); 461462306a36Sopenharmony_ci nw64(TX_LOG_VAL2(channel), 0); 461562306a36Sopenharmony_ci nw64(TX_LOG_PAGE_RELO1(channel), 0); 461662306a36Sopenharmony_ci nw64(TX_LOG_PAGE_RELO2(channel), 0); 461762306a36Sopenharmony_ci nw64(TX_LOG_PAGE_HDL(channel), 0); 461862306a36Sopenharmony_ci 461962306a36Sopenharmony_ci val = (u64)np->port << TX_LOG_PAGE_VLD_FUNC_SHIFT; 462062306a36Sopenharmony_ci val |= (TX_LOG_PAGE_VLD_PAGE0 | TX_LOG_PAGE_VLD_PAGE1); 462162306a36Sopenharmony_ci nw64(TX_LOG_PAGE_VLD(channel), val); 462262306a36Sopenharmony_ci 462362306a36Sopenharmony_ci /* XXX TXDMA 32bit mode? XXX */ 462462306a36Sopenharmony_ci 462562306a36Sopenharmony_ci return 0; 462662306a36Sopenharmony_ci} 462762306a36Sopenharmony_ci 462862306a36Sopenharmony_cistatic void niu_txc_enable_port(struct niu *np, int on) 462962306a36Sopenharmony_ci{ 463062306a36Sopenharmony_ci unsigned long flags; 463162306a36Sopenharmony_ci u64 val, mask; 463262306a36Sopenharmony_ci 463362306a36Sopenharmony_ci niu_lock_parent(np, flags); 463462306a36Sopenharmony_ci val = nr64(TXC_CONTROL); 463562306a36Sopenharmony_ci mask = (u64)1 << np->port; 463662306a36Sopenharmony_ci if (on) { 463762306a36Sopenharmony_ci val |= TXC_CONTROL_ENABLE | mask; 463862306a36Sopenharmony_ci } else { 463962306a36Sopenharmony_ci val &= ~mask; 464062306a36Sopenharmony_ci if ((val & ~TXC_CONTROL_ENABLE) == 0) 464162306a36Sopenharmony_ci val &= ~TXC_CONTROL_ENABLE; 464262306a36Sopenharmony_ci } 464362306a36Sopenharmony_ci nw64(TXC_CONTROL, val); 464462306a36Sopenharmony_ci niu_unlock_parent(np, flags); 464562306a36Sopenharmony_ci} 464662306a36Sopenharmony_ci 464762306a36Sopenharmony_cistatic void niu_txc_set_imask(struct niu *np, u64 imask) 464862306a36Sopenharmony_ci{ 464962306a36Sopenharmony_ci unsigned long flags; 465062306a36Sopenharmony_ci u64 val; 465162306a36Sopenharmony_ci 465262306a36Sopenharmony_ci niu_lock_parent(np, flags); 465362306a36Sopenharmony_ci val = nr64(TXC_INT_MASK); 465462306a36Sopenharmony_ci val &= ~TXC_INT_MASK_VAL(np->port); 465562306a36Sopenharmony_ci val |= (imask << TXC_INT_MASK_VAL_SHIFT(np->port)); 465662306a36Sopenharmony_ci niu_unlock_parent(np, flags); 465762306a36Sopenharmony_ci} 465862306a36Sopenharmony_ci 465962306a36Sopenharmony_cistatic void niu_txc_port_dma_enable(struct niu *np, int on) 466062306a36Sopenharmony_ci{ 466162306a36Sopenharmony_ci u64 val = 0; 466262306a36Sopenharmony_ci 466362306a36Sopenharmony_ci if (on) { 466462306a36Sopenharmony_ci int i; 466562306a36Sopenharmony_ci 466662306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) 466762306a36Sopenharmony_ci val |= (1 << np->tx_rings[i].tx_channel); 466862306a36Sopenharmony_ci } 466962306a36Sopenharmony_ci nw64(TXC_PORT_DMA(np->port), val); 467062306a36Sopenharmony_ci} 467162306a36Sopenharmony_ci 467262306a36Sopenharmony_cistatic int niu_init_one_tx_channel(struct niu *np, struct tx_ring_info *rp) 467362306a36Sopenharmony_ci{ 467462306a36Sopenharmony_ci int err, channel = rp->tx_channel; 467562306a36Sopenharmony_ci u64 val, ring_len; 467662306a36Sopenharmony_ci 467762306a36Sopenharmony_ci err = niu_tx_channel_stop(np, channel); 467862306a36Sopenharmony_ci if (err) 467962306a36Sopenharmony_ci return err; 468062306a36Sopenharmony_ci 468162306a36Sopenharmony_ci err = niu_tx_channel_reset(np, channel); 468262306a36Sopenharmony_ci if (err) 468362306a36Sopenharmony_ci return err; 468462306a36Sopenharmony_ci 468562306a36Sopenharmony_ci err = niu_tx_channel_lpage_init(np, channel); 468662306a36Sopenharmony_ci if (err) 468762306a36Sopenharmony_ci return err; 468862306a36Sopenharmony_ci 468962306a36Sopenharmony_ci nw64(TXC_DMA_MAX(channel), rp->max_burst); 469062306a36Sopenharmony_ci nw64(TX_ENT_MSK(channel), 0); 469162306a36Sopenharmony_ci 469262306a36Sopenharmony_ci if (rp->descr_dma & ~(TX_RNG_CFIG_STADDR_BASE | 469362306a36Sopenharmony_ci TX_RNG_CFIG_STADDR)) { 469462306a36Sopenharmony_ci netdev_err(np->dev, "TX ring channel %d DMA addr (%llx) is not aligned\n", 469562306a36Sopenharmony_ci channel, (unsigned long long)rp->descr_dma); 469662306a36Sopenharmony_ci return -EINVAL; 469762306a36Sopenharmony_ci } 469862306a36Sopenharmony_ci 469962306a36Sopenharmony_ci /* The length field in TX_RNG_CFIG is measured in 64-byte 470062306a36Sopenharmony_ci * blocks. rp->pending is the number of TX descriptors in 470162306a36Sopenharmony_ci * our ring, 8 bytes each, thus we divide by 8 bytes more 470262306a36Sopenharmony_ci * to get the proper value the chip wants. 470362306a36Sopenharmony_ci */ 470462306a36Sopenharmony_ci ring_len = (rp->pending / 8); 470562306a36Sopenharmony_ci 470662306a36Sopenharmony_ci val = ((ring_len << TX_RNG_CFIG_LEN_SHIFT) | 470762306a36Sopenharmony_ci rp->descr_dma); 470862306a36Sopenharmony_ci nw64(TX_RNG_CFIG(channel), val); 470962306a36Sopenharmony_ci 471062306a36Sopenharmony_ci if (((rp->mbox_dma >> 32) & ~TXDMA_MBH_MBADDR) || 471162306a36Sopenharmony_ci ((u32)rp->mbox_dma & ~TXDMA_MBL_MBADDR)) { 471262306a36Sopenharmony_ci netdev_err(np->dev, "TX ring channel %d MBOX addr (%llx) has invalid bits\n", 471362306a36Sopenharmony_ci channel, (unsigned long long)rp->mbox_dma); 471462306a36Sopenharmony_ci return -EINVAL; 471562306a36Sopenharmony_ci } 471662306a36Sopenharmony_ci nw64(TXDMA_MBH(channel), rp->mbox_dma >> 32); 471762306a36Sopenharmony_ci nw64(TXDMA_MBL(channel), rp->mbox_dma & TXDMA_MBL_MBADDR); 471862306a36Sopenharmony_ci 471962306a36Sopenharmony_ci nw64(TX_CS(channel), 0); 472062306a36Sopenharmony_ci 472162306a36Sopenharmony_ci rp->last_pkt_cnt = 0; 472262306a36Sopenharmony_ci 472362306a36Sopenharmony_ci return 0; 472462306a36Sopenharmony_ci} 472562306a36Sopenharmony_ci 472662306a36Sopenharmony_cistatic void niu_init_rdc_groups(struct niu *np) 472762306a36Sopenharmony_ci{ 472862306a36Sopenharmony_ci struct niu_rdc_tables *tp = &np->parent->rdc_group_cfg[np->port]; 472962306a36Sopenharmony_ci int i, first_table_num = tp->first_table_num; 473062306a36Sopenharmony_ci 473162306a36Sopenharmony_ci for (i = 0; i < tp->num_tables; i++) { 473262306a36Sopenharmony_ci struct rdc_table *tbl = &tp->tables[i]; 473362306a36Sopenharmony_ci int this_table = first_table_num + i; 473462306a36Sopenharmony_ci int slot; 473562306a36Sopenharmony_ci 473662306a36Sopenharmony_ci for (slot = 0; slot < NIU_RDC_TABLE_SLOTS; slot++) 473762306a36Sopenharmony_ci nw64(RDC_TBL(this_table, slot), 473862306a36Sopenharmony_ci tbl->rxdma_channel[slot]); 473962306a36Sopenharmony_ci } 474062306a36Sopenharmony_ci 474162306a36Sopenharmony_ci nw64(DEF_RDC(np->port), np->parent->rdc_default[np->port]); 474262306a36Sopenharmony_ci} 474362306a36Sopenharmony_ci 474462306a36Sopenharmony_cistatic void niu_init_drr_weight(struct niu *np) 474562306a36Sopenharmony_ci{ 474662306a36Sopenharmony_ci int type = phy_decode(np->parent->port_phy, np->port); 474762306a36Sopenharmony_ci u64 val; 474862306a36Sopenharmony_ci 474962306a36Sopenharmony_ci switch (type) { 475062306a36Sopenharmony_ci case PORT_TYPE_10G: 475162306a36Sopenharmony_ci val = PT_DRR_WEIGHT_DEFAULT_10G; 475262306a36Sopenharmony_ci break; 475362306a36Sopenharmony_ci 475462306a36Sopenharmony_ci case PORT_TYPE_1G: 475562306a36Sopenharmony_ci default: 475662306a36Sopenharmony_ci val = PT_DRR_WEIGHT_DEFAULT_1G; 475762306a36Sopenharmony_ci break; 475862306a36Sopenharmony_ci } 475962306a36Sopenharmony_ci nw64(PT_DRR_WT(np->port), val); 476062306a36Sopenharmony_ci} 476162306a36Sopenharmony_ci 476262306a36Sopenharmony_cistatic int niu_init_hostinfo(struct niu *np) 476362306a36Sopenharmony_ci{ 476462306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 476562306a36Sopenharmony_ci struct niu_rdc_tables *tp = &parent->rdc_group_cfg[np->port]; 476662306a36Sopenharmony_ci int i, err, num_alt = niu_num_alt_addr(np); 476762306a36Sopenharmony_ci int first_rdc_table = tp->first_table_num; 476862306a36Sopenharmony_ci 476962306a36Sopenharmony_ci err = niu_set_primary_mac_rdc_table(np, first_rdc_table, 1); 477062306a36Sopenharmony_ci if (err) 477162306a36Sopenharmony_ci return err; 477262306a36Sopenharmony_ci 477362306a36Sopenharmony_ci err = niu_set_multicast_mac_rdc_table(np, first_rdc_table, 1); 477462306a36Sopenharmony_ci if (err) 477562306a36Sopenharmony_ci return err; 477662306a36Sopenharmony_ci 477762306a36Sopenharmony_ci for (i = 0; i < num_alt; i++) { 477862306a36Sopenharmony_ci err = niu_set_alt_mac_rdc_table(np, i, first_rdc_table, 1); 477962306a36Sopenharmony_ci if (err) 478062306a36Sopenharmony_ci return err; 478162306a36Sopenharmony_ci } 478262306a36Sopenharmony_ci 478362306a36Sopenharmony_ci return 0; 478462306a36Sopenharmony_ci} 478562306a36Sopenharmony_ci 478662306a36Sopenharmony_cistatic int niu_rx_channel_reset(struct niu *np, int channel) 478762306a36Sopenharmony_ci{ 478862306a36Sopenharmony_ci return niu_set_and_wait_clear(np, RXDMA_CFIG1(channel), 478962306a36Sopenharmony_ci RXDMA_CFIG1_RST, 1000, 10, 479062306a36Sopenharmony_ci "RXDMA_CFIG1"); 479162306a36Sopenharmony_ci} 479262306a36Sopenharmony_ci 479362306a36Sopenharmony_cistatic int niu_rx_channel_lpage_init(struct niu *np, int channel) 479462306a36Sopenharmony_ci{ 479562306a36Sopenharmony_ci u64 val; 479662306a36Sopenharmony_ci 479762306a36Sopenharmony_ci nw64(RX_LOG_MASK1(channel), 0); 479862306a36Sopenharmony_ci nw64(RX_LOG_VAL1(channel), 0); 479962306a36Sopenharmony_ci nw64(RX_LOG_MASK2(channel), 0); 480062306a36Sopenharmony_ci nw64(RX_LOG_VAL2(channel), 0); 480162306a36Sopenharmony_ci nw64(RX_LOG_PAGE_RELO1(channel), 0); 480262306a36Sopenharmony_ci nw64(RX_LOG_PAGE_RELO2(channel), 0); 480362306a36Sopenharmony_ci nw64(RX_LOG_PAGE_HDL(channel), 0); 480462306a36Sopenharmony_ci 480562306a36Sopenharmony_ci val = (u64)np->port << RX_LOG_PAGE_VLD_FUNC_SHIFT; 480662306a36Sopenharmony_ci val |= (RX_LOG_PAGE_VLD_PAGE0 | RX_LOG_PAGE_VLD_PAGE1); 480762306a36Sopenharmony_ci nw64(RX_LOG_PAGE_VLD(channel), val); 480862306a36Sopenharmony_ci 480962306a36Sopenharmony_ci return 0; 481062306a36Sopenharmony_ci} 481162306a36Sopenharmony_ci 481262306a36Sopenharmony_cistatic void niu_rx_channel_wred_init(struct niu *np, struct rx_ring_info *rp) 481362306a36Sopenharmony_ci{ 481462306a36Sopenharmony_ci u64 val; 481562306a36Sopenharmony_ci 481662306a36Sopenharmony_ci val = (((u64)rp->nonsyn_window << RDC_RED_PARA_WIN_SHIFT) | 481762306a36Sopenharmony_ci ((u64)rp->nonsyn_threshold << RDC_RED_PARA_THRE_SHIFT) | 481862306a36Sopenharmony_ci ((u64)rp->syn_window << RDC_RED_PARA_WIN_SYN_SHIFT) | 481962306a36Sopenharmony_ci ((u64)rp->syn_threshold << RDC_RED_PARA_THRE_SYN_SHIFT)); 482062306a36Sopenharmony_ci nw64(RDC_RED_PARA(rp->rx_channel), val); 482162306a36Sopenharmony_ci} 482262306a36Sopenharmony_ci 482362306a36Sopenharmony_cistatic int niu_compute_rbr_cfig_b(struct rx_ring_info *rp, u64 *ret) 482462306a36Sopenharmony_ci{ 482562306a36Sopenharmony_ci u64 val = 0; 482662306a36Sopenharmony_ci 482762306a36Sopenharmony_ci *ret = 0; 482862306a36Sopenharmony_ci switch (rp->rbr_block_size) { 482962306a36Sopenharmony_ci case 4 * 1024: 483062306a36Sopenharmony_ci val |= (RBR_BLKSIZE_4K << RBR_CFIG_B_BLKSIZE_SHIFT); 483162306a36Sopenharmony_ci break; 483262306a36Sopenharmony_ci case 8 * 1024: 483362306a36Sopenharmony_ci val |= (RBR_BLKSIZE_8K << RBR_CFIG_B_BLKSIZE_SHIFT); 483462306a36Sopenharmony_ci break; 483562306a36Sopenharmony_ci case 16 * 1024: 483662306a36Sopenharmony_ci val |= (RBR_BLKSIZE_16K << RBR_CFIG_B_BLKSIZE_SHIFT); 483762306a36Sopenharmony_ci break; 483862306a36Sopenharmony_ci case 32 * 1024: 483962306a36Sopenharmony_ci val |= (RBR_BLKSIZE_32K << RBR_CFIG_B_BLKSIZE_SHIFT); 484062306a36Sopenharmony_ci break; 484162306a36Sopenharmony_ci default: 484262306a36Sopenharmony_ci return -EINVAL; 484362306a36Sopenharmony_ci } 484462306a36Sopenharmony_ci val |= RBR_CFIG_B_VLD2; 484562306a36Sopenharmony_ci switch (rp->rbr_sizes[2]) { 484662306a36Sopenharmony_ci case 2 * 1024: 484762306a36Sopenharmony_ci val |= (RBR_BUFSZ2_2K << RBR_CFIG_B_BUFSZ2_SHIFT); 484862306a36Sopenharmony_ci break; 484962306a36Sopenharmony_ci case 4 * 1024: 485062306a36Sopenharmony_ci val |= (RBR_BUFSZ2_4K << RBR_CFIG_B_BUFSZ2_SHIFT); 485162306a36Sopenharmony_ci break; 485262306a36Sopenharmony_ci case 8 * 1024: 485362306a36Sopenharmony_ci val |= (RBR_BUFSZ2_8K << RBR_CFIG_B_BUFSZ2_SHIFT); 485462306a36Sopenharmony_ci break; 485562306a36Sopenharmony_ci case 16 * 1024: 485662306a36Sopenharmony_ci val |= (RBR_BUFSZ2_16K << RBR_CFIG_B_BUFSZ2_SHIFT); 485762306a36Sopenharmony_ci break; 485862306a36Sopenharmony_ci 485962306a36Sopenharmony_ci default: 486062306a36Sopenharmony_ci return -EINVAL; 486162306a36Sopenharmony_ci } 486262306a36Sopenharmony_ci val |= RBR_CFIG_B_VLD1; 486362306a36Sopenharmony_ci switch (rp->rbr_sizes[1]) { 486462306a36Sopenharmony_ci case 1 * 1024: 486562306a36Sopenharmony_ci val |= (RBR_BUFSZ1_1K << RBR_CFIG_B_BUFSZ1_SHIFT); 486662306a36Sopenharmony_ci break; 486762306a36Sopenharmony_ci case 2 * 1024: 486862306a36Sopenharmony_ci val |= (RBR_BUFSZ1_2K << RBR_CFIG_B_BUFSZ1_SHIFT); 486962306a36Sopenharmony_ci break; 487062306a36Sopenharmony_ci case 4 * 1024: 487162306a36Sopenharmony_ci val |= (RBR_BUFSZ1_4K << RBR_CFIG_B_BUFSZ1_SHIFT); 487262306a36Sopenharmony_ci break; 487362306a36Sopenharmony_ci case 8 * 1024: 487462306a36Sopenharmony_ci val |= (RBR_BUFSZ1_8K << RBR_CFIG_B_BUFSZ1_SHIFT); 487562306a36Sopenharmony_ci break; 487662306a36Sopenharmony_ci 487762306a36Sopenharmony_ci default: 487862306a36Sopenharmony_ci return -EINVAL; 487962306a36Sopenharmony_ci } 488062306a36Sopenharmony_ci val |= RBR_CFIG_B_VLD0; 488162306a36Sopenharmony_ci switch (rp->rbr_sizes[0]) { 488262306a36Sopenharmony_ci case 256: 488362306a36Sopenharmony_ci val |= (RBR_BUFSZ0_256 << RBR_CFIG_B_BUFSZ0_SHIFT); 488462306a36Sopenharmony_ci break; 488562306a36Sopenharmony_ci case 512: 488662306a36Sopenharmony_ci val |= (RBR_BUFSZ0_512 << RBR_CFIG_B_BUFSZ0_SHIFT); 488762306a36Sopenharmony_ci break; 488862306a36Sopenharmony_ci case 1 * 1024: 488962306a36Sopenharmony_ci val |= (RBR_BUFSZ0_1K << RBR_CFIG_B_BUFSZ0_SHIFT); 489062306a36Sopenharmony_ci break; 489162306a36Sopenharmony_ci case 2 * 1024: 489262306a36Sopenharmony_ci val |= (RBR_BUFSZ0_2K << RBR_CFIG_B_BUFSZ0_SHIFT); 489362306a36Sopenharmony_ci break; 489462306a36Sopenharmony_ci 489562306a36Sopenharmony_ci default: 489662306a36Sopenharmony_ci return -EINVAL; 489762306a36Sopenharmony_ci } 489862306a36Sopenharmony_ci 489962306a36Sopenharmony_ci *ret = val; 490062306a36Sopenharmony_ci return 0; 490162306a36Sopenharmony_ci} 490262306a36Sopenharmony_ci 490362306a36Sopenharmony_cistatic int niu_enable_rx_channel(struct niu *np, int channel, int on) 490462306a36Sopenharmony_ci{ 490562306a36Sopenharmony_ci u64 val = nr64(RXDMA_CFIG1(channel)); 490662306a36Sopenharmony_ci int limit; 490762306a36Sopenharmony_ci 490862306a36Sopenharmony_ci if (on) 490962306a36Sopenharmony_ci val |= RXDMA_CFIG1_EN; 491062306a36Sopenharmony_ci else 491162306a36Sopenharmony_ci val &= ~RXDMA_CFIG1_EN; 491262306a36Sopenharmony_ci nw64(RXDMA_CFIG1(channel), val); 491362306a36Sopenharmony_ci 491462306a36Sopenharmony_ci limit = 1000; 491562306a36Sopenharmony_ci while (--limit > 0) { 491662306a36Sopenharmony_ci if (nr64(RXDMA_CFIG1(channel)) & RXDMA_CFIG1_QST) 491762306a36Sopenharmony_ci break; 491862306a36Sopenharmony_ci udelay(10); 491962306a36Sopenharmony_ci } 492062306a36Sopenharmony_ci if (limit <= 0) 492162306a36Sopenharmony_ci return -ENODEV; 492262306a36Sopenharmony_ci return 0; 492362306a36Sopenharmony_ci} 492462306a36Sopenharmony_ci 492562306a36Sopenharmony_cistatic int niu_init_one_rx_channel(struct niu *np, struct rx_ring_info *rp) 492662306a36Sopenharmony_ci{ 492762306a36Sopenharmony_ci int err, channel = rp->rx_channel; 492862306a36Sopenharmony_ci u64 val; 492962306a36Sopenharmony_ci 493062306a36Sopenharmony_ci err = niu_rx_channel_reset(np, channel); 493162306a36Sopenharmony_ci if (err) 493262306a36Sopenharmony_ci return err; 493362306a36Sopenharmony_ci 493462306a36Sopenharmony_ci err = niu_rx_channel_lpage_init(np, channel); 493562306a36Sopenharmony_ci if (err) 493662306a36Sopenharmony_ci return err; 493762306a36Sopenharmony_ci 493862306a36Sopenharmony_ci niu_rx_channel_wred_init(np, rp); 493962306a36Sopenharmony_ci 494062306a36Sopenharmony_ci nw64(RX_DMA_ENT_MSK(channel), RX_DMA_ENT_MSK_RBR_EMPTY); 494162306a36Sopenharmony_ci nw64(RX_DMA_CTL_STAT(channel), 494262306a36Sopenharmony_ci (RX_DMA_CTL_STAT_MEX | 494362306a36Sopenharmony_ci RX_DMA_CTL_STAT_RCRTHRES | 494462306a36Sopenharmony_ci RX_DMA_CTL_STAT_RCRTO | 494562306a36Sopenharmony_ci RX_DMA_CTL_STAT_RBR_EMPTY)); 494662306a36Sopenharmony_ci nw64(RXDMA_CFIG1(channel), rp->mbox_dma >> 32); 494762306a36Sopenharmony_ci nw64(RXDMA_CFIG2(channel), 494862306a36Sopenharmony_ci ((rp->mbox_dma & RXDMA_CFIG2_MBADDR_L) | 494962306a36Sopenharmony_ci RXDMA_CFIG2_FULL_HDR)); 495062306a36Sopenharmony_ci nw64(RBR_CFIG_A(channel), 495162306a36Sopenharmony_ci ((u64)rp->rbr_table_size << RBR_CFIG_A_LEN_SHIFT) | 495262306a36Sopenharmony_ci (rp->rbr_dma & (RBR_CFIG_A_STADDR_BASE | RBR_CFIG_A_STADDR))); 495362306a36Sopenharmony_ci err = niu_compute_rbr_cfig_b(rp, &val); 495462306a36Sopenharmony_ci if (err) 495562306a36Sopenharmony_ci return err; 495662306a36Sopenharmony_ci nw64(RBR_CFIG_B(channel), val); 495762306a36Sopenharmony_ci nw64(RCRCFIG_A(channel), 495862306a36Sopenharmony_ci ((u64)rp->rcr_table_size << RCRCFIG_A_LEN_SHIFT) | 495962306a36Sopenharmony_ci (rp->rcr_dma & (RCRCFIG_A_STADDR_BASE | RCRCFIG_A_STADDR))); 496062306a36Sopenharmony_ci nw64(RCRCFIG_B(channel), 496162306a36Sopenharmony_ci ((u64)rp->rcr_pkt_threshold << RCRCFIG_B_PTHRES_SHIFT) | 496262306a36Sopenharmony_ci RCRCFIG_B_ENTOUT | 496362306a36Sopenharmony_ci ((u64)rp->rcr_timeout << RCRCFIG_B_TIMEOUT_SHIFT)); 496462306a36Sopenharmony_ci 496562306a36Sopenharmony_ci err = niu_enable_rx_channel(np, channel, 1); 496662306a36Sopenharmony_ci if (err) 496762306a36Sopenharmony_ci return err; 496862306a36Sopenharmony_ci 496962306a36Sopenharmony_ci nw64(RBR_KICK(channel), rp->rbr_index); 497062306a36Sopenharmony_ci 497162306a36Sopenharmony_ci val = nr64(RX_DMA_CTL_STAT(channel)); 497262306a36Sopenharmony_ci val |= RX_DMA_CTL_STAT_RBR_EMPTY; 497362306a36Sopenharmony_ci nw64(RX_DMA_CTL_STAT(channel), val); 497462306a36Sopenharmony_ci 497562306a36Sopenharmony_ci return 0; 497662306a36Sopenharmony_ci} 497762306a36Sopenharmony_ci 497862306a36Sopenharmony_cistatic int niu_init_rx_channels(struct niu *np) 497962306a36Sopenharmony_ci{ 498062306a36Sopenharmony_ci unsigned long flags; 498162306a36Sopenharmony_ci u64 seed = jiffies_64; 498262306a36Sopenharmony_ci int err, i; 498362306a36Sopenharmony_ci 498462306a36Sopenharmony_ci niu_lock_parent(np, flags); 498562306a36Sopenharmony_ci nw64(RX_DMA_CK_DIV, np->parent->rxdma_clock_divider); 498662306a36Sopenharmony_ci nw64(RED_RAN_INIT, RED_RAN_INIT_OPMODE | (seed & RED_RAN_INIT_VAL)); 498762306a36Sopenharmony_ci niu_unlock_parent(np, flags); 498862306a36Sopenharmony_ci 498962306a36Sopenharmony_ci /* XXX RXDMA 32bit mode? XXX */ 499062306a36Sopenharmony_ci 499162306a36Sopenharmony_ci niu_init_rdc_groups(np); 499262306a36Sopenharmony_ci niu_init_drr_weight(np); 499362306a36Sopenharmony_ci 499462306a36Sopenharmony_ci err = niu_init_hostinfo(np); 499562306a36Sopenharmony_ci if (err) 499662306a36Sopenharmony_ci return err; 499762306a36Sopenharmony_ci 499862306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 499962306a36Sopenharmony_ci struct rx_ring_info *rp = &np->rx_rings[i]; 500062306a36Sopenharmony_ci 500162306a36Sopenharmony_ci err = niu_init_one_rx_channel(np, rp); 500262306a36Sopenharmony_ci if (err) 500362306a36Sopenharmony_ci return err; 500462306a36Sopenharmony_ci } 500562306a36Sopenharmony_ci 500662306a36Sopenharmony_ci return 0; 500762306a36Sopenharmony_ci} 500862306a36Sopenharmony_ci 500962306a36Sopenharmony_cistatic int niu_set_ip_frag_rule(struct niu *np) 501062306a36Sopenharmony_ci{ 501162306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 501262306a36Sopenharmony_ci struct niu_classifier *cp = &np->clas; 501362306a36Sopenharmony_ci struct niu_tcam_entry *tp; 501462306a36Sopenharmony_ci int index, err; 501562306a36Sopenharmony_ci 501662306a36Sopenharmony_ci index = cp->tcam_top; 501762306a36Sopenharmony_ci tp = &parent->tcam[index]; 501862306a36Sopenharmony_ci 501962306a36Sopenharmony_ci /* Note that the noport bit is the same in both ipv4 and 502062306a36Sopenharmony_ci * ipv6 format TCAM entries. 502162306a36Sopenharmony_ci */ 502262306a36Sopenharmony_ci memset(tp, 0, sizeof(*tp)); 502362306a36Sopenharmony_ci tp->key[1] = TCAM_V4KEY1_NOPORT; 502462306a36Sopenharmony_ci tp->key_mask[1] = TCAM_V4KEY1_NOPORT; 502562306a36Sopenharmony_ci tp->assoc_data = (TCAM_ASSOCDATA_TRES_USE_OFFSET | 502662306a36Sopenharmony_ci ((u64)0 << TCAM_ASSOCDATA_OFFSET_SHIFT)); 502762306a36Sopenharmony_ci err = tcam_write(np, index, tp->key, tp->key_mask); 502862306a36Sopenharmony_ci if (err) 502962306a36Sopenharmony_ci return err; 503062306a36Sopenharmony_ci err = tcam_assoc_write(np, index, tp->assoc_data); 503162306a36Sopenharmony_ci if (err) 503262306a36Sopenharmony_ci return err; 503362306a36Sopenharmony_ci tp->valid = 1; 503462306a36Sopenharmony_ci cp->tcam_valid_entries++; 503562306a36Sopenharmony_ci 503662306a36Sopenharmony_ci return 0; 503762306a36Sopenharmony_ci} 503862306a36Sopenharmony_ci 503962306a36Sopenharmony_cistatic int niu_init_classifier_hw(struct niu *np) 504062306a36Sopenharmony_ci{ 504162306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 504262306a36Sopenharmony_ci struct niu_classifier *cp = &np->clas; 504362306a36Sopenharmony_ci int i, err; 504462306a36Sopenharmony_ci 504562306a36Sopenharmony_ci nw64(H1POLY, cp->h1_init); 504662306a36Sopenharmony_ci nw64(H2POLY, cp->h2_init); 504762306a36Sopenharmony_ci 504862306a36Sopenharmony_ci err = niu_init_hostinfo(np); 504962306a36Sopenharmony_ci if (err) 505062306a36Sopenharmony_ci return err; 505162306a36Sopenharmony_ci 505262306a36Sopenharmony_ci for (i = 0; i < ENET_VLAN_TBL_NUM_ENTRIES; i++) { 505362306a36Sopenharmony_ci struct niu_vlan_rdc *vp = &cp->vlan_mappings[i]; 505462306a36Sopenharmony_ci 505562306a36Sopenharmony_ci vlan_tbl_write(np, i, np->port, 505662306a36Sopenharmony_ci vp->vlan_pref, vp->rdc_num); 505762306a36Sopenharmony_ci } 505862306a36Sopenharmony_ci 505962306a36Sopenharmony_ci for (i = 0; i < cp->num_alt_mac_mappings; i++) { 506062306a36Sopenharmony_ci struct niu_altmac_rdc *ap = &cp->alt_mac_mappings[i]; 506162306a36Sopenharmony_ci 506262306a36Sopenharmony_ci err = niu_set_alt_mac_rdc_table(np, ap->alt_mac_num, 506362306a36Sopenharmony_ci ap->rdc_num, ap->mac_pref); 506462306a36Sopenharmony_ci if (err) 506562306a36Sopenharmony_ci return err; 506662306a36Sopenharmony_ci } 506762306a36Sopenharmony_ci 506862306a36Sopenharmony_ci for (i = CLASS_CODE_USER_PROG1; i <= CLASS_CODE_SCTP_IPV6; i++) { 506962306a36Sopenharmony_ci int index = i - CLASS_CODE_USER_PROG1; 507062306a36Sopenharmony_ci 507162306a36Sopenharmony_ci err = niu_set_tcam_key(np, i, parent->tcam_key[index]); 507262306a36Sopenharmony_ci if (err) 507362306a36Sopenharmony_ci return err; 507462306a36Sopenharmony_ci err = niu_set_flow_key(np, i, parent->flow_key[index]); 507562306a36Sopenharmony_ci if (err) 507662306a36Sopenharmony_ci return err; 507762306a36Sopenharmony_ci } 507862306a36Sopenharmony_ci 507962306a36Sopenharmony_ci err = niu_set_ip_frag_rule(np); 508062306a36Sopenharmony_ci if (err) 508162306a36Sopenharmony_ci return err; 508262306a36Sopenharmony_ci 508362306a36Sopenharmony_ci tcam_enable(np, 1); 508462306a36Sopenharmony_ci 508562306a36Sopenharmony_ci return 0; 508662306a36Sopenharmony_ci} 508762306a36Sopenharmony_ci 508862306a36Sopenharmony_cistatic int niu_zcp_write(struct niu *np, int index, u64 *data) 508962306a36Sopenharmony_ci{ 509062306a36Sopenharmony_ci nw64(ZCP_RAM_DATA0, data[0]); 509162306a36Sopenharmony_ci nw64(ZCP_RAM_DATA1, data[1]); 509262306a36Sopenharmony_ci nw64(ZCP_RAM_DATA2, data[2]); 509362306a36Sopenharmony_ci nw64(ZCP_RAM_DATA3, data[3]); 509462306a36Sopenharmony_ci nw64(ZCP_RAM_DATA4, data[4]); 509562306a36Sopenharmony_ci nw64(ZCP_RAM_BE, ZCP_RAM_BE_VAL); 509662306a36Sopenharmony_ci nw64(ZCP_RAM_ACC, 509762306a36Sopenharmony_ci (ZCP_RAM_ACC_WRITE | 509862306a36Sopenharmony_ci (0 << ZCP_RAM_ACC_ZFCID_SHIFT) | 509962306a36Sopenharmony_ci (ZCP_RAM_SEL_CFIFO(np->port) << ZCP_RAM_ACC_RAM_SEL_SHIFT))); 510062306a36Sopenharmony_ci 510162306a36Sopenharmony_ci return niu_wait_bits_clear(np, ZCP_RAM_ACC, ZCP_RAM_ACC_BUSY, 510262306a36Sopenharmony_ci 1000, 100); 510362306a36Sopenharmony_ci} 510462306a36Sopenharmony_ci 510562306a36Sopenharmony_cistatic int niu_zcp_read(struct niu *np, int index, u64 *data) 510662306a36Sopenharmony_ci{ 510762306a36Sopenharmony_ci int err; 510862306a36Sopenharmony_ci 510962306a36Sopenharmony_ci err = niu_wait_bits_clear(np, ZCP_RAM_ACC, ZCP_RAM_ACC_BUSY, 511062306a36Sopenharmony_ci 1000, 100); 511162306a36Sopenharmony_ci if (err) { 511262306a36Sopenharmony_ci netdev_err(np->dev, "ZCP read busy won't clear, ZCP_RAM_ACC[%llx]\n", 511362306a36Sopenharmony_ci (unsigned long long)nr64(ZCP_RAM_ACC)); 511462306a36Sopenharmony_ci return err; 511562306a36Sopenharmony_ci } 511662306a36Sopenharmony_ci 511762306a36Sopenharmony_ci nw64(ZCP_RAM_ACC, 511862306a36Sopenharmony_ci (ZCP_RAM_ACC_READ | 511962306a36Sopenharmony_ci (0 << ZCP_RAM_ACC_ZFCID_SHIFT) | 512062306a36Sopenharmony_ci (ZCP_RAM_SEL_CFIFO(np->port) << ZCP_RAM_ACC_RAM_SEL_SHIFT))); 512162306a36Sopenharmony_ci 512262306a36Sopenharmony_ci err = niu_wait_bits_clear(np, ZCP_RAM_ACC, ZCP_RAM_ACC_BUSY, 512362306a36Sopenharmony_ci 1000, 100); 512462306a36Sopenharmony_ci if (err) { 512562306a36Sopenharmony_ci netdev_err(np->dev, "ZCP read busy2 won't clear, ZCP_RAM_ACC[%llx]\n", 512662306a36Sopenharmony_ci (unsigned long long)nr64(ZCP_RAM_ACC)); 512762306a36Sopenharmony_ci return err; 512862306a36Sopenharmony_ci } 512962306a36Sopenharmony_ci 513062306a36Sopenharmony_ci data[0] = nr64(ZCP_RAM_DATA0); 513162306a36Sopenharmony_ci data[1] = nr64(ZCP_RAM_DATA1); 513262306a36Sopenharmony_ci data[2] = nr64(ZCP_RAM_DATA2); 513362306a36Sopenharmony_ci data[3] = nr64(ZCP_RAM_DATA3); 513462306a36Sopenharmony_ci data[4] = nr64(ZCP_RAM_DATA4); 513562306a36Sopenharmony_ci 513662306a36Sopenharmony_ci return 0; 513762306a36Sopenharmony_ci} 513862306a36Sopenharmony_ci 513962306a36Sopenharmony_cistatic void niu_zcp_cfifo_reset(struct niu *np) 514062306a36Sopenharmony_ci{ 514162306a36Sopenharmony_ci u64 val = nr64(RESET_CFIFO); 514262306a36Sopenharmony_ci 514362306a36Sopenharmony_ci val |= RESET_CFIFO_RST(np->port); 514462306a36Sopenharmony_ci nw64(RESET_CFIFO, val); 514562306a36Sopenharmony_ci udelay(10); 514662306a36Sopenharmony_ci 514762306a36Sopenharmony_ci val &= ~RESET_CFIFO_RST(np->port); 514862306a36Sopenharmony_ci nw64(RESET_CFIFO, val); 514962306a36Sopenharmony_ci} 515062306a36Sopenharmony_ci 515162306a36Sopenharmony_cistatic int niu_init_zcp(struct niu *np) 515262306a36Sopenharmony_ci{ 515362306a36Sopenharmony_ci u64 data[5], rbuf[5]; 515462306a36Sopenharmony_ci int i, max, err; 515562306a36Sopenharmony_ci 515662306a36Sopenharmony_ci if (np->parent->plat_type != PLAT_TYPE_NIU) { 515762306a36Sopenharmony_ci if (np->port == 0 || np->port == 1) 515862306a36Sopenharmony_ci max = ATLAS_P0_P1_CFIFO_ENTRIES; 515962306a36Sopenharmony_ci else 516062306a36Sopenharmony_ci max = ATLAS_P2_P3_CFIFO_ENTRIES; 516162306a36Sopenharmony_ci } else 516262306a36Sopenharmony_ci max = NIU_CFIFO_ENTRIES; 516362306a36Sopenharmony_ci 516462306a36Sopenharmony_ci data[0] = 0; 516562306a36Sopenharmony_ci data[1] = 0; 516662306a36Sopenharmony_ci data[2] = 0; 516762306a36Sopenharmony_ci data[3] = 0; 516862306a36Sopenharmony_ci data[4] = 0; 516962306a36Sopenharmony_ci 517062306a36Sopenharmony_ci for (i = 0; i < max; i++) { 517162306a36Sopenharmony_ci err = niu_zcp_write(np, i, data); 517262306a36Sopenharmony_ci if (err) 517362306a36Sopenharmony_ci return err; 517462306a36Sopenharmony_ci err = niu_zcp_read(np, i, rbuf); 517562306a36Sopenharmony_ci if (err) 517662306a36Sopenharmony_ci return err; 517762306a36Sopenharmony_ci } 517862306a36Sopenharmony_ci 517962306a36Sopenharmony_ci niu_zcp_cfifo_reset(np); 518062306a36Sopenharmony_ci nw64(CFIFO_ECC(np->port), 0); 518162306a36Sopenharmony_ci nw64(ZCP_INT_STAT, ZCP_INT_STAT_ALL); 518262306a36Sopenharmony_ci (void) nr64(ZCP_INT_STAT); 518362306a36Sopenharmony_ci nw64(ZCP_INT_MASK, ZCP_INT_MASK_ALL); 518462306a36Sopenharmony_ci 518562306a36Sopenharmony_ci return 0; 518662306a36Sopenharmony_ci} 518762306a36Sopenharmony_ci 518862306a36Sopenharmony_cistatic void niu_ipp_write(struct niu *np, int index, u64 *data) 518962306a36Sopenharmony_ci{ 519062306a36Sopenharmony_ci u64 val = nr64_ipp(IPP_CFIG); 519162306a36Sopenharmony_ci 519262306a36Sopenharmony_ci nw64_ipp(IPP_CFIG, val | IPP_CFIG_DFIFO_PIO_W); 519362306a36Sopenharmony_ci nw64_ipp(IPP_DFIFO_WR_PTR, index); 519462306a36Sopenharmony_ci nw64_ipp(IPP_DFIFO_WR0, data[0]); 519562306a36Sopenharmony_ci nw64_ipp(IPP_DFIFO_WR1, data[1]); 519662306a36Sopenharmony_ci nw64_ipp(IPP_DFIFO_WR2, data[2]); 519762306a36Sopenharmony_ci nw64_ipp(IPP_DFIFO_WR3, data[3]); 519862306a36Sopenharmony_ci nw64_ipp(IPP_DFIFO_WR4, data[4]); 519962306a36Sopenharmony_ci nw64_ipp(IPP_CFIG, val & ~IPP_CFIG_DFIFO_PIO_W); 520062306a36Sopenharmony_ci} 520162306a36Sopenharmony_ci 520262306a36Sopenharmony_cistatic void niu_ipp_read(struct niu *np, int index, u64 *data) 520362306a36Sopenharmony_ci{ 520462306a36Sopenharmony_ci nw64_ipp(IPP_DFIFO_RD_PTR, index); 520562306a36Sopenharmony_ci data[0] = nr64_ipp(IPP_DFIFO_RD0); 520662306a36Sopenharmony_ci data[1] = nr64_ipp(IPP_DFIFO_RD1); 520762306a36Sopenharmony_ci data[2] = nr64_ipp(IPP_DFIFO_RD2); 520862306a36Sopenharmony_ci data[3] = nr64_ipp(IPP_DFIFO_RD3); 520962306a36Sopenharmony_ci data[4] = nr64_ipp(IPP_DFIFO_RD4); 521062306a36Sopenharmony_ci} 521162306a36Sopenharmony_ci 521262306a36Sopenharmony_cistatic int niu_ipp_reset(struct niu *np) 521362306a36Sopenharmony_ci{ 521462306a36Sopenharmony_ci return niu_set_and_wait_clear_ipp(np, IPP_CFIG, IPP_CFIG_SOFT_RST, 521562306a36Sopenharmony_ci 1000, 100, "IPP_CFIG"); 521662306a36Sopenharmony_ci} 521762306a36Sopenharmony_ci 521862306a36Sopenharmony_cistatic int niu_init_ipp(struct niu *np) 521962306a36Sopenharmony_ci{ 522062306a36Sopenharmony_ci u64 data[5], rbuf[5], val; 522162306a36Sopenharmony_ci int i, max, err; 522262306a36Sopenharmony_ci 522362306a36Sopenharmony_ci if (np->parent->plat_type != PLAT_TYPE_NIU) { 522462306a36Sopenharmony_ci if (np->port == 0 || np->port == 1) 522562306a36Sopenharmony_ci max = ATLAS_P0_P1_DFIFO_ENTRIES; 522662306a36Sopenharmony_ci else 522762306a36Sopenharmony_ci max = ATLAS_P2_P3_DFIFO_ENTRIES; 522862306a36Sopenharmony_ci } else 522962306a36Sopenharmony_ci max = NIU_DFIFO_ENTRIES; 523062306a36Sopenharmony_ci 523162306a36Sopenharmony_ci data[0] = 0; 523262306a36Sopenharmony_ci data[1] = 0; 523362306a36Sopenharmony_ci data[2] = 0; 523462306a36Sopenharmony_ci data[3] = 0; 523562306a36Sopenharmony_ci data[4] = 0; 523662306a36Sopenharmony_ci 523762306a36Sopenharmony_ci for (i = 0; i < max; i++) { 523862306a36Sopenharmony_ci niu_ipp_write(np, i, data); 523962306a36Sopenharmony_ci niu_ipp_read(np, i, rbuf); 524062306a36Sopenharmony_ci } 524162306a36Sopenharmony_ci 524262306a36Sopenharmony_ci (void) nr64_ipp(IPP_INT_STAT); 524362306a36Sopenharmony_ci (void) nr64_ipp(IPP_INT_STAT); 524462306a36Sopenharmony_ci 524562306a36Sopenharmony_ci err = niu_ipp_reset(np); 524662306a36Sopenharmony_ci if (err) 524762306a36Sopenharmony_ci return err; 524862306a36Sopenharmony_ci 524962306a36Sopenharmony_ci (void) nr64_ipp(IPP_PKT_DIS); 525062306a36Sopenharmony_ci (void) nr64_ipp(IPP_BAD_CS_CNT); 525162306a36Sopenharmony_ci (void) nr64_ipp(IPP_ECC); 525262306a36Sopenharmony_ci 525362306a36Sopenharmony_ci (void) nr64_ipp(IPP_INT_STAT); 525462306a36Sopenharmony_ci 525562306a36Sopenharmony_ci nw64_ipp(IPP_MSK, ~IPP_MSK_ALL); 525662306a36Sopenharmony_ci 525762306a36Sopenharmony_ci val = nr64_ipp(IPP_CFIG); 525862306a36Sopenharmony_ci val &= ~IPP_CFIG_IP_MAX_PKT; 525962306a36Sopenharmony_ci val |= (IPP_CFIG_IPP_ENABLE | 526062306a36Sopenharmony_ci IPP_CFIG_DFIFO_ECC_EN | 526162306a36Sopenharmony_ci IPP_CFIG_DROP_BAD_CRC | 526262306a36Sopenharmony_ci IPP_CFIG_CKSUM_EN | 526362306a36Sopenharmony_ci (0x1ffff << IPP_CFIG_IP_MAX_PKT_SHIFT)); 526462306a36Sopenharmony_ci nw64_ipp(IPP_CFIG, val); 526562306a36Sopenharmony_ci 526662306a36Sopenharmony_ci return 0; 526762306a36Sopenharmony_ci} 526862306a36Sopenharmony_ci 526962306a36Sopenharmony_cistatic void niu_handle_led(struct niu *np, int status) 527062306a36Sopenharmony_ci{ 527162306a36Sopenharmony_ci u64 val; 527262306a36Sopenharmony_ci val = nr64_mac(XMAC_CONFIG); 527362306a36Sopenharmony_ci 527462306a36Sopenharmony_ci if ((np->flags & NIU_FLAGS_10G) != 0 && 527562306a36Sopenharmony_ci (np->flags & NIU_FLAGS_FIBER) != 0) { 527662306a36Sopenharmony_ci if (status) { 527762306a36Sopenharmony_ci val |= XMAC_CONFIG_LED_POLARITY; 527862306a36Sopenharmony_ci val &= ~XMAC_CONFIG_FORCE_LED_ON; 527962306a36Sopenharmony_ci } else { 528062306a36Sopenharmony_ci val |= XMAC_CONFIG_FORCE_LED_ON; 528162306a36Sopenharmony_ci val &= ~XMAC_CONFIG_LED_POLARITY; 528262306a36Sopenharmony_ci } 528362306a36Sopenharmony_ci } 528462306a36Sopenharmony_ci 528562306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 528662306a36Sopenharmony_ci} 528762306a36Sopenharmony_ci 528862306a36Sopenharmony_cistatic void niu_init_xif_xmac(struct niu *np) 528962306a36Sopenharmony_ci{ 529062306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 529162306a36Sopenharmony_ci u64 val; 529262306a36Sopenharmony_ci 529362306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XCVR_SERDES) { 529462306a36Sopenharmony_ci val = nr64(MIF_CONFIG); 529562306a36Sopenharmony_ci val |= MIF_CONFIG_ATCA_GE; 529662306a36Sopenharmony_ci nw64(MIF_CONFIG, val); 529762306a36Sopenharmony_ci } 529862306a36Sopenharmony_ci 529962306a36Sopenharmony_ci val = nr64_mac(XMAC_CONFIG); 530062306a36Sopenharmony_ci val &= ~XMAC_CONFIG_SEL_POR_CLK_SRC; 530162306a36Sopenharmony_ci 530262306a36Sopenharmony_ci val |= XMAC_CONFIG_TX_OUTPUT_EN; 530362306a36Sopenharmony_ci 530462306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_MAC) { 530562306a36Sopenharmony_ci val &= ~XMAC_CONFIG_SEL_POR_CLK_SRC; 530662306a36Sopenharmony_ci val |= XMAC_CONFIG_LOOPBACK; 530762306a36Sopenharmony_ci } else { 530862306a36Sopenharmony_ci val &= ~XMAC_CONFIG_LOOPBACK; 530962306a36Sopenharmony_ci } 531062306a36Sopenharmony_ci 531162306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_10G) { 531262306a36Sopenharmony_ci val &= ~XMAC_CONFIG_LFS_DISABLE; 531362306a36Sopenharmony_ci } else { 531462306a36Sopenharmony_ci val |= XMAC_CONFIG_LFS_DISABLE; 531562306a36Sopenharmony_ci if (!(np->flags & NIU_FLAGS_FIBER) && 531662306a36Sopenharmony_ci !(np->flags & NIU_FLAGS_XCVR_SERDES)) 531762306a36Sopenharmony_ci val |= XMAC_CONFIG_1G_PCS_BYPASS; 531862306a36Sopenharmony_ci else 531962306a36Sopenharmony_ci val &= ~XMAC_CONFIG_1G_PCS_BYPASS; 532062306a36Sopenharmony_ci } 532162306a36Sopenharmony_ci 532262306a36Sopenharmony_ci val &= ~XMAC_CONFIG_10G_XPCS_BYPASS; 532362306a36Sopenharmony_ci 532462306a36Sopenharmony_ci if (lp->active_speed == SPEED_100) 532562306a36Sopenharmony_ci val |= XMAC_CONFIG_SEL_CLK_25MHZ; 532662306a36Sopenharmony_ci else 532762306a36Sopenharmony_ci val &= ~XMAC_CONFIG_SEL_CLK_25MHZ; 532862306a36Sopenharmony_ci 532962306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 533062306a36Sopenharmony_ci 533162306a36Sopenharmony_ci val = nr64_mac(XMAC_CONFIG); 533262306a36Sopenharmony_ci val &= ~XMAC_CONFIG_MODE_MASK; 533362306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_10G) { 533462306a36Sopenharmony_ci val |= XMAC_CONFIG_MODE_XGMII; 533562306a36Sopenharmony_ci } else { 533662306a36Sopenharmony_ci if (lp->active_speed == SPEED_1000) 533762306a36Sopenharmony_ci val |= XMAC_CONFIG_MODE_GMII; 533862306a36Sopenharmony_ci else 533962306a36Sopenharmony_ci val |= XMAC_CONFIG_MODE_MII; 534062306a36Sopenharmony_ci } 534162306a36Sopenharmony_ci 534262306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 534362306a36Sopenharmony_ci} 534462306a36Sopenharmony_ci 534562306a36Sopenharmony_cistatic void niu_init_xif_bmac(struct niu *np) 534662306a36Sopenharmony_ci{ 534762306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 534862306a36Sopenharmony_ci u64 val; 534962306a36Sopenharmony_ci 535062306a36Sopenharmony_ci val = BMAC_XIF_CONFIG_TX_OUTPUT_EN; 535162306a36Sopenharmony_ci 535262306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_MAC) 535362306a36Sopenharmony_ci val |= BMAC_XIF_CONFIG_MII_LOOPBACK; 535462306a36Sopenharmony_ci else 535562306a36Sopenharmony_ci val &= ~BMAC_XIF_CONFIG_MII_LOOPBACK; 535662306a36Sopenharmony_ci 535762306a36Sopenharmony_ci if (lp->active_speed == SPEED_1000) 535862306a36Sopenharmony_ci val |= BMAC_XIF_CONFIG_GMII_MODE; 535962306a36Sopenharmony_ci else 536062306a36Sopenharmony_ci val &= ~BMAC_XIF_CONFIG_GMII_MODE; 536162306a36Sopenharmony_ci 536262306a36Sopenharmony_ci val &= ~(BMAC_XIF_CONFIG_LINK_LED | 536362306a36Sopenharmony_ci BMAC_XIF_CONFIG_LED_POLARITY); 536462306a36Sopenharmony_ci 536562306a36Sopenharmony_ci if (!(np->flags & NIU_FLAGS_10G) && 536662306a36Sopenharmony_ci !(np->flags & NIU_FLAGS_FIBER) && 536762306a36Sopenharmony_ci lp->active_speed == SPEED_100) 536862306a36Sopenharmony_ci val |= BMAC_XIF_CONFIG_25MHZ_CLOCK; 536962306a36Sopenharmony_ci else 537062306a36Sopenharmony_ci val &= ~BMAC_XIF_CONFIG_25MHZ_CLOCK; 537162306a36Sopenharmony_ci 537262306a36Sopenharmony_ci nw64_mac(BMAC_XIF_CONFIG, val); 537362306a36Sopenharmony_ci} 537462306a36Sopenharmony_ci 537562306a36Sopenharmony_cistatic void niu_init_xif(struct niu *np) 537662306a36Sopenharmony_ci{ 537762306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 537862306a36Sopenharmony_ci niu_init_xif_xmac(np); 537962306a36Sopenharmony_ci else 538062306a36Sopenharmony_ci niu_init_xif_bmac(np); 538162306a36Sopenharmony_ci} 538262306a36Sopenharmony_ci 538362306a36Sopenharmony_cistatic void niu_pcs_mii_reset(struct niu *np) 538462306a36Sopenharmony_ci{ 538562306a36Sopenharmony_ci int limit = 1000; 538662306a36Sopenharmony_ci u64 val = nr64_pcs(PCS_MII_CTL); 538762306a36Sopenharmony_ci val |= PCS_MII_CTL_RST; 538862306a36Sopenharmony_ci nw64_pcs(PCS_MII_CTL, val); 538962306a36Sopenharmony_ci while ((--limit >= 0) && (val & PCS_MII_CTL_RST)) { 539062306a36Sopenharmony_ci udelay(100); 539162306a36Sopenharmony_ci val = nr64_pcs(PCS_MII_CTL); 539262306a36Sopenharmony_ci } 539362306a36Sopenharmony_ci} 539462306a36Sopenharmony_ci 539562306a36Sopenharmony_cistatic void niu_xpcs_reset(struct niu *np) 539662306a36Sopenharmony_ci{ 539762306a36Sopenharmony_ci int limit = 1000; 539862306a36Sopenharmony_ci u64 val = nr64_xpcs(XPCS_CONTROL1); 539962306a36Sopenharmony_ci val |= XPCS_CONTROL1_RESET; 540062306a36Sopenharmony_ci nw64_xpcs(XPCS_CONTROL1, val); 540162306a36Sopenharmony_ci while ((--limit >= 0) && (val & XPCS_CONTROL1_RESET)) { 540262306a36Sopenharmony_ci udelay(100); 540362306a36Sopenharmony_ci val = nr64_xpcs(XPCS_CONTROL1); 540462306a36Sopenharmony_ci } 540562306a36Sopenharmony_ci} 540662306a36Sopenharmony_ci 540762306a36Sopenharmony_cistatic int niu_init_pcs(struct niu *np) 540862306a36Sopenharmony_ci{ 540962306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 541062306a36Sopenharmony_ci u64 val; 541162306a36Sopenharmony_ci 541262306a36Sopenharmony_ci switch (np->flags & (NIU_FLAGS_10G | 541362306a36Sopenharmony_ci NIU_FLAGS_FIBER | 541462306a36Sopenharmony_ci NIU_FLAGS_XCVR_SERDES)) { 541562306a36Sopenharmony_ci case NIU_FLAGS_FIBER: 541662306a36Sopenharmony_ci /* 1G fiber */ 541762306a36Sopenharmony_ci nw64_pcs(PCS_CONF, PCS_CONF_MASK | PCS_CONF_ENABLE); 541862306a36Sopenharmony_ci nw64_pcs(PCS_DPATH_MODE, 0); 541962306a36Sopenharmony_ci niu_pcs_mii_reset(np); 542062306a36Sopenharmony_ci break; 542162306a36Sopenharmony_ci 542262306a36Sopenharmony_ci case NIU_FLAGS_10G: 542362306a36Sopenharmony_ci case NIU_FLAGS_10G | NIU_FLAGS_FIBER: 542462306a36Sopenharmony_ci case NIU_FLAGS_10G | NIU_FLAGS_XCVR_SERDES: 542562306a36Sopenharmony_ci /* 10G SERDES */ 542662306a36Sopenharmony_ci if (!(np->flags & NIU_FLAGS_XMAC)) 542762306a36Sopenharmony_ci return -EINVAL; 542862306a36Sopenharmony_ci 542962306a36Sopenharmony_ci /* 10G copper or fiber */ 543062306a36Sopenharmony_ci val = nr64_mac(XMAC_CONFIG); 543162306a36Sopenharmony_ci val &= ~XMAC_CONFIG_10G_XPCS_BYPASS; 543262306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 543362306a36Sopenharmony_ci 543462306a36Sopenharmony_ci niu_xpcs_reset(np); 543562306a36Sopenharmony_ci 543662306a36Sopenharmony_ci val = nr64_xpcs(XPCS_CONTROL1); 543762306a36Sopenharmony_ci if (lp->loopback_mode == LOOPBACK_PHY) 543862306a36Sopenharmony_ci val |= XPCS_CONTROL1_LOOPBACK; 543962306a36Sopenharmony_ci else 544062306a36Sopenharmony_ci val &= ~XPCS_CONTROL1_LOOPBACK; 544162306a36Sopenharmony_ci nw64_xpcs(XPCS_CONTROL1, val); 544262306a36Sopenharmony_ci 544362306a36Sopenharmony_ci nw64_xpcs(XPCS_DESKEW_ERR_CNT, 0); 544462306a36Sopenharmony_ci (void) nr64_xpcs(XPCS_SYMERR_CNT01); 544562306a36Sopenharmony_ci (void) nr64_xpcs(XPCS_SYMERR_CNT23); 544662306a36Sopenharmony_ci break; 544762306a36Sopenharmony_ci 544862306a36Sopenharmony_ci 544962306a36Sopenharmony_ci case NIU_FLAGS_XCVR_SERDES: 545062306a36Sopenharmony_ci /* 1G SERDES */ 545162306a36Sopenharmony_ci niu_pcs_mii_reset(np); 545262306a36Sopenharmony_ci nw64_pcs(PCS_CONF, PCS_CONF_MASK | PCS_CONF_ENABLE); 545362306a36Sopenharmony_ci nw64_pcs(PCS_DPATH_MODE, 0); 545462306a36Sopenharmony_ci break; 545562306a36Sopenharmony_ci 545662306a36Sopenharmony_ci case 0: 545762306a36Sopenharmony_ci /* 1G copper */ 545862306a36Sopenharmony_ci case NIU_FLAGS_XCVR_SERDES | NIU_FLAGS_FIBER: 545962306a36Sopenharmony_ci /* 1G RGMII FIBER */ 546062306a36Sopenharmony_ci nw64_pcs(PCS_DPATH_MODE, PCS_DPATH_MODE_MII); 546162306a36Sopenharmony_ci niu_pcs_mii_reset(np); 546262306a36Sopenharmony_ci break; 546362306a36Sopenharmony_ci 546462306a36Sopenharmony_ci default: 546562306a36Sopenharmony_ci return -EINVAL; 546662306a36Sopenharmony_ci } 546762306a36Sopenharmony_ci 546862306a36Sopenharmony_ci return 0; 546962306a36Sopenharmony_ci} 547062306a36Sopenharmony_ci 547162306a36Sopenharmony_cistatic int niu_reset_tx_xmac(struct niu *np) 547262306a36Sopenharmony_ci{ 547362306a36Sopenharmony_ci return niu_set_and_wait_clear_mac(np, XTXMAC_SW_RST, 547462306a36Sopenharmony_ci (XTXMAC_SW_RST_REG_RS | 547562306a36Sopenharmony_ci XTXMAC_SW_RST_SOFT_RST), 547662306a36Sopenharmony_ci 1000, 100, "XTXMAC_SW_RST"); 547762306a36Sopenharmony_ci} 547862306a36Sopenharmony_ci 547962306a36Sopenharmony_cistatic int niu_reset_tx_bmac(struct niu *np) 548062306a36Sopenharmony_ci{ 548162306a36Sopenharmony_ci int limit; 548262306a36Sopenharmony_ci 548362306a36Sopenharmony_ci nw64_mac(BTXMAC_SW_RST, BTXMAC_SW_RST_RESET); 548462306a36Sopenharmony_ci limit = 1000; 548562306a36Sopenharmony_ci while (--limit >= 0) { 548662306a36Sopenharmony_ci if (!(nr64_mac(BTXMAC_SW_RST) & BTXMAC_SW_RST_RESET)) 548762306a36Sopenharmony_ci break; 548862306a36Sopenharmony_ci udelay(100); 548962306a36Sopenharmony_ci } 549062306a36Sopenharmony_ci if (limit < 0) { 549162306a36Sopenharmony_ci dev_err(np->device, "Port %u TX BMAC would not reset, BTXMAC_SW_RST[%llx]\n", 549262306a36Sopenharmony_ci np->port, 549362306a36Sopenharmony_ci (unsigned long long) nr64_mac(BTXMAC_SW_RST)); 549462306a36Sopenharmony_ci return -ENODEV; 549562306a36Sopenharmony_ci } 549662306a36Sopenharmony_ci 549762306a36Sopenharmony_ci return 0; 549862306a36Sopenharmony_ci} 549962306a36Sopenharmony_ci 550062306a36Sopenharmony_cistatic int niu_reset_tx_mac(struct niu *np) 550162306a36Sopenharmony_ci{ 550262306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 550362306a36Sopenharmony_ci return niu_reset_tx_xmac(np); 550462306a36Sopenharmony_ci else 550562306a36Sopenharmony_ci return niu_reset_tx_bmac(np); 550662306a36Sopenharmony_ci} 550762306a36Sopenharmony_ci 550862306a36Sopenharmony_cistatic void niu_init_tx_xmac(struct niu *np, u64 min, u64 max) 550962306a36Sopenharmony_ci{ 551062306a36Sopenharmony_ci u64 val; 551162306a36Sopenharmony_ci 551262306a36Sopenharmony_ci val = nr64_mac(XMAC_MIN); 551362306a36Sopenharmony_ci val &= ~(XMAC_MIN_TX_MIN_PKT_SIZE | 551462306a36Sopenharmony_ci XMAC_MIN_RX_MIN_PKT_SIZE); 551562306a36Sopenharmony_ci val |= (min << XMAC_MIN_RX_MIN_PKT_SIZE_SHFT); 551662306a36Sopenharmony_ci val |= (min << XMAC_MIN_TX_MIN_PKT_SIZE_SHFT); 551762306a36Sopenharmony_ci nw64_mac(XMAC_MIN, val); 551862306a36Sopenharmony_ci 551962306a36Sopenharmony_ci nw64_mac(XMAC_MAX, max); 552062306a36Sopenharmony_ci 552162306a36Sopenharmony_ci nw64_mac(XTXMAC_STAT_MSK, ~(u64)0); 552262306a36Sopenharmony_ci 552362306a36Sopenharmony_ci val = nr64_mac(XMAC_IPG); 552462306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_10G) { 552562306a36Sopenharmony_ci val &= ~XMAC_IPG_IPG_XGMII; 552662306a36Sopenharmony_ci val |= (IPG_12_15_XGMII << XMAC_IPG_IPG_XGMII_SHIFT); 552762306a36Sopenharmony_ci } else { 552862306a36Sopenharmony_ci val &= ~XMAC_IPG_IPG_MII_GMII; 552962306a36Sopenharmony_ci val |= (IPG_12_MII_GMII << XMAC_IPG_IPG_MII_GMII_SHIFT); 553062306a36Sopenharmony_ci } 553162306a36Sopenharmony_ci nw64_mac(XMAC_IPG, val); 553262306a36Sopenharmony_ci 553362306a36Sopenharmony_ci val = nr64_mac(XMAC_CONFIG); 553462306a36Sopenharmony_ci val &= ~(XMAC_CONFIG_ALWAYS_NO_CRC | 553562306a36Sopenharmony_ci XMAC_CONFIG_STRETCH_MODE | 553662306a36Sopenharmony_ci XMAC_CONFIG_VAR_MIN_IPG_EN | 553762306a36Sopenharmony_ci XMAC_CONFIG_TX_ENABLE); 553862306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 553962306a36Sopenharmony_ci 554062306a36Sopenharmony_ci nw64_mac(TXMAC_FRM_CNT, 0); 554162306a36Sopenharmony_ci nw64_mac(TXMAC_BYTE_CNT, 0); 554262306a36Sopenharmony_ci} 554362306a36Sopenharmony_ci 554462306a36Sopenharmony_cistatic void niu_init_tx_bmac(struct niu *np, u64 min, u64 max) 554562306a36Sopenharmony_ci{ 554662306a36Sopenharmony_ci u64 val; 554762306a36Sopenharmony_ci 554862306a36Sopenharmony_ci nw64_mac(BMAC_MIN_FRAME, min); 554962306a36Sopenharmony_ci nw64_mac(BMAC_MAX_FRAME, max); 555062306a36Sopenharmony_ci 555162306a36Sopenharmony_ci nw64_mac(BTXMAC_STATUS_MASK, ~(u64)0); 555262306a36Sopenharmony_ci nw64_mac(BMAC_CTRL_TYPE, 0x8808); 555362306a36Sopenharmony_ci nw64_mac(BMAC_PREAMBLE_SIZE, 7); 555462306a36Sopenharmony_ci 555562306a36Sopenharmony_ci val = nr64_mac(BTXMAC_CONFIG); 555662306a36Sopenharmony_ci val &= ~(BTXMAC_CONFIG_FCS_DISABLE | 555762306a36Sopenharmony_ci BTXMAC_CONFIG_ENABLE); 555862306a36Sopenharmony_ci nw64_mac(BTXMAC_CONFIG, val); 555962306a36Sopenharmony_ci} 556062306a36Sopenharmony_ci 556162306a36Sopenharmony_cistatic void niu_init_tx_mac(struct niu *np) 556262306a36Sopenharmony_ci{ 556362306a36Sopenharmony_ci u64 min, max; 556462306a36Sopenharmony_ci 556562306a36Sopenharmony_ci min = 64; 556662306a36Sopenharmony_ci if (np->dev->mtu > ETH_DATA_LEN) 556762306a36Sopenharmony_ci max = 9216; 556862306a36Sopenharmony_ci else 556962306a36Sopenharmony_ci max = 1522; 557062306a36Sopenharmony_ci 557162306a36Sopenharmony_ci /* The XMAC_MIN register only accepts values for TX min which 557262306a36Sopenharmony_ci * have the low 3 bits cleared. 557362306a36Sopenharmony_ci */ 557462306a36Sopenharmony_ci BUG_ON(min & 0x7); 557562306a36Sopenharmony_ci 557662306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 557762306a36Sopenharmony_ci niu_init_tx_xmac(np, min, max); 557862306a36Sopenharmony_ci else 557962306a36Sopenharmony_ci niu_init_tx_bmac(np, min, max); 558062306a36Sopenharmony_ci} 558162306a36Sopenharmony_ci 558262306a36Sopenharmony_cistatic int niu_reset_rx_xmac(struct niu *np) 558362306a36Sopenharmony_ci{ 558462306a36Sopenharmony_ci int limit; 558562306a36Sopenharmony_ci 558662306a36Sopenharmony_ci nw64_mac(XRXMAC_SW_RST, 558762306a36Sopenharmony_ci XRXMAC_SW_RST_REG_RS | XRXMAC_SW_RST_SOFT_RST); 558862306a36Sopenharmony_ci limit = 1000; 558962306a36Sopenharmony_ci while (--limit >= 0) { 559062306a36Sopenharmony_ci if (!(nr64_mac(XRXMAC_SW_RST) & (XRXMAC_SW_RST_REG_RS | 559162306a36Sopenharmony_ci XRXMAC_SW_RST_SOFT_RST))) 559262306a36Sopenharmony_ci break; 559362306a36Sopenharmony_ci udelay(100); 559462306a36Sopenharmony_ci } 559562306a36Sopenharmony_ci if (limit < 0) { 559662306a36Sopenharmony_ci dev_err(np->device, "Port %u RX XMAC would not reset, XRXMAC_SW_RST[%llx]\n", 559762306a36Sopenharmony_ci np->port, 559862306a36Sopenharmony_ci (unsigned long long) nr64_mac(XRXMAC_SW_RST)); 559962306a36Sopenharmony_ci return -ENODEV; 560062306a36Sopenharmony_ci } 560162306a36Sopenharmony_ci 560262306a36Sopenharmony_ci return 0; 560362306a36Sopenharmony_ci} 560462306a36Sopenharmony_ci 560562306a36Sopenharmony_cistatic int niu_reset_rx_bmac(struct niu *np) 560662306a36Sopenharmony_ci{ 560762306a36Sopenharmony_ci int limit; 560862306a36Sopenharmony_ci 560962306a36Sopenharmony_ci nw64_mac(BRXMAC_SW_RST, BRXMAC_SW_RST_RESET); 561062306a36Sopenharmony_ci limit = 1000; 561162306a36Sopenharmony_ci while (--limit >= 0) { 561262306a36Sopenharmony_ci if (!(nr64_mac(BRXMAC_SW_RST) & BRXMAC_SW_RST_RESET)) 561362306a36Sopenharmony_ci break; 561462306a36Sopenharmony_ci udelay(100); 561562306a36Sopenharmony_ci } 561662306a36Sopenharmony_ci if (limit < 0) { 561762306a36Sopenharmony_ci dev_err(np->device, "Port %u RX BMAC would not reset, BRXMAC_SW_RST[%llx]\n", 561862306a36Sopenharmony_ci np->port, 561962306a36Sopenharmony_ci (unsigned long long) nr64_mac(BRXMAC_SW_RST)); 562062306a36Sopenharmony_ci return -ENODEV; 562162306a36Sopenharmony_ci } 562262306a36Sopenharmony_ci 562362306a36Sopenharmony_ci return 0; 562462306a36Sopenharmony_ci} 562562306a36Sopenharmony_ci 562662306a36Sopenharmony_cistatic int niu_reset_rx_mac(struct niu *np) 562762306a36Sopenharmony_ci{ 562862306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 562962306a36Sopenharmony_ci return niu_reset_rx_xmac(np); 563062306a36Sopenharmony_ci else 563162306a36Sopenharmony_ci return niu_reset_rx_bmac(np); 563262306a36Sopenharmony_ci} 563362306a36Sopenharmony_ci 563462306a36Sopenharmony_cistatic void niu_init_rx_xmac(struct niu *np) 563562306a36Sopenharmony_ci{ 563662306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 563762306a36Sopenharmony_ci struct niu_rdc_tables *tp = &parent->rdc_group_cfg[np->port]; 563862306a36Sopenharmony_ci int first_rdc_table = tp->first_table_num; 563962306a36Sopenharmony_ci unsigned long i; 564062306a36Sopenharmony_ci u64 val; 564162306a36Sopenharmony_ci 564262306a36Sopenharmony_ci nw64_mac(XMAC_ADD_FILT0, 0); 564362306a36Sopenharmony_ci nw64_mac(XMAC_ADD_FILT1, 0); 564462306a36Sopenharmony_ci nw64_mac(XMAC_ADD_FILT2, 0); 564562306a36Sopenharmony_ci nw64_mac(XMAC_ADD_FILT12_MASK, 0); 564662306a36Sopenharmony_ci nw64_mac(XMAC_ADD_FILT00_MASK, 0); 564762306a36Sopenharmony_ci for (i = 0; i < MAC_NUM_HASH; i++) 564862306a36Sopenharmony_ci nw64_mac(XMAC_HASH_TBL(i), 0); 564962306a36Sopenharmony_ci nw64_mac(XRXMAC_STAT_MSK, ~(u64)0); 565062306a36Sopenharmony_ci niu_set_primary_mac_rdc_table(np, first_rdc_table, 1); 565162306a36Sopenharmony_ci niu_set_multicast_mac_rdc_table(np, first_rdc_table, 1); 565262306a36Sopenharmony_ci 565362306a36Sopenharmony_ci val = nr64_mac(XMAC_CONFIG); 565462306a36Sopenharmony_ci val &= ~(XMAC_CONFIG_RX_MAC_ENABLE | 565562306a36Sopenharmony_ci XMAC_CONFIG_PROMISCUOUS | 565662306a36Sopenharmony_ci XMAC_CONFIG_PROMISC_GROUP | 565762306a36Sopenharmony_ci XMAC_CONFIG_ERR_CHK_DIS | 565862306a36Sopenharmony_ci XMAC_CONFIG_RX_CRC_CHK_DIS | 565962306a36Sopenharmony_ci XMAC_CONFIG_RESERVED_MULTICAST | 566062306a36Sopenharmony_ci XMAC_CONFIG_RX_CODEV_CHK_DIS | 566162306a36Sopenharmony_ci XMAC_CONFIG_ADDR_FILTER_EN | 566262306a36Sopenharmony_ci XMAC_CONFIG_RCV_PAUSE_ENABLE | 566362306a36Sopenharmony_ci XMAC_CONFIG_STRIP_CRC | 566462306a36Sopenharmony_ci XMAC_CONFIG_PASS_FLOW_CTRL | 566562306a36Sopenharmony_ci XMAC_CONFIG_MAC2IPP_PKT_CNT_EN); 566662306a36Sopenharmony_ci val |= (XMAC_CONFIG_HASH_FILTER_EN); 566762306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 566862306a36Sopenharmony_ci 566962306a36Sopenharmony_ci nw64_mac(RXMAC_BT_CNT, 0); 567062306a36Sopenharmony_ci nw64_mac(RXMAC_BC_FRM_CNT, 0); 567162306a36Sopenharmony_ci nw64_mac(RXMAC_MC_FRM_CNT, 0); 567262306a36Sopenharmony_ci nw64_mac(RXMAC_FRAG_CNT, 0); 567362306a36Sopenharmony_ci nw64_mac(RXMAC_HIST_CNT1, 0); 567462306a36Sopenharmony_ci nw64_mac(RXMAC_HIST_CNT2, 0); 567562306a36Sopenharmony_ci nw64_mac(RXMAC_HIST_CNT3, 0); 567662306a36Sopenharmony_ci nw64_mac(RXMAC_HIST_CNT4, 0); 567762306a36Sopenharmony_ci nw64_mac(RXMAC_HIST_CNT5, 0); 567862306a36Sopenharmony_ci nw64_mac(RXMAC_HIST_CNT6, 0); 567962306a36Sopenharmony_ci nw64_mac(RXMAC_HIST_CNT7, 0); 568062306a36Sopenharmony_ci nw64_mac(RXMAC_MPSZER_CNT, 0); 568162306a36Sopenharmony_ci nw64_mac(RXMAC_CRC_ER_CNT, 0); 568262306a36Sopenharmony_ci nw64_mac(RXMAC_CD_VIO_CNT, 0); 568362306a36Sopenharmony_ci nw64_mac(LINK_FAULT_CNT, 0); 568462306a36Sopenharmony_ci} 568562306a36Sopenharmony_ci 568662306a36Sopenharmony_cistatic void niu_init_rx_bmac(struct niu *np) 568762306a36Sopenharmony_ci{ 568862306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 568962306a36Sopenharmony_ci struct niu_rdc_tables *tp = &parent->rdc_group_cfg[np->port]; 569062306a36Sopenharmony_ci int first_rdc_table = tp->first_table_num; 569162306a36Sopenharmony_ci unsigned long i; 569262306a36Sopenharmony_ci u64 val; 569362306a36Sopenharmony_ci 569462306a36Sopenharmony_ci nw64_mac(BMAC_ADD_FILT0, 0); 569562306a36Sopenharmony_ci nw64_mac(BMAC_ADD_FILT1, 0); 569662306a36Sopenharmony_ci nw64_mac(BMAC_ADD_FILT2, 0); 569762306a36Sopenharmony_ci nw64_mac(BMAC_ADD_FILT12_MASK, 0); 569862306a36Sopenharmony_ci nw64_mac(BMAC_ADD_FILT00_MASK, 0); 569962306a36Sopenharmony_ci for (i = 0; i < MAC_NUM_HASH; i++) 570062306a36Sopenharmony_ci nw64_mac(BMAC_HASH_TBL(i), 0); 570162306a36Sopenharmony_ci niu_set_primary_mac_rdc_table(np, first_rdc_table, 1); 570262306a36Sopenharmony_ci niu_set_multicast_mac_rdc_table(np, first_rdc_table, 1); 570362306a36Sopenharmony_ci nw64_mac(BRXMAC_STATUS_MASK, ~(u64)0); 570462306a36Sopenharmony_ci 570562306a36Sopenharmony_ci val = nr64_mac(BRXMAC_CONFIG); 570662306a36Sopenharmony_ci val &= ~(BRXMAC_CONFIG_ENABLE | 570762306a36Sopenharmony_ci BRXMAC_CONFIG_STRIP_PAD | 570862306a36Sopenharmony_ci BRXMAC_CONFIG_STRIP_FCS | 570962306a36Sopenharmony_ci BRXMAC_CONFIG_PROMISC | 571062306a36Sopenharmony_ci BRXMAC_CONFIG_PROMISC_GRP | 571162306a36Sopenharmony_ci BRXMAC_CONFIG_ADDR_FILT_EN | 571262306a36Sopenharmony_ci BRXMAC_CONFIG_DISCARD_DIS); 571362306a36Sopenharmony_ci val |= (BRXMAC_CONFIG_HASH_FILT_EN); 571462306a36Sopenharmony_ci nw64_mac(BRXMAC_CONFIG, val); 571562306a36Sopenharmony_ci 571662306a36Sopenharmony_ci val = nr64_mac(BMAC_ADDR_CMPEN); 571762306a36Sopenharmony_ci val |= BMAC_ADDR_CMPEN_EN0; 571862306a36Sopenharmony_ci nw64_mac(BMAC_ADDR_CMPEN, val); 571962306a36Sopenharmony_ci} 572062306a36Sopenharmony_ci 572162306a36Sopenharmony_cistatic void niu_init_rx_mac(struct niu *np) 572262306a36Sopenharmony_ci{ 572362306a36Sopenharmony_ci niu_set_primary_mac(np, np->dev->dev_addr); 572462306a36Sopenharmony_ci 572562306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 572662306a36Sopenharmony_ci niu_init_rx_xmac(np); 572762306a36Sopenharmony_ci else 572862306a36Sopenharmony_ci niu_init_rx_bmac(np); 572962306a36Sopenharmony_ci} 573062306a36Sopenharmony_ci 573162306a36Sopenharmony_cistatic void niu_enable_tx_xmac(struct niu *np, int on) 573262306a36Sopenharmony_ci{ 573362306a36Sopenharmony_ci u64 val = nr64_mac(XMAC_CONFIG); 573462306a36Sopenharmony_ci 573562306a36Sopenharmony_ci if (on) 573662306a36Sopenharmony_ci val |= XMAC_CONFIG_TX_ENABLE; 573762306a36Sopenharmony_ci else 573862306a36Sopenharmony_ci val &= ~XMAC_CONFIG_TX_ENABLE; 573962306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 574062306a36Sopenharmony_ci} 574162306a36Sopenharmony_ci 574262306a36Sopenharmony_cistatic void niu_enable_tx_bmac(struct niu *np, int on) 574362306a36Sopenharmony_ci{ 574462306a36Sopenharmony_ci u64 val = nr64_mac(BTXMAC_CONFIG); 574562306a36Sopenharmony_ci 574662306a36Sopenharmony_ci if (on) 574762306a36Sopenharmony_ci val |= BTXMAC_CONFIG_ENABLE; 574862306a36Sopenharmony_ci else 574962306a36Sopenharmony_ci val &= ~BTXMAC_CONFIG_ENABLE; 575062306a36Sopenharmony_ci nw64_mac(BTXMAC_CONFIG, val); 575162306a36Sopenharmony_ci} 575262306a36Sopenharmony_ci 575362306a36Sopenharmony_cistatic void niu_enable_tx_mac(struct niu *np, int on) 575462306a36Sopenharmony_ci{ 575562306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 575662306a36Sopenharmony_ci niu_enable_tx_xmac(np, on); 575762306a36Sopenharmony_ci else 575862306a36Sopenharmony_ci niu_enable_tx_bmac(np, on); 575962306a36Sopenharmony_ci} 576062306a36Sopenharmony_ci 576162306a36Sopenharmony_cistatic void niu_enable_rx_xmac(struct niu *np, int on) 576262306a36Sopenharmony_ci{ 576362306a36Sopenharmony_ci u64 val = nr64_mac(XMAC_CONFIG); 576462306a36Sopenharmony_ci 576562306a36Sopenharmony_ci val &= ~(XMAC_CONFIG_HASH_FILTER_EN | 576662306a36Sopenharmony_ci XMAC_CONFIG_PROMISCUOUS); 576762306a36Sopenharmony_ci 576862306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_MCAST) 576962306a36Sopenharmony_ci val |= XMAC_CONFIG_HASH_FILTER_EN; 577062306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_PROMISC) 577162306a36Sopenharmony_ci val |= XMAC_CONFIG_PROMISCUOUS; 577262306a36Sopenharmony_ci 577362306a36Sopenharmony_ci if (on) 577462306a36Sopenharmony_ci val |= XMAC_CONFIG_RX_MAC_ENABLE; 577562306a36Sopenharmony_ci else 577662306a36Sopenharmony_ci val &= ~XMAC_CONFIG_RX_MAC_ENABLE; 577762306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 577862306a36Sopenharmony_ci} 577962306a36Sopenharmony_ci 578062306a36Sopenharmony_cistatic void niu_enable_rx_bmac(struct niu *np, int on) 578162306a36Sopenharmony_ci{ 578262306a36Sopenharmony_ci u64 val = nr64_mac(BRXMAC_CONFIG); 578362306a36Sopenharmony_ci 578462306a36Sopenharmony_ci val &= ~(BRXMAC_CONFIG_HASH_FILT_EN | 578562306a36Sopenharmony_ci BRXMAC_CONFIG_PROMISC); 578662306a36Sopenharmony_ci 578762306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_MCAST) 578862306a36Sopenharmony_ci val |= BRXMAC_CONFIG_HASH_FILT_EN; 578962306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_PROMISC) 579062306a36Sopenharmony_ci val |= BRXMAC_CONFIG_PROMISC; 579162306a36Sopenharmony_ci 579262306a36Sopenharmony_ci if (on) 579362306a36Sopenharmony_ci val |= BRXMAC_CONFIG_ENABLE; 579462306a36Sopenharmony_ci else 579562306a36Sopenharmony_ci val &= ~BRXMAC_CONFIG_ENABLE; 579662306a36Sopenharmony_ci nw64_mac(BRXMAC_CONFIG, val); 579762306a36Sopenharmony_ci} 579862306a36Sopenharmony_ci 579962306a36Sopenharmony_cistatic void niu_enable_rx_mac(struct niu *np, int on) 580062306a36Sopenharmony_ci{ 580162306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 580262306a36Sopenharmony_ci niu_enable_rx_xmac(np, on); 580362306a36Sopenharmony_ci else 580462306a36Sopenharmony_ci niu_enable_rx_bmac(np, on); 580562306a36Sopenharmony_ci} 580662306a36Sopenharmony_ci 580762306a36Sopenharmony_cistatic int niu_init_mac(struct niu *np) 580862306a36Sopenharmony_ci{ 580962306a36Sopenharmony_ci int err; 581062306a36Sopenharmony_ci 581162306a36Sopenharmony_ci niu_init_xif(np); 581262306a36Sopenharmony_ci err = niu_init_pcs(np); 581362306a36Sopenharmony_ci if (err) 581462306a36Sopenharmony_ci return err; 581562306a36Sopenharmony_ci 581662306a36Sopenharmony_ci err = niu_reset_tx_mac(np); 581762306a36Sopenharmony_ci if (err) 581862306a36Sopenharmony_ci return err; 581962306a36Sopenharmony_ci niu_init_tx_mac(np); 582062306a36Sopenharmony_ci err = niu_reset_rx_mac(np); 582162306a36Sopenharmony_ci if (err) 582262306a36Sopenharmony_ci return err; 582362306a36Sopenharmony_ci niu_init_rx_mac(np); 582462306a36Sopenharmony_ci 582562306a36Sopenharmony_ci /* This looks hookey but the RX MAC reset we just did will 582662306a36Sopenharmony_ci * undo some of the state we setup in niu_init_tx_mac() so we 582762306a36Sopenharmony_ci * have to call it again. In particular, the RX MAC reset will 582862306a36Sopenharmony_ci * set the XMAC_MAX register back to it's default value. 582962306a36Sopenharmony_ci */ 583062306a36Sopenharmony_ci niu_init_tx_mac(np); 583162306a36Sopenharmony_ci niu_enable_tx_mac(np, 1); 583262306a36Sopenharmony_ci 583362306a36Sopenharmony_ci niu_enable_rx_mac(np, 1); 583462306a36Sopenharmony_ci 583562306a36Sopenharmony_ci return 0; 583662306a36Sopenharmony_ci} 583762306a36Sopenharmony_ci 583862306a36Sopenharmony_cistatic void niu_stop_one_tx_channel(struct niu *np, struct tx_ring_info *rp) 583962306a36Sopenharmony_ci{ 584062306a36Sopenharmony_ci (void) niu_tx_channel_stop(np, rp->tx_channel); 584162306a36Sopenharmony_ci} 584262306a36Sopenharmony_ci 584362306a36Sopenharmony_cistatic void niu_stop_tx_channels(struct niu *np) 584462306a36Sopenharmony_ci{ 584562306a36Sopenharmony_ci int i; 584662306a36Sopenharmony_ci 584762306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 584862306a36Sopenharmony_ci struct tx_ring_info *rp = &np->tx_rings[i]; 584962306a36Sopenharmony_ci 585062306a36Sopenharmony_ci niu_stop_one_tx_channel(np, rp); 585162306a36Sopenharmony_ci } 585262306a36Sopenharmony_ci} 585362306a36Sopenharmony_ci 585462306a36Sopenharmony_cistatic void niu_reset_one_tx_channel(struct niu *np, struct tx_ring_info *rp) 585562306a36Sopenharmony_ci{ 585662306a36Sopenharmony_ci (void) niu_tx_channel_reset(np, rp->tx_channel); 585762306a36Sopenharmony_ci} 585862306a36Sopenharmony_ci 585962306a36Sopenharmony_cistatic void niu_reset_tx_channels(struct niu *np) 586062306a36Sopenharmony_ci{ 586162306a36Sopenharmony_ci int i; 586262306a36Sopenharmony_ci 586362306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 586462306a36Sopenharmony_ci struct tx_ring_info *rp = &np->tx_rings[i]; 586562306a36Sopenharmony_ci 586662306a36Sopenharmony_ci niu_reset_one_tx_channel(np, rp); 586762306a36Sopenharmony_ci } 586862306a36Sopenharmony_ci} 586962306a36Sopenharmony_ci 587062306a36Sopenharmony_cistatic void niu_stop_one_rx_channel(struct niu *np, struct rx_ring_info *rp) 587162306a36Sopenharmony_ci{ 587262306a36Sopenharmony_ci (void) niu_enable_rx_channel(np, rp->rx_channel, 0); 587362306a36Sopenharmony_ci} 587462306a36Sopenharmony_ci 587562306a36Sopenharmony_cistatic void niu_stop_rx_channels(struct niu *np) 587662306a36Sopenharmony_ci{ 587762306a36Sopenharmony_ci int i; 587862306a36Sopenharmony_ci 587962306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 588062306a36Sopenharmony_ci struct rx_ring_info *rp = &np->rx_rings[i]; 588162306a36Sopenharmony_ci 588262306a36Sopenharmony_ci niu_stop_one_rx_channel(np, rp); 588362306a36Sopenharmony_ci } 588462306a36Sopenharmony_ci} 588562306a36Sopenharmony_ci 588662306a36Sopenharmony_cistatic void niu_reset_one_rx_channel(struct niu *np, struct rx_ring_info *rp) 588762306a36Sopenharmony_ci{ 588862306a36Sopenharmony_ci int channel = rp->rx_channel; 588962306a36Sopenharmony_ci 589062306a36Sopenharmony_ci (void) niu_rx_channel_reset(np, channel); 589162306a36Sopenharmony_ci nw64(RX_DMA_ENT_MSK(channel), RX_DMA_ENT_MSK_ALL); 589262306a36Sopenharmony_ci nw64(RX_DMA_CTL_STAT(channel), 0); 589362306a36Sopenharmony_ci (void) niu_enable_rx_channel(np, channel, 0); 589462306a36Sopenharmony_ci} 589562306a36Sopenharmony_ci 589662306a36Sopenharmony_cistatic void niu_reset_rx_channels(struct niu *np) 589762306a36Sopenharmony_ci{ 589862306a36Sopenharmony_ci int i; 589962306a36Sopenharmony_ci 590062306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 590162306a36Sopenharmony_ci struct rx_ring_info *rp = &np->rx_rings[i]; 590262306a36Sopenharmony_ci 590362306a36Sopenharmony_ci niu_reset_one_rx_channel(np, rp); 590462306a36Sopenharmony_ci } 590562306a36Sopenharmony_ci} 590662306a36Sopenharmony_ci 590762306a36Sopenharmony_cistatic void niu_disable_ipp(struct niu *np) 590862306a36Sopenharmony_ci{ 590962306a36Sopenharmony_ci u64 rd, wr, val; 591062306a36Sopenharmony_ci int limit; 591162306a36Sopenharmony_ci 591262306a36Sopenharmony_ci rd = nr64_ipp(IPP_DFIFO_RD_PTR); 591362306a36Sopenharmony_ci wr = nr64_ipp(IPP_DFIFO_WR_PTR); 591462306a36Sopenharmony_ci limit = 100; 591562306a36Sopenharmony_ci while (--limit >= 0 && (rd != wr)) { 591662306a36Sopenharmony_ci rd = nr64_ipp(IPP_DFIFO_RD_PTR); 591762306a36Sopenharmony_ci wr = nr64_ipp(IPP_DFIFO_WR_PTR); 591862306a36Sopenharmony_ci } 591962306a36Sopenharmony_ci if (limit < 0 && 592062306a36Sopenharmony_ci (rd != 0 && wr != 1)) { 592162306a36Sopenharmony_ci netdev_err(np->dev, "IPP would not quiesce, rd_ptr[%llx] wr_ptr[%llx]\n", 592262306a36Sopenharmony_ci (unsigned long long)nr64_ipp(IPP_DFIFO_RD_PTR), 592362306a36Sopenharmony_ci (unsigned long long)nr64_ipp(IPP_DFIFO_WR_PTR)); 592462306a36Sopenharmony_ci } 592562306a36Sopenharmony_ci 592662306a36Sopenharmony_ci val = nr64_ipp(IPP_CFIG); 592762306a36Sopenharmony_ci val &= ~(IPP_CFIG_IPP_ENABLE | 592862306a36Sopenharmony_ci IPP_CFIG_DFIFO_ECC_EN | 592962306a36Sopenharmony_ci IPP_CFIG_DROP_BAD_CRC | 593062306a36Sopenharmony_ci IPP_CFIG_CKSUM_EN); 593162306a36Sopenharmony_ci nw64_ipp(IPP_CFIG, val); 593262306a36Sopenharmony_ci 593362306a36Sopenharmony_ci (void) niu_ipp_reset(np); 593462306a36Sopenharmony_ci} 593562306a36Sopenharmony_ci 593662306a36Sopenharmony_cistatic int niu_init_hw(struct niu *np) 593762306a36Sopenharmony_ci{ 593862306a36Sopenharmony_ci int i, err; 593962306a36Sopenharmony_ci 594062306a36Sopenharmony_ci netif_printk(np, ifup, KERN_DEBUG, np->dev, "Initialize TXC\n"); 594162306a36Sopenharmony_ci niu_txc_enable_port(np, 1); 594262306a36Sopenharmony_ci niu_txc_port_dma_enable(np, 1); 594362306a36Sopenharmony_ci niu_txc_set_imask(np, 0); 594462306a36Sopenharmony_ci 594562306a36Sopenharmony_ci netif_printk(np, ifup, KERN_DEBUG, np->dev, "Initialize TX channels\n"); 594662306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 594762306a36Sopenharmony_ci struct tx_ring_info *rp = &np->tx_rings[i]; 594862306a36Sopenharmony_ci 594962306a36Sopenharmony_ci err = niu_init_one_tx_channel(np, rp); 595062306a36Sopenharmony_ci if (err) 595162306a36Sopenharmony_ci return err; 595262306a36Sopenharmony_ci } 595362306a36Sopenharmony_ci 595462306a36Sopenharmony_ci netif_printk(np, ifup, KERN_DEBUG, np->dev, "Initialize RX channels\n"); 595562306a36Sopenharmony_ci err = niu_init_rx_channels(np); 595662306a36Sopenharmony_ci if (err) 595762306a36Sopenharmony_ci goto out_uninit_tx_channels; 595862306a36Sopenharmony_ci 595962306a36Sopenharmony_ci netif_printk(np, ifup, KERN_DEBUG, np->dev, "Initialize classifier\n"); 596062306a36Sopenharmony_ci err = niu_init_classifier_hw(np); 596162306a36Sopenharmony_ci if (err) 596262306a36Sopenharmony_ci goto out_uninit_rx_channels; 596362306a36Sopenharmony_ci 596462306a36Sopenharmony_ci netif_printk(np, ifup, KERN_DEBUG, np->dev, "Initialize ZCP\n"); 596562306a36Sopenharmony_ci err = niu_init_zcp(np); 596662306a36Sopenharmony_ci if (err) 596762306a36Sopenharmony_ci goto out_uninit_rx_channels; 596862306a36Sopenharmony_ci 596962306a36Sopenharmony_ci netif_printk(np, ifup, KERN_DEBUG, np->dev, "Initialize IPP\n"); 597062306a36Sopenharmony_ci err = niu_init_ipp(np); 597162306a36Sopenharmony_ci if (err) 597262306a36Sopenharmony_ci goto out_uninit_rx_channels; 597362306a36Sopenharmony_ci 597462306a36Sopenharmony_ci netif_printk(np, ifup, KERN_DEBUG, np->dev, "Initialize MAC\n"); 597562306a36Sopenharmony_ci err = niu_init_mac(np); 597662306a36Sopenharmony_ci if (err) 597762306a36Sopenharmony_ci goto out_uninit_ipp; 597862306a36Sopenharmony_ci 597962306a36Sopenharmony_ci return 0; 598062306a36Sopenharmony_ci 598162306a36Sopenharmony_ciout_uninit_ipp: 598262306a36Sopenharmony_ci netif_printk(np, ifup, KERN_DEBUG, np->dev, "Uninit IPP\n"); 598362306a36Sopenharmony_ci niu_disable_ipp(np); 598462306a36Sopenharmony_ci 598562306a36Sopenharmony_ciout_uninit_rx_channels: 598662306a36Sopenharmony_ci netif_printk(np, ifup, KERN_DEBUG, np->dev, "Uninit RX channels\n"); 598762306a36Sopenharmony_ci niu_stop_rx_channels(np); 598862306a36Sopenharmony_ci niu_reset_rx_channels(np); 598962306a36Sopenharmony_ci 599062306a36Sopenharmony_ciout_uninit_tx_channels: 599162306a36Sopenharmony_ci netif_printk(np, ifup, KERN_DEBUG, np->dev, "Uninit TX channels\n"); 599262306a36Sopenharmony_ci niu_stop_tx_channels(np); 599362306a36Sopenharmony_ci niu_reset_tx_channels(np); 599462306a36Sopenharmony_ci 599562306a36Sopenharmony_ci return err; 599662306a36Sopenharmony_ci} 599762306a36Sopenharmony_ci 599862306a36Sopenharmony_cistatic void niu_stop_hw(struct niu *np) 599962306a36Sopenharmony_ci{ 600062306a36Sopenharmony_ci netif_printk(np, ifdown, KERN_DEBUG, np->dev, "Disable interrupts\n"); 600162306a36Sopenharmony_ci niu_enable_interrupts(np, 0); 600262306a36Sopenharmony_ci 600362306a36Sopenharmony_ci netif_printk(np, ifdown, KERN_DEBUG, np->dev, "Disable RX MAC\n"); 600462306a36Sopenharmony_ci niu_enable_rx_mac(np, 0); 600562306a36Sopenharmony_ci 600662306a36Sopenharmony_ci netif_printk(np, ifdown, KERN_DEBUG, np->dev, "Disable IPP\n"); 600762306a36Sopenharmony_ci niu_disable_ipp(np); 600862306a36Sopenharmony_ci 600962306a36Sopenharmony_ci netif_printk(np, ifdown, KERN_DEBUG, np->dev, "Stop TX channels\n"); 601062306a36Sopenharmony_ci niu_stop_tx_channels(np); 601162306a36Sopenharmony_ci 601262306a36Sopenharmony_ci netif_printk(np, ifdown, KERN_DEBUG, np->dev, "Stop RX channels\n"); 601362306a36Sopenharmony_ci niu_stop_rx_channels(np); 601462306a36Sopenharmony_ci 601562306a36Sopenharmony_ci netif_printk(np, ifdown, KERN_DEBUG, np->dev, "Reset TX channels\n"); 601662306a36Sopenharmony_ci niu_reset_tx_channels(np); 601762306a36Sopenharmony_ci 601862306a36Sopenharmony_ci netif_printk(np, ifdown, KERN_DEBUG, np->dev, "Reset RX channels\n"); 601962306a36Sopenharmony_ci niu_reset_rx_channels(np); 602062306a36Sopenharmony_ci} 602162306a36Sopenharmony_ci 602262306a36Sopenharmony_cistatic void niu_set_irq_name(struct niu *np) 602362306a36Sopenharmony_ci{ 602462306a36Sopenharmony_ci int port = np->port; 602562306a36Sopenharmony_ci int i, j = 1; 602662306a36Sopenharmony_ci 602762306a36Sopenharmony_ci sprintf(np->irq_name[0], "%s:MAC", np->dev->name); 602862306a36Sopenharmony_ci 602962306a36Sopenharmony_ci if (port == 0) { 603062306a36Sopenharmony_ci sprintf(np->irq_name[1], "%s:MIF", np->dev->name); 603162306a36Sopenharmony_ci sprintf(np->irq_name[2], "%s:SYSERR", np->dev->name); 603262306a36Sopenharmony_ci j = 3; 603362306a36Sopenharmony_ci } 603462306a36Sopenharmony_ci 603562306a36Sopenharmony_ci for (i = 0; i < np->num_ldg - j; i++) { 603662306a36Sopenharmony_ci if (i < np->num_rx_rings) 603762306a36Sopenharmony_ci sprintf(np->irq_name[i+j], "%s-rx-%d", 603862306a36Sopenharmony_ci np->dev->name, i); 603962306a36Sopenharmony_ci else if (i < np->num_tx_rings + np->num_rx_rings) 604062306a36Sopenharmony_ci sprintf(np->irq_name[i+j], "%s-tx-%d", np->dev->name, 604162306a36Sopenharmony_ci i - np->num_rx_rings); 604262306a36Sopenharmony_ci } 604362306a36Sopenharmony_ci} 604462306a36Sopenharmony_ci 604562306a36Sopenharmony_cistatic int niu_request_irq(struct niu *np) 604662306a36Sopenharmony_ci{ 604762306a36Sopenharmony_ci int i, j, err; 604862306a36Sopenharmony_ci 604962306a36Sopenharmony_ci niu_set_irq_name(np); 605062306a36Sopenharmony_ci 605162306a36Sopenharmony_ci err = 0; 605262306a36Sopenharmony_ci for (i = 0; i < np->num_ldg; i++) { 605362306a36Sopenharmony_ci struct niu_ldg *lp = &np->ldg[i]; 605462306a36Sopenharmony_ci 605562306a36Sopenharmony_ci err = request_irq(lp->irq, niu_interrupt, IRQF_SHARED, 605662306a36Sopenharmony_ci np->irq_name[i], lp); 605762306a36Sopenharmony_ci if (err) 605862306a36Sopenharmony_ci goto out_free_irqs; 605962306a36Sopenharmony_ci 606062306a36Sopenharmony_ci } 606162306a36Sopenharmony_ci 606262306a36Sopenharmony_ci return 0; 606362306a36Sopenharmony_ci 606462306a36Sopenharmony_ciout_free_irqs: 606562306a36Sopenharmony_ci for (j = 0; j < i; j++) { 606662306a36Sopenharmony_ci struct niu_ldg *lp = &np->ldg[j]; 606762306a36Sopenharmony_ci 606862306a36Sopenharmony_ci free_irq(lp->irq, lp); 606962306a36Sopenharmony_ci } 607062306a36Sopenharmony_ci return err; 607162306a36Sopenharmony_ci} 607262306a36Sopenharmony_ci 607362306a36Sopenharmony_cistatic void niu_free_irq(struct niu *np) 607462306a36Sopenharmony_ci{ 607562306a36Sopenharmony_ci int i; 607662306a36Sopenharmony_ci 607762306a36Sopenharmony_ci for (i = 0; i < np->num_ldg; i++) { 607862306a36Sopenharmony_ci struct niu_ldg *lp = &np->ldg[i]; 607962306a36Sopenharmony_ci 608062306a36Sopenharmony_ci free_irq(lp->irq, lp); 608162306a36Sopenharmony_ci } 608262306a36Sopenharmony_ci} 608362306a36Sopenharmony_ci 608462306a36Sopenharmony_cistatic void niu_enable_napi(struct niu *np) 608562306a36Sopenharmony_ci{ 608662306a36Sopenharmony_ci int i; 608762306a36Sopenharmony_ci 608862306a36Sopenharmony_ci for (i = 0; i < np->num_ldg; i++) 608962306a36Sopenharmony_ci napi_enable(&np->ldg[i].napi); 609062306a36Sopenharmony_ci} 609162306a36Sopenharmony_ci 609262306a36Sopenharmony_cistatic void niu_disable_napi(struct niu *np) 609362306a36Sopenharmony_ci{ 609462306a36Sopenharmony_ci int i; 609562306a36Sopenharmony_ci 609662306a36Sopenharmony_ci for (i = 0; i < np->num_ldg; i++) 609762306a36Sopenharmony_ci napi_disable(&np->ldg[i].napi); 609862306a36Sopenharmony_ci} 609962306a36Sopenharmony_ci 610062306a36Sopenharmony_cistatic int niu_open(struct net_device *dev) 610162306a36Sopenharmony_ci{ 610262306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 610362306a36Sopenharmony_ci int err; 610462306a36Sopenharmony_ci 610562306a36Sopenharmony_ci netif_carrier_off(dev); 610662306a36Sopenharmony_ci 610762306a36Sopenharmony_ci err = niu_alloc_channels(np); 610862306a36Sopenharmony_ci if (err) 610962306a36Sopenharmony_ci goto out_err; 611062306a36Sopenharmony_ci 611162306a36Sopenharmony_ci err = niu_enable_interrupts(np, 0); 611262306a36Sopenharmony_ci if (err) 611362306a36Sopenharmony_ci goto out_free_channels; 611462306a36Sopenharmony_ci 611562306a36Sopenharmony_ci err = niu_request_irq(np); 611662306a36Sopenharmony_ci if (err) 611762306a36Sopenharmony_ci goto out_free_channels; 611862306a36Sopenharmony_ci 611962306a36Sopenharmony_ci niu_enable_napi(np); 612062306a36Sopenharmony_ci 612162306a36Sopenharmony_ci spin_lock_irq(&np->lock); 612262306a36Sopenharmony_ci 612362306a36Sopenharmony_ci err = niu_init_hw(np); 612462306a36Sopenharmony_ci if (!err) { 612562306a36Sopenharmony_ci timer_setup(&np->timer, niu_timer, 0); 612662306a36Sopenharmony_ci np->timer.expires = jiffies + HZ; 612762306a36Sopenharmony_ci 612862306a36Sopenharmony_ci err = niu_enable_interrupts(np, 1); 612962306a36Sopenharmony_ci if (err) 613062306a36Sopenharmony_ci niu_stop_hw(np); 613162306a36Sopenharmony_ci } 613262306a36Sopenharmony_ci 613362306a36Sopenharmony_ci spin_unlock_irq(&np->lock); 613462306a36Sopenharmony_ci 613562306a36Sopenharmony_ci if (err) { 613662306a36Sopenharmony_ci niu_disable_napi(np); 613762306a36Sopenharmony_ci goto out_free_irq; 613862306a36Sopenharmony_ci } 613962306a36Sopenharmony_ci 614062306a36Sopenharmony_ci netif_tx_start_all_queues(dev); 614162306a36Sopenharmony_ci 614262306a36Sopenharmony_ci if (np->link_config.loopback_mode != LOOPBACK_DISABLED) 614362306a36Sopenharmony_ci netif_carrier_on(dev); 614462306a36Sopenharmony_ci 614562306a36Sopenharmony_ci add_timer(&np->timer); 614662306a36Sopenharmony_ci 614762306a36Sopenharmony_ci return 0; 614862306a36Sopenharmony_ci 614962306a36Sopenharmony_ciout_free_irq: 615062306a36Sopenharmony_ci niu_free_irq(np); 615162306a36Sopenharmony_ci 615262306a36Sopenharmony_ciout_free_channels: 615362306a36Sopenharmony_ci niu_free_channels(np); 615462306a36Sopenharmony_ci 615562306a36Sopenharmony_ciout_err: 615662306a36Sopenharmony_ci return err; 615762306a36Sopenharmony_ci} 615862306a36Sopenharmony_ci 615962306a36Sopenharmony_cistatic void niu_full_shutdown(struct niu *np, struct net_device *dev) 616062306a36Sopenharmony_ci{ 616162306a36Sopenharmony_ci cancel_work_sync(&np->reset_task); 616262306a36Sopenharmony_ci 616362306a36Sopenharmony_ci niu_disable_napi(np); 616462306a36Sopenharmony_ci netif_tx_stop_all_queues(dev); 616562306a36Sopenharmony_ci 616662306a36Sopenharmony_ci del_timer_sync(&np->timer); 616762306a36Sopenharmony_ci 616862306a36Sopenharmony_ci spin_lock_irq(&np->lock); 616962306a36Sopenharmony_ci 617062306a36Sopenharmony_ci niu_stop_hw(np); 617162306a36Sopenharmony_ci 617262306a36Sopenharmony_ci spin_unlock_irq(&np->lock); 617362306a36Sopenharmony_ci} 617462306a36Sopenharmony_ci 617562306a36Sopenharmony_cistatic int niu_close(struct net_device *dev) 617662306a36Sopenharmony_ci{ 617762306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 617862306a36Sopenharmony_ci 617962306a36Sopenharmony_ci niu_full_shutdown(np, dev); 618062306a36Sopenharmony_ci 618162306a36Sopenharmony_ci niu_free_irq(np); 618262306a36Sopenharmony_ci 618362306a36Sopenharmony_ci niu_free_channels(np); 618462306a36Sopenharmony_ci 618562306a36Sopenharmony_ci niu_handle_led(np, 0); 618662306a36Sopenharmony_ci 618762306a36Sopenharmony_ci return 0; 618862306a36Sopenharmony_ci} 618962306a36Sopenharmony_ci 619062306a36Sopenharmony_cistatic void niu_sync_xmac_stats(struct niu *np) 619162306a36Sopenharmony_ci{ 619262306a36Sopenharmony_ci struct niu_xmac_stats *mp = &np->mac_stats.xmac; 619362306a36Sopenharmony_ci 619462306a36Sopenharmony_ci mp->tx_frames += nr64_mac(TXMAC_FRM_CNT); 619562306a36Sopenharmony_ci mp->tx_bytes += nr64_mac(TXMAC_BYTE_CNT); 619662306a36Sopenharmony_ci 619762306a36Sopenharmony_ci mp->rx_link_faults += nr64_mac(LINK_FAULT_CNT); 619862306a36Sopenharmony_ci mp->rx_align_errors += nr64_mac(RXMAC_ALIGN_ERR_CNT); 619962306a36Sopenharmony_ci mp->rx_frags += nr64_mac(RXMAC_FRAG_CNT); 620062306a36Sopenharmony_ci mp->rx_mcasts += nr64_mac(RXMAC_MC_FRM_CNT); 620162306a36Sopenharmony_ci mp->rx_bcasts += nr64_mac(RXMAC_BC_FRM_CNT); 620262306a36Sopenharmony_ci mp->rx_hist_cnt1 += nr64_mac(RXMAC_HIST_CNT1); 620362306a36Sopenharmony_ci mp->rx_hist_cnt2 += nr64_mac(RXMAC_HIST_CNT2); 620462306a36Sopenharmony_ci mp->rx_hist_cnt3 += nr64_mac(RXMAC_HIST_CNT3); 620562306a36Sopenharmony_ci mp->rx_hist_cnt4 += nr64_mac(RXMAC_HIST_CNT4); 620662306a36Sopenharmony_ci mp->rx_hist_cnt5 += nr64_mac(RXMAC_HIST_CNT5); 620762306a36Sopenharmony_ci mp->rx_hist_cnt6 += nr64_mac(RXMAC_HIST_CNT6); 620862306a36Sopenharmony_ci mp->rx_hist_cnt7 += nr64_mac(RXMAC_HIST_CNT7); 620962306a36Sopenharmony_ci mp->rx_octets += nr64_mac(RXMAC_BT_CNT); 621062306a36Sopenharmony_ci mp->rx_code_violations += nr64_mac(RXMAC_CD_VIO_CNT); 621162306a36Sopenharmony_ci mp->rx_len_errors += nr64_mac(RXMAC_MPSZER_CNT); 621262306a36Sopenharmony_ci mp->rx_crc_errors += nr64_mac(RXMAC_CRC_ER_CNT); 621362306a36Sopenharmony_ci} 621462306a36Sopenharmony_ci 621562306a36Sopenharmony_cistatic void niu_sync_bmac_stats(struct niu *np) 621662306a36Sopenharmony_ci{ 621762306a36Sopenharmony_ci struct niu_bmac_stats *mp = &np->mac_stats.bmac; 621862306a36Sopenharmony_ci 621962306a36Sopenharmony_ci mp->tx_bytes += nr64_mac(BTXMAC_BYTE_CNT); 622062306a36Sopenharmony_ci mp->tx_frames += nr64_mac(BTXMAC_FRM_CNT); 622162306a36Sopenharmony_ci 622262306a36Sopenharmony_ci mp->rx_frames += nr64_mac(BRXMAC_FRAME_CNT); 622362306a36Sopenharmony_ci mp->rx_align_errors += nr64_mac(BRXMAC_ALIGN_ERR_CNT); 622462306a36Sopenharmony_ci mp->rx_crc_errors += nr64_mac(BRXMAC_ALIGN_ERR_CNT); 622562306a36Sopenharmony_ci mp->rx_len_errors += nr64_mac(BRXMAC_CODE_VIOL_ERR_CNT); 622662306a36Sopenharmony_ci} 622762306a36Sopenharmony_ci 622862306a36Sopenharmony_cistatic void niu_sync_mac_stats(struct niu *np) 622962306a36Sopenharmony_ci{ 623062306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 623162306a36Sopenharmony_ci niu_sync_xmac_stats(np); 623262306a36Sopenharmony_ci else 623362306a36Sopenharmony_ci niu_sync_bmac_stats(np); 623462306a36Sopenharmony_ci} 623562306a36Sopenharmony_ci 623662306a36Sopenharmony_cistatic void niu_get_rx_stats(struct niu *np, 623762306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 623862306a36Sopenharmony_ci{ 623962306a36Sopenharmony_ci u64 pkts, dropped, errors, bytes; 624062306a36Sopenharmony_ci struct rx_ring_info *rx_rings; 624162306a36Sopenharmony_ci int i; 624262306a36Sopenharmony_ci 624362306a36Sopenharmony_ci pkts = dropped = errors = bytes = 0; 624462306a36Sopenharmony_ci 624562306a36Sopenharmony_ci rx_rings = READ_ONCE(np->rx_rings); 624662306a36Sopenharmony_ci if (!rx_rings) 624762306a36Sopenharmony_ci goto no_rings; 624862306a36Sopenharmony_ci 624962306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 625062306a36Sopenharmony_ci struct rx_ring_info *rp = &rx_rings[i]; 625162306a36Sopenharmony_ci 625262306a36Sopenharmony_ci niu_sync_rx_discard_stats(np, rp, 0); 625362306a36Sopenharmony_ci 625462306a36Sopenharmony_ci pkts += rp->rx_packets; 625562306a36Sopenharmony_ci bytes += rp->rx_bytes; 625662306a36Sopenharmony_ci dropped += rp->rx_dropped; 625762306a36Sopenharmony_ci errors += rp->rx_errors; 625862306a36Sopenharmony_ci } 625962306a36Sopenharmony_ci 626062306a36Sopenharmony_cino_rings: 626162306a36Sopenharmony_ci stats->rx_packets = pkts; 626262306a36Sopenharmony_ci stats->rx_bytes = bytes; 626362306a36Sopenharmony_ci stats->rx_dropped = dropped; 626462306a36Sopenharmony_ci stats->rx_errors = errors; 626562306a36Sopenharmony_ci} 626662306a36Sopenharmony_ci 626762306a36Sopenharmony_cistatic void niu_get_tx_stats(struct niu *np, 626862306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 626962306a36Sopenharmony_ci{ 627062306a36Sopenharmony_ci u64 pkts, errors, bytes; 627162306a36Sopenharmony_ci struct tx_ring_info *tx_rings; 627262306a36Sopenharmony_ci int i; 627362306a36Sopenharmony_ci 627462306a36Sopenharmony_ci pkts = errors = bytes = 0; 627562306a36Sopenharmony_ci 627662306a36Sopenharmony_ci tx_rings = READ_ONCE(np->tx_rings); 627762306a36Sopenharmony_ci if (!tx_rings) 627862306a36Sopenharmony_ci goto no_rings; 627962306a36Sopenharmony_ci 628062306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 628162306a36Sopenharmony_ci struct tx_ring_info *rp = &tx_rings[i]; 628262306a36Sopenharmony_ci 628362306a36Sopenharmony_ci pkts += rp->tx_packets; 628462306a36Sopenharmony_ci bytes += rp->tx_bytes; 628562306a36Sopenharmony_ci errors += rp->tx_errors; 628662306a36Sopenharmony_ci } 628762306a36Sopenharmony_ci 628862306a36Sopenharmony_cino_rings: 628962306a36Sopenharmony_ci stats->tx_packets = pkts; 629062306a36Sopenharmony_ci stats->tx_bytes = bytes; 629162306a36Sopenharmony_ci stats->tx_errors = errors; 629262306a36Sopenharmony_ci} 629362306a36Sopenharmony_ci 629462306a36Sopenharmony_cistatic void niu_get_stats(struct net_device *dev, 629562306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 629662306a36Sopenharmony_ci{ 629762306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 629862306a36Sopenharmony_ci 629962306a36Sopenharmony_ci if (netif_running(dev)) { 630062306a36Sopenharmony_ci niu_get_rx_stats(np, stats); 630162306a36Sopenharmony_ci niu_get_tx_stats(np, stats); 630262306a36Sopenharmony_ci } 630362306a36Sopenharmony_ci} 630462306a36Sopenharmony_ci 630562306a36Sopenharmony_cistatic void niu_load_hash_xmac(struct niu *np, u16 *hash) 630662306a36Sopenharmony_ci{ 630762306a36Sopenharmony_ci int i; 630862306a36Sopenharmony_ci 630962306a36Sopenharmony_ci for (i = 0; i < 16; i++) 631062306a36Sopenharmony_ci nw64_mac(XMAC_HASH_TBL(i), hash[i]); 631162306a36Sopenharmony_ci} 631262306a36Sopenharmony_ci 631362306a36Sopenharmony_cistatic void niu_load_hash_bmac(struct niu *np, u16 *hash) 631462306a36Sopenharmony_ci{ 631562306a36Sopenharmony_ci int i; 631662306a36Sopenharmony_ci 631762306a36Sopenharmony_ci for (i = 0; i < 16; i++) 631862306a36Sopenharmony_ci nw64_mac(BMAC_HASH_TBL(i), hash[i]); 631962306a36Sopenharmony_ci} 632062306a36Sopenharmony_ci 632162306a36Sopenharmony_cistatic void niu_load_hash(struct niu *np, u16 *hash) 632262306a36Sopenharmony_ci{ 632362306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 632462306a36Sopenharmony_ci niu_load_hash_xmac(np, hash); 632562306a36Sopenharmony_ci else 632662306a36Sopenharmony_ci niu_load_hash_bmac(np, hash); 632762306a36Sopenharmony_ci} 632862306a36Sopenharmony_ci 632962306a36Sopenharmony_cistatic void niu_set_rx_mode(struct net_device *dev) 633062306a36Sopenharmony_ci{ 633162306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 633262306a36Sopenharmony_ci int i, alt_cnt, err; 633362306a36Sopenharmony_ci struct netdev_hw_addr *ha; 633462306a36Sopenharmony_ci unsigned long flags; 633562306a36Sopenharmony_ci u16 hash[16] = { 0, }; 633662306a36Sopenharmony_ci 633762306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 633862306a36Sopenharmony_ci niu_enable_rx_mac(np, 0); 633962306a36Sopenharmony_ci 634062306a36Sopenharmony_ci np->flags &= ~(NIU_FLAGS_MCAST | NIU_FLAGS_PROMISC); 634162306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) 634262306a36Sopenharmony_ci np->flags |= NIU_FLAGS_PROMISC; 634362306a36Sopenharmony_ci if ((dev->flags & IFF_ALLMULTI) || (!netdev_mc_empty(dev))) 634462306a36Sopenharmony_ci np->flags |= NIU_FLAGS_MCAST; 634562306a36Sopenharmony_ci 634662306a36Sopenharmony_ci alt_cnt = netdev_uc_count(dev); 634762306a36Sopenharmony_ci if (alt_cnt > niu_num_alt_addr(np)) { 634862306a36Sopenharmony_ci alt_cnt = 0; 634962306a36Sopenharmony_ci np->flags |= NIU_FLAGS_PROMISC; 635062306a36Sopenharmony_ci } 635162306a36Sopenharmony_ci 635262306a36Sopenharmony_ci if (alt_cnt) { 635362306a36Sopenharmony_ci int index = 0; 635462306a36Sopenharmony_ci 635562306a36Sopenharmony_ci netdev_for_each_uc_addr(ha, dev) { 635662306a36Sopenharmony_ci err = niu_set_alt_mac(np, index, ha->addr); 635762306a36Sopenharmony_ci if (err) 635862306a36Sopenharmony_ci netdev_warn(dev, "Error %d adding alt mac %d\n", 635962306a36Sopenharmony_ci err, index); 636062306a36Sopenharmony_ci err = niu_enable_alt_mac(np, index, 1); 636162306a36Sopenharmony_ci if (err) 636262306a36Sopenharmony_ci netdev_warn(dev, "Error %d enabling alt mac %d\n", 636362306a36Sopenharmony_ci err, index); 636462306a36Sopenharmony_ci 636562306a36Sopenharmony_ci index++; 636662306a36Sopenharmony_ci } 636762306a36Sopenharmony_ci } else { 636862306a36Sopenharmony_ci int alt_start; 636962306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 637062306a36Sopenharmony_ci alt_start = 0; 637162306a36Sopenharmony_ci else 637262306a36Sopenharmony_ci alt_start = 1; 637362306a36Sopenharmony_ci for (i = alt_start; i < niu_num_alt_addr(np); i++) { 637462306a36Sopenharmony_ci err = niu_enable_alt_mac(np, i, 0); 637562306a36Sopenharmony_ci if (err) 637662306a36Sopenharmony_ci netdev_warn(dev, "Error %d disabling alt mac %d\n", 637762306a36Sopenharmony_ci err, i); 637862306a36Sopenharmony_ci } 637962306a36Sopenharmony_ci } 638062306a36Sopenharmony_ci if (dev->flags & IFF_ALLMULTI) { 638162306a36Sopenharmony_ci for (i = 0; i < 16; i++) 638262306a36Sopenharmony_ci hash[i] = 0xffff; 638362306a36Sopenharmony_ci } else if (!netdev_mc_empty(dev)) { 638462306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 638562306a36Sopenharmony_ci u32 crc = ether_crc_le(ETH_ALEN, ha->addr); 638662306a36Sopenharmony_ci 638762306a36Sopenharmony_ci crc >>= 24; 638862306a36Sopenharmony_ci hash[crc >> 4] |= (1 << (15 - (crc & 0xf))); 638962306a36Sopenharmony_ci } 639062306a36Sopenharmony_ci } 639162306a36Sopenharmony_ci 639262306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_MCAST) 639362306a36Sopenharmony_ci niu_load_hash(np, hash); 639462306a36Sopenharmony_ci 639562306a36Sopenharmony_ci niu_enable_rx_mac(np, 1); 639662306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 639762306a36Sopenharmony_ci} 639862306a36Sopenharmony_ci 639962306a36Sopenharmony_cistatic int niu_set_mac_addr(struct net_device *dev, void *p) 640062306a36Sopenharmony_ci{ 640162306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 640262306a36Sopenharmony_ci struct sockaddr *addr = p; 640362306a36Sopenharmony_ci unsigned long flags; 640462306a36Sopenharmony_ci 640562306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 640662306a36Sopenharmony_ci return -EADDRNOTAVAIL; 640762306a36Sopenharmony_ci 640862306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 640962306a36Sopenharmony_ci 641062306a36Sopenharmony_ci if (!netif_running(dev)) 641162306a36Sopenharmony_ci return 0; 641262306a36Sopenharmony_ci 641362306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 641462306a36Sopenharmony_ci niu_enable_rx_mac(np, 0); 641562306a36Sopenharmony_ci niu_set_primary_mac(np, dev->dev_addr); 641662306a36Sopenharmony_ci niu_enable_rx_mac(np, 1); 641762306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 641862306a36Sopenharmony_ci 641962306a36Sopenharmony_ci return 0; 642062306a36Sopenharmony_ci} 642162306a36Sopenharmony_ci 642262306a36Sopenharmony_cistatic int niu_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 642362306a36Sopenharmony_ci{ 642462306a36Sopenharmony_ci return -EOPNOTSUPP; 642562306a36Sopenharmony_ci} 642662306a36Sopenharmony_ci 642762306a36Sopenharmony_cistatic void niu_netif_stop(struct niu *np) 642862306a36Sopenharmony_ci{ 642962306a36Sopenharmony_ci netif_trans_update(np->dev); /* prevent tx timeout */ 643062306a36Sopenharmony_ci 643162306a36Sopenharmony_ci niu_disable_napi(np); 643262306a36Sopenharmony_ci 643362306a36Sopenharmony_ci netif_tx_disable(np->dev); 643462306a36Sopenharmony_ci} 643562306a36Sopenharmony_ci 643662306a36Sopenharmony_cistatic void niu_netif_start(struct niu *np) 643762306a36Sopenharmony_ci{ 643862306a36Sopenharmony_ci /* NOTE: unconditional netif_wake_queue is only appropriate 643962306a36Sopenharmony_ci * so long as all callers are assured to have free tx slots 644062306a36Sopenharmony_ci * (such as after niu_init_hw). 644162306a36Sopenharmony_ci */ 644262306a36Sopenharmony_ci netif_tx_wake_all_queues(np->dev); 644362306a36Sopenharmony_ci 644462306a36Sopenharmony_ci niu_enable_napi(np); 644562306a36Sopenharmony_ci 644662306a36Sopenharmony_ci niu_enable_interrupts(np, 1); 644762306a36Sopenharmony_ci} 644862306a36Sopenharmony_ci 644962306a36Sopenharmony_cistatic void niu_reset_buffers(struct niu *np) 645062306a36Sopenharmony_ci{ 645162306a36Sopenharmony_ci int i, j, k, err; 645262306a36Sopenharmony_ci 645362306a36Sopenharmony_ci if (np->rx_rings) { 645462306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 645562306a36Sopenharmony_ci struct rx_ring_info *rp = &np->rx_rings[i]; 645662306a36Sopenharmony_ci 645762306a36Sopenharmony_ci for (j = 0, k = 0; j < MAX_RBR_RING_SIZE; j++) { 645862306a36Sopenharmony_ci struct page *page; 645962306a36Sopenharmony_ci 646062306a36Sopenharmony_ci page = rp->rxhash[j]; 646162306a36Sopenharmony_ci while (page) { 646262306a36Sopenharmony_ci struct page *next = niu_next_page(page); 646362306a36Sopenharmony_ci u64 base = page->index; 646462306a36Sopenharmony_ci base = base >> RBR_DESCR_ADDR_SHIFT; 646562306a36Sopenharmony_ci rp->rbr[k++] = cpu_to_le32(base); 646662306a36Sopenharmony_ci page = next; 646762306a36Sopenharmony_ci } 646862306a36Sopenharmony_ci } 646962306a36Sopenharmony_ci for (; k < MAX_RBR_RING_SIZE; k++) { 647062306a36Sopenharmony_ci err = niu_rbr_add_page(np, rp, GFP_ATOMIC, k); 647162306a36Sopenharmony_ci if (unlikely(err)) 647262306a36Sopenharmony_ci break; 647362306a36Sopenharmony_ci } 647462306a36Sopenharmony_ci 647562306a36Sopenharmony_ci rp->rbr_index = rp->rbr_table_size - 1; 647662306a36Sopenharmony_ci rp->rcr_index = 0; 647762306a36Sopenharmony_ci rp->rbr_pending = 0; 647862306a36Sopenharmony_ci rp->rbr_refill_pending = 0; 647962306a36Sopenharmony_ci } 648062306a36Sopenharmony_ci } 648162306a36Sopenharmony_ci if (np->tx_rings) { 648262306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 648362306a36Sopenharmony_ci struct tx_ring_info *rp = &np->tx_rings[i]; 648462306a36Sopenharmony_ci 648562306a36Sopenharmony_ci for (j = 0; j < MAX_TX_RING_SIZE; j++) { 648662306a36Sopenharmony_ci if (rp->tx_buffs[j].skb) 648762306a36Sopenharmony_ci (void) release_tx_packet(np, rp, j); 648862306a36Sopenharmony_ci } 648962306a36Sopenharmony_ci 649062306a36Sopenharmony_ci rp->pending = MAX_TX_RING_SIZE; 649162306a36Sopenharmony_ci rp->prod = 0; 649262306a36Sopenharmony_ci rp->cons = 0; 649362306a36Sopenharmony_ci rp->wrap_bit = 0; 649462306a36Sopenharmony_ci } 649562306a36Sopenharmony_ci } 649662306a36Sopenharmony_ci} 649762306a36Sopenharmony_ci 649862306a36Sopenharmony_cistatic void niu_reset_task(struct work_struct *work) 649962306a36Sopenharmony_ci{ 650062306a36Sopenharmony_ci struct niu *np = container_of(work, struct niu, reset_task); 650162306a36Sopenharmony_ci unsigned long flags; 650262306a36Sopenharmony_ci int err; 650362306a36Sopenharmony_ci 650462306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 650562306a36Sopenharmony_ci if (!netif_running(np->dev)) { 650662306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 650762306a36Sopenharmony_ci return; 650862306a36Sopenharmony_ci } 650962306a36Sopenharmony_ci 651062306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 651162306a36Sopenharmony_ci 651262306a36Sopenharmony_ci del_timer_sync(&np->timer); 651362306a36Sopenharmony_ci 651462306a36Sopenharmony_ci niu_netif_stop(np); 651562306a36Sopenharmony_ci 651662306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 651762306a36Sopenharmony_ci 651862306a36Sopenharmony_ci niu_stop_hw(np); 651962306a36Sopenharmony_ci 652062306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 652162306a36Sopenharmony_ci 652262306a36Sopenharmony_ci niu_reset_buffers(np); 652362306a36Sopenharmony_ci 652462306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 652562306a36Sopenharmony_ci 652662306a36Sopenharmony_ci err = niu_init_hw(np); 652762306a36Sopenharmony_ci if (!err) { 652862306a36Sopenharmony_ci np->timer.expires = jiffies + HZ; 652962306a36Sopenharmony_ci add_timer(&np->timer); 653062306a36Sopenharmony_ci niu_netif_start(np); 653162306a36Sopenharmony_ci } 653262306a36Sopenharmony_ci 653362306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 653462306a36Sopenharmony_ci} 653562306a36Sopenharmony_ci 653662306a36Sopenharmony_cistatic void niu_tx_timeout(struct net_device *dev, unsigned int txqueue) 653762306a36Sopenharmony_ci{ 653862306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 653962306a36Sopenharmony_ci 654062306a36Sopenharmony_ci dev_err(np->device, "%s: Transmit timed out, resetting\n", 654162306a36Sopenharmony_ci dev->name); 654262306a36Sopenharmony_ci 654362306a36Sopenharmony_ci schedule_work(&np->reset_task); 654462306a36Sopenharmony_ci} 654562306a36Sopenharmony_ci 654662306a36Sopenharmony_cistatic void niu_set_txd(struct tx_ring_info *rp, int index, 654762306a36Sopenharmony_ci u64 mapping, u64 len, u64 mark, 654862306a36Sopenharmony_ci u64 n_frags) 654962306a36Sopenharmony_ci{ 655062306a36Sopenharmony_ci __le64 *desc = &rp->descr[index]; 655162306a36Sopenharmony_ci 655262306a36Sopenharmony_ci *desc = cpu_to_le64(mark | 655362306a36Sopenharmony_ci (n_frags << TX_DESC_NUM_PTR_SHIFT) | 655462306a36Sopenharmony_ci (len << TX_DESC_TR_LEN_SHIFT) | 655562306a36Sopenharmony_ci (mapping & TX_DESC_SAD)); 655662306a36Sopenharmony_ci} 655762306a36Sopenharmony_ci 655862306a36Sopenharmony_cistatic u64 niu_compute_tx_flags(struct sk_buff *skb, struct ethhdr *ehdr, 655962306a36Sopenharmony_ci u64 pad_bytes, u64 len) 656062306a36Sopenharmony_ci{ 656162306a36Sopenharmony_ci u16 eth_proto, eth_proto_inner; 656262306a36Sopenharmony_ci u64 csum_bits, l3off, ihl, ret; 656362306a36Sopenharmony_ci u8 ip_proto; 656462306a36Sopenharmony_ci int ipv6; 656562306a36Sopenharmony_ci 656662306a36Sopenharmony_ci eth_proto = be16_to_cpu(ehdr->h_proto); 656762306a36Sopenharmony_ci eth_proto_inner = eth_proto; 656862306a36Sopenharmony_ci if (eth_proto == ETH_P_8021Q) { 656962306a36Sopenharmony_ci struct vlan_ethhdr *vp = (struct vlan_ethhdr *) ehdr; 657062306a36Sopenharmony_ci __be16 val = vp->h_vlan_encapsulated_proto; 657162306a36Sopenharmony_ci 657262306a36Sopenharmony_ci eth_proto_inner = be16_to_cpu(val); 657362306a36Sopenharmony_ci } 657462306a36Sopenharmony_ci 657562306a36Sopenharmony_ci ipv6 = ihl = 0; 657662306a36Sopenharmony_ci switch (skb->protocol) { 657762306a36Sopenharmony_ci case cpu_to_be16(ETH_P_IP): 657862306a36Sopenharmony_ci ip_proto = ip_hdr(skb)->protocol; 657962306a36Sopenharmony_ci ihl = ip_hdr(skb)->ihl; 658062306a36Sopenharmony_ci break; 658162306a36Sopenharmony_ci case cpu_to_be16(ETH_P_IPV6): 658262306a36Sopenharmony_ci ip_proto = ipv6_hdr(skb)->nexthdr; 658362306a36Sopenharmony_ci ihl = (40 >> 2); 658462306a36Sopenharmony_ci ipv6 = 1; 658562306a36Sopenharmony_ci break; 658662306a36Sopenharmony_ci default: 658762306a36Sopenharmony_ci ip_proto = ihl = 0; 658862306a36Sopenharmony_ci break; 658962306a36Sopenharmony_ci } 659062306a36Sopenharmony_ci 659162306a36Sopenharmony_ci csum_bits = TXHDR_CSUM_NONE; 659262306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 659362306a36Sopenharmony_ci u64 start, stuff; 659462306a36Sopenharmony_ci 659562306a36Sopenharmony_ci csum_bits = (ip_proto == IPPROTO_TCP ? 659662306a36Sopenharmony_ci TXHDR_CSUM_TCP : 659762306a36Sopenharmony_ci (ip_proto == IPPROTO_UDP ? 659862306a36Sopenharmony_ci TXHDR_CSUM_UDP : TXHDR_CSUM_SCTP)); 659962306a36Sopenharmony_ci 660062306a36Sopenharmony_ci start = skb_checksum_start_offset(skb) - 660162306a36Sopenharmony_ci (pad_bytes + sizeof(struct tx_pkt_hdr)); 660262306a36Sopenharmony_ci stuff = start + skb->csum_offset; 660362306a36Sopenharmony_ci 660462306a36Sopenharmony_ci csum_bits |= (start / 2) << TXHDR_L4START_SHIFT; 660562306a36Sopenharmony_ci csum_bits |= (stuff / 2) << TXHDR_L4STUFF_SHIFT; 660662306a36Sopenharmony_ci } 660762306a36Sopenharmony_ci 660862306a36Sopenharmony_ci l3off = skb_network_offset(skb) - 660962306a36Sopenharmony_ci (pad_bytes + sizeof(struct tx_pkt_hdr)); 661062306a36Sopenharmony_ci 661162306a36Sopenharmony_ci ret = (((pad_bytes / 2) << TXHDR_PAD_SHIFT) | 661262306a36Sopenharmony_ci (len << TXHDR_LEN_SHIFT) | 661362306a36Sopenharmony_ci ((l3off / 2) << TXHDR_L3START_SHIFT) | 661462306a36Sopenharmony_ci (ihl << TXHDR_IHL_SHIFT) | 661562306a36Sopenharmony_ci ((eth_proto_inner < ETH_P_802_3_MIN) ? TXHDR_LLC : 0) | 661662306a36Sopenharmony_ci ((eth_proto == ETH_P_8021Q) ? TXHDR_VLAN : 0) | 661762306a36Sopenharmony_ci (ipv6 ? TXHDR_IP_VER : 0) | 661862306a36Sopenharmony_ci csum_bits); 661962306a36Sopenharmony_ci 662062306a36Sopenharmony_ci return ret; 662162306a36Sopenharmony_ci} 662262306a36Sopenharmony_ci 662362306a36Sopenharmony_cistatic netdev_tx_t niu_start_xmit(struct sk_buff *skb, 662462306a36Sopenharmony_ci struct net_device *dev) 662562306a36Sopenharmony_ci{ 662662306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 662762306a36Sopenharmony_ci unsigned long align, headroom; 662862306a36Sopenharmony_ci struct netdev_queue *txq; 662962306a36Sopenharmony_ci struct tx_ring_info *rp; 663062306a36Sopenharmony_ci struct tx_pkt_hdr *tp; 663162306a36Sopenharmony_ci unsigned int len, nfg; 663262306a36Sopenharmony_ci struct ethhdr *ehdr; 663362306a36Sopenharmony_ci int prod, i, tlen; 663462306a36Sopenharmony_ci u64 mapping, mrk; 663562306a36Sopenharmony_ci 663662306a36Sopenharmony_ci i = skb_get_queue_mapping(skb); 663762306a36Sopenharmony_ci rp = &np->tx_rings[i]; 663862306a36Sopenharmony_ci txq = netdev_get_tx_queue(dev, i); 663962306a36Sopenharmony_ci 664062306a36Sopenharmony_ci if (niu_tx_avail(rp) <= (skb_shinfo(skb)->nr_frags + 1)) { 664162306a36Sopenharmony_ci netif_tx_stop_queue(txq); 664262306a36Sopenharmony_ci dev_err(np->device, "%s: BUG! Tx ring full when queue awake!\n", dev->name); 664362306a36Sopenharmony_ci rp->tx_errors++; 664462306a36Sopenharmony_ci return NETDEV_TX_BUSY; 664562306a36Sopenharmony_ci } 664662306a36Sopenharmony_ci 664762306a36Sopenharmony_ci if (eth_skb_pad(skb)) 664862306a36Sopenharmony_ci goto out; 664962306a36Sopenharmony_ci 665062306a36Sopenharmony_ci len = sizeof(struct tx_pkt_hdr) + 15; 665162306a36Sopenharmony_ci if (skb_headroom(skb) < len) { 665262306a36Sopenharmony_ci struct sk_buff *skb_new; 665362306a36Sopenharmony_ci 665462306a36Sopenharmony_ci skb_new = skb_realloc_headroom(skb, len); 665562306a36Sopenharmony_ci if (!skb_new) 665662306a36Sopenharmony_ci goto out_drop; 665762306a36Sopenharmony_ci kfree_skb(skb); 665862306a36Sopenharmony_ci skb = skb_new; 665962306a36Sopenharmony_ci } else 666062306a36Sopenharmony_ci skb_orphan(skb); 666162306a36Sopenharmony_ci 666262306a36Sopenharmony_ci align = ((unsigned long) skb->data & (16 - 1)); 666362306a36Sopenharmony_ci headroom = align + sizeof(struct tx_pkt_hdr); 666462306a36Sopenharmony_ci 666562306a36Sopenharmony_ci ehdr = (struct ethhdr *) skb->data; 666662306a36Sopenharmony_ci tp = skb_push(skb, headroom); 666762306a36Sopenharmony_ci 666862306a36Sopenharmony_ci len = skb->len - sizeof(struct tx_pkt_hdr); 666962306a36Sopenharmony_ci tp->flags = cpu_to_le64(niu_compute_tx_flags(skb, ehdr, align, len)); 667062306a36Sopenharmony_ci tp->resv = 0; 667162306a36Sopenharmony_ci 667262306a36Sopenharmony_ci len = skb_headlen(skb); 667362306a36Sopenharmony_ci mapping = np->ops->map_single(np->device, skb->data, 667462306a36Sopenharmony_ci len, DMA_TO_DEVICE); 667562306a36Sopenharmony_ci 667662306a36Sopenharmony_ci prod = rp->prod; 667762306a36Sopenharmony_ci 667862306a36Sopenharmony_ci rp->tx_buffs[prod].skb = skb; 667962306a36Sopenharmony_ci rp->tx_buffs[prod].mapping = mapping; 668062306a36Sopenharmony_ci 668162306a36Sopenharmony_ci mrk = TX_DESC_SOP; 668262306a36Sopenharmony_ci if (++rp->mark_counter == rp->mark_freq) { 668362306a36Sopenharmony_ci rp->mark_counter = 0; 668462306a36Sopenharmony_ci mrk |= TX_DESC_MARK; 668562306a36Sopenharmony_ci rp->mark_pending++; 668662306a36Sopenharmony_ci } 668762306a36Sopenharmony_ci 668862306a36Sopenharmony_ci tlen = len; 668962306a36Sopenharmony_ci nfg = skb_shinfo(skb)->nr_frags; 669062306a36Sopenharmony_ci while (tlen > 0) { 669162306a36Sopenharmony_ci tlen -= MAX_TX_DESC_LEN; 669262306a36Sopenharmony_ci nfg++; 669362306a36Sopenharmony_ci } 669462306a36Sopenharmony_ci 669562306a36Sopenharmony_ci while (len > 0) { 669662306a36Sopenharmony_ci unsigned int this_len = len; 669762306a36Sopenharmony_ci 669862306a36Sopenharmony_ci if (this_len > MAX_TX_DESC_LEN) 669962306a36Sopenharmony_ci this_len = MAX_TX_DESC_LEN; 670062306a36Sopenharmony_ci 670162306a36Sopenharmony_ci niu_set_txd(rp, prod, mapping, this_len, mrk, nfg); 670262306a36Sopenharmony_ci mrk = nfg = 0; 670362306a36Sopenharmony_ci 670462306a36Sopenharmony_ci prod = NEXT_TX(rp, prod); 670562306a36Sopenharmony_ci mapping += this_len; 670662306a36Sopenharmony_ci len -= this_len; 670762306a36Sopenharmony_ci } 670862306a36Sopenharmony_ci 670962306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 671062306a36Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 671162306a36Sopenharmony_ci 671262306a36Sopenharmony_ci len = skb_frag_size(frag); 671362306a36Sopenharmony_ci mapping = np->ops->map_page(np->device, skb_frag_page(frag), 671462306a36Sopenharmony_ci skb_frag_off(frag), len, 671562306a36Sopenharmony_ci DMA_TO_DEVICE); 671662306a36Sopenharmony_ci 671762306a36Sopenharmony_ci rp->tx_buffs[prod].skb = NULL; 671862306a36Sopenharmony_ci rp->tx_buffs[prod].mapping = mapping; 671962306a36Sopenharmony_ci 672062306a36Sopenharmony_ci niu_set_txd(rp, prod, mapping, len, 0, 0); 672162306a36Sopenharmony_ci 672262306a36Sopenharmony_ci prod = NEXT_TX(rp, prod); 672362306a36Sopenharmony_ci } 672462306a36Sopenharmony_ci 672562306a36Sopenharmony_ci if (prod < rp->prod) 672662306a36Sopenharmony_ci rp->wrap_bit ^= TX_RING_KICK_WRAP; 672762306a36Sopenharmony_ci rp->prod = prod; 672862306a36Sopenharmony_ci 672962306a36Sopenharmony_ci nw64(TX_RING_KICK(rp->tx_channel), rp->wrap_bit | (prod << 3)); 673062306a36Sopenharmony_ci 673162306a36Sopenharmony_ci if (unlikely(niu_tx_avail(rp) <= (MAX_SKB_FRAGS + 1))) { 673262306a36Sopenharmony_ci netif_tx_stop_queue(txq); 673362306a36Sopenharmony_ci if (niu_tx_avail(rp) > NIU_TX_WAKEUP_THRESH(rp)) 673462306a36Sopenharmony_ci netif_tx_wake_queue(txq); 673562306a36Sopenharmony_ci } 673662306a36Sopenharmony_ci 673762306a36Sopenharmony_ciout: 673862306a36Sopenharmony_ci return NETDEV_TX_OK; 673962306a36Sopenharmony_ci 674062306a36Sopenharmony_ciout_drop: 674162306a36Sopenharmony_ci rp->tx_errors++; 674262306a36Sopenharmony_ci kfree_skb(skb); 674362306a36Sopenharmony_ci goto out; 674462306a36Sopenharmony_ci} 674562306a36Sopenharmony_ci 674662306a36Sopenharmony_cistatic int niu_change_mtu(struct net_device *dev, int new_mtu) 674762306a36Sopenharmony_ci{ 674862306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 674962306a36Sopenharmony_ci int err, orig_jumbo, new_jumbo; 675062306a36Sopenharmony_ci 675162306a36Sopenharmony_ci orig_jumbo = (dev->mtu > ETH_DATA_LEN); 675262306a36Sopenharmony_ci new_jumbo = (new_mtu > ETH_DATA_LEN); 675362306a36Sopenharmony_ci 675462306a36Sopenharmony_ci dev->mtu = new_mtu; 675562306a36Sopenharmony_ci 675662306a36Sopenharmony_ci if (!netif_running(dev) || 675762306a36Sopenharmony_ci (orig_jumbo == new_jumbo)) 675862306a36Sopenharmony_ci return 0; 675962306a36Sopenharmony_ci 676062306a36Sopenharmony_ci niu_full_shutdown(np, dev); 676162306a36Sopenharmony_ci 676262306a36Sopenharmony_ci niu_free_channels(np); 676362306a36Sopenharmony_ci 676462306a36Sopenharmony_ci niu_enable_napi(np); 676562306a36Sopenharmony_ci 676662306a36Sopenharmony_ci err = niu_alloc_channels(np); 676762306a36Sopenharmony_ci if (err) 676862306a36Sopenharmony_ci return err; 676962306a36Sopenharmony_ci 677062306a36Sopenharmony_ci spin_lock_irq(&np->lock); 677162306a36Sopenharmony_ci 677262306a36Sopenharmony_ci err = niu_init_hw(np); 677362306a36Sopenharmony_ci if (!err) { 677462306a36Sopenharmony_ci timer_setup(&np->timer, niu_timer, 0); 677562306a36Sopenharmony_ci np->timer.expires = jiffies + HZ; 677662306a36Sopenharmony_ci 677762306a36Sopenharmony_ci err = niu_enable_interrupts(np, 1); 677862306a36Sopenharmony_ci if (err) 677962306a36Sopenharmony_ci niu_stop_hw(np); 678062306a36Sopenharmony_ci } 678162306a36Sopenharmony_ci 678262306a36Sopenharmony_ci spin_unlock_irq(&np->lock); 678362306a36Sopenharmony_ci 678462306a36Sopenharmony_ci if (!err) { 678562306a36Sopenharmony_ci netif_tx_start_all_queues(dev); 678662306a36Sopenharmony_ci if (np->link_config.loopback_mode != LOOPBACK_DISABLED) 678762306a36Sopenharmony_ci netif_carrier_on(dev); 678862306a36Sopenharmony_ci 678962306a36Sopenharmony_ci add_timer(&np->timer); 679062306a36Sopenharmony_ci } 679162306a36Sopenharmony_ci 679262306a36Sopenharmony_ci return err; 679362306a36Sopenharmony_ci} 679462306a36Sopenharmony_ci 679562306a36Sopenharmony_cistatic void niu_get_drvinfo(struct net_device *dev, 679662306a36Sopenharmony_ci struct ethtool_drvinfo *info) 679762306a36Sopenharmony_ci{ 679862306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 679962306a36Sopenharmony_ci struct niu_vpd *vpd = &np->vpd; 680062306a36Sopenharmony_ci 680162306a36Sopenharmony_ci strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); 680262306a36Sopenharmony_ci strscpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); 680362306a36Sopenharmony_ci snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d", 680462306a36Sopenharmony_ci vpd->fcode_major, vpd->fcode_minor); 680562306a36Sopenharmony_ci if (np->parent->plat_type != PLAT_TYPE_NIU) 680662306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(np->pdev), 680762306a36Sopenharmony_ci sizeof(info->bus_info)); 680862306a36Sopenharmony_ci} 680962306a36Sopenharmony_ci 681062306a36Sopenharmony_cistatic int niu_get_link_ksettings(struct net_device *dev, 681162306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 681262306a36Sopenharmony_ci{ 681362306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 681462306a36Sopenharmony_ci struct niu_link_config *lp; 681562306a36Sopenharmony_ci 681662306a36Sopenharmony_ci lp = &np->link_config; 681762306a36Sopenharmony_ci 681862306a36Sopenharmony_ci memset(cmd, 0, sizeof(*cmd)); 681962306a36Sopenharmony_ci cmd->base.phy_address = np->phy_addr; 682062306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 682162306a36Sopenharmony_ci lp->supported); 682262306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 682362306a36Sopenharmony_ci lp->active_advertising); 682462306a36Sopenharmony_ci cmd->base.autoneg = lp->active_autoneg; 682562306a36Sopenharmony_ci cmd->base.speed = lp->active_speed; 682662306a36Sopenharmony_ci cmd->base.duplex = lp->active_duplex; 682762306a36Sopenharmony_ci cmd->base.port = (np->flags & NIU_FLAGS_FIBER) ? PORT_FIBRE : PORT_TP; 682862306a36Sopenharmony_ci 682962306a36Sopenharmony_ci return 0; 683062306a36Sopenharmony_ci} 683162306a36Sopenharmony_ci 683262306a36Sopenharmony_cistatic int niu_set_link_ksettings(struct net_device *dev, 683362306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 683462306a36Sopenharmony_ci{ 683562306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 683662306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 683762306a36Sopenharmony_ci 683862306a36Sopenharmony_ci ethtool_convert_link_mode_to_legacy_u32(&lp->advertising, 683962306a36Sopenharmony_ci cmd->link_modes.advertising); 684062306a36Sopenharmony_ci lp->speed = cmd->base.speed; 684162306a36Sopenharmony_ci lp->duplex = cmd->base.duplex; 684262306a36Sopenharmony_ci lp->autoneg = cmd->base.autoneg; 684362306a36Sopenharmony_ci return niu_init_link(np); 684462306a36Sopenharmony_ci} 684562306a36Sopenharmony_ci 684662306a36Sopenharmony_cistatic u32 niu_get_msglevel(struct net_device *dev) 684762306a36Sopenharmony_ci{ 684862306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 684962306a36Sopenharmony_ci return np->msg_enable; 685062306a36Sopenharmony_ci} 685162306a36Sopenharmony_ci 685262306a36Sopenharmony_cistatic void niu_set_msglevel(struct net_device *dev, u32 value) 685362306a36Sopenharmony_ci{ 685462306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 685562306a36Sopenharmony_ci np->msg_enable = value; 685662306a36Sopenharmony_ci} 685762306a36Sopenharmony_ci 685862306a36Sopenharmony_cistatic int niu_nway_reset(struct net_device *dev) 685962306a36Sopenharmony_ci{ 686062306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 686162306a36Sopenharmony_ci 686262306a36Sopenharmony_ci if (np->link_config.autoneg) 686362306a36Sopenharmony_ci return niu_init_link(np); 686462306a36Sopenharmony_ci 686562306a36Sopenharmony_ci return 0; 686662306a36Sopenharmony_ci} 686762306a36Sopenharmony_ci 686862306a36Sopenharmony_cistatic int niu_get_eeprom_len(struct net_device *dev) 686962306a36Sopenharmony_ci{ 687062306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 687162306a36Sopenharmony_ci 687262306a36Sopenharmony_ci return np->eeprom_len; 687362306a36Sopenharmony_ci} 687462306a36Sopenharmony_ci 687562306a36Sopenharmony_cistatic int niu_get_eeprom(struct net_device *dev, 687662306a36Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data) 687762306a36Sopenharmony_ci{ 687862306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 687962306a36Sopenharmony_ci u32 offset, len, val; 688062306a36Sopenharmony_ci 688162306a36Sopenharmony_ci offset = eeprom->offset; 688262306a36Sopenharmony_ci len = eeprom->len; 688362306a36Sopenharmony_ci 688462306a36Sopenharmony_ci if (offset + len < offset) 688562306a36Sopenharmony_ci return -EINVAL; 688662306a36Sopenharmony_ci if (offset >= np->eeprom_len) 688762306a36Sopenharmony_ci return -EINVAL; 688862306a36Sopenharmony_ci if (offset + len > np->eeprom_len) 688962306a36Sopenharmony_ci len = eeprom->len = np->eeprom_len - offset; 689062306a36Sopenharmony_ci 689162306a36Sopenharmony_ci if (offset & 3) { 689262306a36Sopenharmony_ci u32 b_offset, b_count; 689362306a36Sopenharmony_ci 689462306a36Sopenharmony_ci b_offset = offset & 3; 689562306a36Sopenharmony_ci b_count = 4 - b_offset; 689662306a36Sopenharmony_ci if (b_count > len) 689762306a36Sopenharmony_ci b_count = len; 689862306a36Sopenharmony_ci 689962306a36Sopenharmony_ci val = nr64(ESPC_NCR((offset - b_offset) / 4)); 690062306a36Sopenharmony_ci memcpy(data, ((char *)&val) + b_offset, b_count); 690162306a36Sopenharmony_ci data += b_count; 690262306a36Sopenharmony_ci len -= b_count; 690362306a36Sopenharmony_ci offset += b_count; 690462306a36Sopenharmony_ci } 690562306a36Sopenharmony_ci while (len >= 4) { 690662306a36Sopenharmony_ci val = nr64(ESPC_NCR(offset / 4)); 690762306a36Sopenharmony_ci memcpy(data, &val, 4); 690862306a36Sopenharmony_ci data += 4; 690962306a36Sopenharmony_ci len -= 4; 691062306a36Sopenharmony_ci offset += 4; 691162306a36Sopenharmony_ci } 691262306a36Sopenharmony_ci if (len) { 691362306a36Sopenharmony_ci val = nr64(ESPC_NCR(offset / 4)); 691462306a36Sopenharmony_ci memcpy(data, &val, len); 691562306a36Sopenharmony_ci } 691662306a36Sopenharmony_ci return 0; 691762306a36Sopenharmony_ci} 691862306a36Sopenharmony_ci 691962306a36Sopenharmony_cistatic void niu_ethflow_to_l3proto(int flow_type, u8 *pid) 692062306a36Sopenharmony_ci{ 692162306a36Sopenharmony_ci switch (flow_type) { 692262306a36Sopenharmony_ci case TCP_V4_FLOW: 692362306a36Sopenharmony_ci case TCP_V6_FLOW: 692462306a36Sopenharmony_ci *pid = IPPROTO_TCP; 692562306a36Sopenharmony_ci break; 692662306a36Sopenharmony_ci case UDP_V4_FLOW: 692762306a36Sopenharmony_ci case UDP_V6_FLOW: 692862306a36Sopenharmony_ci *pid = IPPROTO_UDP; 692962306a36Sopenharmony_ci break; 693062306a36Sopenharmony_ci case SCTP_V4_FLOW: 693162306a36Sopenharmony_ci case SCTP_V6_FLOW: 693262306a36Sopenharmony_ci *pid = IPPROTO_SCTP; 693362306a36Sopenharmony_ci break; 693462306a36Sopenharmony_ci case AH_V4_FLOW: 693562306a36Sopenharmony_ci case AH_V6_FLOW: 693662306a36Sopenharmony_ci *pid = IPPROTO_AH; 693762306a36Sopenharmony_ci break; 693862306a36Sopenharmony_ci case ESP_V4_FLOW: 693962306a36Sopenharmony_ci case ESP_V6_FLOW: 694062306a36Sopenharmony_ci *pid = IPPROTO_ESP; 694162306a36Sopenharmony_ci break; 694262306a36Sopenharmony_ci default: 694362306a36Sopenharmony_ci *pid = 0; 694462306a36Sopenharmony_ci break; 694562306a36Sopenharmony_ci } 694662306a36Sopenharmony_ci} 694762306a36Sopenharmony_ci 694862306a36Sopenharmony_cistatic int niu_class_to_ethflow(u64 class, int *flow_type) 694962306a36Sopenharmony_ci{ 695062306a36Sopenharmony_ci switch (class) { 695162306a36Sopenharmony_ci case CLASS_CODE_TCP_IPV4: 695262306a36Sopenharmony_ci *flow_type = TCP_V4_FLOW; 695362306a36Sopenharmony_ci break; 695462306a36Sopenharmony_ci case CLASS_CODE_UDP_IPV4: 695562306a36Sopenharmony_ci *flow_type = UDP_V4_FLOW; 695662306a36Sopenharmony_ci break; 695762306a36Sopenharmony_ci case CLASS_CODE_AH_ESP_IPV4: 695862306a36Sopenharmony_ci *flow_type = AH_V4_FLOW; 695962306a36Sopenharmony_ci break; 696062306a36Sopenharmony_ci case CLASS_CODE_SCTP_IPV4: 696162306a36Sopenharmony_ci *flow_type = SCTP_V4_FLOW; 696262306a36Sopenharmony_ci break; 696362306a36Sopenharmony_ci case CLASS_CODE_TCP_IPV6: 696462306a36Sopenharmony_ci *flow_type = TCP_V6_FLOW; 696562306a36Sopenharmony_ci break; 696662306a36Sopenharmony_ci case CLASS_CODE_UDP_IPV6: 696762306a36Sopenharmony_ci *flow_type = UDP_V6_FLOW; 696862306a36Sopenharmony_ci break; 696962306a36Sopenharmony_ci case CLASS_CODE_AH_ESP_IPV6: 697062306a36Sopenharmony_ci *flow_type = AH_V6_FLOW; 697162306a36Sopenharmony_ci break; 697262306a36Sopenharmony_ci case CLASS_CODE_SCTP_IPV6: 697362306a36Sopenharmony_ci *flow_type = SCTP_V6_FLOW; 697462306a36Sopenharmony_ci break; 697562306a36Sopenharmony_ci case CLASS_CODE_USER_PROG1: 697662306a36Sopenharmony_ci case CLASS_CODE_USER_PROG2: 697762306a36Sopenharmony_ci case CLASS_CODE_USER_PROG3: 697862306a36Sopenharmony_ci case CLASS_CODE_USER_PROG4: 697962306a36Sopenharmony_ci *flow_type = IP_USER_FLOW; 698062306a36Sopenharmony_ci break; 698162306a36Sopenharmony_ci default: 698262306a36Sopenharmony_ci return -EINVAL; 698362306a36Sopenharmony_ci } 698462306a36Sopenharmony_ci 698562306a36Sopenharmony_ci return 0; 698662306a36Sopenharmony_ci} 698762306a36Sopenharmony_ci 698862306a36Sopenharmony_cistatic int niu_ethflow_to_class(int flow_type, u64 *class) 698962306a36Sopenharmony_ci{ 699062306a36Sopenharmony_ci switch (flow_type) { 699162306a36Sopenharmony_ci case TCP_V4_FLOW: 699262306a36Sopenharmony_ci *class = CLASS_CODE_TCP_IPV4; 699362306a36Sopenharmony_ci break; 699462306a36Sopenharmony_ci case UDP_V4_FLOW: 699562306a36Sopenharmony_ci *class = CLASS_CODE_UDP_IPV4; 699662306a36Sopenharmony_ci break; 699762306a36Sopenharmony_ci case AH_ESP_V4_FLOW: 699862306a36Sopenharmony_ci case AH_V4_FLOW: 699962306a36Sopenharmony_ci case ESP_V4_FLOW: 700062306a36Sopenharmony_ci *class = CLASS_CODE_AH_ESP_IPV4; 700162306a36Sopenharmony_ci break; 700262306a36Sopenharmony_ci case SCTP_V4_FLOW: 700362306a36Sopenharmony_ci *class = CLASS_CODE_SCTP_IPV4; 700462306a36Sopenharmony_ci break; 700562306a36Sopenharmony_ci case TCP_V6_FLOW: 700662306a36Sopenharmony_ci *class = CLASS_CODE_TCP_IPV6; 700762306a36Sopenharmony_ci break; 700862306a36Sopenharmony_ci case UDP_V6_FLOW: 700962306a36Sopenharmony_ci *class = CLASS_CODE_UDP_IPV6; 701062306a36Sopenharmony_ci break; 701162306a36Sopenharmony_ci case AH_ESP_V6_FLOW: 701262306a36Sopenharmony_ci case AH_V6_FLOW: 701362306a36Sopenharmony_ci case ESP_V6_FLOW: 701462306a36Sopenharmony_ci *class = CLASS_CODE_AH_ESP_IPV6; 701562306a36Sopenharmony_ci break; 701662306a36Sopenharmony_ci case SCTP_V6_FLOW: 701762306a36Sopenharmony_ci *class = CLASS_CODE_SCTP_IPV6; 701862306a36Sopenharmony_ci break; 701962306a36Sopenharmony_ci default: 702062306a36Sopenharmony_ci return 0; 702162306a36Sopenharmony_ci } 702262306a36Sopenharmony_ci 702362306a36Sopenharmony_ci return 1; 702462306a36Sopenharmony_ci} 702562306a36Sopenharmony_ci 702662306a36Sopenharmony_cistatic u64 niu_flowkey_to_ethflow(u64 flow_key) 702762306a36Sopenharmony_ci{ 702862306a36Sopenharmony_ci u64 ethflow = 0; 702962306a36Sopenharmony_ci 703062306a36Sopenharmony_ci if (flow_key & FLOW_KEY_L2DA) 703162306a36Sopenharmony_ci ethflow |= RXH_L2DA; 703262306a36Sopenharmony_ci if (flow_key & FLOW_KEY_VLAN) 703362306a36Sopenharmony_ci ethflow |= RXH_VLAN; 703462306a36Sopenharmony_ci if (flow_key & FLOW_KEY_IPSA) 703562306a36Sopenharmony_ci ethflow |= RXH_IP_SRC; 703662306a36Sopenharmony_ci if (flow_key & FLOW_KEY_IPDA) 703762306a36Sopenharmony_ci ethflow |= RXH_IP_DST; 703862306a36Sopenharmony_ci if (flow_key & FLOW_KEY_PROTO) 703962306a36Sopenharmony_ci ethflow |= RXH_L3_PROTO; 704062306a36Sopenharmony_ci if (flow_key & (FLOW_KEY_L4_BYTE12 << FLOW_KEY_L4_0_SHIFT)) 704162306a36Sopenharmony_ci ethflow |= RXH_L4_B_0_1; 704262306a36Sopenharmony_ci if (flow_key & (FLOW_KEY_L4_BYTE12 << FLOW_KEY_L4_1_SHIFT)) 704362306a36Sopenharmony_ci ethflow |= RXH_L4_B_2_3; 704462306a36Sopenharmony_ci 704562306a36Sopenharmony_ci return ethflow; 704662306a36Sopenharmony_ci 704762306a36Sopenharmony_ci} 704862306a36Sopenharmony_ci 704962306a36Sopenharmony_cistatic int niu_ethflow_to_flowkey(u64 ethflow, u64 *flow_key) 705062306a36Sopenharmony_ci{ 705162306a36Sopenharmony_ci u64 key = 0; 705262306a36Sopenharmony_ci 705362306a36Sopenharmony_ci if (ethflow & RXH_L2DA) 705462306a36Sopenharmony_ci key |= FLOW_KEY_L2DA; 705562306a36Sopenharmony_ci if (ethflow & RXH_VLAN) 705662306a36Sopenharmony_ci key |= FLOW_KEY_VLAN; 705762306a36Sopenharmony_ci if (ethflow & RXH_IP_SRC) 705862306a36Sopenharmony_ci key |= FLOW_KEY_IPSA; 705962306a36Sopenharmony_ci if (ethflow & RXH_IP_DST) 706062306a36Sopenharmony_ci key |= FLOW_KEY_IPDA; 706162306a36Sopenharmony_ci if (ethflow & RXH_L3_PROTO) 706262306a36Sopenharmony_ci key |= FLOW_KEY_PROTO; 706362306a36Sopenharmony_ci if (ethflow & RXH_L4_B_0_1) 706462306a36Sopenharmony_ci key |= (FLOW_KEY_L4_BYTE12 << FLOW_KEY_L4_0_SHIFT); 706562306a36Sopenharmony_ci if (ethflow & RXH_L4_B_2_3) 706662306a36Sopenharmony_ci key |= (FLOW_KEY_L4_BYTE12 << FLOW_KEY_L4_1_SHIFT); 706762306a36Sopenharmony_ci 706862306a36Sopenharmony_ci *flow_key = key; 706962306a36Sopenharmony_ci 707062306a36Sopenharmony_ci return 1; 707162306a36Sopenharmony_ci 707262306a36Sopenharmony_ci} 707362306a36Sopenharmony_ci 707462306a36Sopenharmony_cistatic int niu_get_hash_opts(struct niu *np, struct ethtool_rxnfc *nfc) 707562306a36Sopenharmony_ci{ 707662306a36Sopenharmony_ci u64 class; 707762306a36Sopenharmony_ci 707862306a36Sopenharmony_ci nfc->data = 0; 707962306a36Sopenharmony_ci 708062306a36Sopenharmony_ci if (!niu_ethflow_to_class(nfc->flow_type, &class)) 708162306a36Sopenharmony_ci return -EINVAL; 708262306a36Sopenharmony_ci 708362306a36Sopenharmony_ci if (np->parent->tcam_key[class - CLASS_CODE_USER_PROG1] & 708462306a36Sopenharmony_ci TCAM_KEY_DISC) 708562306a36Sopenharmony_ci nfc->data = RXH_DISCARD; 708662306a36Sopenharmony_ci else 708762306a36Sopenharmony_ci nfc->data = niu_flowkey_to_ethflow(np->parent->flow_key[class - 708862306a36Sopenharmony_ci CLASS_CODE_USER_PROG1]); 708962306a36Sopenharmony_ci return 0; 709062306a36Sopenharmony_ci} 709162306a36Sopenharmony_ci 709262306a36Sopenharmony_cistatic void niu_get_ip4fs_from_tcam_key(struct niu_tcam_entry *tp, 709362306a36Sopenharmony_ci struct ethtool_rx_flow_spec *fsp) 709462306a36Sopenharmony_ci{ 709562306a36Sopenharmony_ci u32 tmp; 709662306a36Sopenharmony_ci u16 prt; 709762306a36Sopenharmony_ci 709862306a36Sopenharmony_ci tmp = (tp->key[3] & TCAM_V4KEY3_SADDR) >> TCAM_V4KEY3_SADDR_SHIFT; 709962306a36Sopenharmony_ci fsp->h_u.tcp_ip4_spec.ip4src = cpu_to_be32(tmp); 710062306a36Sopenharmony_ci 710162306a36Sopenharmony_ci tmp = (tp->key[3] & TCAM_V4KEY3_DADDR) >> TCAM_V4KEY3_DADDR_SHIFT; 710262306a36Sopenharmony_ci fsp->h_u.tcp_ip4_spec.ip4dst = cpu_to_be32(tmp); 710362306a36Sopenharmony_ci 710462306a36Sopenharmony_ci tmp = (tp->key_mask[3] & TCAM_V4KEY3_SADDR) >> TCAM_V4KEY3_SADDR_SHIFT; 710562306a36Sopenharmony_ci fsp->m_u.tcp_ip4_spec.ip4src = cpu_to_be32(tmp); 710662306a36Sopenharmony_ci 710762306a36Sopenharmony_ci tmp = (tp->key_mask[3] & TCAM_V4KEY3_DADDR) >> TCAM_V4KEY3_DADDR_SHIFT; 710862306a36Sopenharmony_ci fsp->m_u.tcp_ip4_spec.ip4dst = cpu_to_be32(tmp); 710962306a36Sopenharmony_ci 711062306a36Sopenharmony_ci fsp->h_u.tcp_ip4_spec.tos = (tp->key[2] & TCAM_V4KEY2_TOS) >> 711162306a36Sopenharmony_ci TCAM_V4KEY2_TOS_SHIFT; 711262306a36Sopenharmony_ci fsp->m_u.tcp_ip4_spec.tos = (tp->key_mask[2] & TCAM_V4KEY2_TOS) >> 711362306a36Sopenharmony_ci TCAM_V4KEY2_TOS_SHIFT; 711462306a36Sopenharmony_ci 711562306a36Sopenharmony_ci switch (fsp->flow_type) { 711662306a36Sopenharmony_ci case TCP_V4_FLOW: 711762306a36Sopenharmony_ci case UDP_V4_FLOW: 711862306a36Sopenharmony_ci case SCTP_V4_FLOW: 711962306a36Sopenharmony_ci prt = ((tp->key[2] & TCAM_V4KEY2_PORT_SPI) >> 712062306a36Sopenharmony_ci TCAM_V4KEY2_PORT_SPI_SHIFT) >> 16; 712162306a36Sopenharmony_ci fsp->h_u.tcp_ip4_spec.psrc = cpu_to_be16(prt); 712262306a36Sopenharmony_ci 712362306a36Sopenharmony_ci prt = ((tp->key[2] & TCAM_V4KEY2_PORT_SPI) >> 712462306a36Sopenharmony_ci TCAM_V4KEY2_PORT_SPI_SHIFT) & 0xffff; 712562306a36Sopenharmony_ci fsp->h_u.tcp_ip4_spec.pdst = cpu_to_be16(prt); 712662306a36Sopenharmony_ci 712762306a36Sopenharmony_ci prt = ((tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >> 712862306a36Sopenharmony_ci TCAM_V4KEY2_PORT_SPI_SHIFT) >> 16; 712962306a36Sopenharmony_ci fsp->m_u.tcp_ip4_spec.psrc = cpu_to_be16(prt); 713062306a36Sopenharmony_ci 713162306a36Sopenharmony_ci prt = ((tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >> 713262306a36Sopenharmony_ci TCAM_V4KEY2_PORT_SPI_SHIFT) & 0xffff; 713362306a36Sopenharmony_ci fsp->m_u.tcp_ip4_spec.pdst = cpu_to_be16(prt); 713462306a36Sopenharmony_ci break; 713562306a36Sopenharmony_ci case AH_V4_FLOW: 713662306a36Sopenharmony_ci case ESP_V4_FLOW: 713762306a36Sopenharmony_ci tmp = (tp->key[2] & TCAM_V4KEY2_PORT_SPI) >> 713862306a36Sopenharmony_ci TCAM_V4KEY2_PORT_SPI_SHIFT; 713962306a36Sopenharmony_ci fsp->h_u.ah_ip4_spec.spi = cpu_to_be32(tmp); 714062306a36Sopenharmony_ci 714162306a36Sopenharmony_ci tmp = (tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >> 714262306a36Sopenharmony_ci TCAM_V4KEY2_PORT_SPI_SHIFT; 714362306a36Sopenharmony_ci fsp->m_u.ah_ip4_spec.spi = cpu_to_be32(tmp); 714462306a36Sopenharmony_ci break; 714562306a36Sopenharmony_ci case IP_USER_FLOW: 714662306a36Sopenharmony_ci tmp = (tp->key[2] & TCAM_V4KEY2_PORT_SPI) >> 714762306a36Sopenharmony_ci TCAM_V4KEY2_PORT_SPI_SHIFT; 714862306a36Sopenharmony_ci fsp->h_u.usr_ip4_spec.l4_4_bytes = cpu_to_be32(tmp); 714962306a36Sopenharmony_ci 715062306a36Sopenharmony_ci tmp = (tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >> 715162306a36Sopenharmony_ci TCAM_V4KEY2_PORT_SPI_SHIFT; 715262306a36Sopenharmony_ci fsp->m_u.usr_ip4_spec.l4_4_bytes = cpu_to_be32(tmp); 715362306a36Sopenharmony_ci 715462306a36Sopenharmony_ci fsp->h_u.usr_ip4_spec.proto = 715562306a36Sopenharmony_ci (tp->key[2] & TCAM_V4KEY2_PROTO) >> 715662306a36Sopenharmony_ci TCAM_V4KEY2_PROTO_SHIFT; 715762306a36Sopenharmony_ci fsp->m_u.usr_ip4_spec.proto = 715862306a36Sopenharmony_ci (tp->key_mask[2] & TCAM_V4KEY2_PROTO) >> 715962306a36Sopenharmony_ci TCAM_V4KEY2_PROTO_SHIFT; 716062306a36Sopenharmony_ci 716162306a36Sopenharmony_ci fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4; 716262306a36Sopenharmony_ci break; 716362306a36Sopenharmony_ci default: 716462306a36Sopenharmony_ci break; 716562306a36Sopenharmony_ci } 716662306a36Sopenharmony_ci} 716762306a36Sopenharmony_ci 716862306a36Sopenharmony_cistatic int niu_get_ethtool_tcam_entry(struct niu *np, 716962306a36Sopenharmony_ci struct ethtool_rxnfc *nfc) 717062306a36Sopenharmony_ci{ 717162306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 717262306a36Sopenharmony_ci struct niu_tcam_entry *tp; 717362306a36Sopenharmony_ci struct ethtool_rx_flow_spec *fsp = &nfc->fs; 717462306a36Sopenharmony_ci u16 idx; 717562306a36Sopenharmony_ci u64 class; 717662306a36Sopenharmony_ci int ret = 0; 717762306a36Sopenharmony_ci 717862306a36Sopenharmony_ci idx = tcam_get_index(np, (u16)nfc->fs.location); 717962306a36Sopenharmony_ci 718062306a36Sopenharmony_ci tp = &parent->tcam[idx]; 718162306a36Sopenharmony_ci if (!tp->valid) { 718262306a36Sopenharmony_ci netdev_info(np->dev, "niu%d: entry [%d] invalid for idx[%d]\n", 718362306a36Sopenharmony_ci parent->index, (u16)nfc->fs.location, idx); 718462306a36Sopenharmony_ci return -EINVAL; 718562306a36Sopenharmony_ci } 718662306a36Sopenharmony_ci 718762306a36Sopenharmony_ci /* fill the flow spec entry */ 718862306a36Sopenharmony_ci class = (tp->key[0] & TCAM_V4KEY0_CLASS_CODE) >> 718962306a36Sopenharmony_ci TCAM_V4KEY0_CLASS_CODE_SHIFT; 719062306a36Sopenharmony_ci ret = niu_class_to_ethflow(class, &fsp->flow_type); 719162306a36Sopenharmony_ci if (ret < 0) { 719262306a36Sopenharmony_ci netdev_info(np->dev, "niu%d: niu_class_to_ethflow failed\n", 719362306a36Sopenharmony_ci parent->index); 719462306a36Sopenharmony_ci goto out; 719562306a36Sopenharmony_ci } 719662306a36Sopenharmony_ci 719762306a36Sopenharmony_ci if (fsp->flow_type == AH_V4_FLOW || fsp->flow_type == AH_V6_FLOW) { 719862306a36Sopenharmony_ci u32 proto = (tp->key[2] & TCAM_V4KEY2_PROTO) >> 719962306a36Sopenharmony_ci TCAM_V4KEY2_PROTO_SHIFT; 720062306a36Sopenharmony_ci if (proto == IPPROTO_ESP) { 720162306a36Sopenharmony_ci if (fsp->flow_type == AH_V4_FLOW) 720262306a36Sopenharmony_ci fsp->flow_type = ESP_V4_FLOW; 720362306a36Sopenharmony_ci else 720462306a36Sopenharmony_ci fsp->flow_type = ESP_V6_FLOW; 720562306a36Sopenharmony_ci } 720662306a36Sopenharmony_ci } 720762306a36Sopenharmony_ci 720862306a36Sopenharmony_ci switch (fsp->flow_type) { 720962306a36Sopenharmony_ci case TCP_V4_FLOW: 721062306a36Sopenharmony_ci case UDP_V4_FLOW: 721162306a36Sopenharmony_ci case SCTP_V4_FLOW: 721262306a36Sopenharmony_ci case AH_V4_FLOW: 721362306a36Sopenharmony_ci case ESP_V4_FLOW: 721462306a36Sopenharmony_ci niu_get_ip4fs_from_tcam_key(tp, fsp); 721562306a36Sopenharmony_ci break; 721662306a36Sopenharmony_ci case TCP_V6_FLOW: 721762306a36Sopenharmony_ci case UDP_V6_FLOW: 721862306a36Sopenharmony_ci case SCTP_V6_FLOW: 721962306a36Sopenharmony_ci case AH_V6_FLOW: 722062306a36Sopenharmony_ci case ESP_V6_FLOW: 722162306a36Sopenharmony_ci /* Not yet implemented */ 722262306a36Sopenharmony_ci ret = -EINVAL; 722362306a36Sopenharmony_ci break; 722462306a36Sopenharmony_ci case IP_USER_FLOW: 722562306a36Sopenharmony_ci niu_get_ip4fs_from_tcam_key(tp, fsp); 722662306a36Sopenharmony_ci break; 722762306a36Sopenharmony_ci default: 722862306a36Sopenharmony_ci ret = -EINVAL; 722962306a36Sopenharmony_ci break; 723062306a36Sopenharmony_ci } 723162306a36Sopenharmony_ci 723262306a36Sopenharmony_ci if (ret < 0) 723362306a36Sopenharmony_ci goto out; 723462306a36Sopenharmony_ci 723562306a36Sopenharmony_ci if (tp->assoc_data & TCAM_ASSOCDATA_DISC) 723662306a36Sopenharmony_ci fsp->ring_cookie = RX_CLS_FLOW_DISC; 723762306a36Sopenharmony_ci else 723862306a36Sopenharmony_ci fsp->ring_cookie = (tp->assoc_data & TCAM_ASSOCDATA_OFFSET) >> 723962306a36Sopenharmony_ci TCAM_ASSOCDATA_OFFSET_SHIFT; 724062306a36Sopenharmony_ci 724162306a36Sopenharmony_ci /* put the tcam size here */ 724262306a36Sopenharmony_ci nfc->data = tcam_get_size(np); 724362306a36Sopenharmony_ciout: 724462306a36Sopenharmony_ci return ret; 724562306a36Sopenharmony_ci} 724662306a36Sopenharmony_ci 724762306a36Sopenharmony_cistatic int niu_get_ethtool_tcam_all(struct niu *np, 724862306a36Sopenharmony_ci struct ethtool_rxnfc *nfc, 724962306a36Sopenharmony_ci u32 *rule_locs) 725062306a36Sopenharmony_ci{ 725162306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 725262306a36Sopenharmony_ci struct niu_tcam_entry *tp; 725362306a36Sopenharmony_ci int i, idx, cnt; 725462306a36Sopenharmony_ci unsigned long flags; 725562306a36Sopenharmony_ci int ret = 0; 725662306a36Sopenharmony_ci 725762306a36Sopenharmony_ci /* put the tcam size here */ 725862306a36Sopenharmony_ci nfc->data = tcam_get_size(np); 725962306a36Sopenharmony_ci 726062306a36Sopenharmony_ci niu_lock_parent(np, flags); 726162306a36Sopenharmony_ci for (cnt = 0, i = 0; i < nfc->data; i++) { 726262306a36Sopenharmony_ci idx = tcam_get_index(np, i); 726362306a36Sopenharmony_ci tp = &parent->tcam[idx]; 726462306a36Sopenharmony_ci if (!tp->valid) 726562306a36Sopenharmony_ci continue; 726662306a36Sopenharmony_ci if (cnt == nfc->rule_cnt) { 726762306a36Sopenharmony_ci ret = -EMSGSIZE; 726862306a36Sopenharmony_ci break; 726962306a36Sopenharmony_ci } 727062306a36Sopenharmony_ci rule_locs[cnt] = i; 727162306a36Sopenharmony_ci cnt++; 727262306a36Sopenharmony_ci } 727362306a36Sopenharmony_ci niu_unlock_parent(np, flags); 727462306a36Sopenharmony_ci 727562306a36Sopenharmony_ci nfc->rule_cnt = cnt; 727662306a36Sopenharmony_ci 727762306a36Sopenharmony_ci return ret; 727862306a36Sopenharmony_ci} 727962306a36Sopenharmony_ci 728062306a36Sopenharmony_cistatic int niu_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 728162306a36Sopenharmony_ci u32 *rule_locs) 728262306a36Sopenharmony_ci{ 728362306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 728462306a36Sopenharmony_ci int ret = 0; 728562306a36Sopenharmony_ci 728662306a36Sopenharmony_ci switch (cmd->cmd) { 728762306a36Sopenharmony_ci case ETHTOOL_GRXFH: 728862306a36Sopenharmony_ci ret = niu_get_hash_opts(np, cmd); 728962306a36Sopenharmony_ci break; 729062306a36Sopenharmony_ci case ETHTOOL_GRXRINGS: 729162306a36Sopenharmony_ci cmd->data = np->num_rx_rings; 729262306a36Sopenharmony_ci break; 729362306a36Sopenharmony_ci case ETHTOOL_GRXCLSRLCNT: 729462306a36Sopenharmony_ci cmd->rule_cnt = tcam_get_valid_entry_cnt(np); 729562306a36Sopenharmony_ci break; 729662306a36Sopenharmony_ci case ETHTOOL_GRXCLSRULE: 729762306a36Sopenharmony_ci ret = niu_get_ethtool_tcam_entry(np, cmd); 729862306a36Sopenharmony_ci break; 729962306a36Sopenharmony_ci case ETHTOOL_GRXCLSRLALL: 730062306a36Sopenharmony_ci ret = niu_get_ethtool_tcam_all(np, cmd, rule_locs); 730162306a36Sopenharmony_ci break; 730262306a36Sopenharmony_ci default: 730362306a36Sopenharmony_ci ret = -EINVAL; 730462306a36Sopenharmony_ci break; 730562306a36Sopenharmony_ci } 730662306a36Sopenharmony_ci 730762306a36Sopenharmony_ci return ret; 730862306a36Sopenharmony_ci} 730962306a36Sopenharmony_ci 731062306a36Sopenharmony_cistatic int niu_set_hash_opts(struct niu *np, struct ethtool_rxnfc *nfc) 731162306a36Sopenharmony_ci{ 731262306a36Sopenharmony_ci u64 class; 731362306a36Sopenharmony_ci u64 flow_key = 0; 731462306a36Sopenharmony_ci unsigned long flags; 731562306a36Sopenharmony_ci 731662306a36Sopenharmony_ci if (!niu_ethflow_to_class(nfc->flow_type, &class)) 731762306a36Sopenharmony_ci return -EINVAL; 731862306a36Sopenharmony_ci 731962306a36Sopenharmony_ci if (class < CLASS_CODE_USER_PROG1 || 732062306a36Sopenharmony_ci class > CLASS_CODE_SCTP_IPV6) 732162306a36Sopenharmony_ci return -EINVAL; 732262306a36Sopenharmony_ci 732362306a36Sopenharmony_ci if (nfc->data & RXH_DISCARD) { 732462306a36Sopenharmony_ci niu_lock_parent(np, flags); 732562306a36Sopenharmony_ci flow_key = np->parent->tcam_key[class - 732662306a36Sopenharmony_ci CLASS_CODE_USER_PROG1]; 732762306a36Sopenharmony_ci flow_key |= TCAM_KEY_DISC; 732862306a36Sopenharmony_ci nw64(TCAM_KEY(class - CLASS_CODE_USER_PROG1), flow_key); 732962306a36Sopenharmony_ci np->parent->tcam_key[class - CLASS_CODE_USER_PROG1] = flow_key; 733062306a36Sopenharmony_ci niu_unlock_parent(np, flags); 733162306a36Sopenharmony_ci return 0; 733262306a36Sopenharmony_ci } else { 733362306a36Sopenharmony_ci /* Discard was set before, but is not set now */ 733462306a36Sopenharmony_ci if (np->parent->tcam_key[class - CLASS_CODE_USER_PROG1] & 733562306a36Sopenharmony_ci TCAM_KEY_DISC) { 733662306a36Sopenharmony_ci niu_lock_parent(np, flags); 733762306a36Sopenharmony_ci flow_key = np->parent->tcam_key[class - 733862306a36Sopenharmony_ci CLASS_CODE_USER_PROG1]; 733962306a36Sopenharmony_ci flow_key &= ~TCAM_KEY_DISC; 734062306a36Sopenharmony_ci nw64(TCAM_KEY(class - CLASS_CODE_USER_PROG1), 734162306a36Sopenharmony_ci flow_key); 734262306a36Sopenharmony_ci np->parent->tcam_key[class - CLASS_CODE_USER_PROG1] = 734362306a36Sopenharmony_ci flow_key; 734462306a36Sopenharmony_ci niu_unlock_parent(np, flags); 734562306a36Sopenharmony_ci } 734662306a36Sopenharmony_ci } 734762306a36Sopenharmony_ci 734862306a36Sopenharmony_ci if (!niu_ethflow_to_flowkey(nfc->data, &flow_key)) 734962306a36Sopenharmony_ci return -EINVAL; 735062306a36Sopenharmony_ci 735162306a36Sopenharmony_ci niu_lock_parent(np, flags); 735262306a36Sopenharmony_ci nw64(FLOW_KEY(class - CLASS_CODE_USER_PROG1), flow_key); 735362306a36Sopenharmony_ci np->parent->flow_key[class - CLASS_CODE_USER_PROG1] = flow_key; 735462306a36Sopenharmony_ci niu_unlock_parent(np, flags); 735562306a36Sopenharmony_ci 735662306a36Sopenharmony_ci return 0; 735762306a36Sopenharmony_ci} 735862306a36Sopenharmony_ci 735962306a36Sopenharmony_cistatic void niu_get_tcamkey_from_ip4fs(struct ethtool_rx_flow_spec *fsp, 736062306a36Sopenharmony_ci struct niu_tcam_entry *tp, 736162306a36Sopenharmony_ci int l2_rdc_tab, u64 class) 736262306a36Sopenharmony_ci{ 736362306a36Sopenharmony_ci u8 pid = 0; 736462306a36Sopenharmony_ci u32 sip, dip, sipm, dipm, spi, spim; 736562306a36Sopenharmony_ci u16 sport, dport, spm, dpm; 736662306a36Sopenharmony_ci 736762306a36Sopenharmony_ci sip = be32_to_cpu(fsp->h_u.tcp_ip4_spec.ip4src); 736862306a36Sopenharmony_ci sipm = be32_to_cpu(fsp->m_u.tcp_ip4_spec.ip4src); 736962306a36Sopenharmony_ci dip = be32_to_cpu(fsp->h_u.tcp_ip4_spec.ip4dst); 737062306a36Sopenharmony_ci dipm = be32_to_cpu(fsp->m_u.tcp_ip4_spec.ip4dst); 737162306a36Sopenharmony_ci 737262306a36Sopenharmony_ci tp->key[0] = class << TCAM_V4KEY0_CLASS_CODE_SHIFT; 737362306a36Sopenharmony_ci tp->key_mask[0] = TCAM_V4KEY0_CLASS_CODE; 737462306a36Sopenharmony_ci tp->key[1] = (u64)l2_rdc_tab << TCAM_V4KEY1_L2RDCNUM_SHIFT; 737562306a36Sopenharmony_ci tp->key_mask[1] = TCAM_V4KEY1_L2RDCNUM; 737662306a36Sopenharmony_ci 737762306a36Sopenharmony_ci tp->key[3] = (u64)sip << TCAM_V4KEY3_SADDR_SHIFT; 737862306a36Sopenharmony_ci tp->key[3] |= dip; 737962306a36Sopenharmony_ci 738062306a36Sopenharmony_ci tp->key_mask[3] = (u64)sipm << TCAM_V4KEY3_SADDR_SHIFT; 738162306a36Sopenharmony_ci tp->key_mask[3] |= dipm; 738262306a36Sopenharmony_ci 738362306a36Sopenharmony_ci tp->key[2] |= ((u64)fsp->h_u.tcp_ip4_spec.tos << 738462306a36Sopenharmony_ci TCAM_V4KEY2_TOS_SHIFT); 738562306a36Sopenharmony_ci tp->key_mask[2] |= ((u64)fsp->m_u.tcp_ip4_spec.tos << 738662306a36Sopenharmony_ci TCAM_V4KEY2_TOS_SHIFT); 738762306a36Sopenharmony_ci switch (fsp->flow_type) { 738862306a36Sopenharmony_ci case TCP_V4_FLOW: 738962306a36Sopenharmony_ci case UDP_V4_FLOW: 739062306a36Sopenharmony_ci case SCTP_V4_FLOW: 739162306a36Sopenharmony_ci sport = be16_to_cpu(fsp->h_u.tcp_ip4_spec.psrc); 739262306a36Sopenharmony_ci spm = be16_to_cpu(fsp->m_u.tcp_ip4_spec.psrc); 739362306a36Sopenharmony_ci dport = be16_to_cpu(fsp->h_u.tcp_ip4_spec.pdst); 739462306a36Sopenharmony_ci dpm = be16_to_cpu(fsp->m_u.tcp_ip4_spec.pdst); 739562306a36Sopenharmony_ci 739662306a36Sopenharmony_ci tp->key[2] |= (((u64)sport << 16) | dport); 739762306a36Sopenharmony_ci tp->key_mask[2] |= (((u64)spm << 16) | dpm); 739862306a36Sopenharmony_ci niu_ethflow_to_l3proto(fsp->flow_type, &pid); 739962306a36Sopenharmony_ci break; 740062306a36Sopenharmony_ci case AH_V4_FLOW: 740162306a36Sopenharmony_ci case ESP_V4_FLOW: 740262306a36Sopenharmony_ci spi = be32_to_cpu(fsp->h_u.ah_ip4_spec.spi); 740362306a36Sopenharmony_ci spim = be32_to_cpu(fsp->m_u.ah_ip4_spec.spi); 740462306a36Sopenharmony_ci 740562306a36Sopenharmony_ci tp->key[2] |= spi; 740662306a36Sopenharmony_ci tp->key_mask[2] |= spim; 740762306a36Sopenharmony_ci niu_ethflow_to_l3proto(fsp->flow_type, &pid); 740862306a36Sopenharmony_ci break; 740962306a36Sopenharmony_ci case IP_USER_FLOW: 741062306a36Sopenharmony_ci spi = be32_to_cpu(fsp->h_u.usr_ip4_spec.l4_4_bytes); 741162306a36Sopenharmony_ci spim = be32_to_cpu(fsp->m_u.usr_ip4_spec.l4_4_bytes); 741262306a36Sopenharmony_ci 741362306a36Sopenharmony_ci tp->key[2] |= spi; 741462306a36Sopenharmony_ci tp->key_mask[2] |= spim; 741562306a36Sopenharmony_ci pid = fsp->h_u.usr_ip4_spec.proto; 741662306a36Sopenharmony_ci break; 741762306a36Sopenharmony_ci default: 741862306a36Sopenharmony_ci break; 741962306a36Sopenharmony_ci } 742062306a36Sopenharmony_ci 742162306a36Sopenharmony_ci tp->key[2] |= ((u64)pid << TCAM_V4KEY2_PROTO_SHIFT); 742262306a36Sopenharmony_ci if (pid) { 742362306a36Sopenharmony_ci tp->key_mask[2] |= TCAM_V4KEY2_PROTO; 742462306a36Sopenharmony_ci } 742562306a36Sopenharmony_ci} 742662306a36Sopenharmony_ci 742762306a36Sopenharmony_cistatic int niu_add_ethtool_tcam_entry(struct niu *np, 742862306a36Sopenharmony_ci struct ethtool_rxnfc *nfc) 742962306a36Sopenharmony_ci{ 743062306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 743162306a36Sopenharmony_ci struct niu_tcam_entry *tp; 743262306a36Sopenharmony_ci struct ethtool_rx_flow_spec *fsp = &nfc->fs; 743362306a36Sopenharmony_ci struct niu_rdc_tables *rdc_table = &parent->rdc_group_cfg[np->port]; 743462306a36Sopenharmony_ci int l2_rdc_table = rdc_table->first_table_num; 743562306a36Sopenharmony_ci u16 idx; 743662306a36Sopenharmony_ci u64 class; 743762306a36Sopenharmony_ci unsigned long flags; 743862306a36Sopenharmony_ci int err, ret; 743962306a36Sopenharmony_ci 744062306a36Sopenharmony_ci ret = 0; 744162306a36Sopenharmony_ci 744262306a36Sopenharmony_ci idx = nfc->fs.location; 744362306a36Sopenharmony_ci if (idx >= tcam_get_size(np)) 744462306a36Sopenharmony_ci return -EINVAL; 744562306a36Sopenharmony_ci 744662306a36Sopenharmony_ci if (fsp->flow_type == IP_USER_FLOW) { 744762306a36Sopenharmony_ci int i; 744862306a36Sopenharmony_ci int add_usr_cls = 0; 744962306a36Sopenharmony_ci struct ethtool_usrip4_spec *uspec = &fsp->h_u.usr_ip4_spec; 745062306a36Sopenharmony_ci struct ethtool_usrip4_spec *umask = &fsp->m_u.usr_ip4_spec; 745162306a36Sopenharmony_ci 745262306a36Sopenharmony_ci if (uspec->ip_ver != ETH_RX_NFC_IP4) 745362306a36Sopenharmony_ci return -EINVAL; 745462306a36Sopenharmony_ci 745562306a36Sopenharmony_ci niu_lock_parent(np, flags); 745662306a36Sopenharmony_ci 745762306a36Sopenharmony_ci for (i = 0; i < NIU_L3_PROG_CLS; i++) { 745862306a36Sopenharmony_ci if (parent->l3_cls[i]) { 745962306a36Sopenharmony_ci if (uspec->proto == parent->l3_cls_pid[i]) { 746062306a36Sopenharmony_ci class = parent->l3_cls[i]; 746162306a36Sopenharmony_ci parent->l3_cls_refcnt[i]++; 746262306a36Sopenharmony_ci add_usr_cls = 1; 746362306a36Sopenharmony_ci break; 746462306a36Sopenharmony_ci } 746562306a36Sopenharmony_ci } else { 746662306a36Sopenharmony_ci /* Program new user IP class */ 746762306a36Sopenharmony_ci switch (i) { 746862306a36Sopenharmony_ci case 0: 746962306a36Sopenharmony_ci class = CLASS_CODE_USER_PROG1; 747062306a36Sopenharmony_ci break; 747162306a36Sopenharmony_ci case 1: 747262306a36Sopenharmony_ci class = CLASS_CODE_USER_PROG2; 747362306a36Sopenharmony_ci break; 747462306a36Sopenharmony_ci case 2: 747562306a36Sopenharmony_ci class = CLASS_CODE_USER_PROG3; 747662306a36Sopenharmony_ci break; 747762306a36Sopenharmony_ci case 3: 747862306a36Sopenharmony_ci class = CLASS_CODE_USER_PROG4; 747962306a36Sopenharmony_ci break; 748062306a36Sopenharmony_ci default: 748162306a36Sopenharmony_ci class = CLASS_CODE_UNRECOG; 748262306a36Sopenharmony_ci break; 748362306a36Sopenharmony_ci } 748462306a36Sopenharmony_ci ret = tcam_user_ip_class_set(np, class, 0, 748562306a36Sopenharmony_ci uspec->proto, 748662306a36Sopenharmony_ci uspec->tos, 748762306a36Sopenharmony_ci umask->tos); 748862306a36Sopenharmony_ci if (ret) 748962306a36Sopenharmony_ci goto out; 749062306a36Sopenharmony_ci 749162306a36Sopenharmony_ci ret = tcam_user_ip_class_enable(np, class, 1); 749262306a36Sopenharmony_ci if (ret) 749362306a36Sopenharmony_ci goto out; 749462306a36Sopenharmony_ci parent->l3_cls[i] = class; 749562306a36Sopenharmony_ci parent->l3_cls_pid[i] = uspec->proto; 749662306a36Sopenharmony_ci parent->l3_cls_refcnt[i]++; 749762306a36Sopenharmony_ci add_usr_cls = 1; 749862306a36Sopenharmony_ci break; 749962306a36Sopenharmony_ci } 750062306a36Sopenharmony_ci } 750162306a36Sopenharmony_ci if (!add_usr_cls) { 750262306a36Sopenharmony_ci netdev_info(np->dev, "niu%d: %s(): Could not find/insert class for pid %d\n", 750362306a36Sopenharmony_ci parent->index, __func__, uspec->proto); 750462306a36Sopenharmony_ci ret = -EINVAL; 750562306a36Sopenharmony_ci goto out; 750662306a36Sopenharmony_ci } 750762306a36Sopenharmony_ci niu_unlock_parent(np, flags); 750862306a36Sopenharmony_ci } else { 750962306a36Sopenharmony_ci if (!niu_ethflow_to_class(fsp->flow_type, &class)) { 751062306a36Sopenharmony_ci return -EINVAL; 751162306a36Sopenharmony_ci } 751262306a36Sopenharmony_ci } 751362306a36Sopenharmony_ci 751462306a36Sopenharmony_ci niu_lock_parent(np, flags); 751562306a36Sopenharmony_ci 751662306a36Sopenharmony_ci idx = tcam_get_index(np, idx); 751762306a36Sopenharmony_ci tp = &parent->tcam[idx]; 751862306a36Sopenharmony_ci 751962306a36Sopenharmony_ci memset(tp, 0, sizeof(*tp)); 752062306a36Sopenharmony_ci 752162306a36Sopenharmony_ci /* fill in the tcam key and mask */ 752262306a36Sopenharmony_ci switch (fsp->flow_type) { 752362306a36Sopenharmony_ci case TCP_V4_FLOW: 752462306a36Sopenharmony_ci case UDP_V4_FLOW: 752562306a36Sopenharmony_ci case SCTP_V4_FLOW: 752662306a36Sopenharmony_ci case AH_V4_FLOW: 752762306a36Sopenharmony_ci case ESP_V4_FLOW: 752862306a36Sopenharmony_ci niu_get_tcamkey_from_ip4fs(fsp, tp, l2_rdc_table, class); 752962306a36Sopenharmony_ci break; 753062306a36Sopenharmony_ci case TCP_V6_FLOW: 753162306a36Sopenharmony_ci case UDP_V6_FLOW: 753262306a36Sopenharmony_ci case SCTP_V6_FLOW: 753362306a36Sopenharmony_ci case AH_V6_FLOW: 753462306a36Sopenharmony_ci case ESP_V6_FLOW: 753562306a36Sopenharmony_ci /* Not yet implemented */ 753662306a36Sopenharmony_ci netdev_info(np->dev, "niu%d: In %s(): flow %d for IPv6 not implemented\n", 753762306a36Sopenharmony_ci parent->index, __func__, fsp->flow_type); 753862306a36Sopenharmony_ci ret = -EINVAL; 753962306a36Sopenharmony_ci goto out; 754062306a36Sopenharmony_ci case IP_USER_FLOW: 754162306a36Sopenharmony_ci niu_get_tcamkey_from_ip4fs(fsp, tp, l2_rdc_table, class); 754262306a36Sopenharmony_ci break; 754362306a36Sopenharmony_ci default: 754462306a36Sopenharmony_ci netdev_info(np->dev, "niu%d: In %s(): Unknown flow type %d\n", 754562306a36Sopenharmony_ci parent->index, __func__, fsp->flow_type); 754662306a36Sopenharmony_ci ret = -EINVAL; 754762306a36Sopenharmony_ci goto out; 754862306a36Sopenharmony_ci } 754962306a36Sopenharmony_ci 755062306a36Sopenharmony_ci /* fill in the assoc data */ 755162306a36Sopenharmony_ci if (fsp->ring_cookie == RX_CLS_FLOW_DISC) { 755262306a36Sopenharmony_ci tp->assoc_data = TCAM_ASSOCDATA_DISC; 755362306a36Sopenharmony_ci } else { 755462306a36Sopenharmony_ci if (fsp->ring_cookie >= np->num_rx_rings) { 755562306a36Sopenharmony_ci netdev_info(np->dev, "niu%d: In %s(): Invalid RX ring %lld\n", 755662306a36Sopenharmony_ci parent->index, __func__, 755762306a36Sopenharmony_ci (long long)fsp->ring_cookie); 755862306a36Sopenharmony_ci ret = -EINVAL; 755962306a36Sopenharmony_ci goto out; 756062306a36Sopenharmony_ci } 756162306a36Sopenharmony_ci tp->assoc_data = (TCAM_ASSOCDATA_TRES_USE_OFFSET | 756262306a36Sopenharmony_ci (fsp->ring_cookie << 756362306a36Sopenharmony_ci TCAM_ASSOCDATA_OFFSET_SHIFT)); 756462306a36Sopenharmony_ci } 756562306a36Sopenharmony_ci 756662306a36Sopenharmony_ci err = tcam_write(np, idx, tp->key, tp->key_mask); 756762306a36Sopenharmony_ci if (err) { 756862306a36Sopenharmony_ci ret = -EINVAL; 756962306a36Sopenharmony_ci goto out; 757062306a36Sopenharmony_ci } 757162306a36Sopenharmony_ci err = tcam_assoc_write(np, idx, tp->assoc_data); 757262306a36Sopenharmony_ci if (err) { 757362306a36Sopenharmony_ci ret = -EINVAL; 757462306a36Sopenharmony_ci goto out; 757562306a36Sopenharmony_ci } 757662306a36Sopenharmony_ci 757762306a36Sopenharmony_ci /* validate the entry */ 757862306a36Sopenharmony_ci tp->valid = 1; 757962306a36Sopenharmony_ci np->clas.tcam_valid_entries++; 758062306a36Sopenharmony_ciout: 758162306a36Sopenharmony_ci niu_unlock_parent(np, flags); 758262306a36Sopenharmony_ci 758362306a36Sopenharmony_ci return ret; 758462306a36Sopenharmony_ci} 758562306a36Sopenharmony_ci 758662306a36Sopenharmony_cistatic int niu_del_ethtool_tcam_entry(struct niu *np, u32 loc) 758762306a36Sopenharmony_ci{ 758862306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 758962306a36Sopenharmony_ci struct niu_tcam_entry *tp; 759062306a36Sopenharmony_ci u16 idx; 759162306a36Sopenharmony_ci unsigned long flags; 759262306a36Sopenharmony_ci u64 class; 759362306a36Sopenharmony_ci int ret = 0; 759462306a36Sopenharmony_ci 759562306a36Sopenharmony_ci if (loc >= tcam_get_size(np)) 759662306a36Sopenharmony_ci return -EINVAL; 759762306a36Sopenharmony_ci 759862306a36Sopenharmony_ci niu_lock_parent(np, flags); 759962306a36Sopenharmony_ci 760062306a36Sopenharmony_ci idx = tcam_get_index(np, loc); 760162306a36Sopenharmony_ci tp = &parent->tcam[idx]; 760262306a36Sopenharmony_ci 760362306a36Sopenharmony_ci /* if the entry is of a user defined class, then update*/ 760462306a36Sopenharmony_ci class = (tp->key[0] & TCAM_V4KEY0_CLASS_CODE) >> 760562306a36Sopenharmony_ci TCAM_V4KEY0_CLASS_CODE_SHIFT; 760662306a36Sopenharmony_ci 760762306a36Sopenharmony_ci if (class >= CLASS_CODE_USER_PROG1 && class <= CLASS_CODE_USER_PROG4) { 760862306a36Sopenharmony_ci int i; 760962306a36Sopenharmony_ci for (i = 0; i < NIU_L3_PROG_CLS; i++) { 761062306a36Sopenharmony_ci if (parent->l3_cls[i] == class) { 761162306a36Sopenharmony_ci parent->l3_cls_refcnt[i]--; 761262306a36Sopenharmony_ci if (!parent->l3_cls_refcnt[i]) { 761362306a36Sopenharmony_ci /* disable class */ 761462306a36Sopenharmony_ci ret = tcam_user_ip_class_enable(np, 761562306a36Sopenharmony_ci class, 761662306a36Sopenharmony_ci 0); 761762306a36Sopenharmony_ci if (ret) 761862306a36Sopenharmony_ci goto out; 761962306a36Sopenharmony_ci parent->l3_cls[i] = 0; 762062306a36Sopenharmony_ci parent->l3_cls_pid[i] = 0; 762162306a36Sopenharmony_ci } 762262306a36Sopenharmony_ci break; 762362306a36Sopenharmony_ci } 762462306a36Sopenharmony_ci } 762562306a36Sopenharmony_ci if (i == NIU_L3_PROG_CLS) { 762662306a36Sopenharmony_ci netdev_info(np->dev, "niu%d: In %s(): Usr class 0x%llx not found\n", 762762306a36Sopenharmony_ci parent->index, __func__, 762862306a36Sopenharmony_ci (unsigned long long)class); 762962306a36Sopenharmony_ci ret = -EINVAL; 763062306a36Sopenharmony_ci goto out; 763162306a36Sopenharmony_ci } 763262306a36Sopenharmony_ci } 763362306a36Sopenharmony_ci 763462306a36Sopenharmony_ci ret = tcam_flush(np, idx); 763562306a36Sopenharmony_ci if (ret) 763662306a36Sopenharmony_ci goto out; 763762306a36Sopenharmony_ci 763862306a36Sopenharmony_ci /* invalidate the entry */ 763962306a36Sopenharmony_ci tp->valid = 0; 764062306a36Sopenharmony_ci np->clas.tcam_valid_entries--; 764162306a36Sopenharmony_ciout: 764262306a36Sopenharmony_ci niu_unlock_parent(np, flags); 764362306a36Sopenharmony_ci 764462306a36Sopenharmony_ci return ret; 764562306a36Sopenharmony_ci} 764662306a36Sopenharmony_ci 764762306a36Sopenharmony_cistatic int niu_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd) 764862306a36Sopenharmony_ci{ 764962306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 765062306a36Sopenharmony_ci int ret = 0; 765162306a36Sopenharmony_ci 765262306a36Sopenharmony_ci switch (cmd->cmd) { 765362306a36Sopenharmony_ci case ETHTOOL_SRXFH: 765462306a36Sopenharmony_ci ret = niu_set_hash_opts(np, cmd); 765562306a36Sopenharmony_ci break; 765662306a36Sopenharmony_ci case ETHTOOL_SRXCLSRLINS: 765762306a36Sopenharmony_ci ret = niu_add_ethtool_tcam_entry(np, cmd); 765862306a36Sopenharmony_ci break; 765962306a36Sopenharmony_ci case ETHTOOL_SRXCLSRLDEL: 766062306a36Sopenharmony_ci ret = niu_del_ethtool_tcam_entry(np, cmd->fs.location); 766162306a36Sopenharmony_ci break; 766262306a36Sopenharmony_ci default: 766362306a36Sopenharmony_ci ret = -EINVAL; 766462306a36Sopenharmony_ci break; 766562306a36Sopenharmony_ci } 766662306a36Sopenharmony_ci 766762306a36Sopenharmony_ci return ret; 766862306a36Sopenharmony_ci} 766962306a36Sopenharmony_ci 767062306a36Sopenharmony_cistatic const struct { 767162306a36Sopenharmony_ci const char string[ETH_GSTRING_LEN]; 767262306a36Sopenharmony_ci} niu_xmac_stat_keys[] = { 767362306a36Sopenharmony_ci { "tx_frames" }, 767462306a36Sopenharmony_ci { "tx_bytes" }, 767562306a36Sopenharmony_ci { "tx_fifo_errors" }, 767662306a36Sopenharmony_ci { "tx_overflow_errors" }, 767762306a36Sopenharmony_ci { "tx_max_pkt_size_errors" }, 767862306a36Sopenharmony_ci { "tx_underflow_errors" }, 767962306a36Sopenharmony_ci { "rx_local_faults" }, 768062306a36Sopenharmony_ci { "rx_remote_faults" }, 768162306a36Sopenharmony_ci { "rx_link_faults" }, 768262306a36Sopenharmony_ci { "rx_align_errors" }, 768362306a36Sopenharmony_ci { "rx_frags" }, 768462306a36Sopenharmony_ci { "rx_mcasts" }, 768562306a36Sopenharmony_ci { "rx_bcasts" }, 768662306a36Sopenharmony_ci { "rx_hist_cnt1" }, 768762306a36Sopenharmony_ci { "rx_hist_cnt2" }, 768862306a36Sopenharmony_ci { "rx_hist_cnt3" }, 768962306a36Sopenharmony_ci { "rx_hist_cnt4" }, 769062306a36Sopenharmony_ci { "rx_hist_cnt5" }, 769162306a36Sopenharmony_ci { "rx_hist_cnt6" }, 769262306a36Sopenharmony_ci { "rx_hist_cnt7" }, 769362306a36Sopenharmony_ci { "rx_octets" }, 769462306a36Sopenharmony_ci { "rx_code_violations" }, 769562306a36Sopenharmony_ci { "rx_len_errors" }, 769662306a36Sopenharmony_ci { "rx_crc_errors" }, 769762306a36Sopenharmony_ci { "rx_underflows" }, 769862306a36Sopenharmony_ci { "rx_overflows" }, 769962306a36Sopenharmony_ci { "pause_off_state" }, 770062306a36Sopenharmony_ci { "pause_on_state" }, 770162306a36Sopenharmony_ci { "pause_received" }, 770262306a36Sopenharmony_ci}; 770362306a36Sopenharmony_ci 770462306a36Sopenharmony_ci#define NUM_XMAC_STAT_KEYS ARRAY_SIZE(niu_xmac_stat_keys) 770562306a36Sopenharmony_ci 770662306a36Sopenharmony_cistatic const struct { 770762306a36Sopenharmony_ci const char string[ETH_GSTRING_LEN]; 770862306a36Sopenharmony_ci} niu_bmac_stat_keys[] = { 770962306a36Sopenharmony_ci { "tx_underflow_errors" }, 771062306a36Sopenharmony_ci { "tx_max_pkt_size_errors" }, 771162306a36Sopenharmony_ci { "tx_bytes" }, 771262306a36Sopenharmony_ci { "tx_frames" }, 771362306a36Sopenharmony_ci { "rx_overflows" }, 771462306a36Sopenharmony_ci { "rx_frames" }, 771562306a36Sopenharmony_ci { "rx_align_errors" }, 771662306a36Sopenharmony_ci { "rx_crc_errors" }, 771762306a36Sopenharmony_ci { "rx_len_errors" }, 771862306a36Sopenharmony_ci { "pause_off_state" }, 771962306a36Sopenharmony_ci { "pause_on_state" }, 772062306a36Sopenharmony_ci { "pause_received" }, 772162306a36Sopenharmony_ci}; 772262306a36Sopenharmony_ci 772362306a36Sopenharmony_ci#define NUM_BMAC_STAT_KEYS ARRAY_SIZE(niu_bmac_stat_keys) 772462306a36Sopenharmony_ci 772562306a36Sopenharmony_cistatic const struct { 772662306a36Sopenharmony_ci const char string[ETH_GSTRING_LEN]; 772762306a36Sopenharmony_ci} niu_rxchan_stat_keys[] = { 772862306a36Sopenharmony_ci { "rx_channel" }, 772962306a36Sopenharmony_ci { "rx_packets" }, 773062306a36Sopenharmony_ci { "rx_bytes" }, 773162306a36Sopenharmony_ci { "rx_dropped" }, 773262306a36Sopenharmony_ci { "rx_errors" }, 773362306a36Sopenharmony_ci}; 773462306a36Sopenharmony_ci 773562306a36Sopenharmony_ci#define NUM_RXCHAN_STAT_KEYS ARRAY_SIZE(niu_rxchan_stat_keys) 773662306a36Sopenharmony_ci 773762306a36Sopenharmony_cistatic const struct { 773862306a36Sopenharmony_ci const char string[ETH_GSTRING_LEN]; 773962306a36Sopenharmony_ci} niu_txchan_stat_keys[] = { 774062306a36Sopenharmony_ci { "tx_channel" }, 774162306a36Sopenharmony_ci { "tx_packets" }, 774262306a36Sopenharmony_ci { "tx_bytes" }, 774362306a36Sopenharmony_ci { "tx_errors" }, 774462306a36Sopenharmony_ci}; 774562306a36Sopenharmony_ci 774662306a36Sopenharmony_ci#define NUM_TXCHAN_STAT_KEYS ARRAY_SIZE(niu_txchan_stat_keys) 774762306a36Sopenharmony_ci 774862306a36Sopenharmony_cistatic void niu_get_strings(struct net_device *dev, u32 stringset, u8 *data) 774962306a36Sopenharmony_ci{ 775062306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 775162306a36Sopenharmony_ci int i; 775262306a36Sopenharmony_ci 775362306a36Sopenharmony_ci if (stringset != ETH_SS_STATS) 775462306a36Sopenharmony_ci return; 775562306a36Sopenharmony_ci 775662306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) { 775762306a36Sopenharmony_ci memcpy(data, niu_xmac_stat_keys, 775862306a36Sopenharmony_ci sizeof(niu_xmac_stat_keys)); 775962306a36Sopenharmony_ci data += sizeof(niu_xmac_stat_keys); 776062306a36Sopenharmony_ci } else { 776162306a36Sopenharmony_ci memcpy(data, niu_bmac_stat_keys, 776262306a36Sopenharmony_ci sizeof(niu_bmac_stat_keys)); 776362306a36Sopenharmony_ci data += sizeof(niu_bmac_stat_keys); 776462306a36Sopenharmony_ci } 776562306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 776662306a36Sopenharmony_ci memcpy(data, niu_rxchan_stat_keys, 776762306a36Sopenharmony_ci sizeof(niu_rxchan_stat_keys)); 776862306a36Sopenharmony_ci data += sizeof(niu_rxchan_stat_keys); 776962306a36Sopenharmony_ci } 777062306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 777162306a36Sopenharmony_ci memcpy(data, niu_txchan_stat_keys, 777262306a36Sopenharmony_ci sizeof(niu_txchan_stat_keys)); 777362306a36Sopenharmony_ci data += sizeof(niu_txchan_stat_keys); 777462306a36Sopenharmony_ci } 777562306a36Sopenharmony_ci} 777662306a36Sopenharmony_ci 777762306a36Sopenharmony_cistatic int niu_get_sset_count(struct net_device *dev, int stringset) 777862306a36Sopenharmony_ci{ 777962306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 778062306a36Sopenharmony_ci 778162306a36Sopenharmony_ci if (stringset != ETH_SS_STATS) 778262306a36Sopenharmony_ci return -EINVAL; 778362306a36Sopenharmony_ci 778462306a36Sopenharmony_ci return (np->flags & NIU_FLAGS_XMAC ? 778562306a36Sopenharmony_ci NUM_XMAC_STAT_KEYS : 778662306a36Sopenharmony_ci NUM_BMAC_STAT_KEYS) + 778762306a36Sopenharmony_ci (np->num_rx_rings * NUM_RXCHAN_STAT_KEYS) + 778862306a36Sopenharmony_ci (np->num_tx_rings * NUM_TXCHAN_STAT_KEYS); 778962306a36Sopenharmony_ci} 779062306a36Sopenharmony_ci 779162306a36Sopenharmony_cistatic void niu_get_ethtool_stats(struct net_device *dev, 779262306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 779362306a36Sopenharmony_ci{ 779462306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 779562306a36Sopenharmony_ci int i; 779662306a36Sopenharmony_ci 779762306a36Sopenharmony_ci niu_sync_mac_stats(np); 779862306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) { 779962306a36Sopenharmony_ci memcpy(data, &np->mac_stats.xmac, 780062306a36Sopenharmony_ci sizeof(struct niu_xmac_stats)); 780162306a36Sopenharmony_ci data += (sizeof(struct niu_xmac_stats) / sizeof(u64)); 780262306a36Sopenharmony_ci } else { 780362306a36Sopenharmony_ci memcpy(data, &np->mac_stats.bmac, 780462306a36Sopenharmony_ci sizeof(struct niu_bmac_stats)); 780562306a36Sopenharmony_ci data += (sizeof(struct niu_bmac_stats) / sizeof(u64)); 780662306a36Sopenharmony_ci } 780762306a36Sopenharmony_ci for (i = 0; i < np->num_rx_rings; i++) { 780862306a36Sopenharmony_ci struct rx_ring_info *rp = &np->rx_rings[i]; 780962306a36Sopenharmony_ci 781062306a36Sopenharmony_ci niu_sync_rx_discard_stats(np, rp, 0); 781162306a36Sopenharmony_ci 781262306a36Sopenharmony_ci data[0] = rp->rx_channel; 781362306a36Sopenharmony_ci data[1] = rp->rx_packets; 781462306a36Sopenharmony_ci data[2] = rp->rx_bytes; 781562306a36Sopenharmony_ci data[3] = rp->rx_dropped; 781662306a36Sopenharmony_ci data[4] = rp->rx_errors; 781762306a36Sopenharmony_ci data += 5; 781862306a36Sopenharmony_ci } 781962306a36Sopenharmony_ci for (i = 0; i < np->num_tx_rings; i++) { 782062306a36Sopenharmony_ci struct tx_ring_info *rp = &np->tx_rings[i]; 782162306a36Sopenharmony_ci 782262306a36Sopenharmony_ci data[0] = rp->tx_channel; 782362306a36Sopenharmony_ci data[1] = rp->tx_packets; 782462306a36Sopenharmony_ci data[2] = rp->tx_bytes; 782562306a36Sopenharmony_ci data[3] = rp->tx_errors; 782662306a36Sopenharmony_ci data += 4; 782762306a36Sopenharmony_ci } 782862306a36Sopenharmony_ci} 782962306a36Sopenharmony_ci 783062306a36Sopenharmony_cistatic u64 niu_led_state_save(struct niu *np) 783162306a36Sopenharmony_ci{ 783262306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 783362306a36Sopenharmony_ci return nr64_mac(XMAC_CONFIG); 783462306a36Sopenharmony_ci else 783562306a36Sopenharmony_ci return nr64_mac(BMAC_XIF_CONFIG); 783662306a36Sopenharmony_ci} 783762306a36Sopenharmony_ci 783862306a36Sopenharmony_cistatic void niu_led_state_restore(struct niu *np, u64 val) 783962306a36Sopenharmony_ci{ 784062306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) 784162306a36Sopenharmony_ci nw64_mac(XMAC_CONFIG, val); 784262306a36Sopenharmony_ci else 784362306a36Sopenharmony_ci nw64_mac(BMAC_XIF_CONFIG, val); 784462306a36Sopenharmony_ci} 784562306a36Sopenharmony_ci 784662306a36Sopenharmony_cistatic void niu_force_led(struct niu *np, int on) 784762306a36Sopenharmony_ci{ 784862306a36Sopenharmony_ci u64 val, reg, bit; 784962306a36Sopenharmony_ci 785062306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_XMAC) { 785162306a36Sopenharmony_ci reg = XMAC_CONFIG; 785262306a36Sopenharmony_ci bit = XMAC_CONFIG_FORCE_LED_ON; 785362306a36Sopenharmony_ci } else { 785462306a36Sopenharmony_ci reg = BMAC_XIF_CONFIG; 785562306a36Sopenharmony_ci bit = BMAC_XIF_CONFIG_LINK_LED; 785662306a36Sopenharmony_ci } 785762306a36Sopenharmony_ci 785862306a36Sopenharmony_ci val = nr64_mac(reg); 785962306a36Sopenharmony_ci if (on) 786062306a36Sopenharmony_ci val |= bit; 786162306a36Sopenharmony_ci else 786262306a36Sopenharmony_ci val &= ~bit; 786362306a36Sopenharmony_ci nw64_mac(reg, val); 786462306a36Sopenharmony_ci} 786562306a36Sopenharmony_ci 786662306a36Sopenharmony_cistatic int niu_set_phys_id(struct net_device *dev, 786762306a36Sopenharmony_ci enum ethtool_phys_id_state state) 786862306a36Sopenharmony_ci 786962306a36Sopenharmony_ci{ 787062306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 787162306a36Sopenharmony_ci 787262306a36Sopenharmony_ci if (!netif_running(dev)) 787362306a36Sopenharmony_ci return -EAGAIN; 787462306a36Sopenharmony_ci 787562306a36Sopenharmony_ci switch (state) { 787662306a36Sopenharmony_ci case ETHTOOL_ID_ACTIVE: 787762306a36Sopenharmony_ci np->orig_led_state = niu_led_state_save(np); 787862306a36Sopenharmony_ci return 1; /* cycle on/off once per second */ 787962306a36Sopenharmony_ci 788062306a36Sopenharmony_ci case ETHTOOL_ID_ON: 788162306a36Sopenharmony_ci niu_force_led(np, 1); 788262306a36Sopenharmony_ci break; 788362306a36Sopenharmony_ci 788462306a36Sopenharmony_ci case ETHTOOL_ID_OFF: 788562306a36Sopenharmony_ci niu_force_led(np, 0); 788662306a36Sopenharmony_ci break; 788762306a36Sopenharmony_ci 788862306a36Sopenharmony_ci case ETHTOOL_ID_INACTIVE: 788962306a36Sopenharmony_ci niu_led_state_restore(np, np->orig_led_state); 789062306a36Sopenharmony_ci } 789162306a36Sopenharmony_ci 789262306a36Sopenharmony_ci return 0; 789362306a36Sopenharmony_ci} 789462306a36Sopenharmony_ci 789562306a36Sopenharmony_cistatic const struct ethtool_ops niu_ethtool_ops = { 789662306a36Sopenharmony_ci .get_drvinfo = niu_get_drvinfo, 789762306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 789862306a36Sopenharmony_ci .get_msglevel = niu_get_msglevel, 789962306a36Sopenharmony_ci .set_msglevel = niu_set_msglevel, 790062306a36Sopenharmony_ci .nway_reset = niu_nway_reset, 790162306a36Sopenharmony_ci .get_eeprom_len = niu_get_eeprom_len, 790262306a36Sopenharmony_ci .get_eeprom = niu_get_eeprom, 790362306a36Sopenharmony_ci .get_strings = niu_get_strings, 790462306a36Sopenharmony_ci .get_sset_count = niu_get_sset_count, 790562306a36Sopenharmony_ci .get_ethtool_stats = niu_get_ethtool_stats, 790662306a36Sopenharmony_ci .set_phys_id = niu_set_phys_id, 790762306a36Sopenharmony_ci .get_rxnfc = niu_get_nfc, 790862306a36Sopenharmony_ci .set_rxnfc = niu_set_nfc, 790962306a36Sopenharmony_ci .get_link_ksettings = niu_get_link_ksettings, 791062306a36Sopenharmony_ci .set_link_ksettings = niu_set_link_ksettings, 791162306a36Sopenharmony_ci}; 791262306a36Sopenharmony_ci 791362306a36Sopenharmony_cistatic int niu_ldg_assign_ldn(struct niu *np, struct niu_parent *parent, 791462306a36Sopenharmony_ci int ldg, int ldn) 791562306a36Sopenharmony_ci{ 791662306a36Sopenharmony_ci if (ldg < NIU_LDG_MIN || ldg > NIU_LDG_MAX) 791762306a36Sopenharmony_ci return -EINVAL; 791862306a36Sopenharmony_ci if (ldn < 0 || ldn > LDN_MAX) 791962306a36Sopenharmony_ci return -EINVAL; 792062306a36Sopenharmony_ci 792162306a36Sopenharmony_ci parent->ldg_map[ldn] = ldg; 792262306a36Sopenharmony_ci 792362306a36Sopenharmony_ci if (np->parent->plat_type == PLAT_TYPE_NIU) { 792462306a36Sopenharmony_ci /* On N2 NIU, the ldn-->ldg assignments are setup and fixed by 792562306a36Sopenharmony_ci * the firmware, and we're not supposed to change them. 792662306a36Sopenharmony_ci * Validate the mapping, because if it's wrong we probably 792762306a36Sopenharmony_ci * won't get any interrupts and that's painful to debug. 792862306a36Sopenharmony_ci */ 792962306a36Sopenharmony_ci if (nr64(LDG_NUM(ldn)) != ldg) { 793062306a36Sopenharmony_ci dev_err(np->device, "Port %u, mismatched LDG assignment for ldn %d, should be %d is %llu\n", 793162306a36Sopenharmony_ci np->port, ldn, ldg, 793262306a36Sopenharmony_ci (unsigned long long) nr64(LDG_NUM(ldn))); 793362306a36Sopenharmony_ci return -EINVAL; 793462306a36Sopenharmony_ci } 793562306a36Sopenharmony_ci } else 793662306a36Sopenharmony_ci nw64(LDG_NUM(ldn), ldg); 793762306a36Sopenharmony_ci 793862306a36Sopenharmony_ci return 0; 793962306a36Sopenharmony_ci} 794062306a36Sopenharmony_ci 794162306a36Sopenharmony_cistatic int niu_set_ldg_timer_res(struct niu *np, int res) 794262306a36Sopenharmony_ci{ 794362306a36Sopenharmony_ci if (res < 0 || res > LDG_TIMER_RES_VAL) 794462306a36Sopenharmony_ci return -EINVAL; 794562306a36Sopenharmony_ci 794662306a36Sopenharmony_ci 794762306a36Sopenharmony_ci nw64(LDG_TIMER_RES, res); 794862306a36Sopenharmony_ci 794962306a36Sopenharmony_ci return 0; 795062306a36Sopenharmony_ci} 795162306a36Sopenharmony_ci 795262306a36Sopenharmony_cistatic int niu_set_ldg_sid(struct niu *np, int ldg, int func, int vector) 795362306a36Sopenharmony_ci{ 795462306a36Sopenharmony_ci if ((ldg < NIU_LDG_MIN || ldg > NIU_LDG_MAX) || 795562306a36Sopenharmony_ci (func < 0 || func > 3) || 795662306a36Sopenharmony_ci (vector < 0 || vector > 0x1f)) 795762306a36Sopenharmony_ci return -EINVAL; 795862306a36Sopenharmony_ci 795962306a36Sopenharmony_ci nw64(SID(ldg), (func << SID_FUNC_SHIFT) | vector); 796062306a36Sopenharmony_ci 796162306a36Sopenharmony_ci return 0; 796262306a36Sopenharmony_ci} 796362306a36Sopenharmony_ci 796462306a36Sopenharmony_cistatic int niu_pci_eeprom_read(struct niu *np, u32 addr) 796562306a36Sopenharmony_ci{ 796662306a36Sopenharmony_ci u64 frame, frame_base = (ESPC_PIO_STAT_READ_START | 796762306a36Sopenharmony_ci (addr << ESPC_PIO_STAT_ADDR_SHIFT)); 796862306a36Sopenharmony_ci int limit; 796962306a36Sopenharmony_ci 797062306a36Sopenharmony_ci if (addr > (ESPC_PIO_STAT_ADDR >> ESPC_PIO_STAT_ADDR_SHIFT)) 797162306a36Sopenharmony_ci return -EINVAL; 797262306a36Sopenharmony_ci 797362306a36Sopenharmony_ci frame = frame_base; 797462306a36Sopenharmony_ci nw64(ESPC_PIO_STAT, frame); 797562306a36Sopenharmony_ci limit = 64; 797662306a36Sopenharmony_ci do { 797762306a36Sopenharmony_ci udelay(5); 797862306a36Sopenharmony_ci frame = nr64(ESPC_PIO_STAT); 797962306a36Sopenharmony_ci if (frame & ESPC_PIO_STAT_READ_END) 798062306a36Sopenharmony_ci break; 798162306a36Sopenharmony_ci } while (limit--); 798262306a36Sopenharmony_ci if (!(frame & ESPC_PIO_STAT_READ_END)) { 798362306a36Sopenharmony_ci dev_err(np->device, "EEPROM read timeout frame[%llx]\n", 798462306a36Sopenharmony_ci (unsigned long long) frame); 798562306a36Sopenharmony_ci return -ENODEV; 798662306a36Sopenharmony_ci } 798762306a36Sopenharmony_ci 798862306a36Sopenharmony_ci frame = frame_base; 798962306a36Sopenharmony_ci nw64(ESPC_PIO_STAT, frame); 799062306a36Sopenharmony_ci limit = 64; 799162306a36Sopenharmony_ci do { 799262306a36Sopenharmony_ci udelay(5); 799362306a36Sopenharmony_ci frame = nr64(ESPC_PIO_STAT); 799462306a36Sopenharmony_ci if (frame & ESPC_PIO_STAT_READ_END) 799562306a36Sopenharmony_ci break; 799662306a36Sopenharmony_ci } while (limit--); 799762306a36Sopenharmony_ci if (!(frame & ESPC_PIO_STAT_READ_END)) { 799862306a36Sopenharmony_ci dev_err(np->device, "EEPROM read timeout frame[%llx]\n", 799962306a36Sopenharmony_ci (unsigned long long) frame); 800062306a36Sopenharmony_ci return -ENODEV; 800162306a36Sopenharmony_ci } 800262306a36Sopenharmony_ci 800362306a36Sopenharmony_ci frame = nr64(ESPC_PIO_STAT); 800462306a36Sopenharmony_ci return (frame & ESPC_PIO_STAT_DATA) >> ESPC_PIO_STAT_DATA_SHIFT; 800562306a36Sopenharmony_ci} 800662306a36Sopenharmony_ci 800762306a36Sopenharmony_cistatic int niu_pci_eeprom_read16(struct niu *np, u32 off) 800862306a36Sopenharmony_ci{ 800962306a36Sopenharmony_ci int err = niu_pci_eeprom_read(np, off); 801062306a36Sopenharmony_ci u16 val; 801162306a36Sopenharmony_ci 801262306a36Sopenharmony_ci if (err < 0) 801362306a36Sopenharmony_ci return err; 801462306a36Sopenharmony_ci val = (err << 8); 801562306a36Sopenharmony_ci err = niu_pci_eeprom_read(np, off + 1); 801662306a36Sopenharmony_ci if (err < 0) 801762306a36Sopenharmony_ci return err; 801862306a36Sopenharmony_ci val |= (err & 0xff); 801962306a36Sopenharmony_ci 802062306a36Sopenharmony_ci return val; 802162306a36Sopenharmony_ci} 802262306a36Sopenharmony_ci 802362306a36Sopenharmony_cistatic int niu_pci_eeprom_read16_swp(struct niu *np, u32 off) 802462306a36Sopenharmony_ci{ 802562306a36Sopenharmony_ci int err = niu_pci_eeprom_read(np, off); 802662306a36Sopenharmony_ci u16 val; 802762306a36Sopenharmony_ci 802862306a36Sopenharmony_ci if (err < 0) 802962306a36Sopenharmony_ci return err; 803062306a36Sopenharmony_ci 803162306a36Sopenharmony_ci val = (err & 0xff); 803262306a36Sopenharmony_ci err = niu_pci_eeprom_read(np, off + 1); 803362306a36Sopenharmony_ci if (err < 0) 803462306a36Sopenharmony_ci return err; 803562306a36Sopenharmony_ci 803662306a36Sopenharmony_ci val |= (err & 0xff) << 8; 803762306a36Sopenharmony_ci 803862306a36Sopenharmony_ci return val; 803962306a36Sopenharmony_ci} 804062306a36Sopenharmony_ci 804162306a36Sopenharmony_cistatic int niu_pci_vpd_get_propname(struct niu *np, u32 off, char *namebuf, 804262306a36Sopenharmony_ci int namebuf_len) 804362306a36Sopenharmony_ci{ 804462306a36Sopenharmony_ci int i; 804562306a36Sopenharmony_ci 804662306a36Sopenharmony_ci for (i = 0; i < namebuf_len; i++) { 804762306a36Sopenharmony_ci int err = niu_pci_eeprom_read(np, off + i); 804862306a36Sopenharmony_ci if (err < 0) 804962306a36Sopenharmony_ci return err; 805062306a36Sopenharmony_ci *namebuf++ = err; 805162306a36Sopenharmony_ci if (!err) 805262306a36Sopenharmony_ci break; 805362306a36Sopenharmony_ci } 805462306a36Sopenharmony_ci if (i >= namebuf_len) 805562306a36Sopenharmony_ci return -EINVAL; 805662306a36Sopenharmony_ci 805762306a36Sopenharmony_ci return i + 1; 805862306a36Sopenharmony_ci} 805962306a36Sopenharmony_ci 806062306a36Sopenharmony_cistatic void niu_vpd_parse_version(struct niu *np) 806162306a36Sopenharmony_ci{ 806262306a36Sopenharmony_ci struct niu_vpd *vpd = &np->vpd; 806362306a36Sopenharmony_ci int len = strlen(vpd->version) + 1; 806462306a36Sopenharmony_ci const char *s = vpd->version; 806562306a36Sopenharmony_ci int i; 806662306a36Sopenharmony_ci 806762306a36Sopenharmony_ci for (i = 0; i < len - 5; i++) { 806862306a36Sopenharmony_ci if (!strncmp(s + i, "FCode ", 6)) 806962306a36Sopenharmony_ci break; 807062306a36Sopenharmony_ci } 807162306a36Sopenharmony_ci if (i >= len - 5) 807262306a36Sopenharmony_ci return; 807362306a36Sopenharmony_ci 807462306a36Sopenharmony_ci s += i + 5; 807562306a36Sopenharmony_ci sscanf(s, "%d.%d", &vpd->fcode_major, &vpd->fcode_minor); 807662306a36Sopenharmony_ci 807762306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 807862306a36Sopenharmony_ci "VPD_SCAN: FCODE major(%d) minor(%d)\n", 807962306a36Sopenharmony_ci vpd->fcode_major, vpd->fcode_minor); 808062306a36Sopenharmony_ci if (vpd->fcode_major > NIU_VPD_MIN_MAJOR || 808162306a36Sopenharmony_ci (vpd->fcode_major == NIU_VPD_MIN_MAJOR && 808262306a36Sopenharmony_ci vpd->fcode_minor >= NIU_VPD_MIN_MINOR)) 808362306a36Sopenharmony_ci np->flags |= NIU_FLAGS_VPD_VALID; 808462306a36Sopenharmony_ci} 808562306a36Sopenharmony_ci 808662306a36Sopenharmony_ci/* ESPC_PIO_EN_ENABLE must be set */ 808762306a36Sopenharmony_cistatic int niu_pci_vpd_scan_props(struct niu *np, u32 start, u32 end) 808862306a36Sopenharmony_ci{ 808962306a36Sopenharmony_ci unsigned int found_mask = 0; 809062306a36Sopenharmony_ci#define FOUND_MASK_MODEL 0x00000001 809162306a36Sopenharmony_ci#define FOUND_MASK_BMODEL 0x00000002 809262306a36Sopenharmony_ci#define FOUND_MASK_VERS 0x00000004 809362306a36Sopenharmony_ci#define FOUND_MASK_MAC 0x00000008 809462306a36Sopenharmony_ci#define FOUND_MASK_NMAC 0x00000010 809562306a36Sopenharmony_ci#define FOUND_MASK_PHY 0x00000020 809662306a36Sopenharmony_ci#define FOUND_MASK_ALL 0x0000003f 809762306a36Sopenharmony_ci 809862306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 809962306a36Sopenharmony_ci "VPD_SCAN: start[%x] end[%x]\n", start, end); 810062306a36Sopenharmony_ci while (start < end) { 810162306a36Sopenharmony_ci int len, err, prop_len; 810262306a36Sopenharmony_ci char namebuf[64]; 810362306a36Sopenharmony_ci u8 *prop_buf; 810462306a36Sopenharmony_ci int max_len; 810562306a36Sopenharmony_ci 810662306a36Sopenharmony_ci if (found_mask == FOUND_MASK_ALL) { 810762306a36Sopenharmony_ci niu_vpd_parse_version(np); 810862306a36Sopenharmony_ci return 1; 810962306a36Sopenharmony_ci } 811062306a36Sopenharmony_ci 811162306a36Sopenharmony_ci err = niu_pci_eeprom_read(np, start + 2); 811262306a36Sopenharmony_ci if (err < 0) 811362306a36Sopenharmony_ci return err; 811462306a36Sopenharmony_ci len = err; 811562306a36Sopenharmony_ci start += 3; 811662306a36Sopenharmony_ci 811762306a36Sopenharmony_ci prop_len = niu_pci_eeprom_read(np, start + 4); 811862306a36Sopenharmony_ci if (prop_len < 0) 811962306a36Sopenharmony_ci return prop_len; 812062306a36Sopenharmony_ci err = niu_pci_vpd_get_propname(np, start + 5, namebuf, 64); 812162306a36Sopenharmony_ci if (err < 0) 812262306a36Sopenharmony_ci return err; 812362306a36Sopenharmony_ci 812462306a36Sopenharmony_ci prop_buf = NULL; 812562306a36Sopenharmony_ci max_len = 0; 812662306a36Sopenharmony_ci if (!strcmp(namebuf, "model")) { 812762306a36Sopenharmony_ci prop_buf = np->vpd.model; 812862306a36Sopenharmony_ci max_len = NIU_VPD_MODEL_MAX; 812962306a36Sopenharmony_ci found_mask |= FOUND_MASK_MODEL; 813062306a36Sopenharmony_ci } else if (!strcmp(namebuf, "board-model")) { 813162306a36Sopenharmony_ci prop_buf = np->vpd.board_model; 813262306a36Sopenharmony_ci max_len = NIU_VPD_BD_MODEL_MAX; 813362306a36Sopenharmony_ci found_mask |= FOUND_MASK_BMODEL; 813462306a36Sopenharmony_ci } else if (!strcmp(namebuf, "version")) { 813562306a36Sopenharmony_ci prop_buf = np->vpd.version; 813662306a36Sopenharmony_ci max_len = NIU_VPD_VERSION_MAX; 813762306a36Sopenharmony_ci found_mask |= FOUND_MASK_VERS; 813862306a36Sopenharmony_ci } else if (!strcmp(namebuf, "local-mac-address")) { 813962306a36Sopenharmony_ci prop_buf = np->vpd.local_mac; 814062306a36Sopenharmony_ci max_len = ETH_ALEN; 814162306a36Sopenharmony_ci found_mask |= FOUND_MASK_MAC; 814262306a36Sopenharmony_ci } else if (!strcmp(namebuf, "num-mac-addresses")) { 814362306a36Sopenharmony_ci prop_buf = &np->vpd.mac_num; 814462306a36Sopenharmony_ci max_len = 1; 814562306a36Sopenharmony_ci found_mask |= FOUND_MASK_NMAC; 814662306a36Sopenharmony_ci } else if (!strcmp(namebuf, "phy-type")) { 814762306a36Sopenharmony_ci prop_buf = np->vpd.phy_type; 814862306a36Sopenharmony_ci max_len = NIU_VPD_PHY_TYPE_MAX; 814962306a36Sopenharmony_ci found_mask |= FOUND_MASK_PHY; 815062306a36Sopenharmony_ci } 815162306a36Sopenharmony_ci 815262306a36Sopenharmony_ci if (max_len && prop_len > max_len) { 815362306a36Sopenharmony_ci dev_err(np->device, "Property '%s' length (%d) is too long\n", namebuf, prop_len); 815462306a36Sopenharmony_ci return -EINVAL; 815562306a36Sopenharmony_ci } 815662306a36Sopenharmony_ci 815762306a36Sopenharmony_ci if (prop_buf) { 815862306a36Sopenharmony_ci u32 off = start + 5 + err; 815962306a36Sopenharmony_ci int i; 816062306a36Sopenharmony_ci 816162306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 816262306a36Sopenharmony_ci "VPD_SCAN: Reading in property [%s] len[%d]\n", 816362306a36Sopenharmony_ci namebuf, prop_len); 816462306a36Sopenharmony_ci for (i = 0; i < prop_len; i++) { 816562306a36Sopenharmony_ci err = niu_pci_eeprom_read(np, off + i); 816662306a36Sopenharmony_ci if (err < 0) 816762306a36Sopenharmony_ci return err; 816862306a36Sopenharmony_ci *prop_buf++ = err; 816962306a36Sopenharmony_ci } 817062306a36Sopenharmony_ci } 817162306a36Sopenharmony_ci 817262306a36Sopenharmony_ci start += len; 817362306a36Sopenharmony_ci } 817462306a36Sopenharmony_ci 817562306a36Sopenharmony_ci return 0; 817662306a36Sopenharmony_ci} 817762306a36Sopenharmony_ci 817862306a36Sopenharmony_ci/* ESPC_PIO_EN_ENABLE must be set */ 817962306a36Sopenharmony_cistatic int niu_pci_vpd_fetch(struct niu *np, u32 start) 818062306a36Sopenharmony_ci{ 818162306a36Sopenharmony_ci u32 offset; 818262306a36Sopenharmony_ci int err; 818362306a36Sopenharmony_ci 818462306a36Sopenharmony_ci err = niu_pci_eeprom_read16_swp(np, start + 1); 818562306a36Sopenharmony_ci if (err < 0) 818662306a36Sopenharmony_ci return err; 818762306a36Sopenharmony_ci 818862306a36Sopenharmony_ci offset = err + 3; 818962306a36Sopenharmony_ci 819062306a36Sopenharmony_ci while (start + offset < ESPC_EEPROM_SIZE) { 819162306a36Sopenharmony_ci u32 here = start + offset; 819262306a36Sopenharmony_ci u32 end; 819362306a36Sopenharmony_ci 819462306a36Sopenharmony_ci err = niu_pci_eeprom_read(np, here); 819562306a36Sopenharmony_ci if (err < 0) 819662306a36Sopenharmony_ci return err; 819762306a36Sopenharmony_ci if (err != 0x90) 819862306a36Sopenharmony_ci return -EINVAL; 819962306a36Sopenharmony_ci 820062306a36Sopenharmony_ci err = niu_pci_eeprom_read16_swp(np, here + 1); 820162306a36Sopenharmony_ci if (err < 0) 820262306a36Sopenharmony_ci return err; 820362306a36Sopenharmony_ci 820462306a36Sopenharmony_ci here = start + offset + 3; 820562306a36Sopenharmony_ci end = start + offset + err; 820662306a36Sopenharmony_ci 820762306a36Sopenharmony_ci offset += err; 820862306a36Sopenharmony_ci 820962306a36Sopenharmony_ci err = niu_pci_vpd_scan_props(np, here, end); 821062306a36Sopenharmony_ci if (err < 0) 821162306a36Sopenharmony_ci return err; 821262306a36Sopenharmony_ci /* ret == 1 is not an error */ 821362306a36Sopenharmony_ci if (err == 1) 821462306a36Sopenharmony_ci return 0; 821562306a36Sopenharmony_ci } 821662306a36Sopenharmony_ci return 0; 821762306a36Sopenharmony_ci} 821862306a36Sopenharmony_ci 821962306a36Sopenharmony_ci/* ESPC_PIO_EN_ENABLE must be set */ 822062306a36Sopenharmony_cistatic u32 niu_pci_vpd_offset(struct niu *np) 822162306a36Sopenharmony_ci{ 822262306a36Sopenharmony_ci u32 start = 0, end = ESPC_EEPROM_SIZE, ret; 822362306a36Sopenharmony_ci int err; 822462306a36Sopenharmony_ci 822562306a36Sopenharmony_ci while (start < end) { 822662306a36Sopenharmony_ci ret = start; 822762306a36Sopenharmony_ci 822862306a36Sopenharmony_ci /* ROM header signature? */ 822962306a36Sopenharmony_ci err = niu_pci_eeprom_read16(np, start + 0); 823062306a36Sopenharmony_ci if (err != 0x55aa) 823162306a36Sopenharmony_ci return 0; 823262306a36Sopenharmony_ci 823362306a36Sopenharmony_ci /* Apply offset to PCI data structure. */ 823462306a36Sopenharmony_ci err = niu_pci_eeprom_read16(np, start + 23); 823562306a36Sopenharmony_ci if (err < 0) 823662306a36Sopenharmony_ci return 0; 823762306a36Sopenharmony_ci start += err; 823862306a36Sopenharmony_ci 823962306a36Sopenharmony_ci /* Check for "PCIR" signature. */ 824062306a36Sopenharmony_ci err = niu_pci_eeprom_read16(np, start + 0); 824162306a36Sopenharmony_ci if (err != 0x5043) 824262306a36Sopenharmony_ci return 0; 824362306a36Sopenharmony_ci err = niu_pci_eeprom_read16(np, start + 2); 824462306a36Sopenharmony_ci if (err != 0x4952) 824562306a36Sopenharmony_ci return 0; 824662306a36Sopenharmony_ci 824762306a36Sopenharmony_ci /* Check for OBP image type. */ 824862306a36Sopenharmony_ci err = niu_pci_eeprom_read(np, start + 20); 824962306a36Sopenharmony_ci if (err < 0) 825062306a36Sopenharmony_ci return 0; 825162306a36Sopenharmony_ci if (err != 0x01) { 825262306a36Sopenharmony_ci err = niu_pci_eeprom_read(np, ret + 2); 825362306a36Sopenharmony_ci if (err < 0) 825462306a36Sopenharmony_ci return 0; 825562306a36Sopenharmony_ci 825662306a36Sopenharmony_ci start = ret + (err * 512); 825762306a36Sopenharmony_ci continue; 825862306a36Sopenharmony_ci } 825962306a36Sopenharmony_ci 826062306a36Sopenharmony_ci err = niu_pci_eeprom_read16_swp(np, start + 8); 826162306a36Sopenharmony_ci if (err < 0) 826262306a36Sopenharmony_ci return err; 826362306a36Sopenharmony_ci ret += err; 826462306a36Sopenharmony_ci 826562306a36Sopenharmony_ci err = niu_pci_eeprom_read(np, ret + 0); 826662306a36Sopenharmony_ci if (err != 0x82) 826762306a36Sopenharmony_ci return 0; 826862306a36Sopenharmony_ci 826962306a36Sopenharmony_ci return ret; 827062306a36Sopenharmony_ci } 827162306a36Sopenharmony_ci 827262306a36Sopenharmony_ci return 0; 827362306a36Sopenharmony_ci} 827462306a36Sopenharmony_ci 827562306a36Sopenharmony_cistatic int niu_phy_type_prop_decode(struct niu *np, const char *phy_prop) 827662306a36Sopenharmony_ci{ 827762306a36Sopenharmony_ci if (!strcmp(phy_prop, "mif")) { 827862306a36Sopenharmony_ci /* 1G copper, MII */ 827962306a36Sopenharmony_ci np->flags &= ~(NIU_FLAGS_FIBER | 828062306a36Sopenharmony_ci NIU_FLAGS_10G); 828162306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_MII; 828262306a36Sopenharmony_ci } else if (!strcmp(phy_prop, "xgf")) { 828362306a36Sopenharmony_ci /* 10G fiber, XPCS */ 828462306a36Sopenharmony_ci np->flags |= (NIU_FLAGS_10G | 828562306a36Sopenharmony_ci NIU_FLAGS_FIBER); 828662306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_XPCS; 828762306a36Sopenharmony_ci } else if (!strcmp(phy_prop, "pcs")) { 828862306a36Sopenharmony_ci /* 1G fiber, PCS */ 828962306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_10G; 829062306a36Sopenharmony_ci np->flags |= NIU_FLAGS_FIBER; 829162306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_PCS; 829262306a36Sopenharmony_ci } else if (!strcmp(phy_prop, "xgc")) { 829362306a36Sopenharmony_ci /* 10G copper, XPCS */ 829462306a36Sopenharmony_ci np->flags |= NIU_FLAGS_10G; 829562306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_FIBER; 829662306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_XPCS; 829762306a36Sopenharmony_ci } else if (!strcmp(phy_prop, "xgsd") || !strcmp(phy_prop, "gsd")) { 829862306a36Sopenharmony_ci /* 10G Serdes or 1G Serdes, default to 10G */ 829962306a36Sopenharmony_ci np->flags |= NIU_FLAGS_10G; 830062306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_FIBER; 830162306a36Sopenharmony_ci np->flags |= NIU_FLAGS_XCVR_SERDES; 830262306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_XPCS; 830362306a36Sopenharmony_ci } else { 830462306a36Sopenharmony_ci return -EINVAL; 830562306a36Sopenharmony_ci } 830662306a36Sopenharmony_ci return 0; 830762306a36Sopenharmony_ci} 830862306a36Sopenharmony_ci 830962306a36Sopenharmony_cistatic int niu_pci_vpd_get_nports(struct niu *np) 831062306a36Sopenharmony_ci{ 831162306a36Sopenharmony_ci int ports = 0; 831262306a36Sopenharmony_ci 831362306a36Sopenharmony_ci if ((!strcmp(np->vpd.model, NIU_QGC_LP_MDL_STR)) || 831462306a36Sopenharmony_ci (!strcmp(np->vpd.model, NIU_QGC_PEM_MDL_STR)) || 831562306a36Sopenharmony_ci (!strcmp(np->vpd.model, NIU_MARAMBA_MDL_STR)) || 831662306a36Sopenharmony_ci (!strcmp(np->vpd.model, NIU_KIMI_MDL_STR)) || 831762306a36Sopenharmony_ci (!strcmp(np->vpd.model, NIU_ALONSO_MDL_STR))) { 831862306a36Sopenharmony_ci ports = 4; 831962306a36Sopenharmony_ci } else if ((!strcmp(np->vpd.model, NIU_2XGF_LP_MDL_STR)) || 832062306a36Sopenharmony_ci (!strcmp(np->vpd.model, NIU_2XGF_PEM_MDL_STR)) || 832162306a36Sopenharmony_ci (!strcmp(np->vpd.model, NIU_FOXXY_MDL_STR)) || 832262306a36Sopenharmony_ci (!strcmp(np->vpd.model, NIU_2XGF_MRVL_MDL_STR))) { 832362306a36Sopenharmony_ci ports = 2; 832462306a36Sopenharmony_ci } 832562306a36Sopenharmony_ci 832662306a36Sopenharmony_ci return ports; 832762306a36Sopenharmony_ci} 832862306a36Sopenharmony_ci 832962306a36Sopenharmony_cistatic void niu_pci_vpd_validate(struct niu *np) 833062306a36Sopenharmony_ci{ 833162306a36Sopenharmony_ci struct net_device *dev = np->dev; 833262306a36Sopenharmony_ci struct niu_vpd *vpd = &np->vpd; 833362306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 833462306a36Sopenharmony_ci u8 val8; 833562306a36Sopenharmony_ci 833662306a36Sopenharmony_ci if (!is_valid_ether_addr(&vpd->local_mac[0])) { 833762306a36Sopenharmony_ci dev_err(np->device, "VPD MAC invalid, falling back to SPROM\n"); 833862306a36Sopenharmony_ci 833962306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_VPD_VALID; 834062306a36Sopenharmony_ci return; 834162306a36Sopenharmony_ci } 834262306a36Sopenharmony_ci 834362306a36Sopenharmony_ci if (!strcmp(np->vpd.model, NIU_ALONSO_MDL_STR) || 834462306a36Sopenharmony_ci !strcmp(np->vpd.model, NIU_KIMI_MDL_STR)) { 834562306a36Sopenharmony_ci np->flags |= NIU_FLAGS_10G; 834662306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_FIBER; 834762306a36Sopenharmony_ci np->flags |= NIU_FLAGS_XCVR_SERDES; 834862306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_PCS; 834962306a36Sopenharmony_ci if (np->port > 1) { 835062306a36Sopenharmony_ci np->flags |= NIU_FLAGS_FIBER; 835162306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_10G; 835262306a36Sopenharmony_ci } 835362306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_10G) 835462306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_XPCS; 835562306a36Sopenharmony_ci } else if (!strcmp(np->vpd.model, NIU_FOXXY_MDL_STR)) { 835662306a36Sopenharmony_ci np->flags |= (NIU_FLAGS_10G | NIU_FLAGS_FIBER | 835762306a36Sopenharmony_ci NIU_FLAGS_HOTPLUG_PHY); 835862306a36Sopenharmony_ci } else if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) { 835962306a36Sopenharmony_ci dev_err(np->device, "Illegal phy string [%s]\n", 836062306a36Sopenharmony_ci np->vpd.phy_type); 836162306a36Sopenharmony_ci dev_err(np->device, "Falling back to SPROM\n"); 836262306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_VPD_VALID; 836362306a36Sopenharmony_ci return; 836462306a36Sopenharmony_ci } 836562306a36Sopenharmony_ci 836662306a36Sopenharmony_ci ether_addr_copy(addr, vpd->local_mac); 836762306a36Sopenharmony_ci 836862306a36Sopenharmony_ci val8 = addr[5]; 836962306a36Sopenharmony_ci addr[5] += np->port; 837062306a36Sopenharmony_ci if (addr[5] < val8) 837162306a36Sopenharmony_ci addr[4]++; 837262306a36Sopenharmony_ci 837362306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 837462306a36Sopenharmony_ci} 837562306a36Sopenharmony_ci 837662306a36Sopenharmony_cistatic int niu_pci_probe_sprom(struct niu *np) 837762306a36Sopenharmony_ci{ 837862306a36Sopenharmony_ci struct net_device *dev = np->dev; 837962306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 838062306a36Sopenharmony_ci int len, i; 838162306a36Sopenharmony_ci u64 val, sum; 838262306a36Sopenharmony_ci u8 val8; 838362306a36Sopenharmony_ci 838462306a36Sopenharmony_ci val = (nr64(ESPC_VER_IMGSZ) & ESPC_VER_IMGSZ_IMGSZ); 838562306a36Sopenharmony_ci val >>= ESPC_VER_IMGSZ_IMGSZ_SHIFT; 838662306a36Sopenharmony_ci len = val / 4; 838762306a36Sopenharmony_ci 838862306a36Sopenharmony_ci np->eeprom_len = len; 838962306a36Sopenharmony_ci 839062306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 839162306a36Sopenharmony_ci "SPROM: Image size %llu\n", (unsigned long long)val); 839262306a36Sopenharmony_ci 839362306a36Sopenharmony_ci sum = 0; 839462306a36Sopenharmony_ci for (i = 0; i < len; i++) { 839562306a36Sopenharmony_ci val = nr64(ESPC_NCR(i)); 839662306a36Sopenharmony_ci sum += (val >> 0) & 0xff; 839762306a36Sopenharmony_ci sum += (val >> 8) & 0xff; 839862306a36Sopenharmony_ci sum += (val >> 16) & 0xff; 839962306a36Sopenharmony_ci sum += (val >> 24) & 0xff; 840062306a36Sopenharmony_ci } 840162306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 840262306a36Sopenharmony_ci "SPROM: Checksum %x\n", (int)(sum & 0xff)); 840362306a36Sopenharmony_ci if ((sum & 0xff) != 0xab) { 840462306a36Sopenharmony_ci dev_err(np->device, "Bad SPROM checksum (%x, should be 0xab)\n", (int)(sum & 0xff)); 840562306a36Sopenharmony_ci return -EINVAL; 840662306a36Sopenharmony_ci } 840762306a36Sopenharmony_ci 840862306a36Sopenharmony_ci val = nr64(ESPC_PHY_TYPE); 840962306a36Sopenharmony_ci switch (np->port) { 841062306a36Sopenharmony_ci case 0: 841162306a36Sopenharmony_ci val8 = (val & ESPC_PHY_TYPE_PORT0) >> 841262306a36Sopenharmony_ci ESPC_PHY_TYPE_PORT0_SHIFT; 841362306a36Sopenharmony_ci break; 841462306a36Sopenharmony_ci case 1: 841562306a36Sopenharmony_ci val8 = (val & ESPC_PHY_TYPE_PORT1) >> 841662306a36Sopenharmony_ci ESPC_PHY_TYPE_PORT1_SHIFT; 841762306a36Sopenharmony_ci break; 841862306a36Sopenharmony_ci case 2: 841962306a36Sopenharmony_ci val8 = (val & ESPC_PHY_TYPE_PORT2) >> 842062306a36Sopenharmony_ci ESPC_PHY_TYPE_PORT2_SHIFT; 842162306a36Sopenharmony_ci break; 842262306a36Sopenharmony_ci case 3: 842362306a36Sopenharmony_ci val8 = (val & ESPC_PHY_TYPE_PORT3) >> 842462306a36Sopenharmony_ci ESPC_PHY_TYPE_PORT3_SHIFT; 842562306a36Sopenharmony_ci break; 842662306a36Sopenharmony_ci default: 842762306a36Sopenharmony_ci dev_err(np->device, "Bogus port number %u\n", 842862306a36Sopenharmony_ci np->port); 842962306a36Sopenharmony_ci return -EINVAL; 843062306a36Sopenharmony_ci } 843162306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 843262306a36Sopenharmony_ci "SPROM: PHY type %x\n", val8); 843362306a36Sopenharmony_ci 843462306a36Sopenharmony_ci switch (val8) { 843562306a36Sopenharmony_ci case ESPC_PHY_TYPE_1G_COPPER: 843662306a36Sopenharmony_ci /* 1G copper, MII */ 843762306a36Sopenharmony_ci np->flags &= ~(NIU_FLAGS_FIBER | 843862306a36Sopenharmony_ci NIU_FLAGS_10G); 843962306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_MII; 844062306a36Sopenharmony_ci break; 844162306a36Sopenharmony_ci 844262306a36Sopenharmony_ci case ESPC_PHY_TYPE_1G_FIBER: 844362306a36Sopenharmony_ci /* 1G fiber, PCS */ 844462306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_10G; 844562306a36Sopenharmony_ci np->flags |= NIU_FLAGS_FIBER; 844662306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_PCS; 844762306a36Sopenharmony_ci break; 844862306a36Sopenharmony_ci 844962306a36Sopenharmony_ci case ESPC_PHY_TYPE_10G_COPPER: 845062306a36Sopenharmony_ci /* 10G copper, XPCS */ 845162306a36Sopenharmony_ci np->flags |= NIU_FLAGS_10G; 845262306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_FIBER; 845362306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_XPCS; 845462306a36Sopenharmony_ci break; 845562306a36Sopenharmony_ci 845662306a36Sopenharmony_ci case ESPC_PHY_TYPE_10G_FIBER: 845762306a36Sopenharmony_ci /* 10G fiber, XPCS */ 845862306a36Sopenharmony_ci np->flags |= (NIU_FLAGS_10G | 845962306a36Sopenharmony_ci NIU_FLAGS_FIBER); 846062306a36Sopenharmony_ci np->mac_xcvr = MAC_XCVR_XPCS; 846162306a36Sopenharmony_ci break; 846262306a36Sopenharmony_ci 846362306a36Sopenharmony_ci default: 846462306a36Sopenharmony_ci dev_err(np->device, "Bogus SPROM phy type %u\n", val8); 846562306a36Sopenharmony_ci return -EINVAL; 846662306a36Sopenharmony_ci } 846762306a36Sopenharmony_ci 846862306a36Sopenharmony_ci val = nr64(ESPC_MAC_ADDR0); 846962306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 847062306a36Sopenharmony_ci "SPROM: MAC_ADDR0[%08llx]\n", (unsigned long long)val); 847162306a36Sopenharmony_ci addr[0] = (val >> 0) & 0xff; 847262306a36Sopenharmony_ci addr[1] = (val >> 8) & 0xff; 847362306a36Sopenharmony_ci addr[2] = (val >> 16) & 0xff; 847462306a36Sopenharmony_ci addr[3] = (val >> 24) & 0xff; 847562306a36Sopenharmony_ci 847662306a36Sopenharmony_ci val = nr64(ESPC_MAC_ADDR1); 847762306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 847862306a36Sopenharmony_ci "SPROM: MAC_ADDR1[%08llx]\n", (unsigned long long)val); 847962306a36Sopenharmony_ci addr[4] = (val >> 0) & 0xff; 848062306a36Sopenharmony_ci addr[5] = (val >> 8) & 0xff; 848162306a36Sopenharmony_ci 848262306a36Sopenharmony_ci if (!is_valid_ether_addr(addr)) { 848362306a36Sopenharmony_ci dev_err(np->device, "SPROM MAC address invalid [ %pM ]\n", 848462306a36Sopenharmony_ci addr); 848562306a36Sopenharmony_ci return -EINVAL; 848662306a36Sopenharmony_ci } 848762306a36Sopenharmony_ci 848862306a36Sopenharmony_ci val8 = addr[5]; 848962306a36Sopenharmony_ci addr[5] += np->port; 849062306a36Sopenharmony_ci if (addr[5] < val8) 849162306a36Sopenharmony_ci addr[4]++; 849262306a36Sopenharmony_ci 849362306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 849462306a36Sopenharmony_ci 849562306a36Sopenharmony_ci val = nr64(ESPC_MOD_STR_LEN); 849662306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 849762306a36Sopenharmony_ci "SPROM: MOD_STR_LEN[%llu]\n", (unsigned long long)val); 849862306a36Sopenharmony_ci if (val >= 8 * 4) 849962306a36Sopenharmony_ci return -EINVAL; 850062306a36Sopenharmony_ci 850162306a36Sopenharmony_ci for (i = 0; i < val; i += 4) { 850262306a36Sopenharmony_ci u64 tmp = nr64(ESPC_NCR(5 + (i / 4))); 850362306a36Sopenharmony_ci 850462306a36Sopenharmony_ci np->vpd.model[i + 3] = (tmp >> 0) & 0xff; 850562306a36Sopenharmony_ci np->vpd.model[i + 2] = (tmp >> 8) & 0xff; 850662306a36Sopenharmony_ci np->vpd.model[i + 1] = (tmp >> 16) & 0xff; 850762306a36Sopenharmony_ci np->vpd.model[i + 0] = (tmp >> 24) & 0xff; 850862306a36Sopenharmony_ci } 850962306a36Sopenharmony_ci np->vpd.model[val] = '\0'; 851062306a36Sopenharmony_ci 851162306a36Sopenharmony_ci val = nr64(ESPC_BD_MOD_STR_LEN); 851262306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 851362306a36Sopenharmony_ci "SPROM: BD_MOD_STR_LEN[%llu]\n", (unsigned long long)val); 851462306a36Sopenharmony_ci if (val >= 4 * 4) 851562306a36Sopenharmony_ci return -EINVAL; 851662306a36Sopenharmony_ci 851762306a36Sopenharmony_ci for (i = 0; i < val; i += 4) { 851862306a36Sopenharmony_ci u64 tmp = nr64(ESPC_NCR(14 + (i / 4))); 851962306a36Sopenharmony_ci 852062306a36Sopenharmony_ci np->vpd.board_model[i + 3] = (tmp >> 0) & 0xff; 852162306a36Sopenharmony_ci np->vpd.board_model[i + 2] = (tmp >> 8) & 0xff; 852262306a36Sopenharmony_ci np->vpd.board_model[i + 1] = (tmp >> 16) & 0xff; 852362306a36Sopenharmony_ci np->vpd.board_model[i + 0] = (tmp >> 24) & 0xff; 852462306a36Sopenharmony_ci } 852562306a36Sopenharmony_ci np->vpd.board_model[val] = '\0'; 852662306a36Sopenharmony_ci 852762306a36Sopenharmony_ci np->vpd.mac_num = 852862306a36Sopenharmony_ci nr64(ESPC_NUM_PORTS_MACS) & ESPC_NUM_PORTS_MACS_VAL; 852962306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 853062306a36Sopenharmony_ci "SPROM: NUM_PORTS_MACS[%d]\n", np->vpd.mac_num); 853162306a36Sopenharmony_ci 853262306a36Sopenharmony_ci return 0; 853362306a36Sopenharmony_ci} 853462306a36Sopenharmony_ci 853562306a36Sopenharmony_cistatic int niu_get_and_validate_port(struct niu *np) 853662306a36Sopenharmony_ci{ 853762306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 853862306a36Sopenharmony_ci 853962306a36Sopenharmony_ci if (np->port <= 1) 854062306a36Sopenharmony_ci np->flags |= NIU_FLAGS_XMAC; 854162306a36Sopenharmony_ci 854262306a36Sopenharmony_ci if (!parent->num_ports) { 854362306a36Sopenharmony_ci if (parent->plat_type == PLAT_TYPE_NIU) { 854462306a36Sopenharmony_ci parent->num_ports = 2; 854562306a36Sopenharmony_ci } else { 854662306a36Sopenharmony_ci parent->num_ports = niu_pci_vpd_get_nports(np); 854762306a36Sopenharmony_ci if (!parent->num_ports) { 854862306a36Sopenharmony_ci /* Fall back to SPROM as last resort. 854962306a36Sopenharmony_ci * This will fail on most cards. 855062306a36Sopenharmony_ci */ 855162306a36Sopenharmony_ci parent->num_ports = nr64(ESPC_NUM_PORTS_MACS) & 855262306a36Sopenharmony_ci ESPC_NUM_PORTS_MACS_VAL; 855362306a36Sopenharmony_ci 855462306a36Sopenharmony_ci /* All of the current probing methods fail on 855562306a36Sopenharmony_ci * Maramba on-board parts. 855662306a36Sopenharmony_ci */ 855762306a36Sopenharmony_ci if (!parent->num_ports) 855862306a36Sopenharmony_ci parent->num_ports = 4; 855962306a36Sopenharmony_ci } 856062306a36Sopenharmony_ci } 856162306a36Sopenharmony_ci } 856262306a36Sopenharmony_ci 856362306a36Sopenharmony_ci if (np->port >= parent->num_ports) 856462306a36Sopenharmony_ci return -ENODEV; 856562306a36Sopenharmony_ci 856662306a36Sopenharmony_ci return 0; 856762306a36Sopenharmony_ci} 856862306a36Sopenharmony_ci 856962306a36Sopenharmony_cistatic int phy_record(struct niu_parent *parent, struct phy_probe_info *p, 857062306a36Sopenharmony_ci int dev_id_1, int dev_id_2, u8 phy_port, int type) 857162306a36Sopenharmony_ci{ 857262306a36Sopenharmony_ci u32 id = (dev_id_1 << 16) | dev_id_2; 857362306a36Sopenharmony_ci u8 idx; 857462306a36Sopenharmony_ci 857562306a36Sopenharmony_ci if (dev_id_1 < 0 || dev_id_2 < 0) 857662306a36Sopenharmony_ci return 0; 857762306a36Sopenharmony_ci if (type == PHY_TYPE_PMA_PMD || type == PHY_TYPE_PCS) { 857862306a36Sopenharmony_ci /* Because of the NIU_PHY_ID_MASK being applied, the 8704 857962306a36Sopenharmony_ci * test covers the 8706 as well. 858062306a36Sopenharmony_ci */ 858162306a36Sopenharmony_ci if (((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8704) && 858262306a36Sopenharmony_ci ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_MRVL88X2011)) 858362306a36Sopenharmony_ci return 0; 858462306a36Sopenharmony_ci } else { 858562306a36Sopenharmony_ci if ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM5464R) 858662306a36Sopenharmony_ci return 0; 858762306a36Sopenharmony_ci } 858862306a36Sopenharmony_ci 858962306a36Sopenharmony_ci pr_info("niu%d: Found PHY %08x type %s at phy_port %u\n", 859062306a36Sopenharmony_ci parent->index, id, 859162306a36Sopenharmony_ci type == PHY_TYPE_PMA_PMD ? "PMA/PMD" : 859262306a36Sopenharmony_ci type == PHY_TYPE_PCS ? "PCS" : "MII", 859362306a36Sopenharmony_ci phy_port); 859462306a36Sopenharmony_ci 859562306a36Sopenharmony_ci if (p->cur[type] >= NIU_MAX_PORTS) { 859662306a36Sopenharmony_ci pr_err("Too many PHY ports\n"); 859762306a36Sopenharmony_ci return -EINVAL; 859862306a36Sopenharmony_ci } 859962306a36Sopenharmony_ci idx = p->cur[type]; 860062306a36Sopenharmony_ci p->phy_id[type][idx] = id; 860162306a36Sopenharmony_ci p->phy_port[type][idx] = phy_port; 860262306a36Sopenharmony_ci p->cur[type] = idx + 1; 860362306a36Sopenharmony_ci return 0; 860462306a36Sopenharmony_ci} 860562306a36Sopenharmony_ci 860662306a36Sopenharmony_cistatic int port_has_10g(struct phy_probe_info *p, int port) 860762306a36Sopenharmony_ci{ 860862306a36Sopenharmony_ci int i; 860962306a36Sopenharmony_ci 861062306a36Sopenharmony_ci for (i = 0; i < p->cur[PHY_TYPE_PMA_PMD]; i++) { 861162306a36Sopenharmony_ci if (p->phy_port[PHY_TYPE_PMA_PMD][i] == port) 861262306a36Sopenharmony_ci return 1; 861362306a36Sopenharmony_ci } 861462306a36Sopenharmony_ci for (i = 0; i < p->cur[PHY_TYPE_PCS]; i++) { 861562306a36Sopenharmony_ci if (p->phy_port[PHY_TYPE_PCS][i] == port) 861662306a36Sopenharmony_ci return 1; 861762306a36Sopenharmony_ci } 861862306a36Sopenharmony_ci 861962306a36Sopenharmony_ci return 0; 862062306a36Sopenharmony_ci} 862162306a36Sopenharmony_ci 862262306a36Sopenharmony_cistatic int count_10g_ports(struct phy_probe_info *p, int *lowest) 862362306a36Sopenharmony_ci{ 862462306a36Sopenharmony_ci int port, cnt; 862562306a36Sopenharmony_ci 862662306a36Sopenharmony_ci cnt = 0; 862762306a36Sopenharmony_ci *lowest = 32; 862862306a36Sopenharmony_ci for (port = 8; port < 32; port++) { 862962306a36Sopenharmony_ci if (port_has_10g(p, port)) { 863062306a36Sopenharmony_ci if (!cnt) 863162306a36Sopenharmony_ci *lowest = port; 863262306a36Sopenharmony_ci cnt++; 863362306a36Sopenharmony_ci } 863462306a36Sopenharmony_ci } 863562306a36Sopenharmony_ci 863662306a36Sopenharmony_ci return cnt; 863762306a36Sopenharmony_ci} 863862306a36Sopenharmony_ci 863962306a36Sopenharmony_cistatic int count_1g_ports(struct phy_probe_info *p, int *lowest) 864062306a36Sopenharmony_ci{ 864162306a36Sopenharmony_ci *lowest = 32; 864262306a36Sopenharmony_ci if (p->cur[PHY_TYPE_MII]) 864362306a36Sopenharmony_ci *lowest = p->phy_port[PHY_TYPE_MII][0]; 864462306a36Sopenharmony_ci 864562306a36Sopenharmony_ci return p->cur[PHY_TYPE_MII]; 864662306a36Sopenharmony_ci} 864762306a36Sopenharmony_ci 864862306a36Sopenharmony_cistatic void niu_n2_divide_channels(struct niu_parent *parent) 864962306a36Sopenharmony_ci{ 865062306a36Sopenharmony_ci int num_ports = parent->num_ports; 865162306a36Sopenharmony_ci int i; 865262306a36Sopenharmony_ci 865362306a36Sopenharmony_ci for (i = 0; i < num_ports; i++) { 865462306a36Sopenharmony_ci parent->rxchan_per_port[i] = (16 / num_ports); 865562306a36Sopenharmony_ci parent->txchan_per_port[i] = (16 / num_ports); 865662306a36Sopenharmony_ci 865762306a36Sopenharmony_ci pr_info("niu%d: Port %u [%u RX chans] [%u TX chans]\n", 865862306a36Sopenharmony_ci parent->index, i, 865962306a36Sopenharmony_ci parent->rxchan_per_port[i], 866062306a36Sopenharmony_ci parent->txchan_per_port[i]); 866162306a36Sopenharmony_ci } 866262306a36Sopenharmony_ci} 866362306a36Sopenharmony_ci 866462306a36Sopenharmony_cistatic void niu_divide_channels(struct niu_parent *parent, 866562306a36Sopenharmony_ci int num_10g, int num_1g) 866662306a36Sopenharmony_ci{ 866762306a36Sopenharmony_ci int num_ports = parent->num_ports; 866862306a36Sopenharmony_ci int rx_chans_per_10g, rx_chans_per_1g; 866962306a36Sopenharmony_ci int tx_chans_per_10g, tx_chans_per_1g; 867062306a36Sopenharmony_ci int i, tot_rx, tot_tx; 867162306a36Sopenharmony_ci 867262306a36Sopenharmony_ci if (!num_10g || !num_1g) { 867362306a36Sopenharmony_ci rx_chans_per_10g = rx_chans_per_1g = 867462306a36Sopenharmony_ci (NIU_NUM_RXCHAN / num_ports); 867562306a36Sopenharmony_ci tx_chans_per_10g = tx_chans_per_1g = 867662306a36Sopenharmony_ci (NIU_NUM_TXCHAN / num_ports); 867762306a36Sopenharmony_ci } else { 867862306a36Sopenharmony_ci rx_chans_per_1g = NIU_NUM_RXCHAN / 8; 867962306a36Sopenharmony_ci rx_chans_per_10g = (NIU_NUM_RXCHAN - 868062306a36Sopenharmony_ci (rx_chans_per_1g * num_1g)) / 868162306a36Sopenharmony_ci num_10g; 868262306a36Sopenharmony_ci 868362306a36Sopenharmony_ci tx_chans_per_1g = NIU_NUM_TXCHAN / 6; 868462306a36Sopenharmony_ci tx_chans_per_10g = (NIU_NUM_TXCHAN - 868562306a36Sopenharmony_ci (tx_chans_per_1g * num_1g)) / 868662306a36Sopenharmony_ci num_10g; 868762306a36Sopenharmony_ci } 868862306a36Sopenharmony_ci 868962306a36Sopenharmony_ci tot_rx = tot_tx = 0; 869062306a36Sopenharmony_ci for (i = 0; i < num_ports; i++) { 869162306a36Sopenharmony_ci int type = phy_decode(parent->port_phy, i); 869262306a36Sopenharmony_ci 869362306a36Sopenharmony_ci if (type == PORT_TYPE_10G) { 869462306a36Sopenharmony_ci parent->rxchan_per_port[i] = rx_chans_per_10g; 869562306a36Sopenharmony_ci parent->txchan_per_port[i] = tx_chans_per_10g; 869662306a36Sopenharmony_ci } else { 869762306a36Sopenharmony_ci parent->rxchan_per_port[i] = rx_chans_per_1g; 869862306a36Sopenharmony_ci parent->txchan_per_port[i] = tx_chans_per_1g; 869962306a36Sopenharmony_ci } 870062306a36Sopenharmony_ci pr_info("niu%d: Port %u [%u RX chans] [%u TX chans]\n", 870162306a36Sopenharmony_ci parent->index, i, 870262306a36Sopenharmony_ci parent->rxchan_per_port[i], 870362306a36Sopenharmony_ci parent->txchan_per_port[i]); 870462306a36Sopenharmony_ci tot_rx += parent->rxchan_per_port[i]; 870562306a36Sopenharmony_ci tot_tx += parent->txchan_per_port[i]; 870662306a36Sopenharmony_ci } 870762306a36Sopenharmony_ci 870862306a36Sopenharmony_ci if (tot_rx > NIU_NUM_RXCHAN) { 870962306a36Sopenharmony_ci pr_err("niu%d: Too many RX channels (%d), resetting to one per port\n", 871062306a36Sopenharmony_ci parent->index, tot_rx); 871162306a36Sopenharmony_ci for (i = 0; i < num_ports; i++) 871262306a36Sopenharmony_ci parent->rxchan_per_port[i] = 1; 871362306a36Sopenharmony_ci } 871462306a36Sopenharmony_ci if (tot_tx > NIU_NUM_TXCHAN) { 871562306a36Sopenharmony_ci pr_err("niu%d: Too many TX channels (%d), resetting to one per port\n", 871662306a36Sopenharmony_ci parent->index, tot_tx); 871762306a36Sopenharmony_ci for (i = 0; i < num_ports; i++) 871862306a36Sopenharmony_ci parent->txchan_per_port[i] = 1; 871962306a36Sopenharmony_ci } 872062306a36Sopenharmony_ci if (tot_rx < NIU_NUM_RXCHAN || tot_tx < NIU_NUM_TXCHAN) { 872162306a36Sopenharmony_ci pr_warn("niu%d: Driver bug, wasted channels, RX[%d] TX[%d]\n", 872262306a36Sopenharmony_ci parent->index, tot_rx, tot_tx); 872362306a36Sopenharmony_ci } 872462306a36Sopenharmony_ci} 872562306a36Sopenharmony_ci 872662306a36Sopenharmony_cistatic void niu_divide_rdc_groups(struct niu_parent *parent, 872762306a36Sopenharmony_ci int num_10g, int num_1g) 872862306a36Sopenharmony_ci{ 872962306a36Sopenharmony_ci int i, num_ports = parent->num_ports; 873062306a36Sopenharmony_ci int rdc_group, rdc_groups_per_port; 873162306a36Sopenharmony_ci int rdc_channel_base; 873262306a36Sopenharmony_ci 873362306a36Sopenharmony_ci rdc_group = 0; 873462306a36Sopenharmony_ci rdc_groups_per_port = NIU_NUM_RDC_TABLES / num_ports; 873562306a36Sopenharmony_ci 873662306a36Sopenharmony_ci rdc_channel_base = 0; 873762306a36Sopenharmony_ci 873862306a36Sopenharmony_ci for (i = 0; i < num_ports; i++) { 873962306a36Sopenharmony_ci struct niu_rdc_tables *tp = &parent->rdc_group_cfg[i]; 874062306a36Sopenharmony_ci int grp, num_channels = parent->rxchan_per_port[i]; 874162306a36Sopenharmony_ci int this_channel_offset; 874262306a36Sopenharmony_ci 874362306a36Sopenharmony_ci tp->first_table_num = rdc_group; 874462306a36Sopenharmony_ci tp->num_tables = rdc_groups_per_port; 874562306a36Sopenharmony_ci this_channel_offset = 0; 874662306a36Sopenharmony_ci for (grp = 0; grp < tp->num_tables; grp++) { 874762306a36Sopenharmony_ci struct rdc_table *rt = &tp->tables[grp]; 874862306a36Sopenharmony_ci int slot; 874962306a36Sopenharmony_ci 875062306a36Sopenharmony_ci pr_info("niu%d: Port %d RDC tbl(%d) [ ", 875162306a36Sopenharmony_ci parent->index, i, tp->first_table_num + grp); 875262306a36Sopenharmony_ci for (slot = 0; slot < NIU_RDC_TABLE_SLOTS; slot++) { 875362306a36Sopenharmony_ci rt->rxdma_channel[slot] = 875462306a36Sopenharmony_ci rdc_channel_base + this_channel_offset; 875562306a36Sopenharmony_ci 875662306a36Sopenharmony_ci pr_cont("%d ", rt->rxdma_channel[slot]); 875762306a36Sopenharmony_ci 875862306a36Sopenharmony_ci if (++this_channel_offset == num_channels) 875962306a36Sopenharmony_ci this_channel_offset = 0; 876062306a36Sopenharmony_ci } 876162306a36Sopenharmony_ci pr_cont("]\n"); 876262306a36Sopenharmony_ci } 876362306a36Sopenharmony_ci 876462306a36Sopenharmony_ci parent->rdc_default[i] = rdc_channel_base; 876562306a36Sopenharmony_ci 876662306a36Sopenharmony_ci rdc_channel_base += num_channels; 876762306a36Sopenharmony_ci rdc_group += rdc_groups_per_port; 876862306a36Sopenharmony_ci } 876962306a36Sopenharmony_ci} 877062306a36Sopenharmony_ci 877162306a36Sopenharmony_cistatic int fill_phy_probe_info(struct niu *np, struct niu_parent *parent, 877262306a36Sopenharmony_ci struct phy_probe_info *info) 877362306a36Sopenharmony_ci{ 877462306a36Sopenharmony_ci unsigned long flags; 877562306a36Sopenharmony_ci int port, err; 877662306a36Sopenharmony_ci 877762306a36Sopenharmony_ci memset(info, 0, sizeof(*info)); 877862306a36Sopenharmony_ci 877962306a36Sopenharmony_ci /* Port 0 to 7 are reserved for onboard Serdes, probe the rest. */ 878062306a36Sopenharmony_ci niu_lock_parent(np, flags); 878162306a36Sopenharmony_ci err = 0; 878262306a36Sopenharmony_ci for (port = 8; port < 32; port++) { 878362306a36Sopenharmony_ci int dev_id_1, dev_id_2; 878462306a36Sopenharmony_ci 878562306a36Sopenharmony_ci dev_id_1 = mdio_read(np, port, 878662306a36Sopenharmony_ci NIU_PMA_PMD_DEV_ADDR, MII_PHYSID1); 878762306a36Sopenharmony_ci dev_id_2 = mdio_read(np, port, 878862306a36Sopenharmony_ci NIU_PMA_PMD_DEV_ADDR, MII_PHYSID2); 878962306a36Sopenharmony_ci err = phy_record(parent, info, dev_id_1, dev_id_2, port, 879062306a36Sopenharmony_ci PHY_TYPE_PMA_PMD); 879162306a36Sopenharmony_ci if (err) 879262306a36Sopenharmony_ci break; 879362306a36Sopenharmony_ci dev_id_1 = mdio_read(np, port, 879462306a36Sopenharmony_ci NIU_PCS_DEV_ADDR, MII_PHYSID1); 879562306a36Sopenharmony_ci dev_id_2 = mdio_read(np, port, 879662306a36Sopenharmony_ci NIU_PCS_DEV_ADDR, MII_PHYSID2); 879762306a36Sopenharmony_ci err = phy_record(parent, info, dev_id_1, dev_id_2, port, 879862306a36Sopenharmony_ci PHY_TYPE_PCS); 879962306a36Sopenharmony_ci if (err) 880062306a36Sopenharmony_ci break; 880162306a36Sopenharmony_ci dev_id_1 = mii_read(np, port, MII_PHYSID1); 880262306a36Sopenharmony_ci dev_id_2 = mii_read(np, port, MII_PHYSID2); 880362306a36Sopenharmony_ci err = phy_record(parent, info, dev_id_1, dev_id_2, port, 880462306a36Sopenharmony_ci PHY_TYPE_MII); 880562306a36Sopenharmony_ci if (err) 880662306a36Sopenharmony_ci break; 880762306a36Sopenharmony_ci } 880862306a36Sopenharmony_ci niu_unlock_parent(np, flags); 880962306a36Sopenharmony_ci 881062306a36Sopenharmony_ci return err; 881162306a36Sopenharmony_ci} 881262306a36Sopenharmony_ci 881362306a36Sopenharmony_cistatic int walk_phys(struct niu *np, struct niu_parent *parent) 881462306a36Sopenharmony_ci{ 881562306a36Sopenharmony_ci struct phy_probe_info *info = &parent->phy_probe_info; 881662306a36Sopenharmony_ci int lowest_10g, lowest_1g; 881762306a36Sopenharmony_ci int num_10g, num_1g; 881862306a36Sopenharmony_ci u32 val; 881962306a36Sopenharmony_ci int err; 882062306a36Sopenharmony_ci 882162306a36Sopenharmony_ci num_10g = num_1g = 0; 882262306a36Sopenharmony_ci 882362306a36Sopenharmony_ci if (!strcmp(np->vpd.model, NIU_ALONSO_MDL_STR) || 882462306a36Sopenharmony_ci !strcmp(np->vpd.model, NIU_KIMI_MDL_STR)) { 882562306a36Sopenharmony_ci num_10g = 0; 882662306a36Sopenharmony_ci num_1g = 2; 882762306a36Sopenharmony_ci parent->plat_type = PLAT_TYPE_ATCA_CP3220; 882862306a36Sopenharmony_ci parent->num_ports = 4; 882962306a36Sopenharmony_ci val = (phy_encode(PORT_TYPE_1G, 0) | 883062306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 1) | 883162306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 2) | 883262306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 3)); 883362306a36Sopenharmony_ci } else if (!strcmp(np->vpd.model, NIU_FOXXY_MDL_STR)) { 883462306a36Sopenharmony_ci num_10g = 2; 883562306a36Sopenharmony_ci num_1g = 0; 883662306a36Sopenharmony_ci parent->num_ports = 2; 883762306a36Sopenharmony_ci val = (phy_encode(PORT_TYPE_10G, 0) | 883862306a36Sopenharmony_ci phy_encode(PORT_TYPE_10G, 1)); 883962306a36Sopenharmony_ci } else if ((np->flags & NIU_FLAGS_XCVR_SERDES) && 884062306a36Sopenharmony_ci (parent->plat_type == PLAT_TYPE_NIU)) { 884162306a36Sopenharmony_ci /* this is the Monza case */ 884262306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_10G) { 884362306a36Sopenharmony_ci val = (phy_encode(PORT_TYPE_10G, 0) | 884462306a36Sopenharmony_ci phy_encode(PORT_TYPE_10G, 1)); 884562306a36Sopenharmony_ci } else { 884662306a36Sopenharmony_ci val = (phy_encode(PORT_TYPE_1G, 0) | 884762306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 1)); 884862306a36Sopenharmony_ci } 884962306a36Sopenharmony_ci } else { 885062306a36Sopenharmony_ci err = fill_phy_probe_info(np, parent, info); 885162306a36Sopenharmony_ci if (err) 885262306a36Sopenharmony_ci return err; 885362306a36Sopenharmony_ci 885462306a36Sopenharmony_ci num_10g = count_10g_ports(info, &lowest_10g); 885562306a36Sopenharmony_ci num_1g = count_1g_ports(info, &lowest_1g); 885662306a36Sopenharmony_ci 885762306a36Sopenharmony_ci switch ((num_10g << 4) | num_1g) { 885862306a36Sopenharmony_ci case 0x24: 885962306a36Sopenharmony_ci if (lowest_1g == 10) 886062306a36Sopenharmony_ci parent->plat_type = PLAT_TYPE_VF_P0; 886162306a36Sopenharmony_ci else if (lowest_1g == 26) 886262306a36Sopenharmony_ci parent->plat_type = PLAT_TYPE_VF_P1; 886362306a36Sopenharmony_ci else 886462306a36Sopenharmony_ci goto unknown_vg_1g_port; 886562306a36Sopenharmony_ci 886662306a36Sopenharmony_ci fallthrough; 886762306a36Sopenharmony_ci case 0x22: 886862306a36Sopenharmony_ci val = (phy_encode(PORT_TYPE_10G, 0) | 886962306a36Sopenharmony_ci phy_encode(PORT_TYPE_10G, 1) | 887062306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 2) | 887162306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 3)); 887262306a36Sopenharmony_ci break; 887362306a36Sopenharmony_ci 887462306a36Sopenharmony_ci case 0x20: 887562306a36Sopenharmony_ci val = (phy_encode(PORT_TYPE_10G, 0) | 887662306a36Sopenharmony_ci phy_encode(PORT_TYPE_10G, 1)); 887762306a36Sopenharmony_ci break; 887862306a36Sopenharmony_ci 887962306a36Sopenharmony_ci case 0x10: 888062306a36Sopenharmony_ci val = phy_encode(PORT_TYPE_10G, np->port); 888162306a36Sopenharmony_ci break; 888262306a36Sopenharmony_ci 888362306a36Sopenharmony_ci case 0x14: 888462306a36Sopenharmony_ci if (lowest_1g == 10) 888562306a36Sopenharmony_ci parent->plat_type = PLAT_TYPE_VF_P0; 888662306a36Sopenharmony_ci else if (lowest_1g == 26) 888762306a36Sopenharmony_ci parent->plat_type = PLAT_TYPE_VF_P1; 888862306a36Sopenharmony_ci else 888962306a36Sopenharmony_ci goto unknown_vg_1g_port; 889062306a36Sopenharmony_ci 889162306a36Sopenharmony_ci fallthrough; 889262306a36Sopenharmony_ci case 0x13: 889362306a36Sopenharmony_ci if ((lowest_10g & 0x7) == 0) 889462306a36Sopenharmony_ci val = (phy_encode(PORT_TYPE_10G, 0) | 889562306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 1) | 889662306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 2) | 889762306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 3)); 889862306a36Sopenharmony_ci else 889962306a36Sopenharmony_ci val = (phy_encode(PORT_TYPE_1G, 0) | 890062306a36Sopenharmony_ci phy_encode(PORT_TYPE_10G, 1) | 890162306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 2) | 890262306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 3)); 890362306a36Sopenharmony_ci break; 890462306a36Sopenharmony_ci 890562306a36Sopenharmony_ci case 0x04: 890662306a36Sopenharmony_ci if (lowest_1g == 10) 890762306a36Sopenharmony_ci parent->plat_type = PLAT_TYPE_VF_P0; 890862306a36Sopenharmony_ci else if (lowest_1g == 26) 890962306a36Sopenharmony_ci parent->plat_type = PLAT_TYPE_VF_P1; 891062306a36Sopenharmony_ci else 891162306a36Sopenharmony_ci goto unknown_vg_1g_port; 891262306a36Sopenharmony_ci 891362306a36Sopenharmony_ci val = (phy_encode(PORT_TYPE_1G, 0) | 891462306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 1) | 891562306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 2) | 891662306a36Sopenharmony_ci phy_encode(PORT_TYPE_1G, 3)); 891762306a36Sopenharmony_ci break; 891862306a36Sopenharmony_ci 891962306a36Sopenharmony_ci default: 892062306a36Sopenharmony_ci pr_err("Unsupported port config 10G[%d] 1G[%d]\n", 892162306a36Sopenharmony_ci num_10g, num_1g); 892262306a36Sopenharmony_ci return -EINVAL; 892362306a36Sopenharmony_ci } 892462306a36Sopenharmony_ci } 892562306a36Sopenharmony_ci 892662306a36Sopenharmony_ci parent->port_phy = val; 892762306a36Sopenharmony_ci 892862306a36Sopenharmony_ci if (parent->plat_type == PLAT_TYPE_NIU) 892962306a36Sopenharmony_ci niu_n2_divide_channels(parent); 893062306a36Sopenharmony_ci else 893162306a36Sopenharmony_ci niu_divide_channels(parent, num_10g, num_1g); 893262306a36Sopenharmony_ci 893362306a36Sopenharmony_ci niu_divide_rdc_groups(parent, num_10g, num_1g); 893462306a36Sopenharmony_ci 893562306a36Sopenharmony_ci return 0; 893662306a36Sopenharmony_ci 893762306a36Sopenharmony_ciunknown_vg_1g_port: 893862306a36Sopenharmony_ci pr_err("Cannot identify platform type, 1gport=%d\n", lowest_1g); 893962306a36Sopenharmony_ci return -EINVAL; 894062306a36Sopenharmony_ci} 894162306a36Sopenharmony_ci 894262306a36Sopenharmony_cistatic int niu_probe_ports(struct niu *np) 894362306a36Sopenharmony_ci{ 894462306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 894562306a36Sopenharmony_ci int err, i; 894662306a36Sopenharmony_ci 894762306a36Sopenharmony_ci if (parent->port_phy == PORT_PHY_UNKNOWN) { 894862306a36Sopenharmony_ci err = walk_phys(np, parent); 894962306a36Sopenharmony_ci if (err) 895062306a36Sopenharmony_ci return err; 895162306a36Sopenharmony_ci 895262306a36Sopenharmony_ci niu_set_ldg_timer_res(np, 2); 895362306a36Sopenharmony_ci for (i = 0; i <= LDN_MAX; i++) 895462306a36Sopenharmony_ci niu_ldn_irq_enable(np, i, 0); 895562306a36Sopenharmony_ci } 895662306a36Sopenharmony_ci 895762306a36Sopenharmony_ci if (parent->port_phy == PORT_PHY_INVALID) 895862306a36Sopenharmony_ci return -EINVAL; 895962306a36Sopenharmony_ci 896062306a36Sopenharmony_ci return 0; 896162306a36Sopenharmony_ci} 896262306a36Sopenharmony_ci 896362306a36Sopenharmony_cistatic int niu_classifier_swstate_init(struct niu *np) 896462306a36Sopenharmony_ci{ 896562306a36Sopenharmony_ci struct niu_classifier *cp = &np->clas; 896662306a36Sopenharmony_ci 896762306a36Sopenharmony_ci cp->tcam_top = (u16) np->port; 896862306a36Sopenharmony_ci cp->tcam_sz = np->parent->tcam_num_entries / np->parent->num_ports; 896962306a36Sopenharmony_ci cp->h1_init = 0xffffffff; 897062306a36Sopenharmony_ci cp->h2_init = 0xffff; 897162306a36Sopenharmony_ci 897262306a36Sopenharmony_ci return fflp_early_init(np); 897362306a36Sopenharmony_ci} 897462306a36Sopenharmony_ci 897562306a36Sopenharmony_cistatic void niu_link_config_init(struct niu *np) 897662306a36Sopenharmony_ci{ 897762306a36Sopenharmony_ci struct niu_link_config *lp = &np->link_config; 897862306a36Sopenharmony_ci 897962306a36Sopenharmony_ci lp->advertising = (ADVERTISED_10baseT_Half | 898062306a36Sopenharmony_ci ADVERTISED_10baseT_Full | 898162306a36Sopenharmony_ci ADVERTISED_100baseT_Half | 898262306a36Sopenharmony_ci ADVERTISED_100baseT_Full | 898362306a36Sopenharmony_ci ADVERTISED_1000baseT_Half | 898462306a36Sopenharmony_ci ADVERTISED_1000baseT_Full | 898562306a36Sopenharmony_ci ADVERTISED_10000baseT_Full | 898662306a36Sopenharmony_ci ADVERTISED_Autoneg); 898762306a36Sopenharmony_ci lp->speed = lp->active_speed = SPEED_INVALID; 898862306a36Sopenharmony_ci lp->duplex = DUPLEX_FULL; 898962306a36Sopenharmony_ci lp->active_duplex = DUPLEX_INVALID; 899062306a36Sopenharmony_ci lp->autoneg = 1; 899162306a36Sopenharmony_ci#if 0 899262306a36Sopenharmony_ci lp->loopback_mode = LOOPBACK_MAC; 899362306a36Sopenharmony_ci lp->active_speed = SPEED_10000; 899462306a36Sopenharmony_ci lp->active_duplex = DUPLEX_FULL; 899562306a36Sopenharmony_ci#else 899662306a36Sopenharmony_ci lp->loopback_mode = LOOPBACK_DISABLED; 899762306a36Sopenharmony_ci#endif 899862306a36Sopenharmony_ci} 899962306a36Sopenharmony_ci 900062306a36Sopenharmony_cistatic int niu_init_mac_ipp_pcs_base(struct niu *np) 900162306a36Sopenharmony_ci{ 900262306a36Sopenharmony_ci switch (np->port) { 900362306a36Sopenharmony_ci case 0: 900462306a36Sopenharmony_ci np->mac_regs = np->regs + XMAC_PORT0_OFF; 900562306a36Sopenharmony_ci np->ipp_off = 0x00000; 900662306a36Sopenharmony_ci np->pcs_off = 0x04000; 900762306a36Sopenharmony_ci np->xpcs_off = 0x02000; 900862306a36Sopenharmony_ci break; 900962306a36Sopenharmony_ci 901062306a36Sopenharmony_ci case 1: 901162306a36Sopenharmony_ci np->mac_regs = np->regs + XMAC_PORT1_OFF; 901262306a36Sopenharmony_ci np->ipp_off = 0x08000; 901362306a36Sopenharmony_ci np->pcs_off = 0x0a000; 901462306a36Sopenharmony_ci np->xpcs_off = 0x08000; 901562306a36Sopenharmony_ci break; 901662306a36Sopenharmony_ci 901762306a36Sopenharmony_ci case 2: 901862306a36Sopenharmony_ci np->mac_regs = np->regs + BMAC_PORT2_OFF; 901962306a36Sopenharmony_ci np->ipp_off = 0x04000; 902062306a36Sopenharmony_ci np->pcs_off = 0x0e000; 902162306a36Sopenharmony_ci np->xpcs_off = ~0UL; 902262306a36Sopenharmony_ci break; 902362306a36Sopenharmony_ci 902462306a36Sopenharmony_ci case 3: 902562306a36Sopenharmony_ci np->mac_regs = np->regs + BMAC_PORT3_OFF; 902662306a36Sopenharmony_ci np->ipp_off = 0x0c000; 902762306a36Sopenharmony_ci np->pcs_off = 0x12000; 902862306a36Sopenharmony_ci np->xpcs_off = ~0UL; 902962306a36Sopenharmony_ci break; 903062306a36Sopenharmony_ci 903162306a36Sopenharmony_ci default: 903262306a36Sopenharmony_ci dev_err(np->device, "Port %u is invalid, cannot compute MAC block offset\n", np->port); 903362306a36Sopenharmony_ci return -EINVAL; 903462306a36Sopenharmony_ci } 903562306a36Sopenharmony_ci 903662306a36Sopenharmony_ci return 0; 903762306a36Sopenharmony_ci} 903862306a36Sopenharmony_ci 903962306a36Sopenharmony_cistatic void niu_try_msix(struct niu *np, u8 *ldg_num_map) 904062306a36Sopenharmony_ci{ 904162306a36Sopenharmony_ci struct msix_entry msi_vec[NIU_NUM_LDG]; 904262306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 904362306a36Sopenharmony_ci struct pci_dev *pdev = np->pdev; 904462306a36Sopenharmony_ci int i, num_irqs; 904562306a36Sopenharmony_ci u8 first_ldg; 904662306a36Sopenharmony_ci 904762306a36Sopenharmony_ci first_ldg = (NIU_NUM_LDG / parent->num_ports) * np->port; 904862306a36Sopenharmony_ci for (i = 0; i < (NIU_NUM_LDG / parent->num_ports); i++) 904962306a36Sopenharmony_ci ldg_num_map[i] = first_ldg + i; 905062306a36Sopenharmony_ci 905162306a36Sopenharmony_ci num_irqs = (parent->rxchan_per_port[np->port] + 905262306a36Sopenharmony_ci parent->txchan_per_port[np->port] + 905362306a36Sopenharmony_ci (np->port == 0 ? 3 : 1)); 905462306a36Sopenharmony_ci BUG_ON(num_irqs > (NIU_NUM_LDG / parent->num_ports)); 905562306a36Sopenharmony_ci 905662306a36Sopenharmony_ci for (i = 0; i < num_irqs; i++) { 905762306a36Sopenharmony_ci msi_vec[i].vector = 0; 905862306a36Sopenharmony_ci msi_vec[i].entry = i; 905962306a36Sopenharmony_ci } 906062306a36Sopenharmony_ci 906162306a36Sopenharmony_ci num_irqs = pci_enable_msix_range(pdev, msi_vec, 1, num_irqs); 906262306a36Sopenharmony_ci if (num_irqs < 0) { 906362306a36Sopenharmony_ci np->flags &= ~NIU_FLAGS_MSIX; 906462306a36Sopenharmony_ci return; 906562306a36Sopenharmony_ci } 906662306a36Sopenharmony_ci 906762306a36Sopenharmony_ci np->flags |= NIU_FLAGS_MSIX; 906862306a36Sopenharmony_ci for (i = 0; i < num_irqs; i++) 906962306a36Sopenharmony_ci np->ldg[i].irq = msi_vec[i].vector; 907062306a36Sopenharmony_ci np->num_ldg = num_irqs; 907162306a36Sopenharmony_ci} 907262306a36Sopenharmony_ci 907362306a36Sopenharmony_cistatic int niu_n2_irq_init(struct niu *np, u8 *ldg_num_map) 907462306a36Sopenharmony_ci{ 907562306a36Sopenharmony_ci#ifdef CONFIG_SPARC64 907662306a36Sopenharmony_ci struct platform_device *op = np->op; 907762306a36Sopenharmony_ci const u32 *int_prop; 907862306a36Sopenharmony_ci int i; 907962306a36Sopenharmony_ci 908062306a36Sopenharmony_ci int_prop = of_get_property(op->dev.of_node, "interrupts", NULL); 908162306a36Sopenharmony_ci if (!int_prop) 908262306a36Sopenharmony_ci return -ENODEV; 908362306a36Sopenharmony_ci 908462306a36Sopenharmony_ci for (i = 0; i < op->archdata.num_irqs; i++) { 908562306a36Sopenharmony_ci ldg_num_map[i] = int_prop[i]; 908662306a36Sopenharmony_ci np->ldg[i].irq = op->archdata.irqs[i]; 908762306a36Sopenharmony_ci } 908862306a36Sopenharmony_ci 908962306a36Sopenharmony_ci np->num_ldg = op->archdata.num_irqs; 909062306a36Sopenharmony_ci 909162306a36Sopenharmony_ci return 0; 909262306a36Sopenharmony_ci#else 909362306a36Sopenharmony_ci return -EINVAL; 909462306a36Sopenharmony_ci#endif 909562306a36Sopenharmony_ci} 909662306a36Sopenharmony_ci 909762306a36Sopenharmony_cistatic int niu_ldg_init(struct niu *np) 909862306a36Sopenharmony_ci{ 909962306a36Sopenharmony_ci struct niu_parent *parent = np->parent; 910062306a36Sopenharmony_ci u8 ldg_num_map[NIU_NUM_LDG]; 910162306a36Sopenharmony_ci int first_chan, num_chan; 910262306a36Sopenharmony_ci int i, err, ldg_rotor; 910362306a36Sopenharmony_ci u8 port; 910462306a36Sopenharmony_ci 910562306a36Sopenharmony_ci np->num_ldg = 1; 910662306a36Sopenharmony_ci np->ldg[0].irq = np->dev->irq; 910762306a36Sopenharmony_ci if (parent->plat_type == PLAT_TYPE_NIU) { 910862306a36Sopenharmony_ci err = niu_n2_irq_init(np, ldg_num_map); 910962306a36Sopenharmony_ci if (err) 911062306a36Sopenharmony_ci return err; 911162306a36Sopenharmony_ci } else 911262306a36Sopenharmony_ci niu_try_msix(np, ldg_num_map); 911362306a36Sopenharmony_ci 911462306a36Sopenharmony_ci port = np->port; 911562306a36Sopenharmony_ci for (i = 0; i < np->num_ldg; i++) { 911662306a36Sopenharmony_ci struct niu_ldg *lp = &np->ldg[i]; 911762306a36Sopenharmony_ci 911862306a36Sopenharmony_ci netif_napi_add(np->dev, &lp->napi, niu_poll); 911962306a36Sopenharmony_ci 912062306a36Sopenharmony_ci lp->np = np; 912162306a36Sopenharmony_ci lp->ldg_num = ldg_num_map[i]; 912262306a36Sopenharmony_ci lp->timer = 2; /* XXX */ 912362306a36Sopenharmony_ci 912462306a36Sopenharmony_ci /* On N2 NIU the firmware has setup the SID mappings so they go 912562306a36Sopenharmony_ci * to the correct values that will route the LDG to the proper 912662306a36Sopenharmony_ci * interrupt in the NCU interrupt table. 912762306a36Sopenharmony_ci */ 912862306a36Sopenharmony_ci if (np->parent->plat_type != PLAT_TYPE_NIU) { 912962306a36Sopenharmony_ci err = niu_set_ldg_sid(np, lp->ldg_num, port, i); 913062306a36Sopenharmony_ci if (err) 913162306a36Sopenharmony_ci return err; 913262306a36Sopenharmony_ci } 913362306a36Sopenharmony_ci } 913462306a36Sopenharmony_ci 913562306a36Sopenharmony_ci /* We adopt the LDG assignment ordering used by the N2 NIU 913662306a36Sopenharmony_ci * 'interrupt' properties because that simplifies a lot of 913762306a36Sopenharmony_ci * things. This ordering is: 913862306a36Sopenharmony_ci * 913962306a36Sopenharmony_ci * MAC 914062306a36Sopenharmony_ci * MIF (if port zero) 914162306a36Sopenharmony_ci * SYSERR (if port zero) 914262306a36Sopenharmony_ci * RX channels 914362306a36Sopenharmony_ci * TX channels 914462306a36Sopenharmony_ci */ 914562306a36Sopenharmony_ci 914662306a36Sopenharmony_ci ldg_rotor = 0; 914762306a36Sopenharmony_ci 914862306a36Sopenharmony_ci err = niu_ldg_assign_ldn(np, parent, ldg_num_map[ldg_rotor], 914962306a36Sopenharmony_ci LDN_MAC(port)); 915062306a36Sopenharmony_ci if (err) 915162306a36Sopenharmony_ci return err; 915262306a36Sopenharmony_ci 915362306a36Sopenharmony_ci ldg_rotor++; 915462306a36Sopenharmony_ci if (ldg_rotor == np->num_ldg) 915562306a36Sopenharmony_ci ldg_rotor = 0; 915662306a36Sopenharmony_ci 915762306a36Sopenharmony_ci if (port == 0) { 915862306a36Sopenharmony_ci err = niu_ldg_assign_ldn(np, parent, 915962306a36Sopenharmony_ci ldg_num_map[ldg_rotor], 916062306a36Sopenharmony_ci LDN_MIF); 916162306a36Sopenharmony_ci if (err) 916262306a36Sopenharmony_ci return err; 916362306a36Sopenharmony_ci 916462306a36Sopenharmony_ci ldg_rotor++; 916562306a36Sopenharmony_ci if (ldg_rotor == np->num_ldg) 916662306a36Sopenharmony_ci ldg_rotor = 0; 916762306a36Sopenharmony_ci 916862306a36Sopenharmony_ci err = niu_ldg_assign_ldn(np, parent, 916962306a36Sopenharmony_ci ldg_num_map[ldg_rotor], 917062306a36Sopenharmony_ci LDN_DEVICE_ERROR); 917162306a36Sopenharmony_ci if (err) 917262306a36Sopenharmony_ci return err; 917362306a36Sopenharmony_ci 917462306a36Sopenharmony_ci ldg_rotor++; 917562306a36Sopenharmony_ci if (ldg_rotor == np->num_ldg) 917662306a36Sopenharmony_ci ldg_rotor = 0; 917762306a36Sopenharmony_ci 917862306a36Sopenharmony_ci } 917962306a36Sopenharmony_ci 918062306a36Sopenharmony_ci first_chan = 0; 918162306a36Sopenharmony_ci for (i = 0; i < port; i++) 918262306a36Sopenharmony_ci first_chan += parent->rxchan_per_port[i]; 918362306a36Sopenharmony_ci num_chan = parent->rxchan_per_port[port]; 918462306a36Sopenharmony_ci 918562306a36Sopenharmony_ci for (i = first_chan; i < (first_chan + num_chan); i++) { 918662306a36Sopenharmony_ci err = niu_ldg_assign_ldn(np, parent, 918762306a36Sopenharmony_ci ldg_num_map[ldg_rotor], 918862306a36Sopenharmony_ci LDN_RXDMA(i)); 918962306a36Sopenharmony_ci if (err) 919062306a36Sopenharmony_ci return err; 919162306a36Sopenharmony_ci ldg_rotor++; 919262306a36Sopenharmony_ci if (ldg_rotor == np->num_ldg) 919362306a36Sopenharmony_ci ldg_rotor = 0; 919462306a36Sopenharmony_ci } 919562306a36Sopenharmony_ci 919662306a36Sopenharmony_ci first_chan = 0; 919762306a36Sopenharmony_ci for (i = 0; i < port; i++) 919862306a36Sopenharmony_ci first_chan += parent->txchan_per_port[i]; 919962306a36Sopenharmony_ci num_chan = parent->txchan_per_port[port]; 920062306a36Sopenharmony_ci for (i = first_chan; i < (first_chan + num_chan); i++) { 920162306a36Sopenharmony_ci err = niu_ldg_assign_ldn(np, parent, 920262306a36Sopenharmony_ci ldg_num_map[ldg_rotor], 920362306a36Sopenharmony_ci LDN_TXDMA(i)); 920462306a36Sopenharmony_ci if (err) 920562306a36Sopenharmony_ci return err; 920662306a36Sopenharmony_ci ldg_rotor++; 920762306a36Sopenharmony_ci if (ldg_rotor == np->num_ldg) 920862306a36Sopenharmony_ci ldg_rotor = 0; 920962306a36Sopenharmony_ci } 921062306a36Sopenharmony_ci 921162306a36Sopenharmony_ci return 0; 921262306a36Sopenharmony_ci} 921362306a36Sopenharmony_ci 921462306a36Sopenharmony_cistatic void niu_ldg_free(struct niu *np) 921562306a36Sopenharmony_ci{ 921662306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_MSIX) 921762306a36Sopenharmony_ci pci_disable_msix(np->pdev); 921862306a36Sopenharmony_ci} 921962306a36Sopenharmony_ci 922062306a36Sopenharmony_cistatic int niu_get_of_props(struct niu *np) 922162306a36Sopenharmony_ci{ 922262306a36Sopenharmony_ci#ifdef CONFIG_SPARC64 922362306a36Sopenharmony_ci struct net_device *dev = np->dev; 922462306a36Sopenharmony_ci struct device_node *dp; 922562306a36Sopenharmony_ci const char *phy_type; 922662306a36Sopenharmony_ci const u8 *mac_addr; 922762306a36Sopenharmony_ci const char *model; 922862306a36Sopenharmony_ci int prop_len; 922962306a36Sopenharmony_ci 923062306a36Sopenharmony_ci if (np->parent->plat_type == PLAT_TYPE_NIU) 923162306a36Sopenharmony_ci dp = np->op->dev.of_node; 923262306a36Sopenharmony_ci else 923362306a36Sopenharmony_ci dp = pci_device_to_OF_node(np->pdev); 923462306a36Sopenharmony_ci 923562306a36Sopenharmony_ci phy_type = of_get_property(dp, "phy-type", NULL); 923662306a36Sopenharmony_ci if (!phy_type) { 923762306a36Sopenharmony_ci netdev_err(dev, "%pOF: OF node lacks phy-type property\n", dp); 923862306a36Sopenharmony_ci return -EINVAL; 923962306a36Sopenharmony_ci } 924062306a36Sopenharmony_ci 924162306a36Sopenharmony_ci if (!strcmp(phy_type, "none")) 924262306a36Sopenharmony_ci return -ENODEV; 924362306a36Sopenharmony_ci 924462306a36Sopenharmony_ci strcpy(np->vpd.phy_type, phy_type); 924562306a36Sopenharmony_ci 924662306a36Sopenharmony_ci if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) { 924762306a36Sopenharmony_ci netdev_err(dev, "%pOF: Illegal phy string [%s]\n", 924862306a36Sopenharmony_ci dp, np->vpd.phy_type); 924962306a36Sopenharmony_ci return -EINVAL; 925062306a36Sopenharmony_ci } 925162306a36Sopenharmony_ci 925262306a36Sopenharmony_ci mac_addr = of_get_property(dp, "local-mac-address", &prop_len); 925362306a36Sopenharmony_ci if (!mac_addr) { 925462306a36Sopenharmony_ci netdev_err(dev, "%pOF: OF node lacks local-mac-address property\n", 925562306a36Sopenharmony_ci dp); 925662306a36Sopenharmony_ci return -EINVAL; 925762306a36Sopenharmony_ci } 925862306a36Sopenharmony_ci if (prop_len != dev->addr_len) { 925962306a36Sopenharmony_ci netdev_err(dev, "%pOF: OF MAC address prop len (%d) is wrong\n", 926062306a36Sopenharmony_ci dp, prop_len); 926162306a36Sopenharmony_ci } 926262306a36Sopenharmony_ci eth_hw_addr_set(dev, mac_addr); 926362306a36Sopenharmony_ci if (!is_valid_ether_addr(&dev->dev_addr[0])) { 926462306a36Sopenharmony_ci netdev_err(dev, "%pOF: OF MAC address is invalid\n", dp); 926562306a36Sopenharmony_ci netdev_err(dev, "%pOF: [ %pM ]\n", dp, dev->dev_addr); 926662306a36Sopenharmony_ci return -EINVAL; 926762306a36Sopenharmony_ci } 926862306a36Sopenharmony_ci 926962306a36Sopenharmony_ci model = of_get_property(dp, "model", NULL); 927062306a36Sopenharmony_ci 927162306a36Sopenharmony_ci if (model) 927262306a36Sopenharmony_ci strcpy(np->vpd.model, model); 927362306a36Sopenharmony_ci 927462306a36Sopenharmony_ci if (of_property_read_bool(dp, "hot-swappable-phy")) { 927562306a36Sopenharmony_ci np->flags |= (NIU_FLAGS_10G | NIU_FLAGS_FIBER | 927662306a36Sopenharmony_ci NIU_FLAGS_HOTPLUG_PHY); 927762306a36Sopenharmony_ci } 927862306a36Sopenharmony_ci 927962306a36Sopenharmony_ci return 0; 928062306a36Sopenharmony_ci#else 928162306a36Sopenharmony_ci return -EINVAL; 928262306a36Sopenharmony_ci#endif 928362306a36Sopenharmony_ci} 928462306a36Sopenharmony_ci 928562306a36Sopenharmony_cistatic int niu_get_invariants(struct niu *np) 928662306a36Sopenharmony_ci{ 928762306a36Sopenharmony_ci int err, have_props; 928862306a36Sopenharmony_ci u32 offset; 928962306a36Sopenharmony_ci 929062306a36Sopenharmony_ci err = niu_get_of_props(np); 929162306a36Sopenharmony_ci if (err == -ENODEV) 929262306a36Sopenharmony_ci return err; 929362306a36Sopenharmony_ci 929462306a36Sopenharmony_ci have_props = !err; 929562306a36Sopenharmony_ci 929662306a36Sopenharmony_ci err = niu_init_mac_ipp_pcs_base(np); 929762306a36Sopenharmony_ci if (err) 929862306a36Sopenharmony_ci return err; 929962306a36Sopenharmony_ci 930062306a36Sopenharmony_ci if (have_props) { 930162306a36Sopenharmony_ci err = niu_get_and_validate_port(np); 930262306a36Sopenharmony_ci if (err) 930362306a36Sopenharmony_ci return err; 930462306a36Sopenharmony_ci 930562306a36Sopenharmony_ci } else { 930662306a36Sopenharmony_ci if (np->parent->plat_type == PLAT_TYPE_NIU) 930762306a36Sopenharmony_ci return -EINVAL; 930862306a36Sopenharmony_ci 930962306a36Sopenharmony_ci nw64(ESPC_PIO_EN, ESPC_PIO_EN_ENABLE); 931062306a36Sopenharmony_ci offset = niu_pci_vpd_offset(np); 931162306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 931262306a36Sopenharmony_ci "%s() VPD offset [%08x]\n", __func__, offset); 931362306a36Sopenharmony_ci if (offset) { 931462306a36Sopenharmony_ci err = niu_pci_vpd_fetch(np, offset); 931562306a36Sopenharmony_ci if (err < 0) 931662306a36Sopenharmony_ci return err; 931762306a36Sopenharmony_ci } 931862306a36Sopenharmony_ci nw64(ESPC_PIO_EN, 0); 931962306a36Sopenharmony_ci 932062306a36Sopenharmony_ci if (np->flags & NIU_FLAGS_VPD_VALID) { 932162306a36Sopenharmony_ci niu_pci_vpd_validate(np); 932262306a36Sopenharmony_ci err = niu_get_and_validate_port(np); 932362306a36Sopenharmony_ci if (err) 932462306a36Sopenharmony_ci return err; 932562306a36Sopenharmony_ci } 932662306a36Sopenharmony_ci 932762306a36Sopenharmony_ci if (!(np->flags & NIU_FLAGS_VPD_VALID)) { 932862306a36Sopenharmony_ci err = niu_get_and_validate_port(np); 932962306a36Sopenharmony_ci if (err) 933062306a36Sopenharmony_ci return err; 933162306a36Sopenharmony_ci err = niu_pci_probe_sprom(np); 933262306a36Sopenharmony_ci if (err) 933362306a36Sopenharmony_ci return err; 933462306a36Sopenharmony_ci } 933562306a36Sopenharmony_ci } 933662306a36Sopenharmony_ci 933762306a36Sopenharmony_ci err = niu_probe_ports(np); 933862306a36Sopenharmony_ci if (err) 933962306a36Sopenharmony_ci return err; 934062306a36Sopenharmony_ci 934162306a36Sopenharmony_ci niu_ldg_init(np); 934262306a36Sopenharmony_ci 934362306a36Sopenharmony_ci niu_classifier_swstate_init(np); 934462306a36Sopenharmony_ci niu_link_config_init(np); 934562306a36Sopenharmony_ci 934662306a36Sopenharmony_ci err = niu_determine_phy_disposition(np); 934762306a36Sopenharmony_ci if (!err) 934862306a36Sopenharmony_ci err = niu_init_link(np); 934962306a36Sopenharmony_ci 935062306a36Sopenharmony_ci return err; 935162306a36Sopenharmony_ci} 935262306a36Sopenharmony_ci 935362306a36Sopenharmony_cistatic LIST_HEAD(niu_parent_list); 935462306a36Sopenharmony_cistatic DEFINE_MUTEX(niu_parent_lock); 935562306a36Sopenharmony_cistatic int niu_parent_index; 935662306a36Sopenharmony_ci 935762306a36Sopenharmony_cistatic ssize_t show_port_phy(struct device *dev, 935862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 935962306a36Sopenharmony_ci{ 936062306a36Sopenharmony_ci struct platform_device *plat_dev = to_platform_device(dev); 936162306a36Sopenharmony_ci struct niu_parent *p = dev_get_platdata(&plat_dev->dev); 936262306a36Sopenharmony_ci u32 port_phy = p->port_phy; 936362306a36Sopenharmony_ci char *orig_buf = buf; 936462306a36Sopenharmony_ci int i; 936562306a36Sopenharmony_ci 936662306a36Sopenharmony_ci if (port_phy == PORT_PHY_UNKNOWN || 936762306a36Sopenharmony_ci port_phy == PORT_PHY_INVALID) 936862306a36Sopenharmony_ci return 0; 936962306a36Sopenharmony_ci 937062306a36Sopenharmony_ci for (i = 0; i < p->num_ports; i++) { 937162306a36Sopenharmony_ci const char *type_str; 937262306a36Sopenharmony_ci int type; 937362306a36Sopenharmony_ci 937462306a36Sopenharmony_ci type = phy_decode(port_phy, i); 937562306a36Sopenharmony_ci if (type == PORT_TYPE_10G) 937662306a36Sopenharmony_ci type_str = "10G"; 937762306a36Sopenharmony_ci else 937862306a36Sopenharmony_ci type_str = "1G"; 937962306a36Sopenharmony_ci buf += sprintf(buf, 938062306a36Sopenharmony_ci (i == 0) ? "%s" : " %s", 938162306a36Sopenharmony_ci type_str); 938262306a36Sopenharmony_ci } 938362306a36Sopenharmony_ci buf += sprintf(buf, "\n"); 938462306a36Sopenharmony_ci return buf - orig_buf; 938562306a36Sopenharmony_ci} 938662306a36Sopenharmony_ci 938762306a36Sopenharmony_cistatic ssize_t show_plat_type(struct device *dev, 938862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 938962306a36Sopenharmony_ci{ 939062306a36Sopenharmony_ci struct platform_device *plat_dev = to_platform_device(dev); 939162306a36Sopenharmony_ci struct niu_parent *p = dev_get_platdata(&plat_dev->dev); 939262306a36Sopenharmony_ci const char *type_str; 939362306a36Sopenharmony_ci 939462306a36Sopenharmony_ci switch (p->plat_type) { 939562306a36Sopenharmony_ci case PLAT_TYPE_ATLAS: 939662306a36Sopenharmony_ci type_str = "atlas"; 939762306a36Sopenharmony_ci break; 939862306a36Sopenharmony_ci case PLAT_TYPE_NIU: 939962306a36Sopenharmony_ci type_str = "niu"; 940062306a36Sopenharmony_ci break; 940162306a36Sopenharmony_ci case PLAT_TYPE_VF_P0: 940262306a36Sopenharmony_ci type_str = "vf_p0"; 940362306a36Sopenharmony_ci break; 940462306a36Sopenharmony_ci case PLAT_TYPE_VF_P1: 940562306a36Sopenharmony_ci type_str = "vf_p1"; 940662306a36Sopenharmony_ci break; 940762306a36Sopenharmony_ci default: 940862306a36Sopenharmony_ci type_str = "unknown"; 940962306a36Sopenharmony_ci break; 941062306a36Sopenharmony_ci } 941162306a36Sopenharmony_ci 941262306a36Sopenharmony_ci return sprintf(buf, "%s\n", type_str); 941362306a36Sopenharmony_ci} 941462306a36Sopenharmony_ci 941562306a36Sopenharmony_cistatic ssize_t __show_chan_per_port(struct device *dev, 941662306a36Sopenharmony_ci struct device_attribute *attr, char *buf, 941762306a36Sopenharmony_ci int rx) 941862306a36Sopenharmony_ci{ 941962306a36Sopenharmony_ci struct platform_device *plat_dev = to_platform_device(dev); 942062306a36Sopenharmony_ci struct niu_parent *p = dev_get_platdata(&plat_dev->dev); 942162306a36Sopenharmony_ci char *orig_buf = buf; 942262306a36Sopenharmony_ci u8 *arr; 942362306a36Sopenharmony_ci int i; 942462306a36Sopenharmony_ci 942562306a36Sopenharmony_ci arr = (rx ? p->rxchan_per_port : p->txchan_per_port); 942662306a36Sopenharmony_ci 942762306a36Sopenharmony_ci for (i = 0; i < p->num_ports; i++) { 942862306a36Sopenharmony_ci buf += sprintf(buf, 942962306a36Sopenharmony_ci (i == 0) ? "%d" : " %d", 943062306a36Sopenharmony_ci arr[i]); 943162306a36Sopenharmony_ci } 943262306a36Sopenharmony_ci buf += sprintf(buf, "\n"); 943362306a36Sopenharmony_ci 943462306a36Sopenharmony_ci return buf - orig_buf; 943562306a36Sopenharmony_ci} 943662306a36Sopenharmony_ci 943762306a36Sopenharmony_cistatic ssize_t show_rxchan_per_port(struct device *dev, 943862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 943962306a36Sopenharmony_ci{ 944062306a36Sopenharmony_ci return __show_chan_per_port(dev, attr, buf, 1); 944162306a36Sopenharmony_ci} 944262306a36Sopenharmony_ci 944362306a36Sopenharmony_cistatic ssize_t show_txchan_per_port(struct device *dev, 944462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 944562306a36Sopenharmony_ci{ 944662306a36Sopenharmony_ci return __show_chan_per_port(dev, attr, buf, 1); 944762306a36Sopenharmony_ci} 944862306a36Sopenharmony_ci 944962306a36Sopenharmony_cistatic ssize_t show_num_ports(struct device *dev, 945062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 945162306a36Sopenharmony_ci{ 945262306a36Sopenharmony_ci struct platform_device *plat_dev = to_platform_device(dev); 945362306a36Sopenharmony_ci struct niu_parent *p = dev_get_platdata(&plat_dev->dev); 945462306a36Sopenharmony_ci 945562306a36Sopenharmony_ci return sprintf(buf, "%d\n", p->num_ports); 945662306a36Sopenharmony_ci} 945762306a36Sopenharmony_ci 945862306a36Sopenharmony_cistatic struct device_attribute niu_parent_attributes[] = { 945962306a36Sopenharmony_ci __ATTR(port_phy, 0444, show_port_phy, NULL), 946062306a36Sopenharmony_ci __ATTR(plat_type, 0444, show_plat_type, NULL), 946162306a36Sopenharmony_ci __ATTR(rxchan_per_port, 0444, show_rxchan_per_port, NULL), 946262306a36Sopenharmony_ci __ATTR(txchan_per_port, 0444, show_txchan_per_port, NULL), 946362306a36Sopenharmony_ci __ATTR(num_ports, 0444, show_num_ports, NULL), 946462306a36Sopenharmony_ci {} 946562306a36Sopenharmony_ci}; 946662306a36Sopenharmony_ci 946762306a36Sopenharmony_cistatic struct niu_parent *niu_new_parent(struct niu *np, 946862306a36Sopenharmony_ci union niu_parent_id *id, u8 ptype) 946962306a36Sopenharmony_ci{ 947062306a36Sopenharmony_ci struct platform_device *plat_dev; 947162306a36Sopenharmony_ci struct niu_parent *p; 947262306a36Sopenharmony_ci int i; 947362306a36Sopenharmony_ci 947462306a36Sopenharmony_ci plat_dev = platform_device_register_simple("niu-board", niu_parent_index, 947562306a36Sopenharmony_ci NULL, 0); 947662306a36Sopenharmony_ci if (IS_ERR(plat_dev)) 947762306a36Sopenharmony_ci return NULL; 947862306a36Sopenharmony_ci 947962306a36Sopenharmony_ci for (i = 0; niu_parent_attributes[i].attr.name; i++) { 948062306a36Sopenharmony_ci int err = device_create_file(&plat_dev->dev, 948162306a36Sopenharmony_ci &niu_parent_attributes[i]); 948262306a36Sopenharmony_ci if (err) 948362306a36Sopenharmony_ci goto fail_unregister; 948462306a36Sopenharmony_ci } 948562306a36Sopenharmony_ci 948662306a36Sopenharmony_ci p = kzalloc(sizeof(*p), GFP_KERNEL); 948762306a36Sopenharmony_ci if (!p) 948862306a36Sopenharmony_ci goto fail_unregister; 948962306a36Sopenharmony_ci 949062306a36Sopenharmony_ci p->index = niu_parent_index++; 949162306a36Sopenharmony_ci 949262306a36Sopenharmony_ci plat_dev->dev.platform_data = p; 949362306a36Sopenharmony_ci p->plat_dev = plat_dev; 949462306a36Sopenharmony_ci 949562306a36Sopenharmony_ci memcpy(&p->id, id, sizeof(*id)); 949662306a36Sopenharmony_ci p->plat_type = ptype; 949762306a36Sopenharmony_ci INIT_LIST_HEAD(&p->list); 949862306a36Sopenharmony_ci atomic_set(&p->refcnt, 0); 949962306a36Sopenharmony_ci list_add(&p->list, &niu_parent_list); 950062306a36Sopenharmony_ci spin_lock_init(&p->lock); 950162306a36Sopenharmony_ci 950262306a36Sopenharmony_ci p->rxdma_clock_divider = 7500; 950362306a36Sopenharmony_ci 950462306a36Sopenharmony_ci p->tcam_num_entries = NIU_PCI_TCAM_ENTRIES; 950562306a36Sopenharmony_ci if (p->plat_type == PLAT_TYPE_NIU) 950662306a36Sopenharmony_ci p->tcam_num_entries = NIU_NONPCI_TCAM_ENTRIES; 950762306a36Sopenharmony_ci 950862306a36Sopenharmony_ci for (i = CLASS_CODE_USER_PROG1; i <= CLASS_CODE_SCTP_IPV6; i++) { 950962306a36Sopenharmony_ci int index = i - CLASS_CODE_USER_PROG1; 951062306a36Sopenharmony_ci 951162306a36Sopenharmony_ci p->tcam_key[index] = TCAM_KEY_TSEL; 951262306a36Sopenharmony_ci p->flow_key[index] = (FLOW_KEY_IPSA | 951362306a36Sopenharmony_ci FLOW_KEY_IPDA | 951462306a36Sopenharmony_ci FLOW_KEY_PROTO | 951562306a36Sopenharmony_ci (FLOW_KEY_L4_BYTE12 << 951662306a36Sopenharmony_ci FLOW_KEY_L4_0_SHIFT) | 951762306a36Sopenharmony_ci (FLOW_KEY_L4_BYTE12 << 951862306a36Sopenharmony_ci FLOW_KEY_L4_1_SHIFT)); 951962306a36Sopenharmony_ci } 952062306a36Sopenharmony_ci 952162306a36Sopenharmony_ci for (i = 0; i < LDN_MAX + 1; i++) 952262306a36Sopenharmony_ci p->ldg_map[i] = LDG_INVALID; 952362306a36Sopenharmony_ci 952462306a36Sopenharmony_ci return p; 952562306a36Sopenharmony_ci 952662306a36Sopenharmony_cifail_unregister: 952762306a36Sopenharmony_ci platform_device_unregister(plat_dev); 952862306a36Sopenharmony_ci return NULL; 952962306a36Sopenharmony_ci} 953062306a36Sopenharmony_ci 953162306a36Sopenharmony_cistatic struct niu_parent *niu_get_parent(struct niu *np, 953262306a36Sopenharmony_ci union niu_parent_id *id, u8 ptype) 953362306a36Sopenharmony_ci{ 953462306a36Sopenharmony_ci struct niu_parent *p, *tmp; 953562306a36Sopenharmony_ci int port = np->port; 953662306a36Sopenharmony_ci 953762306a36Sopenharmony_ci mutex_lock(&niu_parent_lock); 953862306a36Sopenharmony_ci p = NULL; 953962306a36Sopenharmony_ci list_for_each_entry(tmp, &niu_parent_list, list) { 954062306a36Sopenharmony_ci if (!memcmp(id, &tmp->id, sizeof(*id))) { 954162306a36Sopenharmony_ci p = tmp; 954262306a36Sopenharmony_ci break; 954362306a36Sopenharmony_ci } 954462306a36Sopenharmony_ci } 954562306a36Sopenharmony_ci if (!p) 954662306a36Sopenharmony_ci p = niu_new_parent(np, id, ptype); 954762306a36Sopenharmony_ci 954862306a36Sopenharmony_ci if (p) { 954962306a36Sopenharmony_ci char port_name[8]; 955062306a36Sopenharmony_ci int err; 955162306a36Sopenharmony_ci 955262306a36Sopenharmony_ci sprintf(port_name, "port%d", port); 955362306a36Sopenharmony_ci err = sysfs_create_link(&p->plat_dev->dev.kobj, 955462306a36Sopenharmony_ci &np->device->kobj, 955562306a36Sopenharmony_ci port_name); 955662306a36Sopenharmony_ci if (!err) { 955762306a36Sopenharmony_ci p->ports[port] = np; 955862306a36Sopenharmony_ci atomic_inc(&p->refcnt); 955962306a36Sopenharmony_ci } 956062306a36Sopenharmony_ci } 956162306a36Sopenharmony_ci mutex_unlock(&niu_parent_lock); 956262306a36Sopenharmony_ci 956362306a36Sopenharmony_ci return p; 956462306a36Sopenharmony_ci} 956562306a36Sopenharmony_ci 956662306a36Sopenharmony_cistatic void niu_put_parent(struct niu *np) 956762306a36Sopenharmony_ci{ 956862306a36Sopenharmony_ci struct niu_parent *p = np->parent; 956962306a36Sopenharmony_ci u8 port = np->port; 957062306a36Sopenharmony_ci char port_name[8]; 957162306a36Sopenharmony_ci 957262306a36Sopenharmony_ci BUG_ON(!p || p->ports[port] != np); 957362306a36Sopenharmony_ci 957462306a36Sopenharmony_ci netif_printk(np, probe, KERN_DEBUG, np->dev, 957562306a36Sopenharmony_ci "%s() port[%u]\n", __func__, port); 957662306a36Sopenharmony_ci 957762306a36Sopenharmony_ci sprintf(port_name, "port%d", port); 957862306a36Sopenharmony_ci 957962306a36Sopenharmony_ci mutex_lock(&niu_parent_lock); 958062306a36Sopenharmony_ci 958162306a36Sopenharmony_ci sysfs_remove_link(&p->plat_dev->dev.kobj, port_name); 958262306a36Sopenharmony_ci 958362306a36Sopenharmony_ci p->ports[port] = NULL; 958462306a36Sopenharmony_ci np->parent = NULL; 958562306a36Sopenharmony_ci 958662306a36Sopenharmony_ci if (atomic_dec_and_test(&p->refcnt)) { 958762306a36Sopenharmony_ci list_del(&p->list); 958862306a36Sopenharmony_ci platform_device_unregister(p->plat_dev); 958962306a36Sopenharmony_ci } 959062306a36Sopenharmony_ci 959162306a36Sopenharmony_ci mutex_unlock(&niu_parent_lock); 959262306a36Sopenharmony_ci} 959362306a36Sopenharmony_ci 959462306a36Sopenharmony_cistatic void *niu_pci_alloc_coherent(struct device *dev, size_t size, 959562306a36Sopenharmony_ci u64 *handle, gfp_t flag) 959662306a36Sopenharmony_ci{ 959762306a36Sopenharmony_ci dma_addr_t dh; 959862306a36Sopenharmony_ci void *ret; 959962306a36Sopenharmony_ci 960062306a36Sopenharmony_ci ret = dma_alloc_coherent(dev, size, &dh, flag); 960162306a36Sopenharmony_ci if (ret) 960262306a36Sopenharmony_ci *handle = dh; 960362306a36Sopenharmony_ci return ret; 960462306a36Sopenharmony_ci} 960562306a36Sopenharmony_ci 960662306a36Sopenharmony_cistatic void niu_pci_free_coherent(struct device *dev, size_t size, 960762306a36Sopenharmony_ci void *cpu_addr, u64 handle) 960862306a36Sopenharmony_ci{ 960962306a36Sopenharmony_ci dma_free_coherent(dev, size, cpu_addr, handle); 961062306a36Sopenharmony_ci} 961162306a36Sopenharmony_ci 961262306a36Sopenharmony_cistatic u64 niu_pci_map_page(struct device *dev, struct page *page, 961362306a36Sopenharmony_ci unsigned long offset, size_t size, 961462306a36Sopenharmony_ci enum dma_data_direction direction) 961562306a36Sopenharmony_ci{ 961662306a36Sopenharmony_ci return dma_map_page(dev, page, offset, size, direction); 961762306a36Sopenharmony_ci} 961862306a36Sopenharmony_ci 961962306a36Sopenharmony_cistatic void niu_pci_unmap_page(struct device *dev, u64 dma_address, 962062306a36Sopenharmony_ci size_t size, enum dma_data_direction direction) 962162306a36Sopenharmony_ci{ 962262306a36Sopenharmony_ci dma_unmap_page(dev, dma_address, size, direction); 962362306a36Sopenharmony_ci} 962462306a36Sopenharmony_ci 962562306a36Sopenharmony_cistatic u64 niu_pci_map_single(struct device *dev, void *cpu_addr, 962662306a36Sopenharmony_ci size_t size, 962762306a36Sopenharmony_ci enum dma_data_direction direction) 962862306a36Sopenharmony_ci{ 962962306a36Sopenharmony_ci return dma_map_single(dev, cpu_addr, size, direction); 963062306a36Sopenharmony_ci} 963162306a36Sopenharmony_ci 963262306a36Sopenharmony_cistatic void niu_pci_unmap_single(struct device *dev, u64 dma_address, 963362306a36Sopenharmony_ci size_t size, 963462306a36Sopenharmony_ci enum dma_data_direction direction) 963562306a36Sopenharmony_ci{ 963662306a36Sopenharmony_ci dma_unmap_single(dev, dma_address, size, direction); 963762306a36Sopenharmony_ci} 963862306a36Sopenharmony_ci 963962306a36Sopenharmony_cistatic const struct niu_ops niu_pci_ops = { 964062306a36Sopenharmony_ci .alloc_coherent = niu_pci_alloc_coherent, 964162306a36Sopenharmony_ci .free_coherent = niu_pci_free_coherent, 964262306a36Sopenharmony_ci .map_page = niu_pci_map_page, 964362306a36Sopenharmony_ci .unmap_page = niu_pci_unmap_page, 964462306a36Sopenharmony_ci .map_single = niu_pci_map_single, 964562306a36Sopenharmony_ci .unmap_single = niu_pci_unmap_single, 964662306a36Sopenharmony_ci}; 964762306a36Sopenharmony_ci 964862306a36Sopenharmony_cistatic void niu_driver_version(void) 964962306a36Sopenharmony_ci{ 965062306a36Sopenharmony_ci static int niu_version_printed; 965162306a36Sopenharmony_ci 965262306a36Sopenharmony_ci if (niu_version_printed++ == 0) 965362306a36Sopenharmony_ci pr_info("%s", version); 965462306a36Sopenharmony_ci} 965562306a36Sopenharmony_ci 965662306a36Sopenharmony_cistatic struct net_device *niu_alloc_and_init(struct device *gen_dev, 965762306a36Sopenharmony_ci struct pci_dev *pdev, 965862306a36Sopenharmony_ci struct platform_device *op, 965962306a36Sopenharmony_ci const struct niu_ops *ops, u8 port) 966062306a36Sopenharmony_ci{ 966162306a36Sopenharmony_ci struct net_device *dev; 966262306a36Sopenharmony_ci struct niu *np; 966362306a36Sopenharmony_ci 966462306a36Sopenharmony_ci dev = alloc_etherdev_mq(sizeof(struct niu), NIU_NUM_TXCHAN); 966562306a36Sopenharmony_ci if (!dev) 966662306a36Sopenharmony_ci return NULL; 966762306a36Sopenharmony_ci 966862306a36Sopenharmony_ci SET_NETDEV_DEV(dev, gen_dev); 966962306a36Sopenharmony_ci 967062306a36Sopenharmony_ci np = netdev_priv(dev); 967162306a36Sopenharmony_ci np->dev = dev; 967262306a36Sopenharmony_ci np->pdev = pdev; 967362306a36Sopenharmony_ci np->op = op; 967462306a36Sopenharmony_ci np->device = gen_dev; 967562306a36Sopenharmony_ci np->ops = ops; 967662306a36Sopenharmony_ci 967762306a36Sopenharmony_ci np->msg_enable = niu_debug; 967862306a36Sopenharmony_ci 967962306a36Sopenharmony_ci spin_lock_init(&np->lock); 968062306a36Sopenharmony_ci INIT_WORK(&np->reset_task, niu_reset_task); 968162306a36Sopenharmony_ci 968262306a36Sopenharmony_ci np->port = port; 968362306a36Sopenharmony_ci 968462306a36Sopenharmony_ci return dev; 968562306a36Sopenharmony_ci} 968662306a36Sopenharmony_ci 968762306a36Sopenharmony_cistatic const struct net_device_ops niu_netdev_ops = { 968862306a36Sopenharmony_ci .ndo_open = niu_open, 968962306a36Sopenharmony_ci .ndo_stop = niu_close, 969062306a36Sopenharmony_ci .ndo_start_xmit = niu_start_xmit, 969162306a36Sopenharmony_ci .ndo_get_stats64 = niu_get_stats, 969262306a36Sopenharmony_ci .ndo_set_rx_mode = niu_set_rx_mode, 969362306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 969462306a36Sopenharmony_ci .ndo_set_mac_address = niu_set_mac_addr, 969562306a36Sopenharmony_ci .ndo_eth_ioctl = niu_ioctl, 969662306a36Sopenharmony_ci .ndo_tx_timeout = niu_tx_timeout, 969762306a36Sopenharmony_ci .ndo_change_mtu = niu_change_mtu, 969862306a36Sopenharmony_ci}; 969962306a36Sopenharmony_ci 970062306a36Sopenharmony_cistatic void niu_assign_netdev_ops(struct net_device *dev) 970162306a36Sopenharmony_ci{ 970262306a36Sopenharmony_ci dev->netdev_ops = &niu_netdev_ops; 970362306a36Sopenharmony_ci dev->ethtool_ops = &niu_ethtool_ops; 970462306a36Sopenharmony_ci dev->watchdog_timeo = NIU_TX_TIMEOUT; 970562306a36Sopenharmony_ci} 970662306a36Sopenharmony_ci 970762306a36Sopenharmony_cistatic void niu_device_announce(struct niu *np) 970862306a36Sopenharmony_ci{ 970962306a36Sopenharmony_ci struct net_device *dev = np->dev; 971062306a36Sopenharmony_ci 971162306a36Sopenharmony_ci pr_info("%s: NIU Ethernet %pM\n", dev->name, dev->dev_addr); 971262306a36Sopenharmony_ci 971362306a36Sopenharmony_ci if (np->parent->plat_type == PLAT_TYPE_ATCA_CP3220) { 971462306a36Sopenharmony_ci pr_info("%s: Port type[%s] mode[%s:%s] XCVR[%s] phy[%s]\n", 971562306a36Sopenharmony_ci dev->name, 971662306a36Sopenharmony_ci (np->flags & NIU_FLAGS_XMAC ? "XMAC" : "BMAC"), 971762306a36Sopenharmony_ci (np->flags & NIU_FLAGS_10G ? "10G" : "1G"), 971862306a36Sopenharmony_ci (np->flags & NIU_FLAGS_FIBER ? "RGMII FIBER" : "SERDES"), 971962306a36Sopenharmony_ci (np->mac_xcvr == MAC_XCVR_MII ? "MII" : 972062306a36Sopenharmony_ci (np->mac_xcvr == MAC_XCVR_PCS ? "PCS" : "XPCS")), 972162306a36Sopenharmony_ci np->vpd.phy_type); 972262306a36Sopenharmony_ci } else { 972362306a36Sopenharmony_ci pr_info("%s: Port type[%s] mode[%s:%s] XCVR[%s] phy[%s]\n", 972462306a36Sopenharmony_ci dev->name, 972562306a36Sopenharmony_ci (np->flags & NIU_FLAGS_XMAC ? "XMAC" : "BMAC"), 972662306a36Sopenharmony_ci (np->flags & NIU_FLAGS_10G ? "10G" : "1G"), 972762306a36Sopenharmony_ci (np->flags & NIU_FLAGS_FIBER ? "FIBER" : 972862306a36Sopenharmony_ci (np->flags & NIU_FLAGS_XCVR_SERDES ? "SERDES" : 972962306a36Sopenharmony_ci "COPPER")), 973062306a36Sopenharmony_ci (np->mac_xcvr == MAC_XCVR_MII ? "MII" : 973162306a36Sopenharmony_ci (np->mac_xcvr == MAC_XCVR_PCS ? "PCS" : "XPCS")), 973262306a36Sopenharmony_ci np->vpd.phy_type); 973362306a36Sopenharmony_ci } 973462306a36Sopenharmony_ci} 973562306a36Sopenharmony_ci 973662306a36Sopenharmony_cistatic void niu_set_basic_features(struct net_device *dev) 973762306a36Sopenharmony_ci{ 973862306a36Sopenharmony_ci dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXHASH; 973962306a36Sopenharmony_ci dev->features |= dev->hw_features | NETIF_F_RXCSUM; 974062306a36Sopenharmony_ci} 974162306a36Sopenharmony_ci 974262306a36Sopenharmony_cistatic int niu_pci_init_one(struct pci_dev *pdev, 974362306a36Sopenharmony_ci const struct pci_device_id *ent) 974462306a36Sopenharmony_ci{ 974562306a36Sopenharmony_ci union niu_parent_id parent_id; 974662306a36Sopenharmony_ci struct net_device *dev; 974762306a36Sopenharmony_ci struct niu *np; 974862306a36Sopenharmony_ci int err; 974962306a36Sopenharmony_ci 975062306a36Sopenharmony_ci niu_driver_version(); 975162306a36Sopenharmony_ci 975262306a36Sopenharmony_ci err = pci_enable_device(pdev); 975362306a36Sopenharmony_ci if (err) { 975462306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n"); 975562306a36Sopenharmony_ci return err; 975662306a36Sopenharmony_ci } 975762306a36Sopenharmony_ci 975862306a36Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) || 975962306a36Sopenharmony_ci !(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) { 976062306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot find proper PCI device base addresses, aborting\n"); 976162306a36Sopenharmony_ci err = -ENODEV; 976262306a36Sopenharmony_ci goto err_out_disable_pdev; 976362306a36Sopenharmony_ci } 976462306a36Sopenharmony_ci 976562306a36Sopenharmony_ci err = pci_request_regions(pdev, DRV_MODULE_NAME); 976662306a36Sopenharmony_ci if (err) { 976762306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n"); 976862306a36Sopenharmony_ci goto err_out_disable_pdev; 976962306a36Sopenharmony_ci } 977062306a36Sopenharmony_ci 977162306a36Sopenharmony_ci if (!pci_is_pcie(pdev)) { 977262306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot find PCI Express capability, aborting\n"); 977362306a36Sopenharmony_ci err = -ENODEV; 977462306a36Sopenharmony_ci goto err_out_free_res; 977562306a36Sopenharmony_ci } 977662306a36Sopenharmony_ci 977762306a36Sopenharmony_ci dev = niu_alloc_and_init(&pdev->dev, pdev, NULL, 977862306a36Sopenharmony_ci &niu_pci_ops, PCI_FUNC(pdev->devfn)); 977962306a36Sopenharmony_ci if (!dev) { 978062306a36Sopenharmony_ci err = -ENOMEM; 978162306a36Sopenharmony_ci goto err_out_free_res; 978262306a36Sopenharmony_ci } 978362306a36Sopenharmony_ci np = netdev_priv(dev); 978462306a36Sopenharmony_ci 978562306a36Sopenharmony_ci memset(&parent_id, 0, sizeof(parent_id)); 978662306a36Sopenharmony_ci parent_id.pci.domain = pci_domain_nr(pdev->bus); 978762306a36Sopenharmony_ci parent_id.pci.bus = pdev->bus->number; 978862306a36Sopenharmony_ci parent_id.pci.device = PCI_SLOT(pdev->devfn); 978962306a36Sopenharmony_ci 979062306a36Sopenharmony_ci np->parent = niu_get_parent(np, &parent_id, 979162306a36Sopenharmony_ci PLAT_TYPE_ATLAS); 979262306a36Sopenharmony_ci if (!np->parent) { 979362306a36Sopenharmony_ci err = -ENOMEM; 979462306a36Sopenharmony_ci goto err_out_free_dev; 979562306a36Sopenharmony_ci } 979662306a36Sopenharmony_ci 979762306a36Sopenharmony_ci pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL, 979862306a36Sopenharmony_ci PCI_EXP_DEVCTL_NOSNOOP_EN, 979962306a36Sopenharmony_ci PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | 980062306a36Sopenharmony_ci PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE | 980162306a36Sopenharmony_ci PCI_EXP_DEVCTL_RELAX_EN); 980262306a36Sopenharmony_ci 980362306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(44)); 980462306a36Sopenharmony_ci if (!err) 980562306a36Sopenharmony_ci dev->features |= NETIF_F_HIGHDMA; 980662306a36Sopenharmony_ci if (err) { 980762306a36Sopenharmony_ci err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 980862306a36Sopenharmony_ci if (err) { 980962306a36Sopenharmony_ci dev_err(&pdev->dev, "No usable DMA configuration, aborting\n"); 981062306a36Sopenharmony_ci goto err_out_release_parent; 981162306a36Sopenharmony_ci } 981262306a36Sopenharmony_ci } 981362306a36Sopenharmony_ci 981462306a36Sopenharmony_ci niu_set_basic_features(dev); 981562306a36Sopenharmony_ci 981662306a36Sopenharmony_ci dev->priv_flags |= IFF_UNICAST_FLT; 981762306a36Sopenharmony_ci 981862306a36Sopenharmony_ci np->regs = pci_ioremap_bar(pdev, 0); 981962306a36Sopenharmony_ci if (!np->regs) { 982062306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot map device registers, aborting\n"); 982162306a36Sopenharmony_ci err = -ENOMEM; 982262306a36Sopenharmony_ci goto err_out_release_parent; 982362306a36Sopenharmony_ci } 982462306a36Sopenharmony_ci 982562306a36Sopenharmony_ci pci_set_master(pdev); 982662306a36Sopenharmony_ci pci_save_state(pdev); 982762306a36Sopenharmony_ci 982862306a36Sopenharmony_ci dev->irq = pdev->irq; 982962306a36Sopenharmony_ci 983062306a36Sopenharmony_ci /* MTU range: 68 - 9216 */ 983162306a36Sopenharmony_ci dev->min_mtu = ETH_MIN_MTU; 983262306a36Sopenharmony_ci dev->max_mtu = NIU_MAX_MTU; 983362306a36Sopenharmony_ci 983462306a36Sopenharmony_ci niu_assign_netdev_ops(dev); 983562306a36Sopenharmony_ci 983662306a36Sopenharmony_ci err = niu_get_invariants(np); 983762306a36Sopenharmony_ci if (err) { 983862306a36Sopenharmony_ci if (err != -ENODEV) 983962306a36Sopenharmony_ci dev_err(&pdev->dev, "Problem fetching invariants of chip, aborting\n"); 984062306a36Sopenharmony_ci goto err_out_iounmap; 984162306a36Sopenharmony_ci } 984262306a36Sopenharmony_ci 984362306a36Sopenharmony_ci err = register_netdev(dev); 984462306a36Sopenharmony_ci if (err) { 984562306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot register net device, aborting\n"); 984662306a36Sopenharmony_ci goto err_out_iounmap; 984762306a36Sopenharmony_ci } 984862306a36Sopenharmony_ci 984962306a36Sopenharmony_ci pci_set_drvdata(pdev, dev); 985062306a36Sopenharmony_ci 985162306a36Sopenharmony_ci niu_device_announce(np); 985262306a36Sopenharmony_ci 985362306a36Sopenharmony_ci return 0; 985462306a36Sopenharmony_ci 985562306a36Sopenharmony_cierr_out_iounmap: 985662306a36Sopenharmony_ci if (np->regs) { 985762306a36Sopenharmony_ci iounmap(np->regs); 985862306a36Sopenharmony_ci np->regs = NULL; 985962306a36Sopenharmony_ci } 986062306a36Sopenharmony_ci 986162306a36Sopenharmony_cierr_out_release_parent: 986262306a36Sopenharmony_ci niu_put_parent(np); 986362306a36Sopenharmony_ci 986462306a36Sopenharmony_cierr_out_free_dev: 986562306a36Sopenharmony_ci free_netdev(dev); 986662306a36Sopenharmony_ci 986762306a36Sopenharmony_cierr_out_free_res: 986862306a36Sopenharmony_ci pci_release_regions(pdev); 986962306a36Sopenharmony_ci 987062306a36Sopenharmony_cierr_out_disable_pdev: 987162306a36Sopenharmony_ci pci_disable_device(pdev); 987262306a36Sopenharmony_ci 987362306a36Sopenharmony_ci return err; 987462306a36Sopenharmony_ci} 987562306a36Sopenharmony_ci 987662306a36Sopenharmony_cistatic void niu_pci_remove_one(struct pci_dev *pdev) 987762306a36Sopenharmony_ci{ 987862306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 987962306a36Sopenharmony_ci 988062306a36Sopenharmony_ci if (dev) { 988162306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 988262306a36Sopenharmony_ci 988362306a36Sopenharmony_ci unregister_netdev(dev); 988462306a36Sopenharmony_ci if (np->regs) { 988562306a36Sopenharmony_ci iounmap(np->regs); 988662306a36Sopenharmony_ci np->regs = NULL; 988762306a36Sopenharmony_ci } 988862306a36Sopenharmony_ci 988962306a36Sopenharmony_ci niu_ldg_free(np); 989062306a36Sopenharmony_ci 989162306a36Sopenharmony_ci niu_put_parent(np); 989262306a36Sopenharmony_ci 989362306a36Sopenharmony_ci free_netdev(dev); 989462306a36Sopenharmony_ci pci_release_regions(pdev); 989562306a36Sopenharmony_ci pci_disable_device(pdev); 989662306a36Sopenharmony_ci } 989762306a36Sopenharmony_ci} 989862306a36Sopenharmony_ci 989962306a36Sopenharmony_cistatic int __maybe_unused niu_suspend(struct device *dev_d) 990062306a36Sopenharmony_ci{ 990162306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 990262306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 990362306a36Sopenharmony_ci unsigned long flags; 990462306a36Sopenharmony_ci 990562306a36Sopenharmony_ci if (!netif_running(dev)) 990662306a36Sopenharmony_ci return 0; 990762306a36Sopenharmony_ci 990862306a36Sopenharmony_ci flush_work(&np->reset_task); 990962306a36Sopenharmony_ci niu_netif_stop(np); 991062306a36Sopenharmony_ci 991162306a36Sopenharmony_ci del_timer_sync(&np->timer); 991262306a36Sopenharmony_ci 991362306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 991462306a36Sopenharmony_ci niu_enable_interrupts(np, 0); 991562306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 991662306a36Sopenharmony_ci 991762306a36Sopenharmony_ci netif_device_detach(dev); 991862306a36Sopenharmony_ci 991962306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 992062306a36Sopenharmony_ci niu_stop_hw(np); 992162306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 992262306a36Sopenharmony_ci 992362306a36Sopenharmony_ci return 0; 992462306a36Sopenharmony_ci} 992562306a36Sopenharmony_ci 992662306a36Sopenharmony_cistatic int __maybe_unused niu_resume(struct device *dev_d) 992762306a36Sopenharmony_ci{ 992862306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 992962306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 993062306a36Sopenharmony_ci unsigned long flags; 993162306a36Sopenharmony_ci int err; 993262306a36Sopenharmony_ci 993362306a36Sopenharmony_ci if (!netif_running(dev)) 993462306a36Sopenharmony_ci return 0; 993562306a36Sopenharmony_ci 993662306a36Sopenharmony_ci netif_device_attach(dev); 993762306a36Sopenharmony_ci 993862306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 993962306a36Sopenharmony_ci 994062306a36Sopenharmony_ci err = niu_init_hw(np); 994162306a36Sopenharmony_ci if (!err) { 994262306a36Sopenharmony_ci np->timer.expires = jiffies + HZ; 994362306a36Sopenharmony_ci add_timer(&np->timer); 994462306a36Sopenharmony_ci niu_netif_start(np); 994562306a36Sopenharmony_ci } 994662306a36Sopenharmony_ci 994762306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 994862306a36Sopenharmony_ci 994962306a36Sopenharmony_ci return err; 995062306a36Sopenharmony_ci} 995162306a36Sopenharmony_ci 995262306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(niu_pm_ops, niu_suspend, niu_resume); 995362306a36Sopenharmony_ci 995462306a36Sopenharmony_cistatic struct pci_driver niu_pci_driver = { 995562306a36Sopenharmony_ci .name = DRV_MODULE_NAME, 995662306a36Sopenharmony_ci .id_table = niu_pci_tbl, 995762306a36Sopenharmony_ci .probe = niu_pci_init_one, 995862306a36Sopenharmony_ci .remove = niu_pci_remove_one, 995962306a36Sopenharmony_ci .driver.pm = &niu_pm_ops, 996062306a36Sopenharmony_ci}; 996162306a36Sopenharmony_ci 996262306a36Sopenharmony_ci#ifdef CONFIG_SPARC64 996362306a36Sopenharmony_cistatic void *niu_phys_alloc_coherent(struct device *dev, size_t size, 996462306a36Sopenharmony_ci u64 *dma_addr, gfp_t flag) 996562306a36Sopenharmony_ci{ 996662306a36Sopenharmony_ci unsigned long order = get_order(size); 996762306a36Sopenharmony_ci unsigned long page = __get_free_pages(flag, order); 996862306a36Sopenharmony_ci 996962306a36Sopenharmony_ci if (page == 0UL) 997062306a36Sopenharmony_ci return NULL; 997162306a36Sopenharmony_ci memset((char *)page, 0, PAGE_SIZE << order); 997262306a36Sopenharmony_ci *dma_addr = __pa(page); 997362306a36Sopenharmony_ci 997462306a36Sopenharmony_ci return (void *) page; 997562306a36Sopenharmony_ci} 997662306a36Sopenharmony_ci 997762306a36Sopenharmony_cistatic void niu_phys_free_coherent(struct device *dev, size_t size, 997862306a36Sopenharmony_ci void *cpu_addr, u64 handle) 997962306a36Sopenharmony_ci{ 998062306a36Sopenharmony_ci unsigned long order = get_order(size); 998162306a36Sopenharmony_ci 998262306a36Sopenharmony_ci free_pages((unsigned long) cpu_addr, order); 998362306a36Sopenharmony_ci} 998462306a36Sopenharmony_ci 998562306a36Sopenharmony_cistatic u64 niu_phys_map_page(struct device *dev, struct page *page, 998662306a36Sopenharmony_ci unsigned long offset, size_t size, 998762306a36Sopenharmony_ci enum dma_data_direction direction) 998862306a36Sopenharmony_ci{ 998962306a36Sopenharmony_ci return page_to_phys(page) + offset; 999062306a36Sopenharmony_ci} 999162306a36Sopenharmony_ci 999262306a36Sopenharmony_cistatic void niu_phys_unmap_page(struct device *dev, u64 dma_address, 999362306a36Sopenharmony_ci size_t size, enum dma_data_direction direction) 999462306a36Sopenharmony_ci{ 999562306a36Sopenharmony_ci /* Nothing to do. */ 999662306a36Sopenharmony_ci} 999762306a36Sopenharmony_ci 999862306a36Sopenharmony_cistatic u64 niu_phys_map_single(struct device *dev, void *cpu_addr, 999962306a36Sopenharmony_ci size_t size, 1000062306a36Sopenharmony_ci enum dma_data_direction direction) 1000162306a36Sopenharmony_ci{ 1000262306a36Sopenharmony_ci return __pa(cpu_addr); 1000362306a36Sopenharmony_ci} 1000462306a36Sopenharmony_ci 1000562306a36Sopenharmony_cistatic void niu_phys_unmap_single(struct device *dev, u64 dma_address, 1000662306a36Sopenharmony_ci size_t size, 1000762306a36Sopenharmony_ci enum dma_data_direction direction) 1000862306a36Sopenharmony_ci{ 1000962306a36Sopenharmony_ci /* Nothing to do. */ 1001062306a36Sopenharmony_ci} 1001162306a36Sopenharmony_ci 1001262306a36Sopenharmony_cistatic const struct niu_ops niu_phys_ops = { 1001362306a36Sopenharmony_ci .alloc_coherent = niu_phys_alloc_coherent, 1001462306a36Sopenharmony_ci .free_coherent = niu_phys_free_coherent, 1001562306a36Sopenharmony_ci .map_page = niu_phys_map_page, 1001662306a36Sopenharmony_ci .unmap_page = niu_phys_unmap_page, 1001762306a36Sopenharmony_ci .map_single = niu_phys_map_single, 1001862306a36Sopenharmony_ci .unmap_single = niu_phys_unmap_single, 1001962306a36Sopenharmony_ci}; 1002062306a36Sopenharmony_ci 1002162306a36Sopenharmony_cistatic int niu_of_probe(struct platform_device *op) 1002262306a36Sopenharmony_ci{ 1002362306a36Sopenharmony_ci union niu_parent_id parent_id; 1002462306a36Sopenharmony_ci struct net_device *dev; 1002562306a36Sopenharmony_ci struct niu *np; 1002662306a36Sopenharmony_ci const u32 *reg; 1002762306a36Sopenharmony_ci int err; 1002862306a36Sopenharmony_ci 1002962306a36Sopenharmony_ci niu_driver_version(); 1003062306a36Sopenharmony_ci 1003162306a36Sopenharmony_ci reg = of_get_property(op->dev.of_node, "reg", NULL); 1003262306a36Sopenharmony_ci if (!reg) { 1003362306a36Sopenharmony_ci dev_err(&op->dev, "%pOF: No 'reg' property, aborting\n", 1003462306a36Sopenharmony_ci op->dev.of_node); 1003562306a36Sopenharmony_ci return -ENODEV; 1003662306a36Sopenharmony_ci } 1003762306a36Sopenharmony_ci 1003862306a36Sopenharmony_ci dev = niu_alloc_and_init(&op->dev, NULL, op, 1003962306a36Sopenharmony_ci &niu_phys_ops, reg[0] & 0x1); 1004062306a36Sopenharmony_ci if (!dev) { 1004162306a36Sopenharmony_ci err = -ENOMEM; 1004262306a36Sopenharmony_ci goto err_out; 1004362306a36Sopenharmony_ci } 1004462306a36Sopenharmony_ci np = netdev_priv(dev); 1004562306a36Sopenharmony_ci 1004662306a36Sopenharmony_ci memset(&parent_id, 0, sizeof(parent_id)); 1004762306a36Sopenharmony_ci parent_id.of = of_get_parent(op->dev.of_node); 1004862306a36Sopenharmony_ci 1004962306a36Sopenharmony_ci np->parent = niu_get_parent(np, &parent_id, 1005062306a36Sopenharmony_ci PLAT_TYPE_NIU); 1005162306a36Sopenharmony_ci if (!np->parent) { 1005262306a36Sopenharmony_ci err = -ENOMEM; 1005362306a36Sopenharmony_ci goto err_out_free_dev; 1005462306a36Sopenharmony_ci } 1005562306a36Sopenharmony_ci 1005662306a36Sopenharmony_ci niu_set_basic_features(dev); 1005762306a36Sopenharmony_ci 1005862306a36Sopenharmony_ci np->regs = of_ioremap(&op->resource[1], 0, 1005962306a36Sopenharmony_ci resource_size(&op->resource[1]), 1006062306a36Sopenharmony_ci "niu regs"); 1006162306a36Sopenharmony_ci if (!np->regs) { 1006262306a36Sopenharmony_ci dev_err(&op->dev, "Cannot map device registers, aborting\n"); 1006362306a36Sopenharmony_ci err = -ENOMEM; 1006462306a36Sopenharmony_ci goto err_out_release_parent; 1006562306a36Sopenharmony_ci } 1006662306a36Sopenharmony_ci 1006762306a36Sopenharmony_ci np->vir_regs_1 = of_ioremap(&op->resource[2], 0, 1006862306a36Sopenharmony_ci resource_size(&op->resource[2]), 1006962306a36Sopenharmony_ci "niu vregs-1"); 1007062306a36Sopenharmony_ci if (!np->vir_regs_1) { 1007162306a36Sopenharmony_ci dev_err(&op->dev, "Cannot map device vir registers 1, aborting\n"); 1007262306a36Sopenharmony_ci err = -ENOMEM; 1007362306a36Sopenharmony_ci goto err_out_iounmap; 1007462306a36Sopenharmony_ci } 1007562306a36Sopenharmony_ci 1007662306a36Sopenharmony_ci np->vir_regs_2 = of_ioremap(&op->resource[3], 0, 1007762306a36Sopenharmony_ci resource_size(&op->resource[3]), 1007862306a36Sopenharmony_ci "niu vregs-2"); 1007962306a36Sopenharmony_ci if (!np->vir_regs_2) { 1008062306a36Sopenharmony_ci dev_err(&op->dev, "Cannot map device vir registers 2, aborting\n"); 1008162306a36Sopenharmony_ci err = -ENOMEM; 1008262306a36Sopenharmony_ci goto err_out_iounmap; 1008362306a36Sopenharmony_ci } 1008462306a36Sopenharmony_ci 1008562306a36Sopenharmony_ci niu_assign_netdev_ops(dev); 1008662306a36Sopenharmony_ci 1008762306a36Sopenharmony_ci err = niu_get_invariants(np); 1008862306a36Sopenharmony_ci if (err) { 1008962306a36Sopenharmony_ci if (err != -ENODEV) 1009062306a36Sopenharmony_ci dev_err(&op->dev, "Problem fetching invariants of chip, aborting\n"); 1009162306a36Sopenharmony_ci goto err_out_iounmap; 1009262306a36Sopenharmony_ci } 1009362306a36Sopenharmony_ci 1009462306a36Sopenharmony_ci err = register_netdev(dev); 1009562306a36Sopenharmony_ci if (err) { 1009662306a36Sopenharmony_ci dev_err(&op->dev, "Cannot register net device, aborting\n"); 1009762306a36Sopenharmony_ci goto err_out_iounmap; 1009862306a36Sopenharmony_ci } 1009962306a36Sopenharmony_ci 1010062306a36Sopenharmony_ci platform_set_drvdata(op, dev); 1010162306a36Sopenharmony_ci 1010262306a36Sopenharmony_ci niu_device_announce(np); 1010362306a36Sopenharmony_ci 1010462306a36Sopenharmony_ci return 0; 1010562306a36Sopenharmony_ci 1010662306a36Sopenharmony_cierr_out_iounmap: 1010762306a36Sopenharmony_ci if (np->vir_regs_1) { 1010862306a36Sopenharmony_ci of_iounmap(&op->resource[2], np->vir_regs_1, 1010962306a36Sopenharmony_ci resource_size(&op->resource[2])); 1011062306a36Sopenharmony_ci np->vir_regs_1 = NULL; 1011162306a36Sopenharmony_ci } 1011262306a36Sopenharmony_ci 1011362306a36Sopenharmony_ci if (np->vir_regs_2) { 1011462306a36Sopenharmony_ci of_iounmap(&op->resource[3], np->vir_regs_2, 1011562306a36Sopenharmony_ci resource_size(&op->resource[3])); 1011662306a36Sopenharmony_ci np->vir_regs_2 = NULL; 1011762306a36Sopenharmony_ci } 1011862306a36Sopenharmony_ci 1011962306a36Sopenharmony_ci if (np->regs) { 1012062306a36Sopenharmony_ci of_iounmap(&op->resource[1], np->regs, 1012162306a36Sopenharmony_ci resource_size(&op->resource[1])); 1012262306a36Sopenharmony_ci np->regs = NULL; 1012362306a36Sopenharmony_ci } 1012462306a36Sopenharmony_ci 1012562306a36Sopenharmony_cierr_out_release_parent: 1012662306a36Sopenharmony_ci niu_put_parent(np); 1012762306a36Sopenharmony_ci 1012862306a36Sopenharmony_cierr_out_free_dev: 1012962306a36Sopenharmony_ci free_netdev(dev); 1013062306a36Sopenharmony_ci 1013162306a36Sopenharmony_cierr_out: 1013262306a36Sopenharmony_ci return err; 1013362306a36Sopenharmony_ci} 1013462306a36Sopenharmony_ci 1013562306a36Sopenharmony_cistatic int niu_of_remove(struct platform_device *op) 1013662306a36Sopenharmony_ci{ 1013762306a36Sopenharmony_ci struct net_device *dev = platform_get_drvdata(op); 1013862306a36Sopenharmony_ci 1013962306a36Sopenharmony_ci if (dev) { 1014062306a36Sopenharmony_ci struct niu *np = netdev_priv(dev); 1014162306a36Sopenharmony_ci 1014262306a36Sopenharmony_ci unregister_netdev(dev); 1014362306a36Sopenharmony_ci 1014462306a36Sopenharmony_ci if (np->vir_regs_1) { 1014562306a36Sopenharmony_ci of_iounmap(&op->resource[2], np->vir_regs_1, 1014662306a36Sopenharmony_ci resource_size(&op->resource[2])); 1014762306a36Sopenharmony_ci np->vir_regs_1 = NULL; 1014862306a36Sopenharmony_ci } 1014962306a36Sopenharmony_ci 1015062306a36Sopenharmony_ci if (np->vir_regs_2) { 1015162306a36Sopenharmony_ci of_iounmap(&op->resource[3], np->vir_regs_2, 1015262306a36Sopenharmony_ci resource_size(&op->resource[3])); 1015362306a36Sopenharmony_ci np->vir_regs_2 = NULL; 1015462306a36Sopenharmony_ci } 1015562306a36Sopenharmony_ci 1015662306a36Sopenharmony_ci if (np->regs) { 1015762306a36Sopenharmony_ci of_iounmap(&op->resource[1], np->regs, 1015862306a36Sopenharmony_ci resource_size(&op->resource[1])); 1015962306a36Sopenharmony_ci np->regs = NULL; 1016062306a36Sopenharmony_ci } 1016162306a36Sopenharmony_ci 1016262306a36Sopenharmony_ci niu_ldg_free(np); 1016362306a36Sopenharmony_ci 1016462306a36Sopenharmony_ci niu_put_parent(np); 1016562306a36Sopenharmony_ci 1016662306a36Sopenharmony_ci free_netdev(dev); 1016762306a36Sopenharmony_ci } 1016862306a36Sopenharmony_ci return 0; 1016962306a36Sopenharmony_ci} 1017062306a36Sopenharmony_ci 1017162306a36Sopenharmony_cistatic const struct of_device_id niu_match[] = { 1017262306a36Sopenharmony_ci { 1017362306a36Sopenharmony_ci .name = "network", 1017462306a36Sopenharmony_ci .compatible = "SUNW,niusl", 1017562306a36Sopenharmony_ci }, 1017662306a36Sopenharmony_ci {}, 1017762306a36Sopenharmony_ci}; 1017862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, niu_match); 1017962306a36Sopenharmony_ci 1018062306a36Sopenharmony_cistatic struct platform_driver niu_of_driver = { 1018162306a36Sopenharmony_ci .driver = { 1018262306a36Sopenharmony_ci .name = "niu", 1018362306a36Sopenharmony_ci .of_match_table = niu_match, 1018462306a36Sopenharmony_ci }, 1018562306a36Sopenharmony_ci .probe = niu_of_probe, 1018662306a36Sopenharmony_ci .remove = niu_of_remove, 1018762306a36Sopenharmony_ci}; 1018862306a36Sopenharmony_ci 1018962306a36Sopenharmony_ci#endif /* CONFIG_SPARC64 */ 1019062306a36Sopenharmony_ci 1019162306a36Sopenharmony_cistatic int __init niu_init(void) 1019262306a36Sopenharmony_ci{ 1019362306a36Sopenharmony_ci int err = 0; 1019462306a36Sopenharmony_ci 1019562306a36Sopenharmony_ci BUILD_BUG_ON(PAGE_SIZE < 4 * 1024); 1019662306a36Sopenharmony_ci 1019762306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct page, mapping) != 1019862306a36Sopenharmony_ci offsetof(union niu_page, next)); 1019962306a36Sopenharmony_ci 1020062306a36Sopenharmony_ci niu_debug = netif_msg_init(debug, NIU_MSG_DEFAULT); 1020162306a36Sopenharmony_ci 1020262306a36Sopenharmony_ci#ifdef CONFIG_SPARC64 1020362306a36Sopenharmony_ci err = platform_driver_register(&niu_of_driver); 1020462306a36Sopenharmony_ci#endif 1020562306a36Sopenharmony_ci 1020662306a36Sopenharmony_ci if (!err) { 1020762306a36Sopenharmony_ci err = pci_register_driver(&niu_pci_driver); 1020862306a36Sopenharmony_ci#ifdef CONFIG_SPARC64 1020962306a36Sopenharmony_ci if (err) 1021062306a36Sopenharmony_ci platform_driver_unregister(&niu_of_driver); 1021162306a36Sopenharmony_ci#endif 1021262306a36Sopenharmony_ci } 1021362306a36Sopenharmony_ci 1021462306a36Sopenharmony_ci return err; 1021562306a36Sopenharmony_ci} 1021662306a36Sopenharmony_ci 1021762306a36Sopenharmony_cistatic void __exit niu_exit(void) 1021862306a36Sopenharmony_ci{ 1021962306a36Sopenharmony_ci pci_unregister_driver(&niu_pci_driver); 1022062306a36Sopenharmony_ci#ifdef CONFIG_SPARC64 1022162306a36Sopenharmony_ci platform_driver_unregister(&niu_of_driver); 1022262306a36Sopenharmony_ci#endif 1022362306a36Sopenharmony_ci} 1022462306a36Sopenharmony_ci 1022562306a36Sopenharmony_cimodule_init(niu_init); 1022662306a36Sopenharmony_cimodule_exit(niu_exit); 10227