1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-12-19 13:18:25 +02:00
openwrt-xburst/target/linux/ramips/files/drivers/net/ramips.c
juhosg fa4dd3d5de ramips: Fix bridging in ramips ethernet driver
Bridging between the ramips ethernet driver and rt2800pci was somewhat
broken. Frames received by the ethernet driver which were passed to the
wifi driver for transmission were sometimes corrupted or sent out with
huge delays.

The reason for this is the missing assignment of skb->tail in the ramips
ethernet driver's rx path resulting in skb->tail pointing to skb->data.
Since skb->tail is used by mac80211 it writes into skb->data which messes
up the frames content.

Fix this by using skb_put to correctly set skb->len and skb->tail.

Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com>


git-svn-id: svn://svn.openwrt.org/openwrt/trunk@22172 3c298f89-4303-0410-b956-a3cf2f4a3e73
2010-07-13 16:52:07 +00:00

506 lines
12 KiB
C

/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) 2009 John Crispin <blogic@openwrt.org>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/platform_device.h>
#include <ramips_eth_platform.h>
#include "ramips_eth.h"
#define TX_TIMEOUT (20 * HZ / 100)
#define MAX_RX_LENGTH 1600
#ifdef CONFIG_RALINK_RT305X
#include "ramips_esw.c"
#endif
#define phys_to_bus(a) (a & 0x1FFFFFFF)
static struct net_device * ramips_dev;
static void __iomem *ramips_fe_base = 0;
static inline void
ramips_fe_wr(u32 val, unsigned reg)
{
__raw_writel(val, ramips_fe_base + reg);
}
static inline u32
ramips_fe_rr(unsigned reg)
{
return __raw_readl(ramips_fe_base + reg);
}
static inline void
ramips_fe_int_disable(u32 mask)
{
ramips_fe_wr(ramips_fe_rr(RAMIPS_FE_INT_ENABLE) & ~mask,
RAMIPS_FE_INT_ENABLE);
/* flush write */
ramips_fe_rr(RAMIPS_FE_INT_ENABLE);
}
static inline void
ramips_fe_int_enable(u32 mask)
{
ramips_fe_wr(ramips_fe_rr(RAMIPS_FE_INT_ENABLE) | mask,
RAMIPS_FE_INT_ENABLE);
/* flush write */
ramips_fe_rr(RAMIPS_FE_INT_ENABLE);
}
static inline void
ramips_hw_set_macaddr(unsigned char *mac)
{
ramips_fe_wr((mac[0] << 8) | mac[1], RAMIPS_GDMA1_MAC_ADRH);
ramips_fe_wr((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5],
RAMIPS_GDMA1_MAC_ADRL);
}
static void
ramips_cleanup_dma(struct raeth_priv *re)
{
int i;
for (i = 0; i < NUM_RX_DESC; i++)
if (re->rx_skb[i])
dev_kfree_skb_any(re->rx_skb[i]);
if (re->rx)
dma_free_coherent(NULL,
NUM_RX_DESC * sizeof(struct ramips_rx_dma),
re->rx, re->phy_rx);
if (re->tx)
dma_free_coherent(NULL,
NUM_TX_DESC * sizeof(struct ramips_tx_dma),
re->tx, re->phy_tx);
}
static int
ramips_alloc_dma(struct raeth_priv *re)
{
int err = -ENOMEM;
int i;
re->skb_free_idx = 0;
/* setup tx ring */
re->tx = dma_alloc_coherent(NULL,
NUM_TX_DESC * sizeof(struct ramips_tx_dma),
&re->phy_tx, GFP_ATOMIC);
if (!re->tx)
goto err_cleanup;
memset(re->tx, 0, NUM_TX_DESC * sizeof(struct ramips_tx_dma));
for (i = 0; i < NUM_TX_DESC; i++) {
re->tx[i].txd2 = TX_DMA_LSO | TX_DMA_DONE;
re->tx[i].txd4 = TX_DMA_QN(3) | TX_DMA_PN(1);
}
/* setup rx ring */
re->rx = dma_alloc_coherent(NULL,
NUM_RX_DESC * sizeof(struct ramips_rx_dma),
&re->phy_rx, GFP_ATOMIC);
if (!re->rx)
goto err_cleanup;
memset(re->rx, 0, sizeof(struct ramips_rx_dma) * NUM_RX_DESC);
for (i = 0; i < NUM_RX_DESC; i++) {
struct sk_buff *new_skb = dev_alloc_skb(MAX_RX_LENGTH + 2);
if (!new_skb)
goto err_cleanup;
skb_reserve(new_skb, 2);
re->rx[i].rxd1 = dma_map_single(NULL,
skb_put(new_skb, 2),
MAX_RX_LENGTH + 2,
DMA_FROM_DEVICE);
re->rx[i].rxd2 |= RX_DMA_LSO;
re->rx_skb[i] = new_skb;
}
return 0;
err_cleanup:
ramips_cleanup_dma(re);
return err;
}
static void
ramips_setup_dma(struct raeth_priv *re)
{
ramips_fe_wr(phys_to_bus(re->phy_tx), RAMIPS_TX_BASE_PTR0);
ramips_fe_wr(NUM_TX_DESC, RAMIPS_TX_MAX_CNT0);
ramips_fe_wr(0, RAMIPS_TX_CTX_IDX0);
ramips_fe_wr(RAMIPS_PST_DTX_IDX0, RAMIPS_PDMA_RST_CFG);
ramips_fe_wr(phys_to_bus(re->phy_rx), RAMIPS_RX_BASE_PTR0);
ramips_fe_wr(NUM_RX_DESC, RAMIPS_RX_MAX_CNT0);
ramips_fe_wr((NUM_RX_DESC - 1), RAMIPS_RX_CALC_IDX0);
ramips_fe_wr(RAMIPS_PST_DRX_IDX0, RAMIPS_PDMA_RST_CFG);
}
static int
ramips_eth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct raeth_priv *priv = netdev_priv(dev);
unsigned long tx;
unsigned int tx_next;
unsigned int mapped_addr;
unsigned long flags;
if (priv->plat->min_pkt_len) {
if (skb->len < priv->plat->min_pkt_len) {
if (skb_padto(skb, priv->plat->min_pkt_len)) {
printk(KERN_ERR
"ramips_eth: skb_padto failed\n");
kfree_skb(skb);
return 0;
}
skb_put(skb, priv->plat->min_pkt_len - skb->len);
}
}
dev->trans_start = jiffies;
mapped_addr = (unsigned int) dma_map_single(NULL, skb->data, skb->len,
DMA_TO_DEVICE);
dma_sync_single_for_device(NULL, mapped_addr, skb->len, DMA_TO_DEVICE);
spin_lock_irqsave(&priv->page_lock, flags);
tx = ramips_fe_rr(RAMIPS_TX_CTX_IDX0);
tx_next = (tx + 1) % NUM_TX_DESC;
if ((priv->tx_skb[tx]) || (priv->tx_skb[tx_next]) ||
!(priv->tx[tx].txd2 & TX_DMA_DONE) ||
!(priv->tx[tx_next].txd2 & TX_DMA_DONE))
goto out;
priv->tx[tx].txd1 = mapped_addr;
priv->tx[tx].txd2 &= ~(TX_DMA_PLEN0_MASK | TX_DMA_DONE);
priv->tx[tx].txd2 |= TX_DMA_PLEN0(skb->len);
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
priv->tx_skb[tx] = skb;
wmb();
ramips_fe_wr(tx_next, RAMIPS_TX_CTX_IDX0);
spin_unlock_irqrestore(&priv->page_lock, flags);
return NETDEV_TX_OK;
out:
spin_unlock_irqrestore(&priv->page_lock, flags);
dev->stats.tx_dropped++;
kfree_skb(skb);
return NETDEV_TX_OK;
}
static void
ramips_eth_rx_hw(unsigned long ptr)
{
struct net_device *dev = (struct net_device *) ptr;
struct raeth_priv *priv = netdev_priv(dev);
int rx;
int max_rx = 16;
while (max_rx) {
struct sk_buff *rx_skb, *new_skb;
rx = (ramips_fe_rr(RAMIPS_RX_CALC_IDX0) + 1) % NUM_RX_DESC;
if (!(priv->rx[rx].rxd2 & RX_DMA_DONE))
break;
max_rx--;
rx_skb = priv->rx_skb[rx];
skb_put(rx_skb, RX_DMA_PLEN0(priv->rx[rx].rxd2));
rx_skb->dev = dev;
rx_skb->protocol = eth_type_trans(rx_skb, dev);
rx_skb->ip_summed = CHECKSUM_NONE;
dev->stats.rx_packets++;
dev->stats.rx_bytes += rx_skb->len;
netif_rx(rx_skb);
new_skb = netdev_alloc_skb(dev, MAX_RX_LENGTH + 2);
priv->rx_skb[rx] = new_skb;
BUG_ON(!new_skb);
skb_reserve(new_skb, 2);
priv->rx[rx].rxd1 = dma_map_single(NULL,
new_skb->data,
MAX_RX_LENGTH + 2,
DMA_FROM_DEVICE);
priv->rx[rx].rxd2 &= ~RX_DMA_DONE;
wmb();
ramips_fe_wr(rx, RAMIPS_RX_CALC_IDX0);
}
if (max_rx == 0)
tasklet_schedule(&priv->rx_tasklet);
else
ramips_fe_int_enable(RAMIPS_RX_DLY_INT);
}
static void
ramips_eth_tx_housekeeping(unsigned long ptr)
{
struct net_device *dev = (struct net_device*)ptr;
struct raeth_priv *priv = netdev_priv(dev);
while ((priv->tx[priv->skb_free_idx].txd2 & TX_DMA_DONE) &&
(priv->tx_skb[priv->skb_free_idx])) {
dev_kfree_skb_irq(priv->tx_skb[priv->skb_free_idx]);
priv->tx_skb[priv->skb_free_idx] = 0;
priv->skb_free_idx++;
if (priv->skb_free_idx >= NUM_TX_DESC)
priv->skb_free_idx = 0;
}
ramips_fe_int_enable(RAMIPS_TX_DLY_INT);
}
static void
ramips_eth_timeout(struct net_device *dev)
{
struct raeth_priv *priv = netdev_priv(dev);
tasklet_schedule(&priv->tx_housekeeping_tasklet);
}
static irqreturn_t
ramips_eth_irq(int irq, void *dev)
{
struct raeth_priv *priv = netdev_priv(dev);
unsigned long fe_int = ramips_fe_rr(RAMIPS_FE_INT_STATUS);
ramips_fe_wr(0xFFFFFFFF, RAMIPS_FE_INT_STATUS);
if (fe_int & RAMIPS_RX_DLY_INT) {
ramips_fe_int_disable(RAMIPS_RX_DLY_INT);
tasklet_schedule(&priv->rx_tasklet);
}
if (fe_int & RAMIPS_TX_DLY_INT)
ramips_eth_tx_housekeeping((unsigned long)dev);
return IRQ_HANDLED;
}
static int
ramips_eth_open(struct net_device *dev)
{
struct raeth_priv *priv = netdev_priv(dev);
int err;
err = request_irq(dev->irq, ramips_eth_irq, IRQF_DISABLED,
dev->name, dev);
if (err)
return err;
err = ramips_alloc_dma(priv);
if (err)
goto err_free_irq;
ramips_hw_set_macaddr(dev->dev_addr);
ramips_setup_dma(priv);
ramips_fe_wr((ramips_fe_rr(RAMIPS_PDMA_GLO_CFG) & 0xff) |
(RAMIPS_TX_WB_DDONE | RAMIPS_RX_DMA_EN |
RAMIPS_TX_DMA_EN | RAMIPS_PDMA_SIZE_4DWORDS),
RAMIPS_PDMA_GLO_CFG);
ramips_fe_wr((ramips_fe_rr(RAMIPS_FE_GLO_CFG) &
~(RAMIPS_US_CYC_CNT_MASK << RAMIPS_US_CYC_CNT_SHIFT)) |
((priv->plat->sys_freq / RAMIPS_US_CYC_CNT_DIVISOR) << RAMIPS_US_CYC_CNT_SHIFT),
RAMIPS_FE_GLO_CFG);
tasklet_init(&priv->tx_housekeeping_tasklet, ramips_eth_tx_housekeeping,
(unsigned long)dev);
tasklet_init(&priv->rx_tasklet, ramips_eth_rx_hw, (unsigned long)dev);
ramips_fe_wr(RAMIPS_DELAY_INIT, RAMIPS_DLY_INT_CFG);
ramips_fe_wr(RAMIPS_TX_DLY_INT | RAMIPS_RX_DLY_INT, RAMIPS_FE_INT_ENABLE);
ramips_fe_wr(ramips_fe_rr(RAMIPS_GDMA1_FWD_CFG) &
~(RAMIPS_GDM1_ICS_EN | RAMIPS_GDM1_TCS_EN | RAMIPS_GDM1_UCS_EN | 0xffff),
RAMIPS_GDMA1_FWD_CFG);
ramips_fe_wr(ramips_fe_rr(RAMIPS_CDMA_CSG_CFG) &
~(RAMIPS_ICS_GEN_EN | RAMIPS_TCS_GEN_EN | RAMIPS_UCS_GEN_EN),
RAMIPS_CDMA_CSG_CFG);
ramips_fe_wr(RAMIPS_PSE_FQFC_CFG_INIT, RAMIPS_PSE_FQ_CFG);
ramips_fe_wr(1, RAMIPS_FE_RST_GL);
ramips_fe_wr(0, RAMIPS_FE_RST_GL);
netif_start_queue(dev);
return 0;
err_free_irq:
free_irq(dev->irq, dev);
return err;
}
static int
ramips_eth_stop(struct net_device *dev)
{
struct raeth_priv *priv = netdev_priv(dev);
ramips_fe_wr(ramips_fe_rr(RAMIPS_PDMA_GLO_CFG) &
~(RAMIPS_TX_WB_DDONE | RAMIPS_RX_DMA_EN | RAMIPS_TX_DMA_EN),
RAMIPS_PDMA_GLO_CFG);
free_irq(dev->irq, dev);
netif_stop_queue(dev);
tasklet_kill(&priv->tx_housekeeping_tasklet);
tasklet_kill(&priv->rx_tasklet);
ramips_cleanup_dma(priv);
printk(KERN_DEBUG "ramips_eth: stopped\n");
return 0;
}
static int __init
ramips_eth_probe(struct net_device *dev)
{
struct raeth_priv *priv = netdev_priv(dev);
BUG_ON(!priv->plat->reset_fe);
priv->plat->reset_fe();
net_srandom(jiffies);
memcpy(dev->dev_addr, priv->plat->mac, ETH_ALEN);
ether_setup(dev);
dev->mtu = 1500;
dev->watchdog_timeo = TX_TIMEOUT;
spin_lock_init(&priv->page_lock);
return 0;
}
static const struct net_device_ops ramips_eth_netdev_ops = {
.ndo_init = ramips_eth_probe,
.ndo_open = ramips_eth_open,
.ndo_stop = ramips_eth_stop,
.ndo_start_xmit = ramips_eth_hard_start_xmit,
.ndo_tx_timeout = ramips_eth_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
static int
ramips_eth_plat_probe(struct platform_device *plat)
{
struct raeth_priv *priv;
struct ramips_eth_platform_data *data = plat->dev.platform_data;
struct resource *res;
int err;
if (!data) {
dev_err(&plat->dev, "no platform data specified\n");
return -EINVAL;
}
res = platform_get_resource(plat, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&plat->dev, "no memory resource found\n");
return -ENXIO;
}
ramips_fe_base = ioremap_nocache(res->start, res->end - res->start + 1);
if (!ramips_fe_base)
return -ENOMEM;
ramips_dev = alloc_etherdev(sizeof(struct raeth_priv));
if (!ramips_dev) {
dev_err(&plat->dev, "alloc_etherdev failed\n");
err = -ENOMEM;
goto err_unmap;
}
strcpy(ramips_dev->name, "eth%d");
ramips_dev->irq = platform_get_irq(plat, 0);
if (ramips_dev->irq < 0) {
dev_err(&plat->dev, "no IRQ resource found\n");
err = -ENXIO;
goto err_free_dev;
}
ramips_dev->addr_len = ETH_ALEN;
ramips_dev->base_addr = (unsigned long)ramips_fe_base;
ramips_dev->netdev_ops = &ramips_eth_netdev_ops;
priv = netdev_priv(ramips_dev);
priv->plat = data;
err = register_netdev(ramips_dev);
if (err) {
dev_err(&plat->dev, "error bringing up device\n");
goto err_free_dev;
}
#ifdef CONFIG_RALINK_RT305X
rt305x_esw_init();
#endif
printk(KERN_DEBUG "ramips_eth: loaded\n");
return 0;
err_free_dev:
kfree(ramips_dev);
err_unmap:
iounmap(ramips_fe_base);
return err;
}
static int
ramips_eth_plat_remove(struct platform_device *plat)
{
unregister_netdev(ramips_dev);
free_netdev(ramips_dev);
printk(KERN_DEBUG "ramips_eth: unloaded\n");
return 0;
}
static struct platform_driver ramips_eth_driver = {
.probe = ramips_eth_plat_probe,
.remove = ramips_eth_plat_remove,
.driver = {
.name = "ramips_eth",
.owner = THIS_MODULE,
},
};
static int __init
ramips_eth_init(void)
{
int ret = platform_driver_register(&ramips_eth_driver);
if (ret)
printk(KERN_ERR
"ramips_eth: Error registering platfom driver!\n");
return ret;
}
static void __exit
ramips_eth_cleanup(void)
{
platform_driver_unregister(&ramips_eth_driver);
}
module_init(ramips_eth_init);
module_exit(ramips_eth_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
MODULE_DESCRIPTION("ethernet driver for ramips boards");