/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/gmac_enable */ static void bgmac_enable(struct bgmac *bgmac) { u32 cmdcfg_sr; u32 cmdcfg; u32 mode; if (bgmac->feature_flags & BGMAC_FEAT_CMDCFG_SR_REV4) cmdcfg_sr = BGMAC_CMDCFG_SR_REV4; else cmdcfg_sr = BGMAC_CMDCFG_SR_REV0; cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG); bgmac_cmdcfg_maskset(bgmac, ~(BGMAC_CMDCFG_TE | BGMAC_CMDCFG_RE), cmdcfg_sr, true); udelay(2); cmdcfg |= BGMAC_CMDCFG_TE | BGMAC_CMDCFG_RE; bgmac_write(bgmac, BGMAC_CMDCFG, cmdcfg); mode = (bgmac_read(bgmac, BGMAC_DEV_STATUS) & BGMAC_DS_MM_MASK) >> BGMAC_DS_MM_SHIFT; if (bgmac->feature_flags & BGMAC_FEAT_CLKCTLST || mode != 0) bgmac_set(bgmac, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT); if (!(bgmac->feature_flags & BGMAC_FEAT_CLKCTLST) && mode == 2) bgmac_cco_ctl_maskset(bgmac, 1, ~0, BGMAC_CHIPCTL_1_RXC_DLL_BYPASS); if (bgmac->feature_flags & (BGMAC_FEAT_FLW_CTRL1 | BGMAC_FEAT_FLW_CTRL2)) { u32 fl_ctl; if (bgmac->feature_flags & BGMAC_FEAT_FLW_CTRL1) fl_ctl = 0x2300e1; else fl_ctl = 0x03cb04cb; bgmac_write(bgmac, BGMAC_FLOW_CTL_THRESH, fl_ctl); bgmac_write(bgmac, BGMAC_PAUSE_CTL, 0x27fff); } if (bgmac->feature_flags & BGMAC_FEAT_SET_RXQ_CLK) { u32 rxq_ctl; u16 bp_clk; u8 mdp; rxq_ctl = bgmac_read(bgmac, BGMAC_RXQ_CTL); rxq_ctl &= ~BGMAC_RXQ_CTL_MDP_MASK; bp_clk = bgmac_get_bus_clock(bgmac) / 1000000; mdp = (bp_clk * 128 / 1000) - 3; rxq_ctl |= (mdp << BGMAC_RXQ_CTL_MDP_SHIFT); bgmac_write(bgmac, BGMAC_RXQ_CTL, rxq_ctl); } }
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/gmac_enable */ static void bgmac_enable(struct bgmac *bgmac) { struct bcma_chipinfo *ci = &bgmac->core->bus->chipinfo; u32 cmdcfg; u32 mode; u32 rxq_ctl; u32 fl_ctl; u16 bp_clk; u8 mdp; cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG); bgmac_cmdcfg_maskset(bgmac, ~(BGMAC_CMDCFG_TE | BGMAC_CMDCFG_RE), BGMAC_CMDCFG_SR(bgmac->core->id.rev), true); udelay(2); cmdcfg |= BGMAC_CMDCFG_TE | BGMAC_CMDCFG_RE; bgmac_write(bgmac, BGMAC_CMDCFG, cmdcfg); mode = (bgmac_read(bgmac, BGMAC_DEV_STATUS) & BGMAC_DS_MM_MASK) >> BGMAC_DS_MM_SHIFT; if (ci->id != BCMA_CHIP_ID_BCM47162 || mode != 0) bgmac_set(bgmac, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT); if (ci->id == BCMA_CHIP_ID_BCM47162 && mode == 2) bcma_chipco_chipctl_maskset(&bgmac->core->bus->drv_cc, 1, ~0, BGMAC_CHIPCTL_1_RXC_DLL_BYPASS); switch (ci->id) { case BCMA_CHIP_ID_BCM5357: case BCMA_CHIP_ID_BCM4749: case BCMA_CHIP_ID_BCM53572: case BCMA_CHIP_ID_BCM4716: case BCMA_CHIP_ID_BCM47162: fl_ctl = 0x03cb04cb; if (ci->id == BCMA_CHIP_ID_BCM5357 || ci->id == BCMA_CHIP_ID_BCM4749 || ci->id == BCMA_CHIP_ID_BCM53572) fl_ctl = 0x2300e1; bgmac_write(bgmac, BGMAC_FLOW_CTL_THRESH, fl_ctl); bgmac_write(bgmac, BGMAC_PAUSE_CTL, 0x27fff); break; } if (ci->id != BCMA_CHIP_ID_BCM4707 && ci->id != BCMA_CHIP_ID_BCM53018) { rxq_ctl = bgmac_read(bgmac, BGMAC_RXQ_CTL); rxq_ctl &= ~BGMAC_RXQ_CTL_MDP_MASK; bp_clk = bcma_pmu_get_bus_clock(&bgmac->core->bus->drv_cc) / 1000000; mdp = (bp_clk * 128 / 1000) - 3; rxq_ctl |= (mdp << BGMAC_RXQ_CTL_MDP_SHIFT); bgmac_write(bgmac, BGMAC_RXQ_CTL, rxq_ctl); } }
static void bgmac_clear_mib(struct bgmac *bgmac) { int i; if (bgmac->feature_flags & BGMAC_FEAT_NO_CLR_MIB) return; bgmac_set(bgmac, BGMAC_DEV_CTL, BGMAC_DC_MROR); for (i = 0; i < BGMAC_NUM_MIB_TX_REGS; i++) bgmac_read(bgmac, BGMAC_TX_GOOD_OCTETS + (i * 4)); for (i = 0; i < BGMAC_NUM_MIB_RX_REGS; i++) bgmac_read(bgmac, BGMAC_RX_GOOD_OCTETS + (i * 4)); }
static void bgmac_clear_mib(struct bgmac *bgmac) { int i; if (bgmac->core->id.id == BCMA_CORE_4706_MAC_GBIT) return; bgmac_set(bgmac, BGMAC_DEV_CTL, BGMAC_DC_MROR); for (i = 0; i < BGMAC_NUM_MIB_TX_REGS; i++) bgmac_read(bgmac, BGMAC_TX_GOOD_OCTETS + (i * 4)); for (i = 0; i < BGMAC_NUM_MIB_RX_REGS; i++) bgmac_read(bgmac, BGMAC_RX_GOOD_OCTETS + (i * 4)); }
static void bgmac_dma_rx_enable(struct bgmac *bgmac, struct bgmac_dma_ring *ring) { u32 ctl; ctl = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_RX_CTL); /* preserve ONLY bits 16-17 from current hardware value */ ctl &= BGMAC_DMA_RX_ADDREXT_MASK; if (bgmac->feature_flags & BGMAC_FEAT_RX_MASK_SETUP) { ctl &= ~BGMAC_DMA_RX_BL_MASK; ctl |= BGMAC_DMA_RX_BL_128 << BGMAC_DMA_RX_BL_SHIFT; ctl &= ~BGMAC_DMA_RX_PC_MASK; ctl |= BGMAC_DMA_RX_PC_8 << BGMAC_DMA_RX_PC_SHIFT; ctl &= ~BGMAC_DMA_RX_PT_MASK; ctl |= BGMAC_DMA_RX_PT_1 << BGMAC_DMA_RX_PT_SHIFT; } ctl |= BGMAC_DMA_RX_ENABLE; ctl |= BGMAC_DMA_RX_PARITY_DISABLE; ctl |= BGMAC_DMA_RX_OVERFLOW_CONT; ctl |= BGMAC_RX_FRAME_OFFSET << BGMAC_DMA_RX_FRAME_OFFSET_SHIFT; bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_CTL, ctl); }
/* TODO: can we just drop @force? Can we don't reset MAC at all if there is * nothing to change? Try if after stabilizng driver. */ static void bgmac_cmdcfg_maskset(struct bgmac *bgmac, u32 mask, u32 set, bool force) { u32 cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG); u32 new_val = (cmdcfg & mask) | set; bgmac_set(bgmac, BGMAC_CMDCFG, BGMAC_CMDCFG_SR(bgmac->core->id.rev)); udelay(2); if (new_val != cmdcfg || force) bgmac_write(bgmac, BGMAC_CMDCFG, new_val); bgmac_mask(bgmac, BGMAC_CMDCFG, ~BGMAC_CMDCFG_SR(bgmac->core->id.rev)); udelay(2); } static void bgmac_write_mac_address(struct bgmac *bgmac, u8 *addr) { u32 tmp; tmp = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; bgmac_write(bgmac, BGMAC_MACADDR_HIGH, tmp); tmp = (addr[4] << 8) | addr[5]; bgmac_write(bgmac, BGMAC_MACADDR_LOW, tmp); } static void bgmac_set_rx_mode(struct net_device *net_dev) { struct bgmac *bgmac = netdev_priv(net_dev); if (net_dev->flags & IFF_PROMISC) bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_PROM, true); else bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_PROM, 0, true); } #if 0 /* We don't use that regs yet */ static void bgmac_chip_stats_update(struct bgmac *bgmac) { int i; if (bgmac->core->id.id != BCMA_CORE_4706_MAC_GBIT) { for (i = 0; i < BGMAC_NUM_MIB_TX_REGS; i++) bgmac->mib_tx_regs[i] = bgmac_read(bgmac, BGMAC_TX_GOOD_OCTETS + (i * 4)); for (i = 0; i < BGMAC_NUM_MIB_RX_REGS; i++) bgmac->mib_rx_regs[i] = bgmac_read(bgmac, BGMAC_RX_GOOD_OCTETS + (i * 4)); } /* TODO: what else? how to handle BCM4706? Specs are needed */ }
/* TODO: can we just drop @force? Can we don't reset MAC at all if there is * nothing to change? Try if after stabilizng driver. */ static void bgmac_cmdcfg_maskset(struct bgmac *bgmac, u32 mask, u32 set, bool force) { u32 cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG); u32 new_val = (cmdcfg & mask) | set; u32 cmdcfg_sr; if (bgmac->feature_flags & BGMAC_FEAT_CMDCFG_SR_REV4) cmdcfg_sr = BGMAC_CMDCFG_SR_REV4; else cmdcfg_sr = BGMAC_CMDCFG_SR_REV0; bgmac_set(bgmac, BGMAC_CMDCFG, cmdcfg_sr); udelay(2); if (new_val != cmdcfg || force) bgmac_write(bgmac, BGMAC_CMDCFG, new_val); bgmac_mask(bgmac, BGMAC_CMDCFG, ~cmdcfg_sr); udelay(2); } static void bgmac_write_mac_address(struct bgmac *bgmac, u8 *addr) { u32 tmp; tmp = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; bgmac_write(bgmac, BGMAC_MACADDR_HIGH, tmp); tmp = (addr[4] << 8) | addr[5]; bgmac_write(bgmac, BGMAC_MACADDR_LOW, tmp); } static void bgmac_set_rx_mode(struct net_device *net_dev) { struct bgmac *bgmac = netdev_priv(net_dev); if (net_dev->flags & IFF_PROMISC) bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_PROM, true); else bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_PROM, 0, true); } #if 0 /* We don't use that regs yet */ static void bgmac_chip_stats_update(struct bgmac *bgmac) { int i; if (!(bgmac->feature_flags & BGMAC_FEAT_NO_CLR_MIB)) { for (i = 0; i < BGMAC_NUM_MIB_TX_REGS; i++) bgmac->mib_tx_regs[i] = bgmac_read(bgmac, BGMAC_TX_GOOD_OCTETS + (i * 4)); for (i = 0; i < BGMAC_NUM_MIB_RX_REGS; i++) bgmac->mib_rx_regs[i] = bgmac_read(bgmac, BGMAC_RX_GOOD_OCTETS + (i * 4)); } /* TODO: what else? how to handle BCM4706? Specs are needed */ }
/* Free transmitted packets */ static void bgmac_dma_tx_free(struct bgmac *bgmac, struct bgmac_dma_ring *ring) { struct device *dma_dev = bgmac->dma_dev; int empty_slot; bool freed = false; unsigned bytes_compl = 0, pkts_compl = 0; /* The last slot that hardware didn't consume yet */ empty_slot = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_TX_STATUS); empty_slot &= BGMAC_DMA_TX_STATDPTR; empty_slot -= ring->index_base; empty_slot &= BGMAC_DMA_TX_STATDPTR; empty_slot /= sizeof(struct bgmac_dma_desc); while (ring->start != ring->end) { int slot_idx = ring->start % BGMAC_TX_RING_SLOTS; struct bgmac_slot_info *slot = &ring->slots[slot_idx]; u32 ctl0, ctl1; int len; if (slot_idx == empty_slot) break; ctl0 = le32_to_cpu(ring->cpu_base[slot_idx].ctl0); ctl1 = le32_to_cpu(ring->cpu_base[slot_idx].ctl1); len = ctl1 & BGMAC_DESC_CTL1_LEN; if (ctl0 & BGMAC_DESC_CTL0_SOF) /* Unmap no longer used buffer */ dma_unmap_single(dma_dev, slot->dma_addr, len, DMA_TO_DEVICE); else dma_unmap_page(dma_dev, slot->dma_addr, len, DMA_TO_DEVICE); if (slot->skb) { bgmac->net_dev->stats.tx_bytes += slot->skb->len; bgmac->net_dev->stats.tx_packets++; bytes_compl += slot->skb->len; pkts_compl++; /* Free memory! :) */ dev_kfree_skb(slot->skb); slot->skb = NULL; } slot->dma_addr = 0; ring->start++; freed = true; } if (!pkts_compl) return; netdev_completed_queue(bgmac->net_dev, pkts_compl, bytes_compl); if (netif_queue_stopped(bgmac->net_dev)) netif_wake_queue(bgmac->net_dev); }
static void bgmac_dma_tx_reset(struct bgmac *bgmac, struct bgmac_dma_ring *ring) { u32 val; int i; if (!ring->mmio_base) return; /* Suspend DMA TX ring first. * bgmac_wait_value doesn't support waiting for any of few values, so * implement whole loop here. */ bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_CTL, BGMAC_DMA_TX_SUSPEND); for (i = 0; i < 10000 / 10; i++) { val = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_TX_STATUS); val &= BGMAC_DMA_TX_STAT; if (val == BGMAC_DMA_TX_STAT_DISABLED || val == BGMAC_DMA_TX_STAT_IDLEWAIT || val == BGMAC_DMA_TX_STAT_STOPPED) { i = 0; break; } udelay(10); } if (i) bgmac_err(bgmac, "Timeout suspending DMA TX ring 0x%X (BGMAC_DMA_TX_STAT: 0x%08X)\n", ring->mmio_base, val); /* Remove SUSPEND bit */ bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_CTL, 0); if (!bgmac_wait_value(bgmac->core, ring->mmio_base + BGMAC_DMA_TX_STATUS, BGMAC_DMA_TX_STAT, BGMAC_DMA_TX_STAT_DISABLED, 10000)) { bgmac_warn(bgmac, "DMA TX ring 0x%X wasn't disabled on time, waiting additional 300us\n", ring->mmio_base); udelay(300); val = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_TX_STATUS); if ((val & BGMAC_DMA_TX_STAT) != BGMAC_DMA_TX_STAT_DISABLED) bgmac_err(bgmac, "Reset of DMA TX ring 0x%X failed\n", ring->mmio_base); } }
static void bgmac_dma_tx_enable(struct bgmac *bgmac, struct bgmac_dma_ring *ring) { u32 ctl; ctl = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_TX_CTL); ctl |= BGMAC_DMA_TX_ENABLE; ctl |= BGMAC_DMA_TX_PARITY_DISABLE; bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_CTL, ctl); }
static void bgmac_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *ss, uint64_t *data) { struct bgmac *bgmac = netdev_priv(dev); const struct bgmac_stat *s; unsigned int i; u64 val; if (!netif_running(dev)) return; for (i = 0; i < BGMAC_STATS_LEN; i++) { s = &bgmac_get_strings_stats[i]; val = 0; if (s->size == 8) val = (u64)bgmac_read(bgmac, s->offset + 4) << 32; val |= bgmac_read(bgmac, s->offset); data[i] = val; } }
/* Does ring support unaligned addressing? */ static bool bgmac_dma_unaligned(struct bgmac *bgmac, struct bgmac_dma_ring *ring, enum bgmac_dma_ring_type ring_type) { switch (ring_type) { case BGMAC_DMA_RING_TX: bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_RINGLO, 0xff0); if (bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_TX_RINGLO)) return true; break; case BGMAC_DMA_RING_RX: bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_RINGLO, 0xff0); if (bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_RX_RINGLO)) return true; break; } return false; }
static void bgmac_dma_rx_enable(struct bgmac *bgmac, struct bgmac_dma_ring *ring) { u32 ctl; ctl = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_RX_CTL); ctl &= BGMAC_DMA_RX_ADDREXT_MASK; ctl |= BGMAC_DMA_RX_ENABLE; ctl |= BGMAC_DMA_RX_PARITY_DISABLE; ctl |= BGMAC_DMA_RX_OVERFLOW_CONT; ctl |= BGMAC_RX_FRAME_OFFSET << BGMAC_DMA_RX_FRAME_OFFSET_SHIFT; bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_CTL, ctl); }
/* TODO: can we just drop @force? Can we don't reset MAC at all if there is * nothing to change? Try if after stabilizng driver. */ static void bgmac_cmdcfg_maskset(struct bgmac *bgmac, u32 mask, u32 set, bool force) { u32 cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG); u32 new_val = (cmdcfg & mask) | set; bgmac_set(bgmac, BGMAC_CMDCFG, BGMAC_CMDCFG_SR(bgmac->core->id.rev)); udelay(2); if (new_val != cmdcfg || force) bgmac_write(bgmac, BGMAC_CMDCFG, new_val); bgmac_mask(bgmac, BGMAC_CMDCFG, ~BGMAC_CMDCFG_SR(bgmac->core->id.rev)); udelay(2); }
static bool bgmac_wait_value(struct bgmac *bgmac, u16 reg, u32 mask, u32 value, int timeout) { u32 val; int i; for (i = 0; i < timeout / 10; i++) { val = bgmac_read(bgmac, reg); if ((val & mask) == value) return true; udelay(10); } dev_err(bgmac->dev, "Timeout waiting for reg 0x%X\n", reg); return false; }
/* Free transmitted packets */ static void bgmac_dma_tx_free(struct bgmac *bgmac, struct bgmac_dma_ring *ring) { struct device *dma_dev = bgmac->core->dma_dev; int empty_slot; bool freed = false; unsigned bytes_compl = 0, pkts_compl = 0; /* The last slot that hardware didn't consume yet */ empty_slot = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_TX_STATUS); empty_slot &= BGMAC_DMA_TX_STATDPTR; empty_slot -= ring->index_base; empty_slot &= BGMAC_DMA_TX_STATDPTR; empty_slot /= sizeof(struct bgmac_dma_desc); while (ring->start != empty_slot) { struct bgmac_slot_info *slot = &ring->slots[ring->start]; if (slot->skb) { /* Unmap no longer used buffer */ dma_unmap_single(dma_dev, slot->dma_addr, slot->skb->len, DMA_TO_DEVICE); slot->dma_addr = 0; bytes_compl += slot->skb->len; pkts_compl++; /* Free memory! :) */ dev_kfree_skb(slot->skb); slot->skb = NULL; } else { bgmac_err(bgmac, "Hardware reported transmission for empty TX ring slot %d! End of ring: %d\n", ring->start, ring->end); } if (++ring->start >= BGMAC_TX_RING_SLOTS) ring->start = 0; freed = true; } netdev_completed_queue(bgmac->net_dev, pkts_compl, bytes_compl); if (freed && netif_queue_stopped(bgmac->net_dev)) netif_wake_queue(bgmac->net_dev); }
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphywr */ static int bgmac_phy_write(struct bgmac *bgmac, u8 phyaddr, u8 reg, u16 value) { struct bcma_device *core; u16 phy_access_addr; u16 phy_ctl_addr; u32 tmp; if (bgmac->core->id.id == BCMA_CORE_4706_MAC_GBIT) { core = bgmac->core->bus->drv_gmac_cmn.core; phy_access_addr = BCMA_GMAC_CMN_PHY_ACCESS; phy_ctl_addr = BCMA_GMAC_CMN_PHY_CTL; } else { core = bgmac->core; phy_access_addr = BGMAC_PHY_ACCESS; phy_ctl_addr = BGMAC_PHY_CNTL; } tmp = bcma_read32(core, phy_ctl_addr); tmp &= ~BGMAC_PC_EPA_MASK; tmp |= phyaddr; bcma_write32(core, phy_ctl_addr, tmp); bgmac_write(bgmac, BGMAC_INT_STATUS, BGMAC_IS_MDIO); if (bgmac_read(bgmac, BGMAC_INT_STATUS) & BGMAC_IS_MDIO) bgmac_warn(bgmac, "Error setting MDIO int\n"); tmp = BGMAC_PA_START; tmp |= BGMAC_PA_WRITE; tmp |= phyaddr << BGMAC_PA_ADDR_SHIFT; tmp |= reg << BGMAC_PA_REG_SHIFT; tmp |= value; bcma_write32(core, phy_access_addr, tmp); if (!bgmac_wait_value(core, phy_access_addr, BGMAC_PA_START, 0, 1000)) { bgmac_err(bgmac, "Writing to PHY %d register 0x%X failed\n", phyaddr, reg); return -ETIMEDOUT; } return 0; }
static irqreturn_t bgmac_interrupt(int irq, void *dev_id) { struct bgmac *bgmac = netdev_priv(dev_id); u32 int_status = bgmac_read(bgmac, BGMAC_INT_STATUS); int_status &= bgmac->int_mask; if (!int_status) return IRQ_NONE; int_status &= ~(BGMAC_IS_TX0 | BGMAC_IS_RX); if (int_status) bgmac_err(bgmac, "Unknown IRQs: 0x%08X\n", int_status); /* Disable new interrupts until handling existing ones */ bgmac_chip_intrs_off(bgmac); napi_schedule(&bgmac->napi); return IRQ_HANDLED; }
static void bgmac_miiconfig(struct bgmac *bgmac) { if (bgmac->feature_flags & BGMAC_FEAT_FORCE_SPEED_2500) { bgmac_idm_write(bgmac, BCMA_IOCTL, bgmac_idm_read(bgmac, BCMA_IOCTL) | 0x40 | BGMAC_BCMA_IOCTL_SW_CLKEN); bgmac->mac_speed = SPEED_2500; bgmac->mac_duplex = DUPLEX_FULL; bgmac_mac_speed(bgmac); } else { u8 imode; imode = (bgmac_read(bgmac, BGMAC_DEV_STATUS) & BGMAC_DS_MM_MASK) >> BGMAC_DS_MM_SHIFT; if (imode == 0 || imode == 1) { bgmac->mac_speed = SPEED_100; bgmac->mac_duplex = DUPLEX_FULL; bgmac_mac_speed(bgmac); } } }
/* TODO: can we just drop @force? Can we don't reset MAC at all if there is * nothing to change? Try if after stabilizng driver. */ static void bgmac_cmdcfg_maskset(struct bgmac *bgmac, u32 mask, u32 set, bool force) { u32 cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG); u32 new_val = (cmdcfg & mask) | set; u32 cmdcfg_sr; if (bgmac->feature_flags & BGMAC_FEAT_CMDCFG_SR_REV4) cmdcfg_sr = BGMAC_CMDCFG_SR_REV4; else cmdcfg_sr = BGMAC_CMDCFG_SR_REV0; bgmac_set(bgmac, BGMAC_CMDCFG, cmdcfg_sr); udelay(2); if (new_val != cmdcfg || force) bgmac_write(bgmac, BGMAC_CMDCFG, new_val); bgmac_mask(bgmac, BGMAC_CMDCFG, ~cmdcfg_sr); udelay(2); }
static int bgmac_poll(struct napi_struct *napi, int weight) { struct bgmac *bgmac = container_of(napi, struct bgmac, napi); int handled = 0; /* Ack */ bgmac_write(bgmac, BGMAC_INT_STATUS, ~0); bgmac_dma_tx_free(bgmac, &bgmac->tx_ring[0]); handled += bgmac_dma_rx_read(bgmac, &bgmac->rx_ring[0], weight); /* Poll again if more events arrived in the meantime */ if (bgmac_read(bgmac, BGMAC_INT_STATUS) & (BGMAC_IS_TX0 | BGMAC_IS_RX)) return weight; if (handled < weight) { napi_complete(napi); bgmac_chip_intrs_on(bgmac); } return handled; }
static void bgmac_miiconfig(struct bgmac *bgmac) { struct bcma_device *core = bgmac->core; u8 imode; if (bgmac_is_bcm4707_family(bgmac)) { bcma_awrite32(core, BCMA_IOCTL, bcma_aread32(core, BCMA_IOCTL) | 0x40 | BGMAC_BCMA_IOCTL_SW_CLKEN); bgmac->mac_speed = SPEED_2500; bgmac->mac_duplex = DUPLEX_FULL; bgmac_mac_speed(bgmac); } else { imode = (bgmac_read(bgmac, BGMAC_DEV_STATUS) & BGMAC_DS_MM_MASK) >> BGMAC_DS_MM_SHIFT; if (imode == 0 || imode == 1) { bgmac->mac_speed = SPEED_100; bgmac->mac_duplex = DUPLEX_FULL; bgmac_mac_speed(bgmac); } } }
static irqreturn_t bgmac_interrupt(int irq, void *dev_id) { struct bgmac *bgmac = netdev_priv(dev_id); u32 int_status = bgmac_read(bgmac, BGMAC_INT_STATUS); int_status &= bgmac->int_mask; if (!int_status) return IRQ_NONE; /* Ack */ bgmac_write(bgmac, BGMAC_INT_STATUS, int_status); /* Disable new interrupts until handling existing ones */ bgmac_chip_intrs_off(bgmac); bgmac->int_status = int_status; napi_schedule(&bgmac->napi); return IRQ_HANDLED; }
static void bgmac_dma_rx_enable(struct bgmac *bgmac, struct bgmac_dma_ring *ring) { u32 ctl; ctl = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_RX_CTL); if (bgmac->core->id.rev >= 4) { ctl &= ~BGMAC_DMA_RX_BL_MASK; ctl |= BGMAC_DMA_RX_BL_128 << BGMAC_DMA_RX_BL_SHIFT; ctl &= ~BGMAC_DMA_RX_PC_MASK; ctl |= BGMAC_DMA_RX_PC_8 << BGMAC_DMA_RX_PC_SHIFT; ctl &= ~BGMAC_DMA_RX_PT_MASK; ctl |= BGMAC_DMA_RX_PT_1 << BGMAC_DMA_RX_PT_SHIFT; } ctl &= BGMAC_DMA_RX_ADDREXT_MASK; ctl |= BGMAC_DMA_RX_ENABLE; ctl |= BGMAC_DMA_RX_PARITY_DISABLE; ctl |= BGMAC_DMA_RX_OVERFLOW_CONT; ctl |= BGMAC_RX_FRAME_OFFSET << BGMAC_DMA_RX_FRAME_OFFSET_SHIFT; bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_CTL, ctl); }
static void bgmac_dma_tx_enable(struct bgmac *bgmac, struct bgmac_dma_ring *ring) { u32 ctl; ctl = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_TX_CTL); if (bgmac->core->id.rev >= 4) { ctl &= ~BGMAC_DMA_TX_BL_MASK; ctl |= BGMAC_DMA_TX_BL_128 << BGMAC_DMA_TX_BL_SHIFT; ctl &= ~BGMAC_DMA_TX_MR_MASK; ctl |= BGMAC_DMA_TX_MR_2 << BGMAC_DMA_TX_MR_SHIFT; ctl &= ~BGMAC_DMA_TX_PC_MASK; ctl |= BGMAC_DMA_TX_PC_16 << BGMAC_DMA_TX_PC_SHIFT; ctl &= ~BGMAC_DMA_TX_PT_MASK; ctl |= BGMAC_DMA_TX_PT_8 << BGMAC_DMA_TX_PT_SHIFT; } ctl |= BGMAC_DMA_TX_ENABLE; ctl |= BGMAC_DMA_TX_PARITY_DISABLE; bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_CTL, ctl); }
static void bgmac_miiconfig(struct bgmac *bgmac) { struct bcma_device *core = bgmac->core; struct bcma_chipinfo *ci = &core->bus->chipinfo; u8 imode; if (ci->id == BCMA_CHIP_ID_BCM4707 || ci->id == BCMA_CHIP_ID_BCM53018) { bcma_awrite32(core, BCMA_IOCTL, bcma_aread32(core, BCMA_IOCTL) | 0x40 | BGMAC_BCMA_IOCTL_SW_CLKEN); bgmac->mac_speed = SPEED_2500; bgmac->mac_duplex = DUPLEX_FULL; bgmac_mac_speed(bgmac); } else { imode = (bgmac_read(bgmac, BGMAC_DEV_STATUS) & BGMAC_DS_MM_MASK) >> BGMAC_DS_MM_SHIFT; if (imode == 0 || imode == 1) { bgmac->mac_speed = SPEED_100; bgmac->mac_duplex = DUPLEX_FULL; bgmac_mac_speed(bgmac); } } }
static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring, int weight) { u32 end_slot; int handled = 0; end_slot = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_RX_STATUS); end_slot &= BGMAC_DMA_RX_STATDPTR; end_slot /= sizeof(struct bgmac_dma_desc); ring->end = end_slot; while (ring->start != ring->end) { struct device *dma_dev = bgmac->core->dma_dev; struct bgmac_slot_info *slot = &ring->slots[ring->start]; struct sk_buff *skb = slot->skb; struct sk_buff *new_skb; struct bgmac_rx_header *rx; u16 len, flags; /* Unmap buffer to make it accessible to the CPU */ dma_sync_single_for_cpu(dma_dev, slot->dma_addr, BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE); /* Get info from the header */ rx = (struct bgmac_rx_header *)skb->data; len = le16_to_cpu(rx->len); flags = le16_to_cpu(rx->flags); /* Check for poison and drop or pass the packet */ if (len == 0xdead && flags == 0xbeef) { bgmac_err(bgmac, "Found poisoned packet at slot %d, DMA issue!\n", ring->start); } else { /* Omit CRC. */ len -= ETH_FCS_LEN; new_skb = netdev_alloc_skb_ip_align(bgmac->net_dev, len); if (new_skb) { skb_put(new_skb, len); skb_copy_from_linear_data_offset(skb, BGMAC_RX_FRAME_OFFSET, new_skb->data, len); skb_checksum_none_assert(skb); new_skb->protocol = eth_type_trans(new_skb, bgmac->net_dev); netif_receive_skb(new_skb); handled++; } else { bgmac->net_dev->stats.rx_dropped++; bgmac_err(bgmac, "Allocation of skb for copying packet failed!\n"); } /* Poison the old skb */ rx->len = cpu_to_le16(0xdead); rx->flags = cpu_to_le16(0xbeef); } /* Make it back accessible to the hardware */ dma_sync_single_for_device(dma_dev, slot->dma_addr, BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE); if (++ring->start >= BGMAC_RX_RING_SLOTS) ring->start = 0; if (handled >= weight) /* Should never be greater */ break; } return handled; }
static void bgmac_chip_intrs_off(struct bgmac *bgmac) { bgmac_write(bgmac, BGMAC_INT_MASK, 0); bgmac_read(bgmac, BGMAC_INT_MASK); }
static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring, int weight) { u32 end_slot; int handled = 0; end_slot = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_RX_STATUS); end_slot &= BGMAC_DMA_RX_STATDPTR; end_slot -= ring->index_base; end_slot &= BGMAC_DMA_RX_STATDPTR; end_slot /= sizeof(struct bgmac_dma_desc); ring->end = end_slot; while (ring->start != ring->end) { struct device *dma_dev = bgmac->core->dma_dev; struct bgmac_slot_info *slot = &ring->slots[ring->start]; struct sk_buff *skb = slot->skb; struct bgmac_rx_header *rx; u16 len, flags; /* Unmap buffer to make it accessible to the CPU */ dma_sync_single_for_cpu(dma_dev, slot->dma_addr, BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE); /* Get info from the header */ rx = (struct bgmac_rx_header *)skb->data; len = le16_to_cpu(rx->len); flags = le16_to_cpu(rx->flags); do { dma_addr_t old_dma_addr = slot->dma_addr; int err; /* Check for poison and drop or pass the packet */ if (len == 0xdead && flags == 0xbeef) { bgmac_err(bgmac, "Found poisoned packet at slot %d, DMA issue!\n", ring->start); dma_sync_single_for_device(dma_dev, slot->dma_addr, BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE); break; } /* Omit CRC. */ len -= ETH_FCS_LEN; /* Prepare new skb as replacement */ err = bgmac_dma_rx_skb_for_slot(bgmac, slot); if (err) { /* Poison the old skb */ rx->len = cpu_to_le16(0xdead); rx->flags = cpu_to_le16(0xbeef); dma_sync_single_for_device(dma_dev, slot->dma_addr, BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE); break; } bgmac_dma_rx_setup_desc(bgmac, ring, ring->start); /* Unmap old skb, we'll pass it to the netfif */ dma_unmap_single(dma_dev, old_dma_addr, BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE); skb_put(skb, BGMAC_RX_FRAME_OFFSET + len); skb_pull(skb, BGMAC_RX_FRAME_OFFSET); skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, bgmac->net_dev); netif_receive_skb(skb); handled++; } while (0); if (++ring->start >= BGMAC_RX_RING_SLOTS) ring->start = 0; if (handled >= weight) /* Should never be greater */ break; } return handled; }
static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring, int weight) { u32 end_slot; int handled = 0; end_slot = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_RX_STATUS); end_slot &= BGMAC_DMA_RX_STATDPTR; end_slot -= ring->index_base; end_slot &= BGMAC_DMA_RX_STATDPTR; end_slot /= sizeof(struct bgmac_dma_desc); while (ring->start != end_slot) { struct device *dma_dev = bgmac->core->dma_dev; struct bgmac_slot_info *slot = &ring->slots[ring->start]; struct bgmac_rx_header *rx = slot->buf + BGMAC_RX_BUF_OFFSET; struct sk_buff *skb; void *buf = slot->buf; dma_addr_t dma_addr = slot->dma_addr; u16 len, flags; do { /* Prepare new skb as replacement */ if (bgmac_dma_rx_skb_for_slot(bgmac, slot)) { bgmac_dma_rx_poison_buf(dma_dev, slot); break; } /* Unmap buffer to make it accessible to the CPU */ dma_unmap_single(dma_dev, dma_addr, BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE); /* Get info from the header */ len = le16_to_cpu(rx->len); flags = le16_to_cpu(rx->flags); /* Check for poison and drop or pass the packet */ if (len == 0xdead && flags == 0xbeef) { bgmac_err(bgmac, "Found poisoned packet at slot %d, DMA issue!\n", ring->start); put_page(virt_to_head_page(buf)); break; } if (len > BGMAC_RX_ALLOC_SIZE) { bgmac_err(bgmac, "Found oversized packet at slot %d, DMA issue!\n", ring->start); put_page(virt_to_head_page(buf)); break; } /* Omit CRC. */ len -= ETH_FCS_LEN; skb = build_skb(buf, BGMAC_RX_ALLOC_SIZE); if (unlikely(!skb)) { bgmac_err(bgmac, "build_skb failed\n"); put_page(virt_to_head_page(buf)); break; } skb_put(skb, BGMAC_RX_FRAME_OFFSET + BGMAC_RX_BUF_OFFSET + len); skb_pull(skb, BGMAC_RX_FRAME_OFFSET + BGMAC_RX_BUF_OFFSET); skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, bgmac->net_dev); napi_gro_receive(&bgmac->napi, skb); handled++; } while (0); bgmac_dma_rx_setup_desc(bgmac, ring, ring->start); if (++ring->start >= BGMAC_RX_RING_SLOTS) ring->start = 0; if (handled >= weight) /* Should never be greater */ break; } bgmac_dma_rx_update_index(bgmac, ring); return handled; }