/* b44.c: Broadcom 4400 device driver. * * Copyright (C) 2002 David S. Miller (davem@redhat.com) * Fixed by Pekka Pietikainen (pp@ee.oulu.fi) * Copyright (C) 2006 Broadcom Corporation. * * Distribute under GPL. */ #include <linux/kernel.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/types.h>#include <linux/netdevice.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/if_ether.h>#include <linux/etherdevice.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/dma-mapping.h> #include <asm/uaccess.h>#include <asm/io.h>#include <asm/irq.h> #include "b44.h" #define DRV_MODULE_NAME "b44"#define PFX DRV_MODULE_NAME ": "#define DRV_MODULE_VERSION "1.01"#define DRV_MODULE_RELDATE "Jun 16, 2006" #define B44_DEF_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ NETIF_MSG_LINK | \ NETIF_MSG_TIMER | \ NETIF_MSG_IFDOWN | \ NETIF_MSG_IFUP | \ NETIF_MSG_RX_ERR | \ NETIF_MSG_TX_ERR) /* length of time before we decide the hardware is borked, * and dev->tx_timeout() should be called to fix the problem */#define B44_TX_TIMEOUT (5 * HZ) /* hardware minimum and maximum for a single frame's data payload */#define B44_MIN_MTU 60#define B44_MAX_MTU 1500 #define B44_RX_RING_SIZE 512#define B44_DEF_RX_RING_PENDING 200#define B44_RX_RING_BYTES (sizeof(struct dma_desc) * \ B44_RX_RING_SIZE)#define B44_TX_RING_SIZE 512#define B44_DEF_TX_RING_PENDING (B44_TX_RING_SIZE - 1)#define B44_TX_RING_BYTES (sizeof(struct dma_desc) * \ B44_TX_RING_SIZE) #define TX_RING_GAP(BP) \ (B44_TX_RING_SIZE - (BP)->tx_pending)#define TX_BUFFS_AVAIL(BP) \ (((BP)->tx_cons <= (BP)->tx_prod) ? \ (BP)->tx_cons + (BP)->tx_pending - (BP)->tx_prod : \ (BP)->tx_cons - (BP)->tx_prod - TX_RING_GAP(BP))#define NEXT_TX(N) (((N) + 1) & (B44_TX_RING_SIZE - 1)) #define RX_PKT_BUF_SZ (1536 + bp->rx_offset + 64)#define TX_PKT_BUF_SZ (B44_MAX_MTU + ETH_HLEN + 8) /* minimum number of free TX descriptors required to wake up TX process */#define B44_TX_WAKEUP_THRESH (B44_TX_RING_SIZE / 4) /* b44 internal pattern match filter info */#define B44_PATTERN_BASE 0x400#define B44_PATTERN_SIZE 0x80#define B44_PMASK_BASE 0x600#define B44_PMASK_SIZE 0x10#define B44_MAX_PATTERNS 16#define B44_ETHIPV6UDP_HLEN 62#define B44_ETHIPV4UDP_HLEN 42 static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Florian Schirmer, Pekka Pietikainen, David S. Miller");MODULE_DESCRIPTION("Broadcom 4400 10/100 PCI ethernet driver");MODULE_LICENSE("GPL");MODULE_VERSION(DRV_MODULE_VERSION); static int b44_debug = -1; /* -1 == use B44_DEF_MSG_ENABLE as value */module_param(b44_debug, int, 0);MODULE_PARM_DESC(b44_debug, "B44 bitmapped debugging message enable value"); static struct pci_device_id b44_pci_tbl[] = { { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { } /* terminate list with empty entry */}; MODULE_DEVICE_TABLE(pci, b44_pci_tbl); static void b44_halt(struct b44 *);static void b44_init_rings(struct b44 *); #define B44_FULL_RESET 1#define B44_FULL_RESET_SKIP_PHY 2#define B44_PARTIAL_RESET 3 static void b44_init_hw(struct b44 *, int); static int dma_desc_align_mask;static int dma_desc_sync_size; static const char b44_gstrings[][ETH_GSTRING_LEN] = {#define _B44(x...) # x,B44_STAT_REG_DECLARE#undef _B44}; static inline void b44_sync_dma_desc_for_device(struct pci_dev *pdev, dma_addr_t dma_base, unsigned long offset, enum dma_data_direction dir){ dma_sync_single_range_for_device(&pdev->dev, dma_base, offset & dma_desc_align_mask, dma_desc_sync_size, dir);} static inline void b44_sync_dma_desc_for_cpu(struct pci_dev *pdev, dma_addr_t dma_base, unsigned long offset, enum dma_data_direction dir){ dma_sync_single_range_for_cpu(&pdev->dev, dma_base, offset & dma_desc_align_mask, dma_desc_sync_size, dir);} static inline unsigned long br32(const struct b44 *bp, unsigned long reg){ return readl(bp->regs + reg);} static inline void bw32(const struct b44 *bp, unsigned long reg, unsigned long val){ writel(val, bp->regs + reg);} static int b44_wait_bit(struct b44 *bp, unsigned long reg, u32 bit, unsigned long timeout, const int clear){ unsigned long i; for (i = 0; i < timeout; i++) { u32 val = br32(bp, reg); if (clear && !(val & bit)) break; if (!clear && (val & bit)) break; udelay(10); } if (i == timeout) { printk(KERN_ERR PFX "%s: BUG! Timeout waiting for bit %08x of register " "%lx to %s.\n", bp->dev->name, bit, reg, (clear ? "clear" : "set")); return -ENODEV; } return 0;} /* Sonics SiliconBackplane support routines. ROFL, you should see all the * buzz words used on this company's website :-) * * All of these routines must be invoked with bp->lock held and * interrupts disabled. */ #define SB_PCI_DMA 0x40000000 /* Client Mode PCI memory access space (1 GB) */#define BCM4400_PCI_CORE_ADDR 0x18002000 /* Address of PCI core on BCM4400 cards */ static u32 ssb_get_core_rev(struct b44 *bp){ return (br32(bp, B44_SBIDHIGH) & SBIDHIGH_RC_MASK);} static u32 ssb_pci_setup(struct b44 *bp, u32 cores){ u32 bar_orig, pci_rev, val; pci_read_config_dword(bp->pdev, SSB_BAR0_WIN, &bar_orig); pci_write_config_dword(bp->pdev, SSB_BAR0_WIN, BCM4400_PCI_CORE_ADDR); pci_rev = ssb_get_core_rev(bp); val = br32(bp, B44_SBINTVEC); val |= cores; bw32(bp, B44_SBINTVEC, val); val = br32(bp, SSB_PCI_TRANS_2); val |= SSB_PCI_PREF | SSB_PCI_BURST; bw32(bp, SSB_PCI_TRANS_2, val); pci_write_config_dword(bp->pdev, SSB_BAR0_WIN, bar_orig); return pci_rev;} static void ssb_core_disable(struct b44 *bp){ if (br32(bp, B44_SBTMSLOW) & SBTMSLOW_RESET) return; bw32(bp, B44_SBTMSLOW, (SBTMSLOW_REJECT | SBTMSLOW_CLOCK)); b44_wait_bit(bp, B44_SBTMSLOW, SBTMSLOW_REJECT, 100000, 0); b44_wait_bit(bp, B44_SBTMSHIGH, SBTMSHIGH_BUSY, 100000, 1); bw32(bp, B44_SBTMSLOW, (SBTMSLOW_FGC | SBTMSLOW_CLOCK | SBTMSLOW_REJECT | SBTMSLOW_RESET)); br32(bp, B44_SBTMSLOW); udelay(1); bw32(bp, B44_SBTMSLOW, (SBTMSLOW_REJECT | SBTMSLOW_RESET)); br32(bp, B44_SBTMSLOW); udelay(1);} static void ssb_core_reset(struct b44 *bp){ u32 val; ssb_core_disable(bp); bw32(bp, B44_SBTMSLOW, (SBTMSLOW_RESET | SBTMSLOW_CLOCK | SBTMSLOW_FGC)); br32(bp, B44_SBTMSLOW); udelay(1); /* Clear SERR if set, this is a hw bug workaround. */ if (br32(bp, B44_SBTMSHIGH) & SBTMSHIGH_SERR) bw32(bp, B44_SBTMSHIGH, 0); val = br32(bp, B44_SBIMSTATE); if (val & (SBIMSTATE_IBE | SBIMSTATE_TO)) bw32(bp, B44_SBIMSTATE, val & ~(SBIMSTATE_IBE | SBIMSTATE_TO)); bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK | SBTMSLOW_FGC)); br32(bp, B44_SBTMSLOW); udelay(1); bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK)); br32(bp, B44_SBTMSLOW); udelay(1);} static int ssb_core_unit(struct b44 *bp){#if 0 u32 val = br32(bp, B44_SBADMATCH0); u32 base; type = val & SBADMATCH0_TYPE_MASK; switch (type) { case 0: base = val & SBADMATCH0_BS0_MASK; break; case 1: base = val & SBADMATCH0_BS1_MASK; break; case 2: default: base = val & SBADMATCH0_BS2_MASK; break; };#endif return 0;} static int ssb_is_core_up(struct b44 *bp){ return ((br32(bp, B44_SBTMSLOW) & (SBTMSLOW_RESET | SBTMSLOW_REJECT | SBTMSLOW_CLOCK)) == SBTMSLOW_CLOCK);} static void __b44_cam_write(struct b44 *bp, unsigned char *data, int index){ u32 val; val = ((u32) data[2]) << 24; val |= ((u32) data[3]) << 16; val |= ((u32) data[4]) << 8; val |= ((u32) data[5]) << 0; bw32(bp, B44_CAM_DATA_LO, val); val = (CAM_DATA_HI_VALID | (((u32) data[0]) << 8) | (((u32) data[1]) << 0)); bw32(bp, B44_CAM_DATA_HI, val); bw32(bp, B44_CAM_CTRL, (CAM_CTRL_WRITE | (index << CAM_CTRL_INDEX_SHIFT))); b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1);} static inline void __b44_disable_ints(struct b44 *bp){ bw32(bp, B44_IMASK, 0);} static void b44_disable_ints(struct b44 *bp){ __b44_disable_ints(bp); /* Flush posted writes. */ br32(bp, B44_IMASK);} static void b44_enable_ints(struct b44 *bp){ bw32(bp, B44_IMASK, bp->imask);} static int b44_readphy(struct b44 *bp, int reg, u32 *val){ int err; bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII); bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | (MDIO_OP_READ << MDIO_DATA_OP_SHIFT) | (bp->phy_addr << MDIO_DATA_PMD_SHIFT) | (reg << MDIO_DATA_RA_SHIFT) | (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT))); err = b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0); *val = br32(bp, B44_MDIO_DATA) & MDIO_DATA_DATA; return err;} static int b44_writephy(struct b44 *bp, int reg, u32 val){ bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII); bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | (MDIO_OP_WRITE << MDIO_DATA_OP_SHIFT) | (bp->phy_addr << MDIO_DATA_PMD_SHIFT) | (reg << MDIO_DATA_RA_SHIFT) | (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT) | (val & MDIO_DATA_DATA))); return b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0);} /* miilib interface *//* FIXME FIXME: phy_id is ignored, bp->phy_addr use is unconditional * due to code existing before miilib use was added to this driver. * Someone should remove this artificial driver limitation in * b44_{read,write}phy. bp->phy_addr itself is fine (and needed). */static int b44_mii_read(struct net_device *dev, int phy_id, int location){ u32 val; struct b44 *bp = netdev_priv(dev); int rc = b44_readphy(bp, location, &val); if (rc) return 0xffffffff; return val;} static void b44_mii_write(struct net_device *dev, int phy_id, int location, int val){ struct b44 *bp = netdev_priv(dev); b44_writephy(bp, location, val);} static int b44_phy_reset(struct b44 *bp){ u32 val; int err; err = b44_writephy(bp, MII_BMCR, BMCR_RESET); if (err) return err; udelay(100); err = b44_readphy(bp, MII_BMCR, &val); if (!err) { if (val & BMCR_RESET) { printk(KERN_ERR PFX "%s: PHY Reset would not complete.\n", bp->dev->name); err = -ENODEV; } } return 0;} static void __b44_set_flow_ctrl(struct b44 *bp, u32 pause_flags){ u32 val; bp->flags &= ~(B44_FLAG_TX_PAUSE | B44_FLAG_RX_PAUSE); bp->flags |= pause_flags; val = br32(bp, B44_RXCONFIG); if (pause_flags & B44_FLAG_RX_PAUSE) val |= RXCONFIG_FLOW; else val &= ~RXCONFIG_FLOW; bw32(bp, B44_RXCONFIG, val); val = br32(bp, B44_MAC_FLOW); if (pause_flags & B44_FLAG_TX_PAUSE) val |= (MAC_FLOW_PAUSE_ENAB | (0xc0 & MAC_FLOW_RX_HI_WATER)); else val &= ~MAC_FLOW_PAUSE_ENAB; bw32(bp, B44_MAC_FLOW, val);} static void b44_set_flow_ctrl(struct b44 *bp, u32 local, u32 remote){ u32 pause_enab = 0; /* The driver supports only rx pause by default because the b44 mac tx pause mechanism generates excessive pause frames. Use ethtool to turn on b44 tx pause if necessary. */ if ((local & ADVERTISE_PAUSE_CAP) && (local & ADVERTISE_PAUSE_ASYM)){ if ((remote & LPA_PAUSE_ASYM) && !(remote & LPA_PAUSE_CAP)) pause_enab |= B44_FLAG_RX_PAUSE; } __b44_set_flow_ctrl(bp, pause_enab);} static int b44_setup_phy(struct b44 *bp){ u32 val; int err; if ((err = b44_readphy(bp, B44_MII_ALEDCTRL, &val)) != 0) goto out; if ((err = b44_writephy(bp, B44_MII_ALEDCTRL, val & MII_ALEDCTRL_ALLMSK)) != 0) goto out; if ((err = b44_readphy(bp, B44_MII_TLEDCTRL, &val)) != 0) goto out; if ((err = b44_writephy(bp, B44_MII_TLEDCTRL, val | MII_TLEDCTRL_ENABLE)) != 0) goto out; if (!(bp->flags & B44_FLAG_FORCE_LINK)) { u32 adv = ADVERTISE_CSMA; if (bp->flags & B44_FLAG_ADV_10HALF) adv |= ADVERTISE_10HALF; if (bp->flags & B44_FLAG_ADV_10FULL) adv |= ADVERTISE_10FULL; if (bp->flags & B44_FLAG_ADV_100HALF) adv |= ADVERTISE_100HALF; if (bp->flags & B44_FLAG_ADV_100FULL) adv |= ADVERTISE_100FULL; if (bp->flags & B44_FLAG_PAUSE_AUTO) adv |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; if ((err = b44_writephy(bp, MII_ADVERTISE, adv)) != 0) goto out; if ((err = b44_writephy(bp, MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART))) != 0) goto out; } else { u32 bmcr; if ((err = b44_readphy(bp, MII_BMCR, &bmcr)) != 0) goto out; bmcr &= ~(BMCR_FULLDPLX | BMCR_ANENABLE | BMCR_SPEED100); if (bp->flags & B44_FLAG_100_BASE_T) bmcr |= BMCR_SPEED100; if (bp->flags & B44_FLAG_FULL_DUPLEX) bmcr |= BMCR_FULLDPLX; if ((err = b44_writephy(bp, MII_BMCR, bmcr)) != 0) goto out; /* Since we will not be negotiating there is no safe way * to determine if the link partner supports flow control * or not. So just disable it completely in this case. */ b44_set_flow_ctrl(bp, 0, 0); } out:Lines 1–500 of 2386. Load more
/* b44.c: Broadcom 4400 device driver. * * Copyright (C) 2002 David S. Miller (davem@redhat.com) * Fixed by Pekka Pietikainen (pp@ee.oulu.fi) * Copyright (C) 2006 Broadcom Corporation. * * Distribute under GPL. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> #include <linux/netdevice.h> #include <linux/ethtool.h> #include <linux/mii.h> #include <linux/if_ether.h> #include <linux/etherdevice.h> #include <linux/pci.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/dma-mapping.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/irq.h> #include "b44.h" #define DRV_MODULE_NAME "b44" #define PFX DRV_MODULE_NAME ": " #define DRV_MODULE_VERSION "1.01" #define DRV_MODULE_RELDATE "Jun 16, 2006" #define B44_DEF_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ NETIF_MSG_LINK | \ NETIF_MSG_TIMER | \ NETIF_MSG_IFDOWN | \ NETIF_MSG_IFUP | \ NETIF_MSG_RX_ERR | \ NETIF_MSG_TX_ERR) /* length of time before we decide the hardware is borked, * and dev->tx_timeout() should be called to fix the problem */ #define B44_TX_TIMEOUT (5 * HZ) /* hardware minimum and maximum for a single frame's data payload */ #define B44_MIN_MTU 60 #define B44_MAX_MTU 1500 #define B44_RX_RING_SIZE 512 #define B44_DEF_RX_RING_PENDING 200 #define B44_RX_RING_BYTES (sizeof(struct dma_desc) * \ B44_RX_RING_SIZE) #define B44_TX_RING_SIZE 512 #define B44_DEF_TX_RING_PENDING (B44_TX_RING_SIZE - 1) #define B44_TX_RING_BYTES (sizeof(struct dma_desc) * \ B44_TX_RING_SIZE) #define TX_RING_GAP(BP) \ (B44_TX_RING_SIZE - (BP)->tx_pending) #define TX_BUFFS_AVAIL(BP) \ (((BP)->tx_cons <= (BP)->tx_prod) ? \ (BP)->tx_cons + (BP)->tx_pending - (BP)->tx_prod : \ (BP)->tx_cons - (BP)->tx_prod - TX_RING_GAP(BP)) #define NEXT_TX(N) (((N) + 1) & (B44_TX_RING_SIZE - 1)) #define RX_PKT_BUF_SZ (1536 + bp->rx_offset + 64) #define TX_PKT_BUF_SZ (B44_MAX_MTU + ETH_HLEN + 8) /* minimum number of free TX descriptors required to wake up TX process */ #define B44_TX_WAKEUP_THRESH (B44_TX_RING_SIZE / 4) /* b44 internal pattern match filter info */ #define B44_PATTERN_BASE 0x400 #define B44_PATTERN_SIZE 0x80 #define B44_PMASK_BASE 0x600 #define B44_PMASK_SIZE 0x10 #define B44_MAX_PATTERNS 16 #define B44_ETHIPV6UDP_HLEN 62 #define B44_ETHIPV4UDP_HLEN 42 static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Florian Schirmer, Pekka Pietikainen, David S. Miller"); MODULE_DESCRIPTION("Broadcom 4400 10/100 PCI ethernet driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); static int b44_debug = -1; /* -1 == use B44_DEF_MSG_ENABLE as value */ module_param(b44_debug, int, 0); MODULE_PARM_DESC(b44_debug, "B44 bitmapped debugging message enable value"); static struct pci_device_id b44_pci_tbl[] = { { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { } /* terminate list with empty entry */ }; MODULE_DEVICE_TABLE(pci, b44_pci_tbl); static void b44_halt(struct b44 *); static void b44_init_rings(struct b44 *); #define B44_FULL_RESET 1 #define B44_FULL_RESET_SKIP_PHY 2 #define B44_PARTIAL_RESET 3 static void b44_init_hw(struct b44 *, int); static int dma_desc_align_mask; static int dma_desc_sync_size; static const char b44_gstrings[][ETH_GSTRING_LEN] = { #define _B44(x...) # x, B44_STAT_REG_DECLARE #undef _B44 }; static inline void b44_sync_dma_desc_for_device(struct pci_dev *pdev, dma_addr_t dma_base, unsigned long offset, enum dma_data_direction dir) { dma_sync_single_range_for_device(&pdev->dev, dma_base, offset & dma_desc_align_mask, dma_desc_sync_size, dir); } static inline void b44_sync_dma_desc_for_cpu(struct pci_dev *pdev, dma_addr_t dma_base, unsigned long offset, enum dma_data_direction dir) { dma_sync_single_range_for_cpu(&pdev->dev, dma_base, offset & dma_desc_align_mask, dma_desc_sync_size, dir); } static inline unsigned long br32(const struct b44 *bp, unsigned long reg) { return readl(bp->regs + reg); } static inline void bw32(const struct b44 *bp, unsigned long reg, unsigned long val) { writel(val, bp->regs + reg); } static int b44_wait_bit(struct b44 *bp, unsigned long reg, u32 bit, unsigned long timeout, const int clear) { unsigned long i; for (i = 0; i < timeout; i++) { u32 val = br32(bp, reg); if (clear && !(val & bit)) break; if (!clear && (val & bit)) break; udelay(10); } if (i == timeout) { printk(KERN_ERR PFX "%s: BUG! Timeout waiting for bit %08x of register " "%lx to %s.\n", bp->dev->name, bit, reg, (clear ? "clear" : "set")); return -ENODEV; } return 0; } /* Sonics SiliconBackplane support routines. ROFL, you should see all the * buzz words used on this company's website :-) * * All of these routines must be invoked with bp->lock held and * interrupts disabled. */ #define SB_PCI_DMA 0x40000000 /* Client Mode PCI memory access space (1 GB) */ #define BCM4400_PCI_CORE_ADDR 0x18002000 /* Address of PCI core on BCM4400 cards */ static u32 ssb_get_core_rev(struct b44 *bp) { return (br32(bp, B44_SBIDHIGH) & SBIDHIGH_RC_MASK); } static u32 ssb_pci_setup(struct b44 *bp, u32 cores) { u32 bar_orig, pci_rev, val; pci_read_config_dword(bp->pdev, SSB_BAR0_WIN, &bar_orig); pci_write_config_dword(bp->pdev, SSB_BAR0_WIN, BCM4400_PCI_CORE_ADDR); pci_rev = ssb_get_core_rev(bp); val = br32(bp, B44_SBINTVEC); val |= cores; bw32(bp, B44_SBINTVEC, val); val = br32(bp, SSB_PCI_TRANS_2); val |= SSB_PCI_PREF | SSB_PCI_BURST; bw32(bp, SSB_PCI_TRANS_2, val); pci_write_config_dword(bp->pdev, SSB_BAR0_WIN, bar_orig); return pci_rev; } static void ssb_core_disable(struct b44 *bp) { if (br32(bp, B44_SBTMSLOW) & SBTMSLOW_RESET) return; bw32(bp, B44_SBTMSLOW, (SBTMSLOW_REJECT | SBTMSLOW_CLOCK)); b44_wait_bit(bp, B44_SBTMSLOW, SBTMSLOW_REJECT, 100000, 0); b44_wait_bit(bp, B44_SBTMSHIGH, SBTMSHIGH_BUSY, 100000, 1); bw32(bp, B44_SBTMSLOW, (SBTMSLOW_FGC | SBTMSLOW_CLOCK | SBTMSLOW_REJECT | SBTMSLOW_RESET)); br32(bp, B44_SBTMSLOW); udelay(1); bw32(bp, B44_SBTMSLOW, (SBTMSLOW_REJECT | SBTMSLOW_RESET)); br32(bp, B44_SBTMSLOW); udelay(1); } static void ssb_core_reset(struct b44 *bp) { u32 val; ssb_core_disable(bp); bw32(bp, B44_SBTMSLOW, (SBTMSLOW_RESET | SBTMSLOW_CLOCK | SBTMSLOW_FGC)); br32(bp, B44_SBTMSLOW); udelay(1); /* Clear SERR if set, this is a hw bug workaround. */ if (br32(bp, B44_SBTMSHIGH) & SBTMSHIGH_SERR) bw32(bp, B44_SBTMSHIGH, 0); val = br32(bp, B44_SBIMSTATE); if (val & (SBIMSTATE_IBE | SBIMSTATE_TO)) bw32(bp, B44_SBIMSTATE, val & ~(SBIMSTATE_IBE | SBIMSTATE_TO)); bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK | SBTMSLOW_FGC)); br32(bp, B44_SBTMSLOW); udelay(1); bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK)); br32(bp, B44_SBTMSLOW); udelay(1); } static int ssb_core_unit(struct b44 *bp) { #if 0 u32 val = br32(bp, B44_SBADMATCH0); u32 base; type = val & SBADMATCH0_TYPE_MASK; switch (type) { case 0: base = val & SBADMATCH0_BS0_MASK; break; case 1: base = val & SBADMATCH0_BS1_MASK; break; case 2: default: base = val & SBADMATCH0_BS2_MASK; break; }; #endif return 0; } static int ssb_is_core_up(struct b44 *bp) { return ((br32(bp, B44_SBTMSLOW) & (SBTMSLOW_RESET | SBTMSLOW_REJECT | SBTMSLOW_CLOCK)) == SBTMSLOW_CLOCK); } static void __b44_cam_write(struct b44 *bp, unsigned char *data, int index) { u32 val; val = ((u32) data[2]) << 24; val |= ((u32) data[3]) << 16; val |= ((u32) data[4]) << 8; val |= ((u32) data[5]) << 0; bw32(bp, B44_CAM_DATA_LO, val); val = (CAM_DATA_HI_VALID | (((u32) data[0]) << 8) | (((u32) data[1]) << 0)); bw32(bp, B44_CAM_DATA_HI, val); bw32(bp, B44_CAM_CTRL, (CAM_CTRL_WRITE | (index << CAM_CTRL_INDEX_SHIFT))); b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1); } static inline void __b44_disable_ints(struct b44 *bp) { bw32(bp, B44_IMASK, 0); } static void b44_disable_ints(struct b44 *bp) { __b44_disable_ints(bp); /* Flush posted writes. */ br32(bp, B44_IMASK); } static void b44_enable_ints(struct b44 *bp) { bw32(bp, B44_IMASK, bp->imask); } static int b44_readphy(struct b44 *bp, int reg, u32 *val) { int err; bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII); bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | (MDIO_OP_READ << MDIO_DATA_OP_SHIFT) | (bp->phy_addr << MDIO_DATA_PMD_SHIFT) | (reg << MDIO_DATA_RA_SHIFT) | (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT))); err = b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0); *val = br32(bp, B44_MDIO_DATA) & MDIO_DATA_DATA; return err; } static int b44_writephy(struct b44 *bp, int reg, u32 val) { bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII); bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | (MDIO_OP_WRITE << MDIO_DATA_OP_SHIFT) | (bp->phy_addr << MDIO_DATA_PMD_SHIFT) | (reg << MDIO_DATA_RA_SHIFT) | (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT) | (val & MDIO_DATA_DATA))); return b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0); } /* miilib interface */ /* FIXME FIXME: phy_id is ignored, bp->phy_addr use is unconditional * due to code existing before miilib use was added to this driver. * Someone should remove this artificial driver limitation in * b44_{read,write}phy. bp->phy_addr itself is fine (and needed). */ static int b44_mii_read(struct net_device *dev, int phy_id, int location) { u32 val; struct b44 *bp = netdev_priv(dev); int rc = b44_readphy(bp, location, &val); if (rc) return 0xffffffff; return val; } static void b44_mii_write(struct net_device *dev, int phy_id, int location, int val) { struct b44 *bp = netdev_priv(dev); b44_writephy(bp, location, val); } static int b44_phy_reset(struct b44 *bp) { u32 val; int err; err = b44_writephy(bp, MII_BMCR, BMCR_RESET); if (err) return err; udelay(100); err = b44_readphy(bp, MII_BMCR, &val); if (!err) { if (val & BMCR_RESET) { printk(KERN_ERR PFX "%s: PHY Reset would not complete.\n", bp->dev->name); err = -ENODEV; } } return 0; } static void __b44_set_flow_ctrl(struct b44 *bp, u32 pause_flags) { u32 val; bp->flags &= ~(B44_FLAG_TX_PAUSE | B44_FLAG_RX_PAUSE); bp->flags |= pause_flags; val = br32(bp, B44_RXCONFIG); if (pause_flags & B44_FLAG_RX_PAUSE) val |= RXCONFIG_FLOW; else val &= ~RXCONFIG_FLOW; bw32(bp, B44_RXCONFIG, val); val = br32(bp, B44_MAC_FLOW); if (pause_flags & B44_FLAG_TX_PAUSE) val |= (MAC_FLOW_PAUSE_ENAB | (0xc0 & MAC_FLOW_RX_HI_WATER)); else val &= ~MAC_FLOW_PAUSE_ENAB; bw32(bp, B44_MAC_FLOW, val); } static void b44_set_flow_ctrl(struct b44 *bp, u32 local, u32 remote) { u32 pause_enab = 0; /* The driver supports only rx pause by default because the b44 mac tx pause mechanism generates excessive pause frames. Use ethtool to turn on b44 tx pause if necessary. */ if ((local & ADVERTISE_PAUSE_CAP) && (local & ADVERTISE_PAUSE_ASYM)){ if ((remote & LPA_PAUSE_ASYM) && !(remote & LPA_PAUSE_CAP)) pause_enab |= B44_FLAG_RX_PAUSE; } __b44_set_flow_ctrl(bp, pause_enab); } static int b44_setup_phy(struct b44 *bp) { u32 val; int err; if ((err = b44_readphy(bp, B44_MII_ALEDCTRL, &val)) != 0) goto out; if ((err = b44_writephy(bp, B44_MII_ALEDCTRL, val & MII_ALEDCTRL_ALLMSK)) != 0) goto out; if ((err = b44_readphy(bp, B44_MII_TLEDCTRL, &val)) != 0) goto out; if ((err = b44_writephy(bp, B44_MII_TLEDCTRL, val | MII_TLEDCTRL_ENABLE)) != 0) goto out; if (!(bp->flags & B44_FLAG_FORCE_LINK)) { u32 adv = ADVERTISE_CSMA; if (bp->flags & B44_FLAG_ADV_10HALF) adv |= ADVERTISE_10HALF; if (bp->flags & B44_FLAG_ADV_10FULL) adv |= ADVERTISE_10FULL; if (bp->flags & B44_FLAG_ADV_100HALF) adv |= ADVERTISE_100HALF; if (bp->flags & B44_FLAG_ADV_100FULL) adv |= ADVERTISE_100FULL; if (bp->flags & B44_FLAG_PAUSE_AUTO) adv |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; if ((err = b44_writephy(bp, MII_ADVERTISE, adv)) != 0) goto out; if ((err = b44_writephy(bp, MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART))) != 0) goto out; } else { u32 bmcr; if ((err = b44_readphy(bp, MII_BMCR, &bmcr)) != 0) goto out; bmcr &= ~(BMCR_FULLDPLX | BMCR_ANENABLE | BMCR_SPEED100); if (bp->flags & B44_FLAG_100_BASE_T) bmcr |= BMCR_SPEED100; if (bp->flags & B44_FLAG_FULL_DUPLEX) bmcr |= BMCR_FULLDPLX; if ((err = b44_writephy(bp, MII_BMCR, bmcr)) != 0) goto out; /* Since we will not be negotiating there is no safe way * to determine if the link partner supports flow control * or not. So just disable it completely in this case. */ b44_set_flow_ctrl(bp, 0, 0); } out: