#include <asm/system.h>
static char mv643xx_eth_driver_name[] = "mv643xx_eth";
-static char mv643xx_eth_driver_version[] = "1.0";
+static char mv643xx_eth_driver_version[] = "1.1";
#define MV643XX_ETH_CHECKSUM_OFFLOAD_TX
#define MV643XX_ETH_NAPI
#define TX_BW_MTU(p) (0x0458 + ((p) << 10))
#define TX_BW_BURST(p) (0x045c + ((p) << 10))
#define INT_CAUSE(p) (0x0460 + ((p) << 10))
+#define INT_TX_END 0x07f80000
#define INT_RX 0x0007fbfc
#define INT_EXT 0x00000002
#define INT_CAUSE_EXT(p) (0x0464 + ((p) << 10))
#define INT_MASK(p) (0x0468 + ((p) << 10))
#define INT_MASK_EXT(p) (0x046c + ((p) << 10))
#define TX_FIFO_URGENT_THRESHOLD(p) (0x0474 + ((p) << 10))
+#define TXQ_FIX_PRIO_CONF_MOVED(p) (0x04dc + ((p) << 10))
+#define TX_BW_RATE_MOVED(p) (0x04e0 + ((p) << 10))
+#define TX_BW_MTU_MOVED(p) (0x04e8 + ((p) << 10))
+#define TX_BW_BURST_MOVED(p) (0x04ec + ((p) << 10))
#define RXQ_CURRENT_DESC_PTR(p, q) (0x060c + ((p) << 10) + ((q) << 4))
#define RXQ_COMMAND(p) (0x0680 + ((p) << 10))
#define TXQ_CURRENT_DESC_PTR(p, q) (0x06c0 + ((p) << 10) + ((q) << 2))
* Hardware-specific parameters.
*/
unsigned int t_clk;
+ int extended_rx_coal_limit;
+ int tx_bw_control_moved;
};
skb_reserve(skb, 2);
}
- if (rxq->rx_desc_count == 0) {
+ if (rxq->rx_desc_count != rxq->rx_ring_size) {
rxq->rx_oom.expires = jiffies + (HZ / 10);
add_timer(&rxq->rx_oom);
}
netif_rx_complete(mp->dev, napi);
wrl(mp, INT_CAUSE(mp->port_num), 0);
wrl(mp, INT_CAUSE_EXT(mp->port_num), 0);
- wrl(mp, INT_MASK(mp->port_num), INT_RX | INT_EXT);
+ wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
}
return rx;
if (bucket_size > 65535)
bucket_size = 65535;
- wrl(mp, TX_BW_RATE(mp->port_num), token_rate);
- wrl(mp, TX_BW_MTU(mp->port_num), mtu);
- wrl(mp, TX_BW_BURST(mp->port_num), bucket_size);
+ if (mp->shared->tx_bw_control_moved) {
+ wrl(mp, TX_BW_RATE_MOVED(mp->port_num), token_rate);
+ wrl(mp, TX_BW_MTU_MOVED(mp->port_num), mtu);
+ wrl(mp, TX_BW_BURST_MOVED(mp->port_num), bucket_size);
+ } else {
+ wrl(mp, TX_BW_RATE(mp->port_num), token_rate);
+ wrl(mp, TX_BW_MTU(mp->port_num), mtu);
+ wrl(mp, TX_BW_BURST(mp->port_num), bucket_size);
+ }
}
static void txq_set_rate(struct tx_queue *txq, int rate, int burst)
/*
* Turn on fixed priority mode.
*/
- off = TXQ_FIX_PRIO_CONF(mp->port_num);
+ if (mp->shared->tx_bw_control_moved)
+ off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num);
+ else
+ off = TXQ_FIX_PRIO_CONF(mp->port_num);
val = rdl(mp, off);
val |= 1 << txq->index;
/*
* Turn off fixed priority mode.
*/
- off = TXQ_FIX_PRIO_CONF(mp->port_num);
+ if (mp->shared->tx_bw_control_moved)
+ off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num);
+ else
+ off = TXQ_FIX_PRIO_CONF(mp->port_num);
val = rdl(mp, off);
val &= ~(1 << txq->index);
return err;
}
+static int mv643xx_eth_get_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ cmd->supported = SUPPORTED_MII;
+ cmd->advertising = ADVERTISED_MII;
+ cmd->speed = SPEED_1000;
+ cmd->duplex = DUPLEX_FULL;
+ cmd->port = PORT_MII;
+ cmd->phy_address = 0;
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->autoneg = AUTONEG_DISABLE;
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+
+ return 0;
+}
+
static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
return err;
}
+static int mv643xx_eth_set_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ return -EINVAL;
+}
+
static void mv643xx_eth_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *drvinfo)
{
return mii_nway_restart(&mp->mii);
}
+static int mv643xx_eth_nway_reset_phyless(struct net_device *dev)
+{
+ return -EINVAL;
+}
+
static u32 mv643xx_eth_get_link(struct net_device *dev)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
return mii_link_ok(&mp->mii);
}
+static u32 mv643xx_eth_get_link_phyless(struct net_device *dev)
+{
+ return 1;
+}
+
static void mv643xx_eth_get_strings(struct net_device *dev,
uint32_t stringset, uint8_t *data)
{
.get_sset_count = mv643xx_eth_get_sset_count,
};
+static const struct ethtool_ops mv643xx_eth_ethtool_ops_phyless = {
+ .get_settings = mv643xx_eth_get_settings_phyless,
+ .set_settings = mv643xx_eth_set_settings_phyless,
+ .get_drvinfo = mv643xx_eth_get_drvinfo,
+ .nway_reset = mv643xx_eth_nway_reset_phyless,
+ .get_link = mv643xx_eth_get_link_phyless,
+ .set_sg = ethtool_op_set_sg,
+ .get_strings = mv643xx_eth_get_strings,
+ .get_ethtool_stats = mv643xx_eth_get_ethtool_stats,
+ .get_sset_count = mv643xx_eth_get_sset_count,
+};
+
/* address handling *********************************************************/
static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr)
struct mv643xx_eth_private *mp = netdev_priv(dev);
u32 int_cause;
u32 int_cause_ext;
+ u32 txq_active;
- int_cause = rdl(mp, INT_CAUSE(mp->port_num)) & (INT_RX | INT_EXT);
+ int_cause = rdl(mp, INT_CAUSE(mp->port_num)) &
+ (INT_TX_END | INT_RX | INT_EXT);
if (int_cause == 0)
return IRQ_NONE;
}
if (int_cause_ext & (INT_EXT_PHY | INT_EXT_LINK)) {
- if (mii_link_ok(&mp->mii)) {
- struct ethtool_cmd cmd;
+ if (mp->phy_addr == -1 || mii_link_ok(&mp->mii)) {
int i;
- mii_ethtool_gset(&mp->mii, &cmd);
- update_pscr(mp, cmd.speed, cmd.duplex);
+ if (mp->phy_addr != -1) {
+ struct ethtool_cmd cmd;
+
+ mii_ethtool_gset(&mp->mii, &cmd);
+ update_pscr(mp, cmd.speed, cmd.duplex);
+ }
+
for (i = 0; i < 8; i++)
if (mp->txq_mask & (1 << i))
txq_enable(mp->txq + i);
}
#endif
+ txq_active = rdl(mp, TXQ_COMMAND(mp->port_num));
+
/*
* TxBuffer or TxError set for any of the 8 queues?
*/
for (i = 0; i < 8; i++)
if (mp->txq_mask & (1 << i))
txq_reclaim(mp->txq + i, 0);
+ }
- __txq_maybe_wake(mp->txq + mp->txq_primary);
+ /*
+ * Any TxEnd interrupts?
+ */
+ if (int_cause & INT_TX_END) {
+ int i;
+
+ wrl(mp, INT_CAUSE(mp->port_num), ~(int_cause & INT_TX_END));
+ for (i = 0; i < 8; i++) {
+ struct tx_queue *txq = mp->txq + i;
+ if (txq->tx_desc_count && !((txq_active >> i) & 1))
+ txq_enable(txq);
+ }
+ }
+
+ /*
+ * Enough space again in the primary TX queue for a full packet?
+ */
+ if (int_cause_ext & INT_EXT_TX) {
+ struct tx_queue *txq = mp->txq + mp->txq_primary;
+ __txq_maybe_wake(txq);
}
return IRQ_HANDLED;
static void port_start(struct mv643xx_eth_private *mp)
{
u32 pscr;
- struct ethtool_cmd ethtool_cmd;
int i;
/*
wrl(mp, SDMA_CONFIG(mp->port_num), PORT_SDMA_CONFIG_DEFAULT_VALUE);
- mv643xx_eth_get_settings(mp->dev, ðtool_cmd);
- phy_reset(mp);
- mv643xx_eth_set_settings(mp->dev, ðtool_cmd);
+ /*
+ * Perform PHY reset, if there is a PHY.
+ */
+ if (mp->phy_addr != -1) {
+ struct ethtool_cmd cmd;
+
+ mv643xx_eth_get_settings(mp->dev, &cmd);
+ phy_reset(mp);
+ mv643xx_eth_set_settings(mp->dev, &cmd);
+ }
/*
* Configure TX path and queues.
static void set_rx_coal(struct mv643xx_eth_private *mp, unsigned int delay)
{
unsigned int coal = ((mp->shared->t_clk / 1000000) * delay) / 64;
+ u32 val;
- if (coal > 0x3fff)
- coal = 0x3fff;
-
- wrl(mp, SDMA_CONFIG(mp->port_num),
- ((coal & 0x3fff) << 8) |
- (rdl(mp, SDMA_CONFIG(mp->port_num))
- & 0xffc000ff));
+ val = rdl(mp, SDMA_CONFIG(mp->port_num));
+ if (mp->shared->extended_rx_coal_limit) {
+ if (coal > 0xffff)
+ coal = 0xffff;
+ val &= ~0x023fff80;
+ val |= (coal & 0x8000) << 10;
+ val |= (coal & 0x7fff) << 7;
+ } else {
+ if (coal > 0x3fff)
+ coal = 0x3fff;
+ val &= ~0x003fff00;
+ val |= (coal & 0x3fff) << 8;
+ }
+ wrl(mp, SDMA_CONFIG(mp->port_num), val);
}
static void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int delay)
wrl(mp, INT_MASK_EXT(mp->port_num),
INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX);
- wrl(mp, INT_MASK(mp->port_num), INT_RX | INT_EXT);
+ wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
return 0;
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
- return generic_mii_ioctl(&mp->mii, if_mii(ifr), cmd, NULL);
+ if (mp->phy_addr != -1)
+ return generic_mii_ioctl(&mp->mii, if_mii(ifr), cmd, NULL);
+
+ return -EOPNOTSUPP;
}
static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu)
mv643xx_eth_irq(dev->irq, dev);
- wrl(mp, INT_MASK(mp->port_num), INT_RX | INT_CAUSE_EXT);
+ wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
}
#endif
msp->win_protect = win_protect;
}
+static void infer_hw_params(struct mv643xx_eth_shared_private *msp)
+{
+ /*
+ * Check whether we have a 14-bit coal limit field in bits
+ * [21:8], or a 16-bit coal limit in bits [25,21:7] of the
+ * SDMA config register.
+ */
+ writel(0x02000000, msp->base + SDMA_CONFIG(0));
+ if (readl(msp->base + SDMA_CONFIG(0)) & 0x02000000)
+ msp->extended_rx_coal_limit = 1;
+ else
+ msp->extended_rx_coal_limit = 0;
+
+ /*
+ * Check whether the TX rate control registers are in the
+ * old or the new place.
+ */
+ writel(1, msp->base + TX_BW_MTU_MOVED(0));
+ if (readl(msp->base + TX_BW_MTU_MOVED(0)) & 1)
+ msp->tx_bw_control_moved = 1;
+ else
+ msp->tx_bw_control_moved = 0;
+}
+
static int mv643xx_eth_shared_probe(struct platform_device *pdev)
{
static int mv643xx_eth_version_printed = 0;
* Detect hardware parameters.
*/
msp->t_clk = (pd != NULL && pd->t_clk != 0) ? pd->t_clk : 133000000;
+ infer_hw_params(msp);
platform_set_drvdata(pdev, msp);
mib_counters_clear(mp);
INIT_WORK(&mp->tx_timeout_task, tx_timeout_task);
- err = phy_init(mp, pd);
- if (err)
- goto out;
- SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops);
+ if (mp->phy_addr != -1) {
+ err = phy_init(mp, pd);
+ if (err)
+ goto out;
+
+ SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops);
+ } else {
+ SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops_phyless);
+ }
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
}
module_exit(mv643xx_eth_cleanup_module);
-MODULE_AUTHOR("Rabeeh Khoury, Assaf Hoffman, Matthew Dharm, Manish Lachwani "
- "and Dale Farnsworth");
+MODULE_AUTHOR("Rabeeh Khoury, Assaf Hoffman, Matthew Dharm, "
+ "Manish Lachwani, Dale Farnsworth and Lennert Buytenhek");
MODULE_DESCRIPTION("Ethernet driver for Marvell MV643XX");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" MV643XX_ETH_SHARED_NAME);