/*
* Driver for (BCM4706)? GBit MAC core on BCMA bus.
*
* Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bcma/bcma.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/bcm47xx_nvram.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <net/dsa.h>
#include "bgmac.h"
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;
}
/**************************************************
* DMA
**************************************************/
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)
dev_err(bgmac->dev, "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,
ring->mmio_base + BGMAC_DMA_TX_STATUS,
BGMAC_DMA_TX_STAT, BGMAC_DMA_TX_STAT_DISABLED,
10000)) {
dev_warn(bgmac->dev, "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)
dev_err(bgmac->dev, "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);
if (bgmac->feature_flags & BGMAC_FEAT_TX_MASK_SETUP) {
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_tx_add_buf(struct bgmac *bgmac, struct bgmac_dma_ring *ring,
int i, int len, u32 ctl0)
{
struct bgmac_slot_info *slot;
struct bgmac_dma_desc *dma_desc;
u32 ctl1;
if (i == BGMAC_TX_RING_SLOTS - 1)
ctl0 |= BGMAC_DESC_CTL0_EOT;
ctl1 = len & BGMAC_DESC_CTL1_LEN;
slot = &ring->slots[i];
dma_desc = &ring->cpu_base[i];
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);
}
static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac,
struct bgmac_dma_ring *ring,
struct sk_buff *skb)
{
struct device *dma_dev
|