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