162306a36Sopenharmony_ci/* b44.c: Broadcom 44xx/47xx Fast Ethernet device driver.
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * Copyright (C) 2002 David S. Miller (davem@redhat.com)
462306a36Sopenharmony_ci * Copyright (C) 2004 Pekka Pietikainen (pp@ee.oulu.fi)
562306a36Sopenharmony_ci * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org)
662306a36Sopenharmony_ci * Copyright (C) 2006 Felix Fietkau (nbd@openwrt.org)
762306a36Sopenharmony_ci * Copyright (C) 2006 Broadcom Corporation.
862306a36Sopenharmony_ci * Copyright (C) 2007 Michael Buesch <m@bues.ch>
962306a36Sopenharmony_ci * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Distribute under GPL.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/moduleparam.h>
1962306a36Sopenharmony_ci#include <linux/types.h>
2062306a36Sopenharmony_ci#include <linux/netdevice.h>
2162306a36Sopenharmony_ci#include <linux/ethtool.h>
2262306a36Sopenharmony_ci#include <linux/mii.h>
2362306a36Sopenharmony_ci#include <linux/if_ether.h>
2462306a36Sopenharmony_ci#include <linux/if_vlan.h>
2562306a36Sopenharmony_ci#include <linux/etherdevice.h>
2662306a36Sopenharmony_ci#include <linux/pci.h>
2762306a36Sopenharmony_ci#include <linux/delay.h>
2862306a36Sopenharmony_ci#include <linux/init.h>
2962306a36Sopenharmony_ci#include <linux/interrupt.h>
3062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
3162306a36Sopenharmony_ci#include <linux/ssb/ssb.h>
3262306a36Sopenharmony_ci#include <linux/slab.h>
3362306a36Sopenharmony_ci#include <linux/phy.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/uaccess.h>
3662306a36Sopenharmony_ci#include <asm/io.h>
3762306a36Sopenharmony_ci#include <asm/irq.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include "b44.h"
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define DRV_MODULE_NAME		"b44"
4362306a36Sopenharmony_ci#define DRV_DESCRIPTION		"Broadcom 44xx/47xx 10/100 PCI ethernet driver"
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define B44_DEF_MSG_ENABLE	  \
4662306a36Sopenharmony_ci	(NETIF_MSG_DRV		| \
4762306a36Sopenharmony_ci	 NETIF_MSG_PROBE	| \
4862306a36Sopenharmony_ci	 NETIF_MSG_LINK		| \
4962306a36Sopenharmony_ci	 NETIF_MSG_TIMER	| \
5062306a36Sopenharmony_ci	 NETIF_MSG_IFDOWN	| \
5162306a36Sopenharmony_ci	 NETIF_MSG_IFUP		| \
5262306a36Sopenharmony_ci	 NETIF_MSG_RX_ERR	| \
5362306a36Sopenharmony_ci	 NETIF_MSG_TX_ERR)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* length of time before we decide the hardware is borked,
5662306a36Sopenharmony_ci * and dev->tx_timeout() should be called to fix the problem
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_ci#define B44_TX_TIMEOUT			(5 * HZ)
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* hardware minimum and maximum for a single frame's data payload */
6162306a36Sopenharmony_ci#define B44_MIN_MTU			ETH_ZLEN
6262306a36Sopenharmony_ci#define B44_MAX_MTU			ETH_DATA_LEN
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define B44_RX_RING_SIZE		512
6562306a36Sopenharmony_ci#define B44_DEF_RX_RING_PENDING		200
6662306a36Sopenharmony_ci#define B44_RX_RING_BYTES	(sizeof(struct dma_desc) * \
6762306a36Sopenharmony_ci				 B44_RX_RING_SIZE)
6862306a36Sopenharmony_ci#define B44_TX_RING_SIZE		512
6962306a36Sopenharmony_ci#define B44_DEF_TX_RING_PENDING		(B44_TX_RING_SIZE - 1)
7062306a36Sopenharmony_ci#define B44_TX_RING_BYTES	(sizeof(struct dma_desc) * \
7162306a36Sopenharmony_ci				 B44_TX_RING_SIZE)
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define TX_RING_GAP(BP)	\
7462306a36Sopenharmony_ci	(B44_TX_RING_SIZE - (BP)->tx_pending)
7562306a36Sopenharmony_ci#define TX_BUFFS_AVAIL(BP)						\
7662306a36Sopenharmony_ci	(((BP)->tx_cons <= (BP)->tx_prod) ?				\
7762306a36Sopenharmony_ci	  (BP)->tx_cons + (BP)->tx_pending - (BP)->tx_prod :		\
7862306a36Sopenharmony_ci	  (BP)->tx_cons - (BP)->tx_prod - TX_RING_GAP(BP))
7962306a36Sopenharmony_ci#define NEXT_TX(N)		(((N) + 1) & (B44_TX_RING_SIZE - 1))
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define RX_PKT_OFFSET		(RX_HEADER_LEN + 2)
8262306a36Sopenharmony_ci#define RX_PKT_BUF_SZ		(1536 + RX_PKT_OFFSET)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* minimum number of free TX descriptors required to wake up TX process */
8562306a36Sopenharmony_ci#define B44_TX_WAKEUP_THRESH		(B44_TX_RING_SIZE / 4)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* b44 internal pattern match filter info */
8862306a36Sopenharmony_ci#define B44_PATTERN_BASE	0x400
8962306a36Sopenharmony_ci#define B44_PATTERN_SIZE	0x80
9062306a36Sopenharmony_ci#define B44_PMASK_BASE		0x600
9162306a36Sopenharmony_ci#define B44_PMASK_SIZE		0x10
9262306a36Sopenharmony_ci#define B44_MAX_PATTERNS	16
9362306a36Sopenharmony_ci#define B44_ETHIPV6UDP_HLEN	62
9462306a36Sopenharmony_ci#define B44_ETHIPV4UDP_HLEN	42
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciMODULE_AUTHOR("Felix Fietkau, Florian Schirmer, Pekka Pietikainen, David S. Miller");
9762306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION);
9862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int b44_debug = -1;	/* -1 == use B44_DEF_MSG_ENABLE as value */
10162306a36Sopenharmony_cimodule_param(b44_debug, int, 0);
10262306a36Sopenharmony_ciMODULE_PARM_DESC(b44_debug, "B44 bitmapped debugging message enable value");
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#ifdef CONFIG_B44_PCI
10662306a36Sopenharmony_cistatic const struct pci_device_id b44_pci_tbl[] = {
10762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401) },
10862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B0) },
10962306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B1) },
11062306a36Sopenharmony_ci	{ 0 } /* terminate list with empty entry */
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, b44_pci_tbl);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic struct pci_driver b44_pci_driver = {
11562306a36Sopenharmony_ci	.name		= DRV_MODULE_NAME,
11662306a36Sopenharmony_ci	.id_table	= b44_pci_tbl,
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci#endif /* CONFIG_B44_PCI */
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic const struct ssb_device_id b44_ssb_tbl[] = {
12162306a36Sopenharmony_ci	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET, SSB_ANY_REV),
12262306a36Sopenharmony_ci	{},
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(ssb, b44_ssb_tbl);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void b44_halt(struct b44 *);
12762306a36Sopenharmony_cistatic void b44_init_rings(struct b44 *);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define B44_FULL_RESET		1
13062306a36Sopenharmony_ci#define B44_FULL_RESET_SKIP_PHY	2
13162306a36Sopenharmony_ci#define B44_PARTIAL_RESET	3
13262306a36Sopenharmony_ci#define B44_CHIP_RESET_FULL	4
13362306a36Sopenharmony_ci#define B44_CHIP_RESET_PARTIAL	5
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void b44_init_hw(struct b44 *, int);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int dma_desc_sync_size;
13862306a36Sopenharmony_cistatic int instance;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic const char b44_gstrings[][ETH_GSTRING_LEN] = {
14162306a36Sopenharmony_ci#define _B44(x...)	# x,
14262306a36Sopenharmony_ciB44_STAT_REG_DECLARE
14362306a36Sopenharmony_ci#undef _B44
14462306a36Sopenharmony_ci};
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic inline void b44_sync_dma_desc_for_device(struct ssb_device *sdev,
14762306a36Sopenharmony_ci						dma_addr_t dma_base,
14862306a36Sopenharmony_ci						unsigned long offset,
14962306a36Sopenharmony_ci						enum dma_data_direction dir)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	dma_sync_single_for_device(sdev->dma_dev, dma_base + offset,
15262306a36Sopenharmony_ci				   dma_desc_sync_size, dir);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic inline void b44_sync_dma_desc_for_cpu(struct ssb_device *sdev,
15662306a36Sopenharmony_ci					     dma_addr_t dma_base,
15762306a36Sopenharmony_ci					     unsigned long offset,
15862306a36Sopenharmony_ci					     enum dma_data_direction dir)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	dma_sync_single_for_cpu(sdev->dma_dev, dma_base + offset,
16162306a36Sopenharmony_ci				dma_desc_sync_size, dir);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic inline unsigned long br32(const struct b44 *bp, unsigned long reg)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	return ssb_read32(bp->sdev, reg);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic inline void bw32(const struct b44 *bp,
17062306a36Sopenharmony_ci			unsigned long reg, unsigned long val)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	ssb_write32(bp->sdev, reg, val);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int b44_wait_bit(struct b44 *bp, unsigned long reg,
17662306a36Sopenharmony_ci			u32 bit, unsigned long timeout, const int clear)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	unsigned long i;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	for (i = 0; i < timeout; i++) {
18162306a36Sopenharmony_ci		u32 val = br32(bp, reg);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		if (clear && !(val & bit))
18462306a36Sopenharmony_ci			break;
18562306a36Sopenharmony_ci		if (!clear && (val & bit))
18662306a36Sopenharmony_ci			break;
18762306a36Sopenharmony_ci		udelay(10);
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci	if (i == timeout) {
19062306a36Sopenharmony_ci		if (net_ratelimit())
19162306a36Sopenharmony_ci			netdev_err(bp->dev, "BUG!  Timeout waiting for bit %08x of register %lx to %s\n",
19262306a36Sopenharmony_ci				   bit, reg, clear ? "clear" : "set");
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		return -ENODEV;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic inline void __b44_cam_write(struct b44 *bp,
20062306a36Sopenharmony_ci				   const unsigned char *data, int index)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	u32 val;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	val  = ((u32) data[2]) << 24;
20562306a36Sopenharmony_ci	val |= ((u32) data[3]) << 16;
20662306a36Sopenharmony_ci	val |= ((u32) data[4]) <<  8;
20762306a36Sopenharmony_ci	val |= ((u32) data[5]) <<  0;
20862306a36Sopenharmony_ci	bw32(bp, B44_CAM_DATA_LO, val);
20962306a36Sopenharmony_ci	val = (CAM_DATA_HI_VALID |
21062306a36Sopenharmony_ci	       (((u32) data[0]) << 8) |
21162306a36Sopenharmony_ci	       (((u32) data[1]) << 0));
21262306a36Sopenharmony_ci	bw32(bp, B44_CAM_DATA_HI, val);
21362306a36Sopenharmony_ci	bw32(bp, B44_CAM_CTRL, (CAM_CTRL_WRITE |
21462306a36Sopenharmony_ci			    (index << CAM_CTRL_INDEX_SHIFT)));
21562306a36Sopenharmony_ci	b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic inline void __b44_disable_ints(struct b44 *bp)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	bw32(bp, B44_IMASK, 0);
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void b44_disable_ints(struct b44 *bp)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	__b44_disable_ints(bp);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* Flush posted writes. */
22862306a36Sopenharmony_ci	br32(bp, B44_IMASK);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void b44_enable_ints(struct b44 *bp)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	bw32(bp, B44_IMASK, bp->imask);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int __b44_readphy(struct b44 *bp, int phy_addr, int reg, u32 *val)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	int err;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII);
24162306a36Sopenharmony_ci	bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START |
24262306a36Sopenharmony_ci			     (MDIO_OP_READ << MDIO_DATA_OP_SHIFT) |
24362306a36Sopenharmony_ci			     (phy_addr << MDIO_DATA_PMD_SHIFT) |
24462306a36Sopenharmony_ci			     (reg << MDIO_DATA_RA_SHIFT) |
24562306a36Sopenharmony_ci			     (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT)));
24662306a36Sopenharmony_ci	err = b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0);
24762306a36Sopenharmony_ci	*val = br32(bp, B44_MDIO_DATA) & MDIO_DATA_DATA;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return err;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int __b44_writephy(struct b44 *bp, int phy_addr, int reg, u32 val)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII);
25562306a36Sopenharmony_ci	bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START |
25662306a36Sopenharmony_ci			     (MDIO_OP_WRITE << MDIO_DATA_OP_SHIFT) |
25762306a36Sopenharmony_ci			     (phy_addr << MDIO_DATA_PMD_SHIFT) |
25862306a36Sopenharmony_ci			     (reg << MDIO_DATA_RA_SHIFT) |
25962306a36Sopenharmony_ci			     (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT) |
26062306a36Sopenharmony_ci			     (val & MDIO_DATA_DATA)));
26162306a36Sopenharmony_ci	return b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic inline int b44_readphy(struct b44 *bp, int reg, u32 *val)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY)
26762306a36Sopenharmony_ci		return 0;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return __b44_readphy(bp, bp->phy_addr, reg, val);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic inline int b44_writephy(struct b44 *bp, int reg, u32 val)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY)
27562306a36Sopenharmony_ci		return 0;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return __b44_writephy(bp, bp->phy_addr, reg, val);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/* miilib interface */
28162306a36Sopenharmony_cistatic int b44_mdio_read_mii(struct net_device *dev, int phy_id, int location)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	u32 val;
28462306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
28562306a36Sopenharmony_ci	int rc = __b44_readphy(bp, phy_id, location, &val);
28662306a36Sopenharmony_ci	if (rc)
28762306a36Sopenharmony_ci		return 0xffffffff;
28862306a36Sopenharmony_ci	return val;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic void b44_mdio_write_mii(struct net_device *dev, int phy_id, int location,
29262306a36Sopenharmony_ci			       int val)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
29562306a36Sopenharmony_ci	__b44_writephy(bp, phy_id, location, val);
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int b44_mdio_read_phylib(struct mii_bus *bus, int phy_id, int location)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	u32 val;
30162306a36Sopenharmony_ci	struct b44 *bp = bus->priv;
30262306a36Sopenharmony_ci	int rc = __b44_readphy(bp, phy_id, location, &val);
30362306a36Sopenharmony_ci	if (rc)
30462306a36Sopenharmony_ci		return 0xffffffff;
30562306a36Sopenharmony_ci	return val;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic int b44_mdio_write_phylib(struct mii_bus *bus, int phy_id, int location,
30962306a36Sopenharmony_ci				 u16 val)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct b44 *bp = bus->priv;
31262306a36Sopenharmony_ci	return __b44_writephy(bp, phy_id, location, val);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int b44_phy_reset(struct b44 *bp)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	u32 val;
31862306a36Sopenharmony_ci	int err;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY)
32162306a36Sopenharmony_ci		return 0;
32262306a36Sopenharmony_ci	err = b44_writephy(bp, MII_BMCR, BMCR_RESET);
32362306a36Sopenharmony_ci	if (err)
32462306a36Sopenharmony_ci		return err;
32562306a36Sopenharmony_ci	udelay(100);
32662306a36Sopenharmony_ci	err = b44_readphy(bp, MII_BMCR, &val);
32762306a36Sopenharmony_ci	if (!err) {
32862306a36Sopenharmony_ci		if (val & BMCR_RESET) {
32962306a36Sopenharmony_ci			netdev_err(bp->dev, "PHY Reset would not complete\n");
33062306a36Sopenharmony_ci			err = -ENODEV;
33162306a36Sopenharmony_ci		}
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return err;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic void __b44_set_flow_ctrl(struct b44 *bp, u32 pause_flags)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	u32 val;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	bp->flags &= ~(B44_FLAG_TX_PAUSE | B44_FLAG_RX_PAUSE);
34262306a36Sopenharmony_ci	bp->flags |= pause_flags;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	val = br32(bp, B44_RXCONFIG);
34562306a36Sopenharmony_ci	if (pause_flags & B44_FLAG_RX_PAUSE)
34662306a36Sopenharmony_ci		val |= RXCONFIG_FLOW;
34762306a36Sopenharmony_ci	else
34862306a36Sopenharmony_ci		val &= ~RXCONFIG_FLOW;
34962306a36Sopenharmony_ci	bw32(bp, B44_RXCONFIG, val);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	val = br32(bp, B44_MAC_FLOW);
35262306a36Sopenharmony_ci	if (pause_flags & B44_FLAG_TX_PAUSE)
35362306a36Sopenharmony_ci		val |= (MAC_FLOW_PAUSE_ENAB |
35462306a36Sopenharmony_ci			(0xc0 & MAC_FLOW_RX_HI_WATER));
35562306a36Sopenharmony_ci	else
35662306a36Sopenharmony_ci		val &= ~MAC_FLOW_PAUSE_ENAB;
35762306a36Sopenharmony_ci	bw32(bp, B44_MAC_FLOW, val);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic void b44_set_flow_ctrl(struct b44 *bp, u32 local, u32 remote)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	u32 pause_enab = 0;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* The driver supports only rx pause by default because
36562306a36Sopenharmony_ci	   the b44 mac tx pause mechanism generates excessive
36662306a36Sopenharmony_ci	   pause frames.
36762306a36Sopenharmony_ci	   Use ethtool to turn on b44 tx pause if necessary.
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci	if ((local & ADVERTISE_PAUSE_CAP) &&
37062306a36Sopenharmony_ci	    (local & ADVERTISE_PAUSE_ASYM)){
37162306a36Sopenharmony_ci		if ((remote & LPA_PAUSE_ASYM) &&
37262306a36Sopenharmony_ci		    !(remote & LPA_PAUSE_CAP))
37362306a36Sopenharmony_ci			pause_enab |= B44_FLAG_RX_PAUSE;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	__b44_set_flow_ctrl(bp, pause_enab);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci#ifdef CONFIG_BCM47XX
38062306a36Sopenharmony_ci#include <linux/bcm47xx_nvram.h>
38162306a36Sopenharmony_cistatic void b44_wap54g10_workaround(struct b44 *bp)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	char buf[20];
38462306a36Sopenharmony_ci	u32 val;
38562306a36Sopenharmony_ci	int err;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/*
38862306a36Sopenharmony_ci	 * workaround for bad hardware design in Linksys WAP54G v1.0
38962306a36Sopenharmony_ci	 * see https://dev.openwrt.org/ticket/146
39062306a36Sopenharmony_ci	 * check and reset bit "isolate"
39162306a36Sopenharmony_ci	 */
39262306a36Sopenharmony_ci	if (bcm47xx_nvram_getenv("boardnum", buf, sizeof(buf)) < 0)
39362306a36Sopenharmony_ci		return;
39462306a36Sopenharmony_ci	if (simple_strtoul(buf, NULL, 0) == 2) {
39562306a36Sopenharmony_ci		err = __b44_readphy(bp, 0, MII_BMCR, &val);
39662306a36Sopenharmony_ci		if (err)
39762306a36Sopenharmony_ci			goto error;
39862306a36Sopenharmony_ci		if (!(val & BMCR_ISOLATE))
39962306a36Sopenharmony_ci			return;
40062306a36Sopenharmony_ci		val &= ~BMCR_ISOLATE;
40162306a36Sopenharmony_ci		err = __b44_writephy(bp, 0, MII_BMCR, val);
40262306a36Sopenharmony_ci		if (err)
40362306a36Sopenharmony_ci			goto error;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci	return;
40662306a36Sopenharmony_cierror:
40762306a36Sopenharmony_ci	pr_warn("PHY: cannot reset MII transceiver isolate bit\n");
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci#else
41062306a36Sopenharmony_cistatic inline void b44_wap54g10_workaround(struct b44 *bp)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci#endif
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int b44_setup_phy(struct b44 *bp)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	u32 val;
41862306a36Sopenharmony_ci	int err;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	b44_wap54g10_workaround(bp);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY)
42362306a36Sopenharmony_ci		return 0;
42462306a36Sopenharmony_ci	if ((err = b44_readphy(bp, B44_MII_ALEDCTRL, &val)) != 0)
42562306a36Sopenharmony_ci		goto out;
42662306a36Sopenharmony_ci	if ((err = b44_writephy(bp, B44_MII_ALEDCTRL,
42762306a36Sopenharmony_ci				val & MII_ALEDCTRL_ALLMSK)) != 0)
42862306a36Sopenharmony_ci		goto out;
42962306a36Sopenharmony_ci	if ((err = b44_readphy(bp, B44_MII_TLEDCTRL, &val)) != 0)
43062306a36Sopenharmony_ci		goto out;
43162306a36Sopenharmony_ci	if ((err = b44_writephy(bp, B44_MII_TLEDCTRL,
43262306a36Sopenharmony_ci				val | MII_TLEDCTRL_ENABLE)) != 0)
43362306a36Sopenharmony_ci		goto out;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (!(bp->flags & B44_FLAG_FORCE_LINK)) {
43662306a36Sopenharmony_ci		u32 adv = ADVERTISE_CSMA;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		if (bp->flags & B44_FLAG_ADV_10HALF)
43962306a36Sopenharmony_ci			adv |= ADVERTISE_10HALF;
44062306a36Sopenharmony_ci		if (bp->flags & B44_FLAG_ADV_10FULL)
44162306a36Sopenharmony_ci			adv |= ADVERTISE_10FULL;
44262306a36Sopenharmony_ci		if (bp->flags & B44_FLAG_ADV_100HALF)
44362306a36Sopenharmony_ci			adv |= ADVERTISE_100HALF;
44462306a36Sopenharmony_ci		if (bp->flags & B44_FLAG_ADV_100FULL)
44562306a36Sopenharmony_ci			adv |= ADVERTISE_100FULL;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		if (bp->flags & B44_FLAG_PAUSE_AUTO)
44862306a36Sopenharmony_ci			adv |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		if ((err = b44_writephy(bp, MII_ADVERTISE, adv)) != 0)
45162306a36Sopenharmony_ci			goto out;
45262306a36Sopenharmony_ci		if ((err = b44_writephy(bp, MII_BMCR, (BMCR_ANENABLE |
45362306a36Sopenharmony_ci						       BMCR_ANRESTART))) != 0)
45462306a36Sopenharmony_ci			goto out;
45562306a36Sopenharmony_ci	} else {
45662306a36Sopenharmony_ci		u32 bmcr;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		if ((err = b44_readphy(bp, MII_BMCR, &bmcr)) != 0)
45962306a36Sopenharmony_ci			goto out;
46062306a36Sopenharmony_ci		bmcr &= ~(BMCR_FULLDPLX | BMCR_ANENABLE | BMCR_SPEED100);
46162306a36Sopenharmony_ci		if (bp->flags & B44_FLAG_100_BASE_T)
46262306a36Sopenharmony_ci			bmcr |= BMCR_SPEED100;
46362306a36Sopenharmony_ci		if (bp->flags & B44_FLAG_FULL_DUPLEX)
46462306a36Sopenharmony_ci			bmcr |= BMCR_FULLDPLX;
46562306a36Sopenharmony_ci		if ((err = b44_writephy(bp, MII_BMCR, bmcr)) != 0)
46662306a36Sopenharmony_ci			goto out;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		/* Since we will not be negotiating there is no safe way
46962306a36Sopenharmony_ci		 * to determine if the link partner supports flow control
47062306a36Sopenharmony_ci		 * or not.  So just disable it completely in this case.
47162306a36Sopenharmony_ci		 */
47262306a36Sopenharmony_ci		b44_set_flow_ctrl(bp, 0, 0);
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ciout:
47662306a36Sopenharmony_ci	return err;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic void b44_stats_update(struct b44 *bp)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	unsigned long reg;
48262306a36Sopenharmony_ci	u64 *val;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	val = &bp->hw_stats.tx_good_octets;
48562306a36Sopenharmony_ci	u64_stats_update_begin(&bp->hw_stats.syncp);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	for (reg = B44_TX_GOOD_O; reg <= B44_TX_PAUSE; reg += 4UL) {
48862306a36Sopenharmony_ci		*val++ += br32(bp, reg);
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	for (reg = B44_RX_GOOD_O; reg <= B44_RX_NPAUSE; reg += 4UL) {
49262306a36Sopenharmony_ci		*val++ += br32(bp, reg);
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	u64_stats_update_end(&bp->hw_stats.syncp);
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic void b44_link_report(struct b44 *bp)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	if (!netif_carrier_ok(bp->dev)) {
50162306a36Sopenharmony_ci		netdev_info(bp->dev, "Link is down\n");
50262306a36Sopenharmony_ci	} else {
50362306a36Sopenharmony_ci		netdev_info(bp->dev, "Link is up at %d Mbps, %s duplex\n",
50462306a36Sopenharmony_ci			    (bp->flags & B44_FLAG_100_BASE_T) ? 100 : 10,
50562306a36Sopenharmony_ci			    (bp->flags & B44_FLAG_FULL_DUPLEX) ? "full" : "half");
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		netdev_info(bp->dev, "Flow control is %s for TX and %s for RX\n",
50862306a36Sopenharmony_ci			    (bp->flags & B44_FLAG_TX_PAUSE) ? "on" : "off",
50962306a36Sopenharmony_ci			    (bp->flags & B44_FLAG_RX_PAUSE) ? "on" : "off");
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic void b44_check_phy(struct b44 *bp)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	u32 bmsr, aux;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY) {
51862306a36Sopenharmony_ci		bp->flags |= B44_FLAG_100_BASE_T;
51962306a36Sopenharmony_ci		if (!netif_carrier_ok(bp->dev)) {
52062306a36Sopenharmony_ci			u32 val = br32(bp, B44_TX_CTRL);
52162306a36Sopenharmony_ci			if (bp->flags & B44_FLAG_FULL_DUPLEX)
52262306a36Sopenharmony_ci				val |= TX_CTRL_DUPLEX;
52362306a36Sopenharmony_ci			else
52462306a36Sopenharmony_ci				val &= ~TX_CTRL_DUPLEX;
52562306a36Sopenharmony_ci			bw32(bp, B44_TX_CTRL, val);
52662306a36Sopenharmony_ci			netif_carrier_on(bp->dev);
52762306a36Sopenharmony_ci			b44_link_report(bp);
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci		return;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (!b44_readphy(bp, MII_BMSR, &bmsr) &&
53362306a36Sopenharmony_ci	    !b44_readphy(bp, B44_MII_AUXCTRL, &aux) &&
53462306a36Sopenharmony_ci	    (bmsr != 0xffff)) {
53562306a36Sopenharmony_ci		if (aux & MII_AUXCTRL_SPEED)
53662306a36Sopenharmony_ci			bp->flags |= B44_FLAG_100_BASE_T;
53762306a36Sopenharmony_ci		else
53862306a36Sopenharmony_ci			bp->flags &= ~B44_FLAG_100_BASE_T;
53962306a36Sopenharmony_ci		if (aux & MII_AUXCTRL_DUPLEX)
54062306a36Sopenharmony_ci			bp->flags |= B44_FLAG_FULL_DUPLEX;
54162306a36Sopenharmony_ci		else
54262306a36Sopenharmony_ci			bp->flags &= ~B44_FLAG_FULL_DUPLEX;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		if (!netif_carrier_ok(bp->dev) &&
54562306a36Sopenharmony_ci		    (bmsr & BMSR_LSTATUS)) {
54662306a36Sopenharmony_ci			u32 val = br32(bp, B44_TX_CTRL);
54762306a36Sopenharmony_ci			u32 local_adv, remote_adv;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci			if (bp->flags & B44_FLAG_FULL_DUPLEX)
55062306a36Sopenharmony_ci				val |= TX_CTRL_DUPLEX;
55162306a36Sopenharmony_ci			else
55262306a36Sopenharmony_ci				val &= ~TX_CTRL_DUPLEX;
55362306a36Sopenharmony_ci			bw32(bp, B44_TX_CTRL, val);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci			if (!(bp->flags & B44_FLAG_FORCE_LINK) &&
55662306a36Sopenharmony_ci			    !b44_readphy(bp, MII_ADVERTISE, &local_adv) &&
55762306a36Sopenharmony_ci			    !b44_readphy(bp, MII_LPA, &remote_adv))
55862306a36Sopenharmony_ci				b44_set_flow_ctrl(bp, local_adv, remote_adv);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci			/* Link now up */
56162306a36Sopenharmony_ci			netif_carrier_on(bp->dev);
56262306a36Sopenharmony_ci			b44_link_report(bp);
56362306a36Sopenharmony_ci		} else if (netif_carrier_ok(bp->dev) && !(bmsr & BMSR_LSTATUS)) {
56462306a36Sopenharmony_ci			/* Link now down */
56562306a36Sopenharmony_ci			netif_carrier_off(bp->dev);
56662306a36Sopenharmony_ci			b44_link_report(bp);
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		if (bmsr & BMSR_RFAULT)
57062306a36Sopenharmony_ci			netdev_warn(bp->dev, "Remote fault detected in PHY\n");
57162306a36Sopenharmony_ci		if (bmsr & BMSR_JCD)
57262306a36Sopenharmony_ci			netdev_warn(bp->dev, "Jabber detected in PHY\n");
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic void b44_timer(struct timer_list *t)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct b44 *bp = from_timer(bp, t, timer);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	b44_check_phy(bp);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	b44_stats_update(bp);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	mod_timer(&bp->timer, round_jiffies(jiffies + HZ));
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic void b44_tx(struct b44 *bp)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	u32 cur, cons;
59462306a36Sopenharmony_ci	unsigned bytes_compl = 0, pkts_compl = 0;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	cur  = br32(bp, B44_DMATX_STAT) & DMATX_STAT_CDMASK;
59762306a36Sopenharmony_ci	cur /= sizeof(struct dma_desc);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	/* XXX needs updating when NETIF_F_SG is supported */
60062306a36Sopenharmony_ci	for (cons = bp->tx_cons; cons != cur; cons = NEXT_TX(cons)) {
60162306a36Sopenharmony_ci		struct ring_info *rp = &bp->tx_buffers[cons];
60262306a36Sopenharmony_ci		struct sk_buff *skb = rp->skb;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		BUG_ON(skb == NULL);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		dma_unmap_single(bp->sdev->dma_dev,
60762306a36Sopenharmony_ci				 rp->mapping,
60862306a36Sopenharmony_ci				 skb->len,
60962306a36Sopenharmony_ci				 DMA_TO_DEVICE);
61062306a36Sopenharmony_ci		rp->skb = NULL;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		bytes_compl += skb->len;
61362306a36Sopenharmony_ci		pkts_compl++;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		dev_consume_skb_irq(skb);
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	netdev_completed_queue(bp->dev, pkts_compl, bytes_compl);
61962306a36Sopenharmony_ci	bp->tx_cons = cons;
62062306a36Sopenharmony_ci	if (netif_queue_stopped(bp->dev) &&
62162306a36Sopenharmony_ci	    TX_BUFFS_AVAIL(bp) > B44_TX_WAKEUP_THRESH)
62262306a36Sopenharmony_ci		netif_wake_queue(bp->dev);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	bw32(bp, B44_GPTIMER, 0);
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/* Works like this.  This chip writes a 'struct rx_header" 30 bytes
62862306a36Sopenharmony_ci * before the DMA address you give it.  So we allocate 30 more bytes
62962306a36Sopenharmony_ci * for the RX buffer, DMA map all of it, skb_reserve the 30 bytes, then
63062306a36Sopenharmony_ci * point the chip at 30 bytes past where the rx_header will go.
63162306a36Sopenharmony_ci */
63262306a36Sopenharmony_cistatic int b44_alloc_rx_skb(struct b44 *bp, int src_idx, u32 dest_idx_unmasked)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct dma_desc *dp;
63562306a36Sopenharmony_ci	struct ring_info *src_map, *map;
63662306a36Sopenharmony_ci	struct rx_header *rh;
63762306a36Sopenharmony_ci	struct sk_buff *skb;
63862306a36Sopenharmony_ci	dma_addr_t mapping;
63962306a36Sopenharmony_ci	int dest_idx;
64062306a36Sopenharmony_ci	u32 ctrl;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	src_map = NULL;
64362306a36Sopenharmony_ci	if (src_idx >= 0)
64462306a36Sopenharmony_ci		src_map = &bp->rx_buffers[src_idx];
64562306a36Sopenharmony_ci	dest_idx = dest_idx_unmasked & (B44_RX_RING_SIZE - 1);
64662306a36Sopenharmony_ci	map = &bp->rx_buffers[dest_idx];
64762306a36Sopenharmony_ci	skb = netdev_alloc_skb(bp->dev, RX_PKT_BUF_SZ);
64862306a36Sopenharmony_ci	if (skb == NULL)
64962306a36Sopenharmony_ci		return -ENOMEM;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	mapping = dma_map_single(bp->sdev->dma_dev, skb->data,
65262306a36Sopenharmony_ci				 RX_PKT_BUF_SZ,
65362306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	/* Hardware bug work-around, the chip is unable to do PCI DMA
65662306a36Sopenharmony_ci	   to/from anything above 1GB :-( */
65762306a36Sopenharmony_ci	if (dma_mapping_error(bp->sdev->dma_dev, mapping) ||
65862306a36Sopenharmony_ci		mapping + RX_PKT_BUF_SZ > DMA_BIT_MASK(30)) {
65962306a36Sopenharmony_ci		/* Sigh... */
66062306a36Sopenharmony_ci		if (!dma_mapping_error(bp->sdev->dma_dev, mapping))
66162306a36Sopenharmony_ci			dma_unmap_single(bp->sdev->dma_dev, mapping,
66262306a36Sopenharmony_ci					     RX_PKT_BUF_SZ, DMA_FROM_DEVICE);
66362306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
66462306a36Sopenharmony_ci		skb = alloc_skb(RX_PKT_BUF_SZ, GFP_ATOMIC | GFP_DMA);
66562306a36Sopenharmony_ci		if (skb == NULL)
66662306a36Sopenharmony_ci			return -ENOMEM;
66762306a36Sopenharmony_ci		mapping = dma_map_single(bp->sdev->dma_dev, skb->data,
66862306a36Sopenharmony_ci					 RX_PKT_BUF_SZ,
66962306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
67062306a36Sopenharmony_ci		if (dma_mapping_error(bp->sdev->dma_dev, mapping) ||
67162306a36Sopenharmony_ci		    mapping + RX_PKT_BUF_SZ > DMA_BIT_MASK(30)) {
67262306a36Sopenharmony_ci			if (!dma_mapping_error(bp->sdev->dma_dev, mapping))
67362306a36Sopenharmony_ci				dma_unmap_single(bp->sdev->dma_dev, mapping, RX_PKT_BUF_SZ,DMA_FROM_DEVICE);
67462306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
67562306a36Sopenharmony_ci			return -ENOMEM;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci		bp->force_copybreak = 1;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	rh = (struct rx_header *) skb->data;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	rh->len = 0;
68362306a36Sopenharmony_ci	rh->flags = 0;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	map->skb = skb;
68662306a36Sopenharmony_ci	map->mapping = mapping;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	if (src_map != NULL)
68962306a36Sopenharmony_ci		src_map->skb = NULL;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	ctrl = (DESC_CTRL_LEN & RX_PKT_BUF_SZ);
69262306a36Sopenharmony_ci	if (dest_idx == (B44_RX_RING_SIZE - 1))
69362306a36Sopenharmony_ci		ctrl |= DESC_CTRL_EOT;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	dp = &bp->rx_ring[dest_idx];
69662306a36Sopenharmony_ci	dp->ctrl = cpu_to_le32(ctrl);
69762306a36Sopenharmony_ci	dp->addr = cpu_to_le32((u32) mapping + bp->dma_offset);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_RX_RING_HACK)
70062306a36Sopenharmony_ci		b44_sync_dma_desc_for_device(bp->sdev, bp->rx_ring_dma,
70162306a36Sopenharmony_ci			                    dest_idx * sizeof(*dp),
70262306a36Sopenharmony_ci			                    DMA_BIDIRECTIONAL);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	return RX_PKT_BUF_SZ;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic void b44_recycle_rx(struct b44 *bp, int src_idx, u32 dest_idx_unmasked)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct dma_desc *src_desc, *dest_desc;
71062306a36Sopenharmony_ci	struct ring_info *src_map, *dest_map;
71162306a36Sopenharmony_ci	struct rx_header *rh;
71262306a36Sopenharmony_ci	int dest_idx;
71362306a36Sopenharmony_ci	__le32 ctrl;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	dest_idx = dest_idx_unmasked & (B44_RX_RING_SIZE - 1);
71662306a36Sopenharmony_ci	dest_desc = &bp->rx_ring[dest_idx];
71762306a36Sopenharmony_ci	dest_map = &bp->rx_buffers[dest_idx];
71862306a36Sopenharmony_ci	src_desc = &bp->rx_ring[src_idx];
71962306a36Sopenharmony_ci	src_map = &bp->rx_buffers[src_idx];
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	dest_map->skb = src_map->skb;
72262306a36Sopenharmony_ci	rh = (struct rx_header *) src_map->skb->data;
72362306a36Sopenharmony_ci	rh->len = 0;
72462306a36Sopenharmony_ci	rh->flags = 0;
72562306a36Sopenharmony_ci	dest_map->mapping = src_map->mapping;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_RX_RING_HACK)
72862306a36Sopenharmony_ci		b44_sync_dma_desc_for_cpu(bp->sdev, bp->rx_ring_dma,
72962306a36Sopenharmony_ci			                 src_idx * sizeof(*src_desc),
73062306a36Sopenharmony_ci			                 DMA_BIDIRECTIONAL);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	ctrl = src_desc->ctrl;
73362306a36Sopenharmony_ci	if (dest_idx == (B44_RX_RING_SIZE - 1))
73462306a36Sopenharmony_ci		ctrl |= cpu_to_le32(DESC_CTRL_EOT);
73562306a36Sopenharmony_ci	else
73662306a36Sopenharmony_ci		ctrl &= cpu_to_le32(~DESC_CTRL_EOT);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	dest_desc->ctrl = ctrl;
73962306a36Sopenharmony_ci	dest_desc->addr = src_desc->addr;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	src_map->skb = NULL;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_RX_RING_HACK)
74462306a36Sopenharmony_ci		b44_sync_dma_desc_for_device(bp->sdev, bp->rx_ring_dma,
74562306a36Sopenharmony_ci					     dest_idx * sizeof(*dest_desc),
74662306a36Sopenharmony_ci					     DMA_BIDIRECTIONAL);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	dma_sync_single_for_device(bp->sdev->dma_dev, dest_map->mapping,
74962306a36Sopenharmony_ci				   RX_PKT_BUF_SZ,
75062306a36Sopenharmony_ci				   DMA_FROM_DEVICE);
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic int b44_rx(struct b44 *bp, int budget)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	int received;
75662306a36Sopenharmony_ci	u32 cons, prod;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	received = 0;
75962306a36Sopenharmony_ci	prod  = br32(bp, B44_DMARX_STAT) & DMARX_STAT_CDMASK;
76062306a36Sopenharmony_ci	prod /= sizeof(struct dma_desc);
76162306a36Sopenharmony_ci	cons = bp->rx_cons;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	while (cons != prod && budget > 0) {
76462306a36Sopenharmony_ci		struct ring_info *rp = &bp->rx_buffers[cons];
76562306a36Sopenharmony_ci		struct sk_buff *skb = rp->skb;
76662306a36Sopenharmony_ci		dma_addr_t map = rp->mapping;
76762306a36Sopenharmony_ci		struct rx_header *rh;
76862306a36Sopenharmony_ci		u16 len;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		dma_sync_single_for_cpu(bp->sdev->dma_dev, map,
77162306a36Sopenharmony_ci					RX_PKT_BUF_SZ,
77262306a36Sopenharmony_ci					DMA_FROM_DEVICE);
77362306a36Sopenharmony_ci		rh = (struct rx_header *) skb->data;
77462306a36Sopenharmony_ci		len = le16_to_cpu(rh->len);
77562306a36Sopenharmony_ci		if ((len > (RX_PKT_BUF_SZ - RX_PKT_OFFSET)) ||
77662306a36Sopenharmony_ci		    (rh->flags & cpu_to_le16(RX_FLAG_ERRORS))) {
77762306a36Sopenharmony_ci		drop_it:
77862306a36Sopenharmony_ci			b44_recycle_rx(bp, cons, bp->rx_prod);
77962306a36Sopenharmony_ci		drop_it_no_recycle:
78062306a36Sopenharmony_ci			bp->dev->stats.rx_dropped++;
78162306a36Sopenharmony_ci			goto next_pkt;
78262306a36Sopenharmony_ci		}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		if (len == 0) {
78562306a36Sopenharmony_ci			int i = 0;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci			do {
78862306a36Sopenharmony_ci				udelay(2);
78962306a36Sopenharmony_ci				barrier();
79062306a36Sopenharmony_ci				len = le16_to_cpu(rh->len);
79162306a36Sopenharmony_ci			} while (len == 0 && i++ < 5);
79262306a36Sopenharmony_ci			if (len == 0)
79362306a36Sopenharmony_ci				goto drop_it;
79462306a36Sopenharmony_ci		}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		/* Omit CRC. */
79762306a36Sopenharmony_ci		len -= 4;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		if (!bp->force_copybreak && len > RX_COPY_THRESHOLD) {
80062306a36Sopenharmony_ci			int skb_size;
80162306a36Sopenharmony_ci			skb_size = b44_alloc_rx_skb(bp, cons, bp->rx_prod);
80262306a36Sopenharmony_ci			if (skb_size < 0)
80362306a36Sopenharmony_ci				goto drop_it;
80462306a36Sopenharmony_ci			dma_unmap_single(bp->sdev->dma_dev, map,
80562306a36Sopenharmony_ci					 skb_size, DMA_FROM_DEVICE);
80662306a36Sopenharmony_ci			/* Leave out rx_header */
80762306a36Sopenharmony_ci			skb_put(skb, len + RX_PKT_OFFSET);
80862306a36Sopenharmony_ci			skb_pull(skb, RX_PKT_OFFSET);
80962306a36Sopenharmony_ci		} else {
81062306a36Sopenharmony_ci			struct sk_buff *copy_skb;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci			b44_recycle_rx(bp, cons, bp->rx_prod);
81362306a36Sopenharmony_ci			copy_skb = napi_alloc_skb(&bp->napi, len);
81462306a36Sopenharmony_ci			if (copy_skb == NULL)
81562306a36Sopenharmony_ci				goto drop_it_no_recycle;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci			skb_put(copy_skb, len);
81862306a36Sopenharmony_ci			/* DMA sync done above, copy just the actual packet */
81962306a36Sopenharmony_ci			skb_copy_from_linear_data_offset(skb, RX_PKT_OFFSET,
82062306a36Sopenharmony_ci							 copy_skb->data, len);
82162306a36Sopenharmony_ci			skb = copy_skb;
82262306a36Sopenharmony_ci		}
82362306a36Sopenharmony_ci		skb_checksum_none_assert(skb);
82462306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, bp->dev);
82562306a36Sopenharmony_ci		netif_receive_skb(skb);
82662306a36Sopenharmony_ci		received++;
82762306a36Sopenharmony_ci		budget--;
82862306a36Sopenharmony_ci	next_pkt:
82962306a36Sopenharmony_ci		bp->rx_prod = (bp->rx_prod + 1) &
83062306a36Sopenharmony_ci			(B44_RX_RING_SIZE - 1);
83162306a36Sopenharmony_ci		cons = (cons + 1) & (B44_RX_RING_SIZE - 1);
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	bp->rx_cons = cons;
83562306a36Sopenharmony_ci	bw32(bp, B44_DMARX_PTR, cons * sizeof(struct dma_desc));
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	return received;
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cistatic int b44_poll(struct napi_struct *napi, int budget)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	struct b44 *bp = container_of(napi, struct b44, napi);
84362306a36Sopenharmony_ci	int work_done;
84462306a36Sopenharmony_ci	unsigned long flags;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	spin_lock_irqsave(&bp->lock, flags);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (bp->istat & (ISTAT_TX | ISTAT_TO)) {
84962306a36Sopenharmony_ci		/* spin_lock(&bp->tx_lock); */
85062306a36Sopenharmony_ci		b44_tx(bp);
85162306a36Sopenharmony_ci		/* spin_unlock(&bp->tx_lock); */
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci	if (bp->istat & ISTAT_RFO) {	/* fast recovery, in ~20msec */
85462306a36Sopenharmony_ci		bp->istat &= ~ISTAT_RFO;
85562306a36Sopenharmony_ci		b44_disable_ints(bp);
85662306a36Sopenharmony_ci		ssb_device_enable(bp->sdev, 0); /* resets ISTAT_RFO */
85762306a36Sopenharmony_ci		b44_init_rings(bp);
85862306a36Sopenharmony_ci		b44_init_hw(bp, B44_FULL_RESET_SKIP_PHY);
85962306a36Sopenharmony_ci		netif_wake_queue(bp->dev);
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	spin_unlock_irqrestore(&bp->lock, flags);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	work_done = 0;
86562306a36Sopenharmony_ci	if (bp->istat & ISTAT_RX)
86662306a36Sopenharmony_ci		work_done += b44_rx(bp, budget);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	if (bp->istat & ISTAT_ERRORS) {
86962306a36Sopenharmony_ci		spin_lock_irqsave(&bp->lock, flags);
87062306a36Sopenharmony_ci		b44_halt(bp);
87162306a36Sopenharmony_ci		b44_init_rings(bp);
87262306a36Sopenharmony_ci		b44_init_hw(bp, B44_FULL_RESET_SKIP_PHY);
87362306a36Sopenharmony_ci		netif_wake_queue(bp->dev);
87462306a36Sopenharmony_ci		spin_unlock_irqrestore(&bp->lock, flags);
87562306a36Sopenharmony_ci		work_done = 0;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (work_done < budget) {
87962306a36Sopenharmony_ci		napi_complete_done(napi, work_done);
88062306a36Sopenharmony_ci		b44_enable_ints(bp);
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	return work_done;
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic irqreturn_t b44_interrupt(int irq, void *dev_id)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	struct net_device *dev = dev_id;
88962306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
89062306a36Sopenharmony_ci	u32 istat, imask;
89162306a36Sopenharmony_ci	int handled = 0;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	spin_lock(&bp->lock);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	istat = br32(bp, B44_ISTAT);
89662306a36Sopenharmony_ci	imask = br32(bp, B44_IMASK);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	/* The interrupt mask register controls which interrupt bits
89962306a36Sopenharmony_ci	 * will actually raise an interrupt to the CPU when set by hw/firmware,
90062306a36Sopenharmony_ci	 * but doesn't mask off the bits.
90162306a36Sopenharmony_ci	 */
90262306a36Sopenharmony_ci	istat &= imask;
90362306a36Sopenharmony_ci	if (istat) {
90462306a36Sopenharmony_ci		handled = 1;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		if (unlikely(!netif_running(dev))) {
90762306a36Sopenharmony_ci			netdev_info(dev, "late interrupt\n");
90862306a36Sopenharmony_ci			goto irq_ack;
90962306a36Sopenharmony_ci		}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		if (napi_schedule_prep(&bp->napi)) {
91262306a36Sopenharmony_ci			/* NOTE: These writes are posted by the readback of
91362306a36Sopenharmony_ci			 *       the ISTAT register below.
91462306a36Sopenharmony_ci			 */
91562306a36Sopenharmony_ci			bp->istat = istat;
91662306a36Sopenharmony_ci			__b44_disable_ints(bp);
91762306a36Sopenharmony_ci			__napi_schedule(&bp->napi);
91862306a36Sopenharmony_ci		}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ciirq_ack:
92162306a36Sopenharmony_ci		bw32(bp, B44_ISTAT, istat);
92262306a36Sopenharmony_ci		br32(bp, B44_ISTAT);
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci	spin_unlock(&bp->lock);
92562306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic void b44_tx_timeout(struct net_device *dev, unsigned int txqueue)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	netdev_err(dev, "transmit timed out, resetting\n");
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	b44_halt(bp);
93762306a36Sopenharmony_ci	b44_init_rings(bp);
93862306a36Sopenharmony_ci	b44_init_hw(bp, B44_FULL_RESET);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	b44_enable_ints(bp);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	netif_wake_queue(dev);
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cistatic netdev_tx_t b44_start_xmit(struct sk_buff *skb, struct net_device *dev)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
95062306a36Sopenharmony_ci	int rc = NETDEV_TX_OK;
95162306a36Sopenharmony_ci	dma_addr_t mapping;
95262306a36Sopenharmony_ci	u32 len, entry, ctrl;
95362306a36Sopenharmony_ci	unsigned long flags;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	len = skb->len;
95662306a36Sopenharmony_ci	spin_lock_irqsave(&bp->lock, flags);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	/* This is a hard error, log it. */
95962306a36Sopenharmony_ci	if (unlikely(TX_BUFFS_AVAIL(bp) < 1)) {
96062306a36Sopenharmony_ci		netif_stop_queue(dev);
96162306a36Sopenharmony_ci		netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
96262306a36Sopenharmony_ci		goto err_out;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	mapping = dma_map_single(bp->sdev->dma_dev, skb->data, len, DMA_TO_DEVICE);
96662306a36Sopenharmony_ci	if (dma_mapping_error(bp->sdev->dma_dev, mapping) || mapping + len > DMA_BIT_MASK(30)) {
96762306a36Sopenharmony_ci		struct sk_buff *bounce_skb;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		/* Chip can't handle DMA to/from >1GB, use bounce buffer */
97062306a36Sopenharmony_ci		if (!dma_mapping_error(bp->sdev->dma_dev, mapping))
97162306a36Sopenharmony_ci			dma_unmap_single(bp->sdev->dma_dev, mapping, len,
97262306a36Sopenharmony_ci					     DMA_TO_DEVICE);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		bounce_skb = alloc_skb(len, GFP_ATOMIC | GFP_DMA);
97562306a36Sopenharmony_ci		if (!bounce_skb)
97662306a36Sopenharmony_ci			goto err_out;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci		mapping = dma_map_single(bp->sdev->dma_dev, bounce_skb->data,
97962306a36Sopenharmony_ci					 len, DMA_TO_DEVICE);
98062306a36Sopenharmony_ci		if (dma_mapping_error(bp->sdev->dma_dev, mapping) || mapping + len > DMA_BIT_MASK(30)) {
98162306a36Sopenharmony_ci			if (!dma_mapping_error(bp->sdev->dma_dev, mapping))
98262306a36Sopenharmony_ci				dma_unmap_single(bp->sdev->dma_dev, mapping,
98362306a36Sopenharmony_ci						     len, DMA_TO_DEVICE);
98462306a36Sopenharmony_ci			dev_kfree_skb_any(bounce_skb);
98562306a36Sopenharmony_ci			goto err_out;
98662306a36Sopenharmony_ci		}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		skb_copy_from_linear_data(skb, skb_put(bounce_skb, len), len);
98962306a36Sopenharmony_ci		dev_consume_skb_any(skb);
99062306a36Sopenharmony_ci		skb = bounce_skb;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	entry = bp->tx_prod;
99462306a36Sopenharmony_ci	bp->tx_buffers[entry].skb = skb;
99562306a36Sopenharmony_ci	bp->tx_buffers[entry].mapping = mapping;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	ctrl  = (len & DESC_CTRL_LEN);
99862306a36Sopenharmony_ci	ctrl |= DESC_CTRL_IOC | DESC_CTRL_SOF | DESC_CTRL_EOF;
99962306a36Sopenharmony_ci	if (entry == (B44_TX_RING_SIZE - 1))
100062306a36Sopenharmony_ci		ctrl |= DESC_CTRL_EOT;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	bp->tx_ring[entry].ctrl = cpu_to_le32(ctrl);
100362306a36Sopenharmony_ci	bp->tx_ring[entry].addr = cpu_to_le32((u32) mapping+bp->dma_offset);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_TX_RING_HACK)
100662306a36Sopenharmony_ci		b44_sync_dma_desc_for_device(bp->sdev, bp->tx_ring_dma,
100762306a36Sopenharmony_ci			                    entry * sizeof(bp->tx_ring[0]),
100862306a36Sopenharmony_ci			                    DMA_TO_DEVICE);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	entry = NEXT_TX(entry);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	bp->tx_prod = entry;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	wmb();
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	bw32(bp, B44_DMATX_PTR, entry * sizeof(struct dma_desc));
101762306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_BUGGY_TXPTR)
101862306a36Sopenharmony_ci		bw32(bp, B44_DMATX_PTR, entry * sizeof(struct dma_desc));
101962306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_REORDER_BUG)
102062306a36Sopenharmony_ci		br32(bp, B44_DMATX_PTR);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	netdev_sent_queue(dev, skb->len);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	if (TX_BUFFS_AVAIL(bp) < 1)
102562306a36Sopenharmony_ci		netif_stop_queue(dev);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ciout_unlock:
102862306a36Sopenharmony_ci	spin_unlock_irqrestore(&bp->lock, flags);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	return rc;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cierr_out:
103362306a36Sopenharmony_ci	rc = NETDEV_TX_BUSY;
103462306a36Sopenharmony_ci	goto out_unlock;
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_cistatic int b44_change_mtu(struct net_device *dev, int new_mtu)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	if (!netif_running(dev)) {
104262306a36Sopenharmony_ci		/* We'll just catch it later when the
104362306a36Sopenharmony_ci		 * device is up'd.
104462306a36Sopenharmony_ci		 */
104562306a36Sopenharmony_ci		dev->mtu = new_mtu;
104662306a36Sopenharmony_ci		return 0;
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
105062306a36Sopenharmony_ci	b44_halt(bp);
105162306a36Sopenharmony_ci	dev->mtu = new_mtu;
105262306a36Sopenharmony_ci	b44_init_rings(bp);
105362306a36Sopenharmony_ci	b44_init_hw(bp, B44_FULL_RESET);
105462306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	b44_enable_ints(bp);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	return 0;
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci/* Free up pending packets in all rx/tx rings.
106262306a36Sopenharmony_ci *
106362306a36Sopenharmony_ci * The chip has been shut down and the driver detached from
106462306a36Sopenharmony_ci * the networking, so no interrupts or new tx packets will
106562306a36Sopenharmony_ci * end up in the driver.  bp->lock is not held and we are not
106662306a36Sopenharmony_ci * in an interrupt context and thus may sleep.
106762306a36Sopenharmony_ci */
106862306a36Sopenharmony_cistatic void b44_free_rings(struct b44 *bp)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	struct ring_info *rp;
107162306a36Sopenharmony_ci	int i;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	for (i = 0; i < B44_RX_RING_SIZE; i++) {
107462306a36Sopenharmony_ci		rp = &bp->rx_buffers[i];
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		if (rp->skb == NULL)
107762306a36Sopenharmony_ci			continue;
107862306a36Sopenharmony_ci		dma_unmap_single(bp->sdev->dma_dev, rp->mapping, RX_PKT_BUF_SZ,
107962306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
108062306a36Sopenharmony_ci		dev_kfree_skb_any(rp->skb);
108162306a36Sopenharmony_ci		rp->skb = NULL;
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	/* XXX needs changes once NETIF_F_SG is set... */
108562306a36Sopenharmony_ci	for (i = 0; i < B44_TX_RING_SIZE; i++) {
108662306a36Sopenharmony_ci		rp = &bp->tx_buffers[i];
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		if (rp->skb == NULL)
108962306a36Sopenharmony_ci			continue;
109062306a36Sopenharmony_ci		dma_unmap_single(bp->sdev->dma_dev, rp->mapping, rp->skb->len,
109162306a36Sopenharmony_ci				 DMA_TO_DEVICE);
109262306a36Sopenharmony_ci		dev_kfree_skb_any(rp->skb);
109362306a36Sopenharmony_ci		rp->skb = NULL;
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci/* Initialize tx/rx rings for packet processing.
109862306a36Sopenharmony_ci *
109962306a36Sopenharmony_ci * The chip has been shut down and the driver detached from
110062306a36Sopenharmony_ci * the networking, so no interrupts or new tx packets will
110162306a36Sopenharmony_ci * end up in the driver.
110262306a36Sopenharmony_ci */
110362306a36Sopenharmony_cistatic void b44_init_rings(struct b44 *bp)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	int i;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	b44_free_rings(bp);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	memset(bp->rx_ring, 0, B44_RX_RING_BYTES);
111062306a36Sopenharmony_ci	memset(bp->tx_ring, 0, B44_TX_RING_BYTES);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_RX_RING_HACK)
111362306a36Sopenharmony_ci		dma_sync_single_for_device(bp->sdev->dma_dev, bp->rx_ring_dma,
111462306a36Sopenharmony_ci					   DMA_TABLE_BYTES, DMA_BIDIRECTIONAL);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_TX_RING_HACK)
111762306a36Sopenharmony_ci		dma_sync_single_for_device(bp->sdev->dma_dev, bp->tx_ring_dma,
111862306a36Sopenharmony_ci					   DMA_TABLE_BYTES, DMA_TO_DEVICE);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	for (i = 0; i < bp->rx_pending; i++) {
112162306a36Sopenharmony_ci		if (b44_alloc_rx_skb(bp, -1, i) < 0)
112262306a36Sopenharmony_ci			break;
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci/*
112762306a36Sopenharmony_ci * Must not be invoked with interrupt sources disabled and
112862306a36Sopenharmony_ci * the hardware shutdown down.
112962306a36Sopenharmony_ci */
113062306a36Sopenharmony_cistatic void b44_free_consistent(struct b44 *bp)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	kfree(bp->rx_buffers);
113362306a36Sopenharmony_ci	bp->rx_buffers = NULL;
113462306a36Sopenharmony_ci	kfree(bp->tx_buffers);
113562306a36Sopenharmony_ci	bp->tx_buffers = NULL;
113662306a36Sopenharmony_ci	if (bp->rx_ring) {
113762306a36Sopenharmony_ci		if (bp->flags & B44_FLAG_RX_RING_HACK) {
113862306a36Sopenharmony_ci			dma_unmap_single(bp->sdev->dma_dev, bp->rx_ring_dma,
113962306a36Sopenharmony_ci					 DMA_TABLE_BYTES, DMA_BIDIRECTIONAL);
114062306a36Sopenharmony_ci			kfree(bp->rx_ring);
114162306a36Sopenharmony_ci		} else
114262306a36Sopenharmony_ci			dma_free_coherent(bp->sdev->dma_dev, DMA_TABLE_BYTES,
114362306a36Sopenharmony_ci					  bp->rx_ring, bp->rx_ring_dma);
114462306a36Sopenharmony_ci		bp->rx_ring = NULL;
114562306a36Sopenharmony_ci		bp->flags &= ~B44_FLAG_RX_RING_HACK;
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci	if (bp->tx_ring) {
114862306a36Sopenharmony_ci		if (bp->flags & B44_FLAG_TX_RING_HACK) {
114962306a36Sopenharmony_ci			dma_unmap_single(bp->sdev->dma_dev, bp->tx_ring_dma,
115062306a36Sopenharmony_ci					 DMA_TABLE_BYTES, DMA_TO_DEVICE);
115162306a36Sopenharmony_ci			kfree(bp->tx_ring);
115262306a36Sopenharmony_ci		} else
115362306a36Sopenharmony_ci			dma_free_coherent(bp->sdev->dma_dev, DMA_TABLE_BYTES,
115462306a36Sopenharmony_ci					  bp->tx_ring, bp->tx_ring_dma);
115562306a36Sopenharmony_ci		bp->tx_ring = NULL;
115662306a36Sopenharmony_ci		bp->flags &= ~B44_FLAG_TX_RING_HACK;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci/*
116162306a36Sopenharmony_ci * Must not be invoked with interrupt sources disabled and
116262306a36Sopenharmony_ci * the hardware shutdown down.  Can sleep.
116362306a36Sopenharmony_ci */
116462306a36Sopenharmony_cistatic int b44_alloc_consistent(struct b44 *bp, gfp_t gfp)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	int size;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	size  = B44_RX_RING_SIZE * sizeof(struct ring_info);
116962306a36Sopenharmony_ci	bp->rx_buffers = kzalloc(size, gfp);
117062306a36Sopenharmony_ci	if (!bp->rx_buffers)
117162306a36Sopenharmony_ci		goto out_err;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	size = B44_TX_RING_SIZE * sizeof(struct ring_info);
117462306a36Sopenharmony_ci	bp->tx_buffers = kzalloc(size, gfp);
117562306a36Sopenharmony_ci	if (!bp->tx_buffers)
117662306a36Sopenharmony_ci		goto out_err;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	size = DMA_TABLE_BYTES;
117962306a36Sopenharmony_ci	bp->rx_ring = dma_alloc_coherent(bp->sdev->dma_dev, size,
118062306a36Sopenharmony_ci					 &bp->rx_ring_dma, gfp);
118162306a36Sopenharmony_ci	if (!bp->rx_ring) {
118262306a36Sopenharmony_ci		/* Allocation may have failed due to dma_alloc_coherent
118362306a36Sopenharmony_ci		   insisting on use of GFP_DMA, which is more restrictive
118462306a36Sopenharmony_ci		   than necessary...  */
118562306a36Sopenharmony_ci		struct dma_desc *rx_ring;
118662306a36Sopenharmony_ci		dma_addr_t rx_ring_dma;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		rx_ring = kzalloc(size, gfp);
118962306a36Sopenharmony_ci		if (!rx_ring)
119062306a36Sopenharmony_ci			goto out_err;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci		rx_ring_dma = dma_map_single(bp->sdev->dma_dev, rx_ring,
119362306a36Sopenharmony_ci					     DMA_TABLE_BYTES,
119462306a36Sopenharmony_ci					     DMA_BIDIRECTIONAL);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		if (dma_mapping_error(bp->sdev->dma_dev, rx_ring_dma) ||
119762306a36Sopenharmony_ci			rx_ring_dma + size > DMA_BIT_MASK(30)) {
119862306a36Sopenharmony_ci			kfree(rx_ring);
119962306a36Sopenharmony_ci			goto out_err;
120062306a36Sopenharmony_ci		}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci		bp->rx_ring = rx_ring;
120362306a36Sopenharmony_ci		bp->rx_ring_dma = rx_ring_dma;
120462306a36Sopenharmony_ci		bp->flags |= B44_FLAG_RX_RING_HACK;
120562306a36Sopenharmony_ci	}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	bp->tx_ring = dma_alloc_coherent(bp->sdev->dma_dev, size,
120862306a36Sopenharmony_ci					 &bp->tx_ring_dma, gfp);
120962306a36Sopenharmony_ci	if (!bp->tx_ring) {
121062306a36Sopenharmony_ci		/* Allocation may have failed due to ssb_dma_alloc_consistent
121162306a36Sopenharmony_ci		   insisting on use of GFP_DMA, which is more restrictive
121262306a36Sopenharmony_ci		   than necessary...  */
121362306a36Sopenharmony_ci		struct dma_desc *tx_ring;
121462306a36Sopenharmony_ci		dma_addr_t tx_ring_dma;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci		tx_ring = kzalloc(size, gfp);
121762306a36Sopenharmony_ci		if (!tx_ring)
121862306a36Sopenharmony_ci			goto out_err;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		tx_ring_dma = dma_map_single(bp->sdev->dma_dev, tx_ring,
122162306a36Sopenharmony_ci					     DMA_TABLE_BYTES,
122262306a36Sopenharmony_ci					     DMA_TO_DEVICE);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci		if (dma_mapping_error(bp->sdev->dma_dev, tx_ring_dma) ||
122562306a36Sopenharmony_ci			tx_ring_dma + size > DMA_BIT_MASK(30)) {
122662306a36Sopenharmony_ci			kfree(tx_ring);
122762306a36Sopenharmony_ci			goto out_err;
122862306a36Sopenharmony_ci		}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci		bp->tx_ring = tx_ring;
123162306a36Sopenharmony_ci		bp->tx_ring_dma = tx_ring_dma;
123262306a36Sopenharmony_ci		bp->flags |= B44_FLAG_TX_RING_HACK;
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	return 0;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ciout_err:
123862306a36Sopenharmony_ci	b44_free_consistent(bp);
123962306a36Sopenharmony_ci	return -ENOMEM;
124062306a36Sopenharmony_ci}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci/* bp->lock is held. */
124362306a36Sopenharmony_cistatic void b44_clear_stats(struct b44 *bp)
124462306a36Sopenharmony_ci{
124562306a36Sopenharmony_ci	unsigned long reg;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	bw32(bp, B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ);
124862306a36Sopenharmony_ci	for (reg = B44_TX_GOOD_O; reg <= B44_TX_PAUSE; reg += 4UL)
124962306a36Sopenharmony_ci		br32(bp, reg);
125062306a36Sopenharmony_ci	for (reg = B44_RX_GOOD_O; reg <= B44_RX_NPAUSE; reg += 4UL)
125162306a36Sopenharmony_ci		br32(bp, reg);
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci/* bp->lock is held. */
125562306a36Sopenharmony_cistatic void b44_chip_reset(struct b44 *bp, int reset_kind)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	struct ssb_device *sdev = bp->sdev;
125862306a36Sopenharmony_ci	bool was_enabled;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	was_enabled = ssb_device_is_enabled(bp->sdev);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	ssb_device_enable(bp->sdev, 0);
126362306a36Sopenharmony_ci	ssb_pcicore_dev_irqvecs_enable(&sdev->bus->pcicore, sdev);
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	if (was_enabled) {
126662306a36Sopenharmony_ci		bw32(bp, B44_RCV_LAZY, 0);
126762306a36Sopenharmony_ci		bw32(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE);
126862306a36Sopenharmony_ci		b44_wait_bit(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE, 200, 1);
126962306a36Sopenharmony_ci		bw32(bp, B44_DMATX_CTRL, 0);
127062306a36Sopenharmony_ci		bp->tx_prod = bp->tx_cons = 0;
127162306a36Sopenharmony_ci		if (br32(bp, B44_DMARX_STAT) & DMARX_STAT_EMASK) {
127262306a36Sopenharmony_ci			b44_wait_bit(bp, B44_DMARX_STAT, DMARX_STAT_SIDLE,
127362306a36Sopenharmony_ci				     100, 0);
127462306a36Sopenharmony_ci		}
127562306a36Sopenharmony_ci		bw32(bp, B44_DMARX_CTRL, 0);
127662306a36Sopenharmony_ci		bp->rx_prod = bp->rx_cons = 0;
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	b44_clear_stats(bp);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	/*
128262306a36Sopenharmony_ci	 * Don't enable PHY if we are doing a partial reset
128362306a36Sopenharmony_ci	 * we are probably going to power down
128462306a36Sopenharmony_ci	 */
128562306a36Sopenharmony_ci	if (reset_kind == B44_CHIP_RESET_PARTIAL)
128662306a36Sopenharmony_ci		return;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	switch (sdev->bus->bustype) {
128962306a36Sopenharmony_ci	case SSB_BUSTYPE_SSB:
129062306a36Sopenharmony_ci		bw32(bp, B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE |
129162306a36Sopenharmony_ci		     (DIV_ROUND_CLOSEST(ssb_clockspeed(sdev->bus),
129262306a36Sopenharmony_ci					B44_MDC_RATIO)
129362306a36Sopenharmony_ci		     & MDIO_CTRL_MAXF_MASK)));
129462306a36Sopenharmony_ci		break;
129562306a36Sopenharmony_ci	case SSB_BUSTYPE_PCI:
129662306a36Sopenharmony_ci		bw32(bp, B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE |
129762306a36Sopenharmony_ci		     (0x0d & MDIO_CTRL_MAXF_MASK)));
129862306a36Sopenharmony_ci		break;
129962306a36Sopenharmony_ci	case SSB_BUSTYPE_PCMCIA:
130062306a36Sopenharmony_ci	case SSB_BUSTYPE_SDIO:
130162306a36Sopenharmony_ci		WARN_ON(1); /* A device with this bus does not exist. */
130262306a36Sopenharmony_ci		break;
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	br32(bp, B44_MDIO_CTRL);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	if (!(br32(bp, B44_DEVCTRL) & DEVCTRL_IPP)) {
130862306a36Sopenharmony_ci		bw32(bp, B44_ENET_CTRL, ENET_CTRL_EPSEL);
130962306a36Sopenharmony_ci		br32(bp, B44_ENET_CTRL);
131062306a36Sopenharmony_ci		bp->flags |= B44_FLAG_EXTERNAL_PHY;
131162306a36Sopenharmony_ci	} else {
131262306a36Sopenharmony_ci		u32 val = br32(bp, B44_DEVCTRL);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci		if (val & DEVCTRL_EPR) {
131562306a36Sopenharmony_ci			bw32(bp, B44_DEVCTRL, (val & ~DEVCTRL_EPR));
131662306a36Sopenharmony_ci			br32(bp, B44_DEVCTRL);
131762306a36Sopenharmony_ci			udelay(100);
131862306a36Sopenharmony_ci		}
131962306a36Sopenharmony_ci		bp->flags &= ~B44_FLAG_EXTERNAL_PHY;
132062306a36Sopenharmony_ci	}
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci/* bp->lock is held. */
132462306a36Sopenharmony_cistatic void b44_halt(struct b44 *bp)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	b44_disable_ints(bp);
132762306a36Sopenharmony_ci	/* reset PHY */
132862306a36Sopenharmony_ci	b44_phy_reset(bp);
132962306a36Sopenharmony_ci	/* power down PHY */
133062306a36Sopenharmony_ci	netdev_info(bp->dev, "powering down PHY\n");
133162306a36Sopenharmony_ci	bw32(bp, B44_MAC_CTRL, MAC_CTRL_PHY_PDOWN);
133262306a36Sopenharmony_ci	/* now reset the chip, but without enabling the MAC&PHY
133362306a36Sopenharmony_ci	 * part of it. This has to be done _after_ we shut down the PHY */
133462306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY)
133562306a36Sopenharmony_ci		b44_chip_reset(bp, B44_CHIP_RESET_FULL);
133662306a36Sopenharmony_ci	else
133762306a36Sopenharmony_ci		b44_chip_reset(bp, B44_CHIP_RESET_PARTIAL);
133862306a36Sopenharmony_ci}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci/* bp->lock is held. */
134162306a36Sopenharmony_cistatic void __b44_set_mac_addr(struct b44 *bp)
134262306a36Sopenharmony_ci{
134362306a36Sopenharmony_ci	bw32(bp, B44_CAM_CTRL, 0);
134462306a36Sopenharmony_ci	if (!(bp->dev->flags & IFF_PROMISC)) {
134562306a36Sopenharmony_ci		u32 val;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci		__b44_cam_write(bp, bp->dev->dev_addr, 0);
134862306a36Sopenharmony_ci		val = br32(bp, B44_CAM_CTRL);
134962306a36Sopenharmony_ci		bw32(bp, B44_CAM_CTRL, val | CAM_CTRL_ENABLE);
135062306a36Sopenharmony_ci	}
135162306a36Sopenharmony_ci}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_cistatic int b44_set_mac_addr(struct net_device *dev, void *p)
135462306a36Sopenharmony_ci{
135562306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
135662306a36Sopenharmony_ci	struct sockaddr *addr = p;
135762306a36Sopenharmony_ci	u32 val;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	if (netif_running(dev))
136062306a36Sopenharmony_ci		return -EBUSY;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
136362306a36Sopenharmony_ci		return -EINVAL;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr->sa_data);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	val = br32(bp, B44_RXCONFIG);
137062306a36Sopenharmony_ci	if (!(val & RXCONFIG_CAM_ABSENT))
137162306a36Sopenharmony_ci		__b44_set_mac_addr(bp);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	return 0;
137662306a36Sopenharmony_ci}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci/* Called at device open time to get the chip ready for
137962306a36Sopenharmony_ci * packet processing.  Invoked with bp->lock held.
138062306a36Sopenharmony_ci */
138162306a36Sopenharmony_cistatic void __b44_set_rx_mode(struct net_device *);
138262306a36Sopenharmony_cistatic void b44_init_hw(struct b44 *bp, int reset_kind)
138362306a36Sopenharmony_ci{
138462306a36Sopenharmony_ci	u32 val;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	b44_chip_reset(bp, B44_CHIP_RESET_FULL);
138762306a36Sopenharmony_ci	if (reset_kind == B44_FULL_RESET) {
138862306a36Sopenharmony_ci		b44_phy_reset(bp);
138962306a36Sopenharmony_ci		b44_setup_phy(bp);
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	/* Enable CRC32, set proper LED modes and power on PHY */
139362306a36Sopenharmony_ci	bw32(bp, B44_MAC_CTRL, MAC_CTRL_CRC32_ENAB | MAC_CTRL_PHY_LEDCTRL);
139462306a36Sopenharmony_ci	bw32(bp, B44_RCV_LAZY, (1 << RCV_LAZY_FC_SHIFT));
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	/* This sets the MAC address too.  */
139762306a36Sopenharmony_ci	__b44_set_rx_mode(bp->dev);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	/* MTU + eth header + possible VLAN tag + struct rx_header */
140062306a36Sopenharmony_ci	bw32(bp, B44_RXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + RX_HEADER_LEN);
140162306a36Sopenharmony_ci	bw32(bp, B44_TXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + RX_HEADER_LEN);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	bw32(bp, B44_TX_WMARK, 56); /* XXX magic */
140462306a36Sopenharmony_ci	if (reset_kind == B44_PARTIAL_RESET) {
140562306a36Sopenharmony_ci		bw32(bp, B44_DMARX_CTRL, (DMARX_CTRL_ENABLE |
140662306a36Sopenharmony_ci				      (RX_PKT_OFFSET << DMARX_CTRL_ROSHIFT)));
140762306a36Sopenharmony_ci	} else {
140862306a36Sopenharmony_ci		bw32(bp, B44_DMATX_CTRL, DMATX_CTRL_ENABLE);
140962306a36Sopenharmony_ci		bw32(bp, B44_DMATX_ADDR, bp->tx_ring_dma + bp->dma_offset);
141062306a36Sopenharmony_ci		bw32(bp, B44_DMARX_CTRL, (DMARX_CTRL_ENABLE |
141162306a36Sopenharmony_ci				      (RX_PKT_OFFSET << DMARX_CTRL_ROSHIFT)));
141262306a36Sopenharmony_ci		bw32(bp, B44_DMARX_ADDR, bp->rx_ring_dma + bp->dma_offset);
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci		bw32(bp, B44_DMARX_PTR, bp->rx_pending);
141562306a36Sopenharmony_ci		bp->rx_prod = bp->rx_pending;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci		bw32(bp, B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ);
141862306a36Sopenharmony_ci	}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	val = br32(bp, B44_ENET_CTRL);
142162306a36Sopenharmony_ci	bw32(bp, B44_ENET_CTRL, (val | ENET_CTRL_ENABLE));
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	netdev_reset_queue(bp->dev);
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_cistatic int b44_open(struct net_device *dev)
142762306a36Sopenharmony_ci{
142862306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
142962306a36Sopenharmony_ci	int err;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	err = b44_alloc_consistent(bp, GFP_KERNEL);
143262306a36Sopenharmony_ci	if (err)
143362306a36Sopenharmony_ci		goto out;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	napi_enable(&bp->napi);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	b44_init_rings(bp);
143862306a36Sopenharmony_ci	b44_init_hw(bp, B44_FULL_RESET);
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	b44_check_phy(bp);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	err = request_irq(dev->irq, b44_interrupt, IRQF_SHARED, dev->name, dev);
144362306a36Sopenharmony_ci	if (unlikely(err < 0)) {
144462306a36Sopenharmony_ci		napi_disable(&bp->napi);
144562306a36Sopenharmony_ci		b44_chip_reset(bp, B44_CHIP_RESET_PARTIAL);
144662306a36Sopenharmony_ci		b44_free_rings(bp);
144762306a36Sopenharmony_ci		b44_free_consistent(bp);
144862306a36Sopenharmony_ci		goto out;
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	timer_setup(&bp->timer, b44_timer, 0);
145262306a36Sopenharmony_ci	bp->timer.expires = jiffies + HZ;
145362306a36Sopenharmony_ci	add_timer(&bp->timer);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	b44_enable_ints(bp);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY)
145862306a36Sopenharmony_ci		phy_start(dev->phydev);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	netif_start_queue(dev);
146162306a36Sopenharmony_ciout:
146262306a36Sopenharmony_ci	return err;
146362306a36Sopenharmony_ci}
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
146662306a36Sopenharmony_ci/*
146762306a36Sopenharmony_ci * Polling receive - used by netconsole and other diagnostic tools
146862306a36Sopenharmony_ci * to allow network i/o with interrupts disabled.
146962306a36Sopenharmony_ci */
147062306a36Sopenharmony_cistatic void b44_poll_controller(struct net_device *dev)
147162306a36Sopenharmony_ci{
147262306a36Sopenharmony_ci	disable_irq(dev->irq);
147362306a36Sopenharmony_ci	b44_interrupt(dev->irq, dev);
147462306a36Sopenharmony_ci	enable_irq(dev->irq);
147562306a36Sopenharmony_ci}
147662306a36Sopenharmony_ci#endif
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_cistatic void bwfilter_table(struct b44 *bp, u8 *pp, u32 bytes, u32 table_offset)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	u32 i;
148162306a36Sopenharmony_ci	u32 *pattern = (u32 *) pp;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	for (i = 0; i < bytes; i += sizeof(u32)) {
148462306a36Sopenharmony_ci		bw32(bp, B44_FILT_ADDR, table_offset + i);
148562306a36Sopenharmony_ci		bw32(bp, B44_FILT_DATA, pattern[i / sizeof(u32)]);
148662306a36Sopenharmony_ci	}
148762306a36Sopenharmony_ci}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_cistatic int b44_magic_pattern(const u8 *macaddr, u8 *ppattern, u8 *pmask,
149062306a36Sopenharmony_ci			     int offset)
149162306a36Sopenharmony_ci{
149262306a36Sopenharmony_ci	int magicsync = 6;
149362306a36Sopenharmony_ci	int k, j, len = offset;
149462306a36Sopenharmony_ci	int ethaddr_bytes = ETH_ALEN;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	memset(ppattern + offset, 0xff, magicsync);
149762306a36Sopenharmony_ci	for (j = 0; j < magicsync; j++) {
149862306a36Sopenharmony_ci		pmask[len >> 3] |= BIT(len & 7);
149962306a36Sopenharmony_ci		len++;
150062306a36Sopenharmony_ci	}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	for (j = 0; j < B44_MAX_PATTERNS; j++) {
150362306a36Sopenharmony_ci		if ((B44_PATTERN_SIZE - len) >= ETH_ALEN)
150462306a36Sopenharmony_ci			ethaddr_bytes = ETH_ALEN;
150562306a36Sopenharmony_ci		else
150662306a36Sopenharmony_ci			ethaddr_bytes = B44_PATTERN_SIZE - len;
150762306a36Sopenharmony_ci		if (ethaddr_bytes <=0)
150862306a36Sopenharmony_ci			break;
150962306a36Sopenharmony_ci		for (k = 0; k< ethaddr_bytes; k++) {
151062306a36Sopenharmony_ci			ppattern[offset + magicsync +
151162306a36Sopenharmony_ci				(j * ETH_ALEN) + k] = macaddr[k];
151262306a36Sopenharmony_ci			pmask[len >> 3] |= BIT(len & 7);
151362306a36Sopenharmony_ci			len++;
151462306a36Sopenharmony_ci		}
151562306a36Sopenharmony_ci	}
151662306a36Sopenharmony_ci	return len - 1;
151762306a36Sopenharmony_ci}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci/* Setup magic packet patterns in the b44 WOL
152062306a36Sopenharmony_ci * pattern matching filter.
152162306a36Sopenharmony_ci */
152262306a36Sopenharmony_cistatic void b44_setup_pseudo_magicp(struct b44 *bp)
152362306a36Sopenharmony_ci{
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	u32 val;
152662306a36Sopenharmony_ci	int plen0, plen1, plen2;
152762306a36Sopenharmony_ci	u8 *pwol_pattern;
152862306a36Sopenharmony_ci	u8 pwol_mask[B44_PMASK_SIZE];
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	pwol_pattern = kzalloc(B44_PATTERN_SIZE, GFP_KERNEL);
153162306a36Sopenharmony_ci	if (!pwol_pattern)
153262306a36Sopenharmony_ci		return;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	/* Ipv4 magic packet pattern - pattern 0.*/
153562306a36Sopenharmony_ci	memset(pwol_mask, 0, B44_PMASK_SIZE);
153662306a36Sopenharmony_ci	plen0 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask,
153762306a36Sopenharmony_ci				  B44_ETHIPV4UDP_HLEN);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE, B44_PATTERN_BASE);
154062306a36Sopenharmony_ci	bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE, B44_PMASK_BASE);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	/* Raw ethernet II magic packet pattern - pattern 1 */
154362306a36Sopenharmony_ci	memset(pwol_pattern, 0, B44_PATTERN_SIZE);
154462306a36Sopenharmony_ci	memset(pwol_mask, 0, B44_PMASK_SIZE);
154562306a36Sopenharmony_ci	plen1 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask,
154662306a36Sopenharmony_ci				  ETH_HLEN);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE,
154962306a36Sopenharmony_ci		       B44_PATTERN_BASE + B44_PATTERN_SIZE);
155062306a36Sopenharmony_ci	bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE,
155162306a36Sopenharmony_ci		       B44_PMASK_BASE + B44_PMASK_SIZE);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	/* Ipv6 magic packet pattern - pattern 2 */
155462306a36Sopenharmony_ci	memset(pwol_pattern, 0, B44_PATTERN_SIZE);
155562306a36Sopenharmony_ci	memset(pwol_mask, 0, B44_PMASK_SIZE);
155662306a36Sopenharmony_ci	plen2 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask,
155762306a36Sopenharmony_ci				  B44_ETHIPV6UDP_HLEN);
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE,
156062306a36Sopenharmony_ci		       B44_PATTERN_BASE + B44_PATTERN_SIZE + B44_PATTERN_SIZE);
156162306a36Sopenharmony_ci	bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE,
156262306a36Sopenharmony_ci		       B44_PMASK_BASE + B44_PMASK_SIZE + B44_PMASK_SIZE);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	kfree(pwol_pattern);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	/* set these pattern's lengths: one less than each real length */
156762306a36Sopenharmony_ci	val = plen0 | (plen1 << 8) | (plen2 << 16) | WKUP_LEN_ENABLE_THREE;
156862306a36Sopenharmony_ci	bw32(bp, B44_WKUP_LEN, val);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	/* enable wakeup pattern matching */
157162306a36Sopenharmony_ci	val = br32(bp, B44_DEVCTRL);
157262306a36Sopenharmony_ci	bw32(bp, B44_DEVCTRL, val | DEVCTRL_PFE);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci#ifdef CONFIG_B44_PCI
157762306a36Sopenharmony_cistatic void b44_setup_wol_pci(struct b44 *bp)
157862306a36Sopenharmony_ci{
157962306a36Sopenharmony_ci	u16 val;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	if (bp->sdev->bus->bustype != SSB_BUSTYPE_SSB) {
158262306a36Sopenharmony_ci		bw32(bp, SSB_TMSLOW, br32(bp, SSB_TMSLOW) | SSB_TMSLOW_PE);
158362306a36Sopenharmony_ci		pci_read_config_word(bp->sdev->bus->host_pci, SSB_PMCSR, &val);
158462306a36Sopenharmony_ci		pci_write_config_word(bp->sdev->bus->host_pci, SSB_PMCSR, val | SSB_PE);
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci}
158762306a36Sopenharmony_ci#else
158862306a36Sopenharmony_cistatic inline void b44_setup_wol_pci(struct b44 *bp) { }
158962306a36Sopenharmony_ci#endif /* CONFIG_B44_PCI */
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_cistatic void b44_setup_wol(struct b44 *bp)
159262306a36Sopenharmony_ci{
159362306a36Sopenharmony_ci	u32 val;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	bw32(bp, B44_RXCONFIG, RXCONFIG_ALLMULTI);
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_B0_ANDLATER) {
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci		bw32(bp, B44_WKUP_LEN, WKUP_LEN_DISABLE);
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci		val = bp->dev->dev_addr[2] << 24 |
160262306a36Sopenharmony_ci			bp->dev->dev_addr[3] << 16 |
160362306a36Sopenharmony_ci			bp->dev->dev_addr[4] << 8 |
160462306a36Sopenharmony_ci			bp->dev->dev_addr[5];
160562306a36Sopenharmony_ci		bw32(bp, B44_ADDR_LO, val);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci		val = bp->dev->dev_addr[0] << 8 |
160862306a36Sopenharmony_ci			bp->dev->dev_addr[1];
160962306a36Sopenharmony_ci		bw32(bp, B44_ADDR_HI, val);
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci		val = br32(bp, B44_DEVCTRL);
161262306a36Sopenharmony_ci		bw32(bp, B44_DEVCTRL, val | DEVCTRL_MPM | DEVCTRL_PFE);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	} else {
161562306a36Sopenharmony_ci		b44_setup_pseudo_magicp(bp);
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci	b44_setup_wol_pci(bp);
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_cistatic int b44_close(struct net_device *dev)
162162306a36Sopenharmony_ci{
162262306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	netif_stop_queue(dev);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY)
162762306a36Sopenharmony_ci		phy_stop(dev->phydev);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	napi_disable(&bp->napi);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	del_timer_sync(&bp->timer);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	b44_halt(bp);
163662306a36Sopenharmony_ci	b44_free_rings(bp);
163762306a36Sopenharmony_ci	netif_carrier_off(dev);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	free_irq(dev->irq, dev);
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_WOL_ENABLE) {
164462306a36Sopenharmony_ci		b44_init_hw(bp, B44_PARTIAL_RESET);
164562306a36Sopenharmony_ci		b44_setup_wol(bp);
164662306a36Sopenharmony_ci	}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	b44_free_consistent(bp);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	return 0;
165162306a36Sopenharmony_ci}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_cistatic void b44_get_stats64(struct net_device *dev,
165462306a36Sopenharmony_ci			    struct rtnl_link_stats64 *nstat)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
165762306a36Sopenharmony_ci	struct b44_hw_stats *hwstat = &bp->hw_stats;
165862306a36Sopenharmony_ci	unsigned int start;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	do {
166162306a36Sopenharmony_ci		start = u64_stats_fetch_begin(&hwstat->syncp);
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci		/* Convert HW stats into rtnl_link_stats64 stats. */
166462306a36Sopenharmony_ci		nstat->rx_packets = hwstat->rx_pkts;
166562306a36Sopenharmony_ci		nstat->tx_packets = hwstat->tx_pkts;
166662306a36Sopenharmony_ci		nstat->rx_bytes   = hwstat->rx_octets;
166762306a36Sopenharmony_ci		nstat->tx_bytes   = hwstat->tx_octets;
166862306a36Sopenharmony_ci		nstat->tx_errors  = (hwstat->tx_jabber_pkts +
166962306a36Sopenharmony_ci				     hwstat->tx_oversize_pkts +
167062306a36Sopenharmony_ci				     hwstat->tx_underruns +
167162306a36Sopenharmony_ci				     hwstat->tx_excessive_cols +
167262306a36Sopenharmony_ci				     hwstat->tx_late_cols);
167362306a36Sopenharmony_ci		nstat->multicast  = hwstat->rx_multicast_pkts;
167462306a36Sopenharmony_ci		nstat->collisions = hwstat->tx_total_cols;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci		nstat->rx_length_errors = (hwstat->rx_oversize_pkts +
167762306a36Sopenharmony_ci					   hwstat->rx_undersize);
167862306a36Sopenharmony_ci		nstat->rx_over_errors   = hwstat->rx_missed_pkts;
167962306a36Sopenharmony_ci		nstat->rx_frame_errors  = hwstat->rx_align_errs;
168062306a36Sopenharmony_ci		nstat->rx_crc_errors    = hwstat->rx_crc_errs;
168162306a36Sopenharmony_ci		nstat->rx_errors        = (hwstat->rx_jabber_pkts +
168262306a36Sopenharmony_ci					   hwstat->rx_oversize_pkts +
168362306a36Sopenharmony_ci					   hwstat->rx_missed_pkts +
168462306a36Sopenharmony_ci					   hwstat->rx_crc_align_errs +
168562306a36Sopenharmony_ci					   hwstat->rx_undersize +
168662306a36Sopenharmony_ci					   hwstat->rx_crc_errs +
168762306a36Sopenharmony_ci					   hwstat->rx_align_errs +
168862306a36Sopenharmony_ci					   hwstat->rx_symbol_errs);
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci		nstat->tx_aborted_errors = hwstat->tx_underruns;
169162306a36Sopenharmony_ci#if 0
169262306a36Sopenharmony_ci		/* Carrier lost counter seems to be broken for some devices */
169362306a36Sopenharmony_ci		nstat->tx_carrier_errors = hwstat->tx_carrier_lost;
169462306a36Sopenharmony_ci#endif
169562306a36Sopenharmony_ci	} while (u64_stats_fetch_retry(&hwstat->syncp, start));
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci}
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_cistatic int __b44_load_mcast(struct b44 *bp, struct net_device *dev)
170062306a36Sopenharmony_ci{
170162306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
170262306a36Sopenharmony_ci	int i, num_ents;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	num_ents = min_t(int, netdev_mc_count(dev), B44_MCAST_TABLE_SIZE);
170562306a36Sopenharmony_ci	i = 0;
170662306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, dev) {
170762306a36Sopenharmony_ci		if (i == num_ents)
170862306a36Sopenharmony_ci			break;
170962306a36Sopenharmony_ci		__b44_cam_write(bp, ha->addr, i++ + 1);
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci	return i+1;
171262306a36Sopenharmony_ci}
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_cistatic void __b44_set_rx_mode(struct net_device *dev)
171562306a36Sopenharmony_ci{
171662306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
171762306a36Sopenharmony_ci	u32 val;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	val = br32(bp, B44_RXCONFIG);
172062306a36Sopenharmony_ci	val &= ~(RXCONFIG_PROMISC | RXCONFIG_ALLMULTI);
172162306a36Sopenharmony_ci	if ((dev->flags & IFF_PROMISC) || (val & RXCONFIG_CAM_ABSENT)) {
172262306a36Sopenharmony_ci		val |= RXCONFIG_PROMISC;
172362306a36Sopenharmony_ci		bw32(bp, B44_RXCONFIG, val);
172462306a36Sopenharmony_ci	} else {
172562306a36Sopenharmony_ci		unsigned char zero[6] = {0, 0, 0, 0, 0, 0};
172662306a36Sopenharmony_ci		int i = 1;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci		__b44_set_mac_addr(bp);
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci		if ((dev->flags & IFF_ALLMULTI) ||
173162306a36Sopenharmony_ci		    (netdev_mc_count(dev) > B44_MCAST_TABLE_SIZE))
173262306a36Sopenharmony_ci			val |= RXCONFIG_ALLMULTI;
173362306a36Sopenharmony_ci		else
173462306a36Sopenharmony_ci			i = __b44_load_mcast(bp, dev);
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci		for (; i < 64; i++)
173762306a36Sopenharmony_ci			__b44_cam_write(bp, zero, i);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci		bw32(bp, B44_RXCONFIG, val);
174062306a36Sopenharmony_ci		val = br32(bp, B44_CAM_CTRL);
174162306a36Sopenharmony_ci	        bw32(bp, B44_CAM_CTRL, val | CAM_CTRL_ENABLE);
174262306a36Sopenharmony_ci	}
174362306a36Sopenharmony_ci}
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_cistatic void b44_set_rx_mode(struct net_device *dev)
174662306a36Sopenharmony_ci{
174762306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
175062306a36Sopenharmony_ci	__b44_set_rx_mode(dev);
175162306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
175262306a36Sopenharmony_ci}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_cistatic u32 b44_get_msglevel(struct net_device *dev)
175562306a36Sopenharmony_ci{
175662306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
175762306a36Sopenharmony_ci	return bp->msg_enable;
175862306a36Sopenharmony_ci}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_cistatic void b44_set_msglevel(struct net_device *dev, u32 value)
176162306a36Sopenharmony_ci{
176262306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
176362306a36Sopenharmony_ci	bp->msg_enable = value;
176462306a36Sopenharmony_ci}
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_cistatic void b44_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info)
176762306a36Sopenharmony_ci{
176862306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
176962306a36Sopenharmony_ci	struct ssb_bus *bus = bp->sdev->bus;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
177262306a36Sopenharmony_ci	switch (bus->bustype) {
177362306a36Sopenharmony_ci	case SSB_BUSTYPE_PCI:
177462306a36Sopenharmony_ci		strscpy(info->bus_info, pci_name(bus->host_pci), sizeof(info->bus_info));
177562306a36Sopenharmony_ci		break;
177662306a36Sopenharmony_ci	case SSB_BUSTYPE_SSB:
177762306a36Sopenharmony_ci		strscpy(info->bus_info, "SSB", sizeof(info->bus_info));
177862306a36Sopenharmony_ci		break;
177962306a36Sopenharmony_ci	case SSB_BUSTYPE_PCMCIA:
178062306a36Sopenharmony_ci	case SSB_BUSTYPE_SDIO:
178162306a36Sopenharmony_ci		WARN_ON(1); /* A device with this bus does not exist. */
178262306a36Sopenharmony_ci		break;
178362306a36Sopenharmony_ci	}
178462306a36Sopenharmony_ci}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_cistatic int b44_nway_reset(struct net_device *dev)
178762306a36Sopenharmony_ci{
178862306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
178962306a36Sopenharmony_ci	u32 bmcr;
179062306a36Sopenharmony_ci	int r;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
179362306a36Sopenharmony_ci	b44_readphy(bp, MII_BMCR, &bmcr);
179462306a36Sopenharmony_ci	b44_readphy(bp, MII_BMCR, &bmcr);
179562306a36Sopenharmony_ci	r = -EINVAL;
179662306a36Sopenharmony_ci	if (bmcr & BMCR_ANENABLE)
179762306a36Sopenharmony_ci		r = b44_writephy(bp, MII_BMCR,
179862306a36Sopenharmony_ci				 bmcr | BMCR_ANRESTART);
179962306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	return r;
180262306a36Sopenharmony_ci}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_cistatic int b44_get_link_ksettings(struct net_device *dev,
180562306a36Sopenharmony_ci				  struct ethtool_link_ksettings *cmd)
180662306a36Sopenharmony_ci{
180762306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
180862306a36Sopenharmony_ci	u32 supported, advertising;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY) {
181162306a36Sopenharmony_ci		BUG_ON(!dev->phydev);
181262306a36Sopenharmony_ci		phy_ethtool_ksettings_get(dev->phydev, cmd);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci		return 0;
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	supported = (SUPPORTED_Autoneg);
181862306a36Sopenharmony_ci	supported |= (SUPPORTED_100baseT_Half |
181962306a36Sopenharmony_ci		      SUPPORTED_100baseT_Full |
182062306a36Sopenharmony_ci		      SUPPORTED_10baseT_Half |
182162306a36Sopenharmony_ci		      SUPPORTED_10baseT_Full |
182262306a36Sopenharmony_ci		      SUPPORTED_MII);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	advertising = 0;
182562306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_ADV_10HALF)
182662306a36Sopenharmony_ci		advertising |= ADVERTISED_10baseT_Half;
182762306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_ADV_10FULL)
182862306a36Sopenharmony_ci		advertising |= ADVERTISED_10baseT_Full;
182962306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_ADV_100HALF)
183062306a36Sopenharmony_ci		advertising |= ADVERTISED_100baseT_Half;
183162306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_ADV_100FULL)
183262306a36Sopenharmony_ci		advertising |= ADVERTISED_100baseT_Full;
183362306a36Sopenharmony_ci	advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
183462306a36Sopenharmony_ci	cmd->base.speed = (bp->flags & B44_FLAG_100_BASE_T) ?
183562306a36Sopenharmony_ci		SPEED_100 : SPEED_10;
183662306a36Sopenharmony_ci	cmd->base.duplex = (bp->flags & B44_FLAG_FULL_DUPLEX) ?
183762306a36Sopenharmony_ci		DUPLEX_FULL : DUPLEX_HALF;
183862306a36Sopenharmony_ci	cmd->base.port = 0;
183962306a36Sopenharmony_ci	cmd->base.phy_address = bp->phy_addr;
184062306a36Sopenharmony_ci	cmd->base.autoneg = (bp->flags & B44_FLAG_FORCE_LINK) ?
184162306a36Sopenharmony_ci		AUTONEG_DISABLE : AUTONEG_ENABLE;
184262306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE)
184362306a36Sopenharmony_ci		advertising |= ADVERTISED_Autoneg;
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
184662306a36Sopenharmony_ci						supported);
184762306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
184862306a36Sopenharmony_ci						advertising);
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	if (!netif_running(dev)){
185162306a36Sopenharmony_ci		cmd->base.speed = 0;
185262306a36Sopenharmony_ci		cmd->base.duplex = 0xff;
185362306a36Sopenharmony_ci	}
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	return 0;
185662306a36Sopenharmony_ci}
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_cistatic int b44_set_link_ksettings(struct net_device *dev,
185962306a36Sopenharmony_ci				  const struct ethtool_link_ksettings *cmd)
186062306a36Sopenharmony_ci{
186162306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
186262306a36Sopenharmony_ci	u32 speed;
186362306a36Sopenharmony_ci	int ret;
186462306a36Sopenharmony_ci	u32 advertising;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY) {
186762306a36Sopenharmony_ci		BUG_ON(!dev->phydev);
186862306a36Sopenharmony_ci		spin_lock_irq(&bp->lock);
186962306a36Sopenharmony_ci		if (netif_running(dev))
187062306a36Sopenharmony_ci			b44_setup_phy(bp);
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci		ret = phy_ethtool_ksettings_set(dev->phydev, cmd);
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci		spin_unlock_irq(&bp->lock);
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci		return ret;
187762306a36Sopenharmony_ci	}
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	speed = cmd->base.speed;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	ethtool_convert_link_mode_to_legacy_u32(&advertising,
188262306a36Sopenharmony_ci						cmd->link_modes.advertising);
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	/* We do not support gigabit. */
188562306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE) {
188662306a36Sopenharmony_ci		if (advertising &
188762306a36Sopenharmony_ci		    (ADVERTISED_1000baseT_Half |
188862306a36Sopenharmony_ci		     ADVERTISED_1000baseT_Full))
188962306a36Sopenharmony_ci			return -EINVAL;
189062306a36Sopenharmony_ci	} else if ((speed != SPEED_100 &&
189162306a36Sopenharmony_ci		    speed != SPEED_10) ||
189262306a36Sopenharmony_ci		   (cmd->base.duplex != DUPLEX_HALF &&
189362306a36Sopenharmony_ci		    cmd->base.duplex != DUPLEX_FULL)) {
189462306a36Sopenharmony_ci			return -EINVAL;
189562306a36Sopenharmony_ci	}
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE) {
190062306a36Sopenharmony_ci		bp->flags &= ~(B44_FLAG_FORCE_LINK |
190162306a36Sopenharmony_ci			       B44_FLAG_100_BASE_T |
190262306a36Sopenharmony_ci			       B44_FLAG_FULL_DUPLEX |
190362306a36Sopenharmony_ci			       B44_FLAG_ADV_10HALF |
190462306a36Sopenharmony_ci			       B44_FLAG_ADV_10FULL |
190562306a36Sopenharmony_ci			       B44_FLAG_ADV_100HALF |
190662306a36Sopenharmony_ci			       B44_FLAG_ADV_100FULL);
190762306a36Sopenharmony_ci		if (advertising == 0) {
190862306a36Sopenharmony_ci			bp->flags |= (B44_FLAG_ADV_10HALF |
190962306a36Sopenharmony_ci				      B44_FLAG_ADV_10FULL |
191062306a36Sopenharmony_ci				      B44_FLAG_ADV_100HALF |
191162306a36Sopenharmony_ci				      B44_FLAG_ADV_100FULL);
191262306a36Sopenharmony_ci		} else {
191362306a36Sopenharmony_ci			if (advertising & ADVERTISED_10baseT_Half)
191462306a36Sopenharmony_ci				bp->flags |= B44_FLAG_ADV_10HALF;
191562306a36Sopenharmony_ci			if (advertising & ADVERTISED_10baseT_Full)
191662306a36Sopenharmony_ci				bp->flags |= B44_FLAG_ADV_10FULL;
191762306a36Sopenharmony_ci			if (advertising & ADVERTISED_100baseT_Half)
191862306a36Sopenharmony_ci				bp->flags |= B44_FLAG_ADV_100HALF;
191962306a36Sopenharmony_ci			if (advertising & ADVERTISED_100baseT_Full)
192062306a36Sopenharmony_ci				bp->flags |= B44_FLAG_ADV_100FULL;
192162306a36Sopenharmony_ci		}
192262306a36Sopenharmony_ci	} else {
192362306a36Sopenharmony_ci		bp->flags |= B44_FLAG_FORCE_LINK;
192462306a36Sopenharmony_ci		bp->flags &= ~(B44_FLAG_100_BASE_T | B44_FLAG_FULL_DUPLEX);
192562306a36Sopenharmony_ci		if (speed == SPEED_100)
192662306a36Sopenharmony_ci			bp->flags |= B44_FLAG_100_BASE_T;
192762306a36Sopenharmony_ci		if (cmd->base.duplex == DUPLEX_FULL)
192862306a36Sopenharmony_ci			bp->flags |= B44_FLAG_FULL_DUPLEX;
192962306a36Sopenharmony_ci	}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	if (netif_running(dev))
193262306a36Sopenharmony_ci		b44_setup_phy(bp);
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	return 0;
193762306a36Sopenharmony_ci}
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_cistatic void b44_get_ringparam(struct net_device *dev,
194062306a36Sopenharmony_ci			      struct ethtool_ringparam *ering,
194162306a36Sopenharmony_ci			      struct kernel_ethtool_ringparam *kernel_ering,
194262306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
194362306a36Sopenharmony_ci{
194462306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	ering->rx_max_pending = B44_RX_RING_SIZE - 1;
194762306a36Sopenharmony_ci	ering->rx_pending = bp->rx_pending;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	/* XXX ethtool lacks a tx_max_pending, oops... */
195062306a36Sopenharmony_ci}
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_cistatic int b44_set_ringparam(struct net_device *dev,
195362306a36Sopenharmony_ci			     struct ethtool_ringparam *ering,
195462306a36Sopenharmony_ci			     struct kernel_ethtool_ringparam *kernel_ering,
195562306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
195662306a36Sopenharmony_ci{
195762306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	if ((ering->rx_pending > B44_RX_RING_SIZE - 1) ||
196062306a36Sopenharmony_ci	    (ering->rx_mini_pending != 0) ||
196162306a36Sopenharmony_ci	    (ering->rx_jumbo_pending != 0) ||
196262306a36Sopenharmony_ci	    (ering->tx_pending > B44_TX_RING_SIZE - 1))
196362306a36Sopenharmony_ci		return -EINVAL;
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	bp->rx_pending = ering->rx_pending;
196862306a36Sopenharmony_ci	bp->tx_pending = ering->tx_pending;
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	b44_halt(bp);
197162306a36Sopenharmony_ci	b44_init_rings(bp);
197262306a36Sopenharmony_ci	b44_init_hw(bp, B44_FULL_RESET);
197362306a36Sopenharmony_ci	netif_wake_queue(bp->dev);
197462306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	b44_enable_ints(bp);
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	return 0;
197962306a36Sopenharmony_ci}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_cistatic void b44_get_pauseparam(struct net_device *dev,
198262306a36Sopenharmony_ci				struct ethtool_pauseparam *epause)
198362306a36Sopenharmony_ci{
198462306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	epause->autoneg =
198762306a36Sopenharmony_ci		(bp->flags & B44_FLAG_PAUSE_AUTO) != 0;
198862306a36Sopenharmony_ci	epause->rx_pause =
198962306a36Sopenharmony_ci		(bp->flags & B44_FLAG_RX_PAUSE) != 0;
199062306a36Sopenharmony_ci	epause->tx_pause =
199162306a36Sopenharmony_ci		(bp->flags & B44_FLAG_TX_PAUSE) != 0;
199262306a36Sopenharmony_ci}
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_cistatic int b44_set_pauseparam(struct net_device *dev,
199562306a36Sopenharmony_ci				struct ethtool_pauseparam *epause)
199662306a36Sopenharmony_ci{
199762306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
200062306a36Sopenharmony_ci	if (epause->autoneg)
200162306a36Sopenharmony_ci		bp->flags |= B44_FLAG_PAUSE_AUTO;
200262306a36Sopenharmony_ci	else
200362306a36Sopenharmony_ci		bp->flags &= ~B44_FLAG_PAUSE_AUTO;
200462306a36Sopenharmony_ci	if (epause->rx_pause)
200562306a36Sopenharmony_ci		bp->flags |= B44_FLAG_RX_PAUSE;
200662306a36Sopenharmony_ci	else
200762306a36Sopenharmony_ci		bp->flags &= ~B44_FLAG_RX_PAUSE;
200862306a36Sopenharmony_ci	if (epause->tx_pause)
200962306a36Sopenharmony_ci		bp->flags |= B44_FLAG_TX_PAUSE;
201062306a36Sopenharmony_ci	else
201162306a36Sopenharmony_ci		bp->flags &= ~B44_FLAG_TX_PAUSE;
201262306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_PAUSE_AUTO) {
201362306a36Sopenharmony_ci		b44_halt(bp);
201462306a36Sopenharmony_ci		b44_init_rings(bp);
201562306a36Sopenharmony_ci		b44_init_hw(bp, B44_FULL_RESET);
201662306a36Sopenharmony_ci	} else {
201762306a36Sopenharmony_ci		__b44_set_flow_ctrl(bp, bp->flags);
201862306a36Sopenharmony_ci	}
201962306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	b44_enable_ints(bp);
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	return 0;
202462306a36Sopenharmony_ci}
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_cistatic void b44_get_strings(struct net_device *dev, u32 stringset, u8 *data)
202762306a36Sopenharmony_ci{
202862306a36Sopenharmony_ci	switch(stringset) {
202962306a36Sopenharmony_ci	case ETH_SS_STATS:
203062306a36Sopenharmony_ci		memcpy(data, *b44_gstrings, sizeof(b44_gstrings));
203162306a36Sopenharmony_ci		break;
203262306a36Sopenharmony_ci	}
203362306a36Sopenharmony_ci}
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_cistatic int b44_get_sset_count(struct net_device *dev, int sset)
203662306a36Sopenharmony_ci{
203762306a36Sopenharmony_ci	switch (sset) {
203862306a36Sopenharmony_ci	case ETH_SS_STATS:
203962306a36Sopenharmony_ci		return ARRAY_SIZE(b44_gstrings);
204062306a36Sopenharmony_ci	default:
204162306a36Sopenharmony_ci		return -EOPNOTSUPP;
204262306a36Sopenharmony_ci	}
204362306a36Sopenharmony_ci}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_cistatic void b44_get_ethtool_stats(struct net_device *dev,
204662306a36Sopenharmony_ci				  struct ethtool_stats *stats, u64 *data)
204762306a36Sopenharmony_ci{
204862306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
204962306a36Sopenharmony_ci	struct b44_hw_stats *hwstat = &bp->hw_stats;
205062306a36Sopenharmony_ci	u64 *data_src, *data_dst;
205162306a36Sopenharmony_ci	unsigned int start;
205262306a36Sopenharmony_ci	u32 i;
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
205562306a36Sopenharmony_ci	b44_stats_update(bp);
205662306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	do {
205962306a36Sopenharmony_ci		data_src = &hwstat->tx_good_octets;
206062306a36Sopenharmony_ci		data_dst = data;
206162306a36Sopenharmony_ci		start = u64_stats_fetch_begin(&hwstat->syncp);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(b44_gstrings); i++)
206462306a36Sopenharmony_ci			*data_dst++ = *data_src++;
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	} while (u64_stats_fetch_retry(&hwstat->syncp, start));
206762306a36Sopenharmony_ci}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_cistatic void b44_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
207062306a36Sopenharmony_ci{
207162306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	wol->supported = WAKE_MAGIC;
207462306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_WOL_ENABLE)
207562306a36Sopenharmony_ci		wol->wolopts = WAKE_MAGIC;
207662306a36Sopenharmony_ci	else
207762306a36Sopenharmony_ci		wol->wolopts = 0;
207862306a36Sopenharmony_ci	memset(&wol->sopass, 0, sizeof(wol->sopass));
207962306a36Sopenharmony_ci}
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_cistatic int b44_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
208262306a36Sopenharmony_ci{
208362306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
208662306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MAGIC)
208762306a36Sopenharmony_ci		bp->flags |= B44_FLAG_WOL_ENABLE;
208862306a36Sopenharmony_ci	else
208962306a36Sopenharmony_ci		bp->flags &= ~B44_FLAG_WOL_ENABLE;
209062306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	device_set_wakeup_enable(bp->sdev->dev, wol->wolopts & WAKE_MAGIC);
209362306a36Sopenharmony_ci	return 0;
209462306a36Sopenharmony_ci}
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_cistatic const struct ethtool_ops b44_ethtool_ops = {
209762306a36Sopenharmony_ci	.get_drvinfo		= b44_get_drvinfo,
209862306a36Sopenharmony_ci	.nway_reset		= b44_nway_reset,
209962306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
210062306a36Sopenharmony_ci	.get_wol		= b44_get_wol,
210162306a36Sopenharmony_ci	.set_wol		= b44_set_wol,
210262306a36Sopenharmony_ci	.get_ringparam		= b44_get_ringparam,
210362306a36Sopenharmony_ci	.set_ringparam		= b44_set_ringparam,
210462306a36Sopenharmony_ci	.get_pauseparam		= b44_get_pauseparam,
210562306a36Sopenharmony_ci	.set_pauseparam		= b44_set_pauseparam,
210662306a36Sopenharmony_ci	.get_msglevel		= b44_get_msglevel,
210762306a36Sopenharmony_ci	.set_msglevel		= b44_set_msglevel,
210862306a36Sopenharmony_ci	.get_strings		= b44_get_strings,
210962306a36Sopenharmony_ci	.get_sset_count		= b44_get_sset_count,
211062306a36Sopenharmony_ci	.get_ethtool_stats	= b44_get_ethtool_stats,
211162306a36Sopenharmony_ci	.get_link_ksettings	= b44_get_link_ksettings,
211262306a36Sopenharmony_ci	.set_link_ksettings	= b44_set_link_ksettings,
211362306a36Sopenharmony_ci};
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_cistatic int b44_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
211662306a36Sopenharmony_ci{
211762306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
211862306a36Sopenharmony_ci	int err = -EINVAL;
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	if (!netif_running(dev))
212162306a36Sopenharmony_ci		goto out;
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
212462306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY) {
212562306a36Sopenharmony_ci		BUG_ON(!dev->phydev);
212662306a36Sopenharmony_ci		err = phy_mii_ioctl(dev->phydev, ifr, cmd);
212762306a36Sopenharmony_ci	} else {
212862306a36Sopenharmony_ci		err = generic_mii_ioctl(&bp->mii_if, if_mii(ifr), cmd, NULL);
212962306a36Sopenharmony_ci	}
213062306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
213162306a36Sopenharmony_ciout:
213262306a36Sopenharmony_ci	return err;
213362306a36Sopenharmony_ci}
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_cistatic int b44_get_invariants(struct b44 *bp)
213662306a36Sopenharmony_ci{
213762306a36Sopenharmony_ci	struct ssb_device *sdev = bp->sdev;
213862306a36Sopenharmony_ci	int err = 0;
213962306a36Sopenharmony_ci	u8 *addr;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	bp->dma_offset = ssb_dma_translation(sdev);
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	if (sdev->bus->bustype == SSB_BUSTYPE_SSB &&
214462306a36Sopenharmony_ci	    instance > 1) {
214562306a36Sopenharmony_ci		addr = sdev->bus->sprom.et1mac;
214662306a36Sopenharmony_ci		bp->phy_addr = sdev->bus->sprom.et1phyaddr;
214762306a36Sopenharmony_ci	} else {
214862306a36Sopenharmony_ci		addr = sdev->bus->sprom.et0mac;
214962306a36Sopenharmony_ci		bp->phy_addr = sdev->bus->sprom.et0phyaddr;
215062306a36Sopenharmony_ci	}
215162306a36Sopenharmony_ci	/* Some ROMs have buggy PHY addresses with the high
215262306a36Sopenharmony_ci	 * bits set (sign extension?). Truncate them to a
215362306a36Sopenharmony_ci	 * valid PHY address. */
215462306a36Sopenharmony_ci	bp->phy_addr &= 0x1F;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	eth_hw_addr_set(bp->dev, addr);
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){
215962306a36Sopenharmony_ci		pr_err("Invalid MAC address found in EEPROM\n");
216062306a36Sopenharmony_ci		return -EINVAL;
216162306a36Sopenharmony_ci	}
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	bp->imask = IMASK_DEF;
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	/* XXX - really required?
216662306a36Sopenharmony_ci	   bp->flags |= B44_FLAG_BUGGY_TXPTR;
216762306a36Sopenharmony_ci	*/
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	if (bp->sdev->id.revision >= 7)
217062306a36Sopenharmony_ci		bp->flags |= B44_FLAG_B0_ANDLATER;
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	return err;
217362306a36Sopenharmony_ci}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_cistatic const struct net_device_ops b44_netdev_ops = {
217662306a36Sopenharmony_ci	.ndo_open		= b44_open,
217762306a36Sopenharmony_ci	.ndo_stop		= b44_close,
217862306a36Sopenharmony_ci	.ndo_start_xmit		= b44_start_xmit,
217962306a36Sopenharmony_ci	.ndo_get_stats64	= b44_get_stats64,
218062306a36Sopenharmony_ci	.ndo_set_rx_mode	= b44_set_rx_mode,
218162306a36Sopenharmony_ci	.ndo_set_mac_address	= b44_set_mac_addr,
218262306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
218362306a36Sopenharmony_ci	.ndo_eth_ioctl		= b44_ioctl,
218462306a36Sopenharmony_ci	.ndo_tx_timeout		= b44_tx_timeout,
218562306a36Sopenharmony_ci	.ndo_change_mtu		= b44_change_mtu,
218662306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
218762306a36Sopenharmony_ci	.ndo_poll_controller	= b44_poll_controller,
218862306a36Sopenharmony_ci#endif
218962306a36Sopenharmony_ci};
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_cistatic void b44_adjust_link(struct net_device *dev)
219262306a36Sopenharmony_ci{
219362306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
219462306a36Sopenharmony_ci	struct phy_device *phydev = dev->phydev;
219562306a36Sopenharmony_ci	bool status_changed = false;
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	BUG_ON(!phydev);
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci	if (bp->old_link != phydev->link) {
220062306a36Sopenharmony_ci		status_changed = true;
220162306a36Sopenharmony_ci		bp->old_link = phydev->link;
220262306a36Sopenharmony_ci	}
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	/* reflect duplex change */
220562306a36Sopenharmony_ci	if (phydev->link) {
220662306a36Sopenharmony_ci		if ((phydev->duplex == DUPLEX_HALF) &&
220762306a36Sopenharmony_ci		    (bp->flags & B44_FLAG_FULL_DUPLEX)) {
220862306a36Sopenharmony_ci			status_changed = true;
220962306a36Sopenharmony_ci			bp->flags &= ~B44_FLAG_FULL_DUPLEX;
221062306a36Sopenharmony_ci		} else if ((phydev->duplex == DUPLEX_FULL) &&
221162306a36Sopenharmony_ci			   !(bp->flags & B44_FLAG_FULL_DUPLEX)) {
221262306a36Sopenharmony_ci			status_changed = true;
221362306a36Sopenharmony_ci			bp->flags |= B44_FLAG_FULL_DUPLEX;
221462306a36Sopenharmony_ci		}
221562306a36Sopenharmony_ci	}
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	if (status_changed) {
221862306a36Sopenharmony_ci		u32 val = br32(bp, B44_TX_CTRL);
221962306a36Sopenharmony_ci		if (bp->flags & B44_FLAG_FULL_DUPLEX)
222062306a36Sopenharmony_ci			val |= TX_CTRL_DUPLEX;
222162306a36Sopenharmony_ci		else
222262306a36Sopenharmony_ci			val &= ~TX_CTRL_DUPLEX;
222362306a36Sopenharmony_ci		bw32(bp, B44_TX_CTRL, val);
222462306a36Sopenharmony_ci		phy_print_status(phydev);
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci}
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_cistatic int b44_register_phy_one(struct b44 *bp)
222962306a36Sopenharmony_ci{
223062306a36Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
223162306a36Sopenharmony_ci	struct mii_bus *mii_bus;
223262306a36Sopenharmony_ci	struct ssb_device *sdev = bp->sdev;
223362306a36Sopenharmony_ci	struct phy_device *phydev;
223462306a36Sopenharmony_ci	char bus_id[MII_BUS_ID_SIZE + 3];
223562306a36Sopenharmony_ci	struct ssb_sprom *sprom = &sdev->bus->sprom;
223662306a36Sopenharmony_ci	int err;
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	mii_bus = mdiobus_alloc();
223962306a36Sopenharmony_ci	if (!mii_bus) {
224062306a36Sopenharmony_ci		dev_err(sdev->dev, "mdiobus_alloc() failed\n");
224162306a36Sopenharmony_ci		err = -ENOMEM;
224262306a36Sopenharmony_ci		goto err_out;
224362306a36Sopenharmony_ci	}
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	mii_bus->priv = bp;
224662306a36Sopenharmony_ci	mii_bus->read = b44_mdio_read_phylib;
224762306a36Sopenharmony_ci	mii_bus->write = b44_mdio_write_phylib;
224862306a36Sopenharmony_ci	mii_bus->name = "b44_eth_mii";
224962306a36Sopenharmony_ci	mii_bus->parent = sdev->dev;
225062306a36Sopenharmony_ci	mii_bus->phy_mask = ~(1 << bp->phy_addr);
225162306a36Sopenharmony_ci	snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%x", instance);
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	bp->mii_bus = mii_bus;
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	err = mdiobus_register(mii_bus);
225662306a36Sopenharmony_ci	if (err) {
225762306a36Sopenharmony_ci		dev_err(sdev->dev, "failed to register MII bus\n");
225862306a36Sopenharmony_ci		goto err_out_mdiobus;
225962306a36Sopenharmony_ci	}
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	if (!mdiobus_is_registered_device(bp->mii_bus, bp->phy_addr) &&
226262306a36Sopenharmony_ci	    (sprom->boardflags_lo & (B44_BOARDFLAG_ROBO | B44_BOARDFLAG_ADM))) {
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci		dev_info(sdev->dev,
226562306a36Sopenharmony_ci			 "could not find PHY at %i, use fixed one\n",
226662306a36Sopenharmony_ci			 bp->phy_addr);
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci		bp->phy_addr = 0;
226962306a36Sopenharmony_ci		snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, "fixed-0",
227062306a36Sopenharmony_ci			 bp->phy_addr);
227162306a36Sopenharmony_ci	} else {
227262306a36Sopenharmony_ci		snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, mii_bus->id,
227362306a36Sopenharmony_ci			 bp->phy_addr);
227462306a36Sopenharmony_ci	}
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci	phydev = phy_connect(bp->dev, bus_id, &b44_adjust_link,
227762306a36Sopenharmony_ci			     PHY_INTERFACE_MODE_MII);
227862306a36Sopenharmony_ci	if (IS_ERR(phydev)) {
227962306a36Sopenharmony_ci		dev_err(sdev->dev, "could not attach PHY at %i\n",
228062306a36Sopenharmony_ci			bp->phy_addr);
228162306a36Sopenharmony_ci		err = PTR_ERR(phydev);
228262306a36Sopenharmony_ci		goto err_out_mdiobus_unregister;
228362306a36Sopenharmony_ci	}
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	/* mask with MAC supported features */
228662306a36Sopenharmony_ci	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask);
228762306a36Sopenharmony_ci	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mask);
228862306a36Sopenharmony_ci	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
228962306a36Sopenharmony_ci	linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, mask);
229062306a36Sopenharmony_ci	linkmode_and(phydev->supported, phydev->supported, mask);
229162306a36Sopenharmony_ci	linkmode_copy(phydev->advertising, phydev->supported);
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	bp->old_link = 0;
229462306a36Sopenharmony_ci	bp->phy_addr = phydev->mdio.addr;
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	phy_attached_info(phydev);
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	return 0;
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_cierr_out_mdiobus_unregister:
230162306a36Sopenharmony_ci	mdiobus_unregister(mii_bus);
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_cierr_out_mdiobus:
230462306a36Sopenharmony_ci	mdiobus_free(mii_bus);
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_cierr_out:
230762306a36Sopenharmony_ci	return err;
230862306a36Sopenharmony_ci}
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_cistatic void b44_unregister_phy_one(struct b44 *bp)
231162306a36Sopenharmony_ci{
231262306a36Sopenharmony_ci	struct net_device *dev = bp->dev;
231362306a36Sopenharmony_ci	struct mii_bus *mii_bus = bp->mii_bus;
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	phy_disconnect(dev->phydev);
231662306a36Sopenharmony_ci	mdiobus_unregister(mii_bus);
231762306a36Sopenharmony_ci	mdiobus_free(mii_bus);
231862306a36Sopenharmony_ci}
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_cistatic int b44_init_one(struct ssb_device *sdev,
232162306a36Sopenharmony_ci			const struct ssb_device_id *ent)
232262306a36Sopenharmony_ci{
232362306a36Sopenharmony_ci	struct net_device *dev;
232462306a36Sopenharmony_ci	struct b44 *bp;
232562306a36Sopenharmony_ci	int err;
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	instance++;
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(*bp));
233062306a36Sopenharmony_ci	if (!dev) {
233162306a36Sopenharmony_ci		err = -ENOMEM;
233262306a36Sopenharmony_ci		goto out;
233362306a36Sopenharmony_ci	}
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, sdev->dev);
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci	/* No interesting netdevice features in this card... */
233862306a36Sopenharmony_ci	dev->features |= 0;
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci	bp = netdev_priv(dev);
234162306a36Sopenharmony_ci	bp->sdev = sdev;
234262306a36Sopenharmony_ci	bp->dev = dev;
234362306a36Sopenharmony_ci	bp->force_copybreak = 0;
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	bp->msg_enable = netif_msg_init(b44_debug, B44_DEF_MSG_ENABLE);
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	spin_lock_init(&bp->lock);
234862306a36Sopenharmony_ci	u64_stats_init(&bp->hw_stats.syncp);
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	bp->rx_pending = B44_DEF_RX_RING_PENDING;
235162306a36Sopenharmony_ci	bp->tx_pending = B44_DEF_TX_RING_PENDING;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	dev->netdev_ops = &b44_netdev_ops;
235462306a36Sopenharmony_ci	netif_napi_add(dev, &bp->napi, b44_poll);
235562306a36Sopenharmony_ci	dev->watchdog_timeo = B44_TX_TIMEOUT;
235662306a36Sopenharmony_ci	dev->min_mtu = B44_MIN_MTU;
235762306a36Sopenharmony_ci	dev->max_mtu = B44_MAX_MTU;
235862306a36Sopenharmony_ci	dev->irq = sdev->irq;
235962306a36Sopenharmony_ci	dev->ethtool_ops = &b44_ethtool_ops;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	err = ssb_bus_powerup(sdev->bus, 0);
236262306a36Sopenharmony_ci	if (err) {
236362306a36Sopenharmony_ci		dev_err(sdev->dev,
236462306a36Sopenharmony_ci			"Failed to powerup the bus\n");
236562306a36Sopenharmony_ci		goto err_out_free_dev;
236662306a36Sopenharmony_ci	}
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(sdev->dma_dev, DMA_BIT_MASK(30));
236962306a36Sopenharmony_ci	if (err) {
237062306a36Sopenharmony_ci		dev_err(sdev->dev,
237162306a36Sopenharmony_ci			"Required 30BIT DMA mask unsupported by the system\n");
237262306a36Sopenharmony_ci		goto err_out_powerdown;
237362306a36Sopenharmony_ci	}
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	err = b44_get_invariants(bp);
237662306a36Sopenharmony_ci	if (err) {
237762306a36Sopenharmony_ci		dev_err(sdev->dev,
237862306a36Sopenharmony_ci			"Problem fetching invariants of chip, aborting\n");
237962306a36Sopenharmony_ci		goto err_out_powerdown;
238062306a36Sopenharmony_ci	}
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci	if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) {
238362306a36Sopenharmony_ci		dev_err(sdev->dev, "No PHY present on this MAC, aborting\n");
238462306a36Sopenharmony_ci		err = -ENODEV;
238562306a36Sopenharmony_ci		goto err_out_powerdown;
238662306a36Sopenharmony_ci	}
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	bp->mii_if.dev = dev;
238962306a36Sopenharmony_ci	bp->mii_if.mdio_read = b44_mdio_read_mii;
239062306a36Sopenharmony_ci	bp->mii_if.mdio_write = b44_mdio_write_mii;
239162306a36Sopenharmony_ci	bp->mii_if.phy_id = bp->phy_addr;
239262306a36Sopenharmony_ci	bp->mii_if.phy_id_mask = 0x1f;
239362306a36Sopenharmony_ci	bp->mii_if.reg_num_mask = 0x1f;
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	/* By default, advertise all speed/duplex settings. */
239662306a36Sopenharmony_ci	bp->flags |= (B44_FLAG_ADV_10HALF | B44_FLAG_ADV_10FULL |
239762306a36Sopenharmony_ci		      B44_FLAG_ADV_100HALF | B44_FLAG_ADV_100FULL);
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci	/* By default, auto-negotiate PAUSE. */
240062306a36Sopenharmony_ci	bp->flags |= B44_FLAG_PAUSE_AUTO;
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	err = register_netdev(dev);
240362306a36Sopenharmony_ci	if (err) {
240462306a36Sopenharmony_ci		dev_err(sdev->dev, "Cannot register net device, aborting\n");
240562306a36Sopenharmony_ci		goto err_out_powerdown;
240662306a36Sopenharmony_ci	}
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	netif_carrier_off(dev);
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	ssb_set_drvdata(sdev, dev);
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci	/* Chip reset provides power to the b44 MAC & PCI cores, which
241362306a36Sopenharmony_ci	 * is necessary for MAC register access.
241462306a36Sopenharmony_ci	 */
241562306a36Sopenharmony_ci	b44_chip_reset(bp, B44_CHIP_RESET_FULL);
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	/* do a phy reset to test if there is an active phy */
241862306a36Sopenharmony_ci	err = b44_phy_reset(bp);
241962306a36Sopenharmony_ci	if (err < 0) {
242062306a36Sopenharmony_ci		dev_err(sdev->dev, "phy reset failed\n");
242162306a36Sopenharmony_ci		goto err_out_unregister_netdev;
242262306a36Sopenharmony_ci	}
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY) {
242562306a36Sopenharmony_ci		err = b44_register_phy_one(bp);
242662306a36Sopenharmony_ci		if (err) {
242762306a36Sopenharmony_ci			dev_err(sdev->dev, "Cannot register PHY, aborting\n");
242862306a36Sopenharmony_ci			goto err_out_unregister_netdev;
242962306a36Sopenharmony_ci		}
243062306a36Sopenharmony_ci	}
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	device_set_wakeup_capable(sdev->dev, true);
243362306a36Sopenharmony_ci	netdev_info(dev, "%s %pM\n", DRV_DESCRIPTION, dev->dev_addr);
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	return 0;
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_cierr_out_unregister_netdev:
243862306a36Sopenharmony_ci	unregister_netdev(dev);
243962306a36Sopenharmony_cierr_out_powerdown:
244062306a36Sopenharmony_ci	ssb_bus_may_powerdown(sdev->bus);
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_cierr_out_free_dev:
244362306a36Sopenharmony_ci	netif_napi_del(&bp->napi);
244462306a36Sopenharmony_ci	free_netdev(dev);
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ciout:
244762306a36Sopenharmony_ci	return err;
244862306a36Sopenharmony_ci}
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_cistatic void b44_remove_one(struct ssb_device *sdev)
245162306a36Sopenharmony_ci{
245262306a36Sopenharmony_ci	struct net_device *dev = ssb_get_drvdata(sdev);
245362306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	unregister_netdev(dev);
245662306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_EXTERNAL_PHY)
245762306a36Sopenharmony_ci		b44_unregister_phy_one(bp);
245862306a36Sopenharmony_ci	ssb_device_disable(sdev, 0);
245962306a36Sopenharmony_ci	ssb_bus_may_powerdown(sdev->bus);
246062306a36Sopenharmony_ci	netif_napi_del(&bp->napi);
246162306a36Sopenharmony_ci	free_netdev(dev);
246262306a36Sopenharmony_ci	ssb_pcihost_set_power_state(sdev, PCI_D3hot);
246362306a36Sopenharmony_ci	ssb_set_drvdata(sdev, NULL);
246462306a36Sopenharmony_ci}
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_cistatic int b44_suspend(struct ssb_device *sdev, pm_message_t state)
246762306a36Sopenharmony_ci{
246862306a36Sopenharmony_ci	struct net_device *dev = ssb_get_drvdata(sdev);
246962306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	if (!netif_running(dev))
247262306a36Sopenharmony_ci		return 0;
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	del_timer_sync(&bp->timer);
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	b44_halt(bp);
247962306a36Sopenharmony_ci	netif_carrier_off(bp->dev);
248062306a36Sopenharmony_ci	netif_device_detach(bp->dev);
248162306a36Sopenharmony_ci	b44_free_rings(bp);
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	free_irq(dev->irq, dev);
248662306a36Sopenharmony_ci	if (bp->flags & B44_FLAG_WOL_ENABLE) {
248762306a36Sopenharmony_ci		b44_init_hw(bp, B44_PARTIAL_RESET);
248862306a36Sopenharmony_ci		b44_setup_wol(bp);
248962306a36Sopenharmony_ci	}
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	ssb_pcihost_set_power_state(sdev, PCI_D3hot);
249262306a36Sopenharmony_ci	return 0;
249362306a36Sopenharmony_ci}
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_cistatic int b44_resume(struct ssb_device *sdev)
249662306a36Sopenharmony_ci{
249762306a36Sopenharmony_ci	struct net_device *dev = ssb_get_drvdata(sdev);
249862306a36Sopenharmony_ci	struct b44 *bp = netdev_priv(dev);
249962306a36Sopenharmony_ci	int rc = 0;
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci	rc = ssb_bus_powerup(sdev->bus, 0);
250262306a36Sopenharmony_ci	if (rc) {
250362306a36Sopenharmony_ci		dev_err(sdev->dev,
250462306a36Sopenharmony_ci			"Failed to powerup the bus\n");
250562306a36Sopenharmony_ci		return rc;
250662306a36Sopenharmony_ci	}
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	if (!netif_running(dev))
250962306a36Sopenharmony_ci		return 0;
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci	spin_lock_irq(&bp->lock);
251262306a36Sopenharmony_ci	b44_init_rings(bp);
251362306a36Sopenharmony_ci	b44_init_hw(bp, B44_FULL_RESET);
251462306a36Sopenharmony_ci	spin_unlock_irq(&bp->lock);
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	/*
251762306a36Sopenharmony_ci	 * As a shared interrupt, the handler can be called immediately. To be
251862306a36Sopenharmony_ci	 * able to check the interrupt status the hardware must already be
251962306a36Sopenharmony_ci	 * powered back on (b44_init_hw).
252062306a36Sopenharmony_ci	 */
252162306a36Sopenharmony_ci	rc = request_irq(dev->irq, b44_interrupt, IRQF_SHARED, dev->name, dev);
252262306a36Sopenharmony_ci	if (rc) {
252362306a36Sopenharmony_ci		netdev_err(dev, "request_irq failed\n");
252462306a36Sopenharmony_ci		spin_lock_irq(&bp->lock);
252562306a36Sopenharmony_ci		b44_halt(bp);
252662306a36Sopenharmony_ci		b44_free_rings(bp);
252762306a36Sopenharmony_ci		spin_unlock_irq(&bp->lock);
252862306a36Sopenharmony_ci		return rc;
252962306a36Sopenharmony_ci	}
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci	netif_device_attach(bp->dev);
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_ci	b44_enable_ints(bp);
253462306a36Sopenharmony_ci	netif_wake_queue(dev);
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	mod_timer(&bp->timer, jiffies + 1);
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	return 0;
253962306a36Sopenharmony_ci}
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_cistatic struct ssb_driver b44_ssb_driver = {
254262306a36Sopenharmony_ci	.name		= DRV_MODULE_NAME,
254362306a36Sopenharmony_ci	.id_table	= b44_ssb_tbl,
254462306a36Sopenharmony_ci	.probe		= b44_init_one,
254562306a36Sopenharmony_ci	.remove		= b44_remove_one,
254662306a36Sopenharmony_ci	.suspend	= b44_suspend,
254762306a36Sopenharmony_ci	.resume		= b44_resume,
254862306a36Sopenharmony_ci};
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_cistatic inline int __init b44_pci_init(void)
255162306a36Sopenharmony_ci{
255262306a36Sopenharmony_ci	int err = 0;
255362306a36Sopenharmony_ci#ifdef CONFIG_B44_PCI
255462306a36Sopenharmony_ci	err = ssb_pcihost_register(&b44_pci_driver);
255562306a36Sopenharmony_ci#endif
255662306a36Sopenharmony_ci	return err;
255762306a36Sopenharmony_ci}
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_cistatic inline void b44_pci_exit(void)
256062306a36Sopenharmony_ci{
256162306a36Sopenharmony_ci#ifdef CONFIG_B44_PCI
256262306a36Sopenharmony_ci	ssb_pcihost_unregister(&b44_pci_driver);
256362306a36Sopenharmony_ci#endif
256462306a36Sopenharmony_ci}
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_cistatic int __init b44_init(void)
256762306a36Sopenharmony_ci{
256862306a36Sopenharmony_ci	unsigned int dma_desc_align_size = dma_get_cache_alignment();
256962306a36Sopenharmony_ci	int err;
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	/* Setup paramaters for syncing RX/TX DMA descriptors */
257262306a36Sopenharmony_ci	dma_desc_sync_size = max_t(unsigned int, dma_desc_align_size, sizeof(struct dma_desc));
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	err = b44_pci_init();
257562306a36Sopenharmony_ci	if (err)
257662306a36Sopenharmony_ci		return err;
257762306a36Sopenharmony_ci	err = ssb_driver_register(&b44_ssb_driver);
257862306a36Sopenharmony_ci	if (err)
257962306a36Sopenharmony_ci		b44_pci_exit();
258062306a36Sopenharmony_ci	return err;
258162306a36Sopenharmony_ci}
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_cistatic void __exit b44_cleanup(void)
258462306a36Sopenharmony_ci{
258562306a36Sopenharmony_ci	ssb_driver_unregister(&b44_ssb_driver);
258662306a36Sopenharmony_ci	b44_pci_exit();
258762306a36Sopenharmony_ci}
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_cimodule_init(b44_init);
259062306a36Sopenharmony_cimodule_exit(b44_cleanup);
259162306a36Sopenharmony_ci
2592