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