/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipinit */ static void bgmac_chip_init(struct bgmac *bgmac) { /* Clear any erroneously pending interrupts */ bgmac_write(bgmac, BGMAC_INT_STATUS, ~0); /* 1 interrupt per received frame */ bgmac_write(bgmac, BGMAC_INT_RECV_LAZY, 1 << BGMAC_IRL_FC_SHIFT); /* Enable 802.3x tx flow control (honor received PAUSE frames) */ bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_RPI, 0, true); bgmac_set_rx_mode(bgmac->net_dev); bgmac_write_mac_address(bgmac, bgmac->net_dev->dev_addr); if (bgmac->loopback) bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_ML, false); else bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_ML, 0, false); bgmac_write(bgmac, BGMAC_RXMAX_LENGTH, 32 + ETHER_MAX_LEN); bgmac_chip_intrs_on(bgmac); bgmac_enable(bgmac); }
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipinit */ static void bgmac_chip_init(struct bgmac *bgmac, bool full_init) { struct bgmac_dma_ring *ring; int i; /* 1 interrupt per received frame */ bgmac_write(bgmac, BGMAC_INT_RECV_LAZY, 1 << BGMAC_IRL_FC_SHIFT); /* Enable 802.3x tx flow control (honor received PAUSE frames) */ bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_RPI, 0, true); bgmac_set_rx_mode(bgmac->net_dev); bgmac_write_mac_address(bgmac, bgmac->net_dev->dev_addr); if (bgmac->loopback) bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_ML, false); else bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_ML, 0, false); bgmac_write(bgmac, BGMAC_RXMAX_LENGTH, 32 + ETHER_MAX_LEN); if (full_init) { bgmac_dma_init(bgmac); if (1) /* FIXME: is there any case we don't want IRQs? */ bgmac_chip_intrs_on(bgmac); } else { for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) { ring = &bgmac->rx_ring[i]; bgmac_dma_rx_enable(bgmac, ring); } } bgmac_enable(bgmac); }
static void bgmac_dma_init(struct bgmac *bgmac) { struct bgmac_dma_ring *ring; struct bgmac_dma_desc *dma_desc; u32 ctl0, ctl1; int i; for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) { ring = &bgmac->tx_ring[i]; /* We don't implement unaligned addressing, so enable first */ bgmac_dma_tx_enable(bgmac, ring); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_RINGLO, lower_32_bits(ring->dma_base)); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_RINGHI, upper_32_bits(ring->dma_base)); ring->start = 0; ring->end = 0; /* Points the slot that should *not* be read */ } for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) { int j; ring = &bgmac->rx_ring[i]; /* We don't implement unaligned addressing, so enable first */ bgmac_dma_rx_enable(bgmac, ring); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_RINGLO, lower_32_bits(ring->dma_base)); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_RINGHI, upper_32_bits(ring->dma_base)); for (j = 0, dma_desc = ring->cpu_base; j < ring->num_slots; j++, dma_desc++) { ctl0 = ctl1 = 0; if (j == ring->num_slots - 1) ctl0 |= BGMAC_DESC_CTL0_EOT; ctl1 |= BGMAC_RX_BUF_SIZE & BGMAC_DESC_CTL1_LEN; /* Is there any BGMAC device that requires extension? */ /* ctl1 |= (addrext << B43_DMA64_DCTL1_ADDREXT_SHIFT) & * B43_DMA64_DCTL1_ADDREXT_MASK; */ dma_desc->addr_low = cpu_to_le32(lower_32_bits(ring->slots[j].dma_addr)); dma_desc->addr_high = cpu_to_le32(upper_32_bits(ring->slots[j].dma_addr)); dma_desc->ctl0 = cpu_to_le32(ctl0); dma_desc->ctl1 = cpu_to_le32(ctl1); } bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX, ring->num_slots * sizeof(struct bgmac_dma_desc)); ring->start = 0; ring->end = 0; } }
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 int bgmac_dma_init(struct bgmac *bgmac) { struct bgmac_dma_ring *ring; int i, err; for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) { ring = &bgmac->tx_ring[i]; if (!ring->unaligned) bgmac_dma_tx_enable(bgmac, ring); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_RINGLO, lower_32_bits(ring->dma_base)); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_RINGHI, upper_32_bits(ring->dma_base)); if (ring->unaligned) bgmac_dma_tx_enable(bgmac, ring); ring->start = 0; ring->end = 0; /* Points the slot that should *not* be read */ } for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) { int j; ring = &bgmac->rx_ring[i]; if (!ring->unaligned) bgmac_dma_rx_enable(bgmac, ring); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_RINGLO, lower_32_bits(ring->dma_base)); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_RINGHI, upper_32_bits(ring->dma_base)); if (ring->unaligned) bgmac_dma_rx_enable(bgmac, ring); ring->start = 0; ring->end = 0; for (j = 0; j < BGMAC_RX_RING_SLOTS; j++) { err = bgmac_dma_rx_skb_for_slot(bgmac, &ring->slots[j]); if (err) goto error; bgmac_dma_rx_setup_desc(bgmac, ring, j); } bgmac_dma_rx_update_index(bgmac, ring); } return 0; error: bgmac_dma_cleanup(bgmac); return err; }
/* 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_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); }
static void bgmac_dma_init(struct bgmac *bgmac) { struct bgmac_dma_ring *ring; int i; for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) { ring = &bgmac->tx_ring[i]; if (!ring->unaligned) bgmac_dma_tx_enable(bgmac, ring); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_RINGLO, lower_32_bits(ring->dma_base)); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_RINGHI, upper_32_bits(ring->dma_base)); if (ring->unaligned) bgmac_dma_tx_enable(bgmac, ring); ring->start = 0; ring->end = 0; /* Points the slot that should *not* be read */ } for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) { int j; ring = &bgmac->rx_ring[i]; if (!ring->unaligned) bgmac_dma_rx_enable(bgmac, ring); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_RINGLO, lower_32_bits(ring->dma_base)); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_RINGHI, upper_32_bits(ring->dma_base)); if (ring->unaligned) bgmac_dma_rx_enable(bgmac, ring); for (j = 0; j < ring->num_slots; j++) bgmac_dma_rx_setup_desc(bgmac, ring, j); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX, ring->index_base + ring->num_slots * sizeof(struct bgmac_dma_desc)); ring->start = 0; ring->end = 0; } }
static void bgmac_dma_rx_update_index(struct bgmac *bgmac, struct bgmac_dma_ring *ring) { dma_wmb(); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX, ring->index_base + ring->end * sizeof(struct bgmac_dma_desc)); }
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); }
/* 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); }
static void bgmac_dma_rx_reset(struct bgmac *bgmac, struct bgmac_dma_ring *ring) { if (!ring->mmio_base) return; bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_CTL, 0); if (!bgmac_wait_value(bgmac->core, ring->mmio_base + BGMAC_DMA_RX_STATUS, BGMAC_DMA_RX_STAT, BGMAC_DMA_RX_STAT_DISABLED, 10000)) bgmac_err(bgmac, "Reset of ring 0x%X RX failed\n", ring->mmio_base); }
/* 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); }
/* 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; }
/* 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 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 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_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_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_chip_intrs_on(struct bgmac *bgmac) { bgmac_write(bgmac, BGMAC_INT_MASK, bgmac->int_mask); }
static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac, struct bgmac_dma_ring *ring, struct sk_buff *skb) { struct device *dma_dev = bgmac->core->dma_dev; struct net_device *net_dev = bgmac->net_dev; struct bgmac_dma_desc *dma_desc; struct bgmac_slot_info *slot; u32 ctl0, ctl1; int free_slots; if (skb->len > BGMAC_DESC_CTL1_LEN) { bgmac_err(bgmac, "Too long skb (%d)\n", skb->len); goto err_stop_drop; } if (ring->start <= ring->end) free_slots = ring->start - ring->end + BGMAC_TX_RING_SLOTS; else free_slots = ring->start - ring->end; if (free_slots == 1) { bgmac_err(bgmac, "TX ring is full, queue should be stopped!\n"); netif_stop_queue(net_dev); return NETDEV_TX_BUSY; } slot = &ring->slots[ring->end]; slot->skb = skb; slot->dma_addr = dma_map_single(dma_dev, skb->data, skb->len, DMA_TO_DEVICE); if (dma_mapping_error(dma_dev, slot->dma_addr)) { bgmac_err(bgmac, "Mapping error of skb on ring 0x%X\n", ring->mmio_base); goto err_stop_drop; } ctl0 = BGMAC_DESC_CTL0_IOC | BGMAC_DESC_CTL0_SOF | BGMAC_DESC_CTL0_EOF; if (ring->end == ring->num_slots - 1) ctl0 |= BGMAC_DESC_CTL0_EOT; ctl1 = skb->len & BGMAC_DESC_CTL1_LEN; dma_desc = ring->cpu_base; dma_desc += ring->end; dma_desc->addr_low = cpu_to_le32(lower_32_bits(slot->dma_addr)); dma_desc->addr_high = cpu_to_le32(upper_32_bits(slot->dma_addr)); dma_desc->ctl0 = cpu_to_le32(ctl0); dma_desc->ctl1 = cpu_to_le32(ctl1); netdev_sent_queue(net_dev, skb->len); wmb(); /* Increase ring->end to point empty slot. We tell hardware the first * slot it should *not* read. */ if (++ring->end >= BGMAC_TX_RING_SLOTS) ring->end = 0; bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_INDEX, ring->index_base + ring->end * sizeof(struct bgmac_dma_desc)); /* Always keep one slot free to allow detecting bugged calls. */ if (--free_slots == 1) netif_stop_queue(net_dev); return NETDEV_TX_OK; err_stop_drop: netif_stop_queue(net_dev); dev_kfree_skb(skb); return NETDEV_TX_OK; }
static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac, struct bgmac_dma_ring *ring, struct sk_buff *skb) { struct device *dma_dev = bgmac->core->dma_dev; struct net_device *net_dev = bgmac->net_dev; int index = ring->end % BGMAC_TX_RING_SLOTS; struct bgmac_slot_info *slot = &ring->slots[index]; int nr_frags; u32 flags; int i; if (skb->len > BGMAC_DESC_CTL1_LEN) { bgmac_err(bgmac, "Too long skb (%d)\n", skb->len); goto err_drop; } if (skb->ip_summed == CHECKSUM_PARTIAL) skb_checksum_help(skb); nr_frags = skb_shinfo(skb)->nr_frags; /* ring->end - ring->start will return the number of valid slots, * even when ring->end overflows */ if (ring->end - ring->start + nr_frags + 1 >= BGMAC_TX_RING_SLOTS) { bgmac_err(bgmac, "TX ring is full, queue should be stopped!\n"); netif_stop_queue(net_dev); return NETDEV_TX_BUSY; } slot->dma_addr = dma_map_single(dma_dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); if (unlikely(dma_mapping_error(dma_dev, slot->dma_addr))) goto err_dma_head; flags = BGMAC_DESC_CTL0_SOF; if (!nr_frags) flags |= BGMAC_DESC_CTL0_EOF | BGMAC_DESC_CTL0_IOC; bgmac_dma_tx_add_buf(bgmac, ring, index, skb_headlen(skb), flags); flags = 0; for (i = 0; i < nr_frags; i++) { struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i]; int len = skb_frag_size(frag); index = (index + 1) % BGMAC_TX_RING_SLOTS; slot = &ring->slots[index]; slot->dma_addr = skb_frag_dma_map(dma_dev, frag, 0, len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(dma_dev, slot->dma_addr))) goto err_dma; if (i == nr_frags - 1) flags |= BGMAC_DESC_CTL0_EOF | BGMAC_DESC_CTL0_IOC; bgmac_dma_tx_add_buf(bgmac, ring, index, len, flags); } slot->skb = skb; ring->end += nr_frags + 1; netdev_sent_queue(net_dev, skb->len); wmb(); /* Increase ring->end to point empty slot. We tell hardware the first * slot it should *not* read. */ bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_INDEX, ring->index_base + (ring->end % BGMAC_TX_RING_SLOTS) * sizeof(struct bgmac_dma_desc)); if (ring->end - ring->start >= BGMAC_TX_RING_SLOTS - 8) netif_stop_queue(net_dev); return NETDEV_TX_OK; err_dma: dma_unmap_single(dma_dev, slot->dma_addr, skb_headlen(skb), DMA_TO_DEVICE); while (i > 0) { int index = (ring->end + i) % BGMAC_TX_RING_SLOTS; struct bgmac_slot_info *slot = &ring->slots[index]; u32 ctl1 = le32_to_cpu(ring->cpu_base[index].ctl1); int len = ctl1 & BGMAC_DESC_CTL1_LEN; dma_unmap_page(dma_dev, slot->dma_addr, len, DMA_TO_DEVICE); } err_dma_head: bgmac_err(bgmac, "Mapping error of skb on ring 0x%X\n", ring->mmio_base); err_drop: dev_kfree_skb(skb); return NETDEV_TX_OK; }
static void bgmac_chip_intrs_off(struct bgmac *bgmac) { bgmac_write(bgmac, BGMAC_INT_MASK, 0); bgmac_read(bgmac, BGMAC_INT_MASK); }