* [RFC PATCH v0.1] net driver: mpc52xx fec @ 2007-08-10 9:51 Domen Puncer 2007-08-10 13:02 ` Arnaldo Carvalho de Melo ` (3 more replies) 0 siblings, 4 replies; 47+ messages in thread From: Domen Puncer @ 2007-08-10 9:51 UTC (permalink / raw) To: linuxppc-embedded; +Cc: netdev Hi! Not for merge (yet)! But please do review. fec_mpc52xx driver (not in-tree, but floating around) isn't in very good shape, so I tried to change that. Diff against original is quite big (fec_phy.c is completely rewritten) and confuzing, so I'm including whole drivers/net/fec_mpc52xx/ . I still have 'make CONFIG_FEC_MPC52xx_MDIO=n compile and work' on my TODO, maybe even ethtool support. Domen arch/powerpc/boot/dts/lite5200b.dts | 18 arch/powerpc/sysdev/bestcomm/fec.h | 14 drivers/net/fec_mpc52xx/Kconfig | 24 drivers/net/fec_mpc52xx/Makefile | 7 drivers/net/fec_mpc52xx/fec.c | 1002 ++++++++++++++++++++++++++++++++++++ drivers/net/fec_mpc52xx/fec.h | 299 ++++++++++ drivers/net/fec_mpc52xx/fec_phy.c | 229 ++++++++ drivers/net/fec_mpc52xx/fec_phy.h | 49 + 8 files changed, 1641 insertions(+), 1 deletion(-) diff -pruN dummy/fec.c ./drivers/net/fec_mpc52xx/fec.c --- dummy/fec.c 1970-01-01 01:00:00.000000000 +0100 +++ ./drivers/net/fec_mpc52xx/fec.c 2007-08-10 10:59:00.000000000 +0200 @@ -0,0 +1,1002 @@ +/* + * drivers/net/fec_mpc52xx/fec.c + * + * Driver for the MPC5200 Fast Ethernet Controller + * + * Originally written by Dale Farnsworth <dfarnsworth@mvista.com> and + * now maintained by Sylvain Munaut <tnt@246tNt.com> + * + * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com> + * Copyrigth (C) 2003-2004 MontaVista, Software, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/crc32.h> +#include <linux/hardirq.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/skbuff.h> + +#include <asm/of_device.h> +#include <asm/of_platform.h> +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/mpc52xx.h> + +#include <sysdev/bestcomm/bestcomm.h> +#include <sysdev/bestcomm/fec.h> + +#include "fec_phy.h" +#include "fec.h" + +#define DRIVER_NAME "mpc52xx-fec" + +static irqreturn_t fec_interrupt(int, void *); +static irqreturn_t fec_rx_interrupt(int, void *); +static irqreturn_t fec_tx_interrupt(int, void *); +static struct net_device_stats *fec_get_stats(struct net_device *); +static void fec_set_multicast_list(struct net_device *dev); +static void fec_hw_init(struct net_device *dev); +static void fec_stop(struct net_device *dev); +static void fec_start(struct net_device *dev); + +static u8 mpc52xx_fec_mac_addr[6]; +static u8 null_mac[6]; + +static void fec_tx_timeout(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + + dev_warn(&dev->dev, "transmit timed out\n"); + + fec_stop(dev); + fec_start(dev); + + priv->stats.tx_errors++; + + if (!priv->tx_full) + netif_wake_queue(dev); +} + +static void fec_set_paddr(struct net_device *dev, u8 *mac) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + + out_be32(&fec->paddr1, *(u32*)(&mac[0])); + out_be32(&fec->paddr2, (*(u16*)(&mac[4]) << 16) | FEC_PADDR2_TYPE); +} + +static void fec_get_paddr(struct net_device *dev, u8 *mac) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + + *(u32*)(&mac[0]) = in_be32(&fec->paddr1); + *(u16*)(&mac[4]) = in_be32(&fec->paddr2) >> 16; +} + +static int fec_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sock = (struct sockaddr *)addr; + + memcpy(dev->dev_addr, sock->sa_data, dev->addr_len); + + fec_set_paddr(dev, sock->sa_data); + return 0; +} + +static void fec_free_rx_buffers(struct bcom_task *s) +{ + struct sk_buff *skb; + + while (!bcom_queue_empty(s)) { + skb = bcom_retrieve_buffer(s, NULL, NULL); + kfree_skb(skb); + } +} + +static int fec_alloc_rx_buffers(struct bcom_task *rxtsk) +{ + while (!bcom_queue_full(rxtsk)) { + struct sk_buff *skb; + struct bcom_fec_bd *bd; + + skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); + if (skb == 0) + return -EAGAIN; + + /* zero out the initial receive buffers to aid debugging */ + memset(skb->data, 0, FEC_RX_BUFFER_SIZE); + + bd = (struct bcom_fec_bd *)bcom_prepare_next_buffer(rxtsk); + + bd->status = FEC_RX_BUFFER_SIZE; + bd->skb_pa = virt_to_phys(skb->data); + + bcom_submit_next_buffer(rxtsk, skb); + } + + return 0; +} + +/* based on generic_adjust_link - fs_enet-main.c */ +static void fec_adjust_link(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct phy_device *phydev = priv->phydev; + int new_state = 0; + + if (phydev->link != PHY_DOWN) { + if (phydev->duplex != priv->duplex) { + new_state = 1; + priv->duplex = phydev->duplex; + } + + if (phydev->speed != priv->speed) { + new_state = 1; + priv->speed = phydev->speed; + } + + if (priv->link == PHY_DOWN) { + new_state = 1; + priv->link = phydev->link; + netif_schedule(dev); + netif_carrier_on(dev); + netif_start_queue(dev); + } + + } else if (priv->link) { + new_state = 1; + priv->link = PHY_DOWN; + priv->speed = 0; + priv->duplex = -1; + netif_stop_queue(dev); + netif_carrier_off(dev); + } + + if (new_state && netif_msg_link(priv)) { + phy_print_status(phydev); + } +} + +static int fec_init_phy(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct phy_device *phydev; + char phy_id[BUS_ID_SIZE]; + + struct device_node *dn, *phy_dn; + unsigned int phy_addr; + const phandle *ph; + const unsigned int *prop; + struct resource res; + int ret; + + dn = priv->ofdev->node; + ph = of_get_property(dn, "phy-handle", NULL); + if (!ph) { + dev_err(&dev->dev, "can't find \"phy-handle\" in device tree\n"); + return -ENODEV; + } + phy_dn = of_find_node_by_phandle(*ph); + + prop = of_get_property(phy_dn, "reg", NULL); + ret = of_address_to_resource(phy_dn->parent, 0, &res); + if (ret) { + dev_err(&dev->dev, "of_address_to_resource failed\n"); + return ret; + } + + phy_addr = *prop; + of_node_put(phy_dn); + + snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, res.start, phy_addr); + + priv->link = PHY_DOWN; + priv->speed = 0; + priv->duplex = -1; + + phydev = phy_connect(dev, phy_id, &fec_adjust_link, 0, PHY_INTERFACE_MODE_MII); + if (IS_ERR(phydev)) { + printk(KERN_ERR "%s: phy_connect failed\n", dev->name); + return PTR_ERR(phydev); + } + + phydev->advertising &= ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half; + + priv->phydev = phydev; + + return 0; +} + +static int fec_open(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + int err = -EBUSY; + + if (request_irq(dev->irq, &fec_interrupt, IRQF_DISABLED | IRQF_SHARED, + DRIVER_NAME "_ctrl", dev)) { + dev_err(&dev->dev, "ctrl interrupt request failed\n"); + goto out; + } + if (request_irq(priv->r_irq, &fec_rx_interrupt, IRQF_DISABLED, + DRIVER_NAME "_rx", dev)) { + dev_err(&dev->dev, "rx interrupt request failed\n"); + goto free_ctrl_irq; + } + if (request_irq(priv->t_irq, &fec_tx_interrupt, IRQF_DISABLED, + DRIVER_NAME "_tx", dev)) { + dev_err(&dev->dev, "tx interrupt request failed\n"); + goto free_2irqs; + } + + bcom_fec_rx_reset(priv->rx_dmatsk); + bcom_fec_tx_reset(priv->tx_dmatsk); + + err = fec_alloc_rx_buffers(priv->rx_dmatsk); + if (err) { + dev_err(&dev->dev, "fec_alloc_rx_buffers failed\n"); + goto free_irqs; + } + + err = fec_init_phy(dev); + if (err) { + dev_err(&dev->dev, "fec_init_phy failed\n"); + goto free_skbs; + } + bcom_enable(priv->rx_dmatsk); + bcom_enable(priv->tx_dmatsk); + + /* reset phy - this also wakes it from PDOWN */ + phy_write(priv->phydev, MII_BMCR, BMCR_RESET); + phy_start(priv->phydev); + + fec_start(dev); + + netif_start_queue(dev); + + return 0; + + free_skbs: + fec_free_rx_buffers(priv->rx_dmatsk); + + free_irqs: + free_irq(priv->t_irq, dev); + free_2irqs: + free_irq(priv->r_irq, dev); + free_ctrl_irq: + free_irq(dev->irq, dev); + out: + + return err; +} + +static int fec_close(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + + fec_stop(dev); + + fec_free_rx_buffers(priv->rx_dmatsk); + + phy_disconnect(priv->phydev); + + free_irq(dev->irq, dev); + free_irq(priv->r_irq, dev); + free_irq(priv->t_irq, dev); + + /* power down phy */ + phy_stop(priv->phydev); + phy_write(priv->phydev, MII_BMCR, BMCR_PDOWN); + + return 0; +} + +/* This will only be invoked if your driver is _not_ in XOFF state. + * What this means is that you need not check it, and that this + * invariant will hold if you make sure that the netif_*_queue() + * calls are done at the proper times. + */ +static int fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct bcom_fec_bd *bd; + + if (bcom_queue_full(priv->tx_dmatsk)) { + if (net_ratelimit()) + dev_err(&dev->dev, "transmit queue overrun\n"); + return 1; + } + + spin_lock_irq(&priv->lock); + dev->trans_start = jiffies; + + bd = (struct bcom_fec_bd *) + bcom_prepare_next_buffer(priv->tx_dmatsk); + + bd->status = skb->len | BCOM_FEC_TX_BD_TFD | BCOM_FEC_TX_BD_INT; + bd->skb_pa = virt_to_phys(skb->data); + + bcom_submit_next_buffer(priv->tx_dmatsk, skb); + + if (bcom_queue_full(priv->tx_dmatsk)) { + priv->tx_full = 1; + netif_stop_queue(dev); + } + + spin_unlock_irq(&priv->lock); + + return 0; +} + +/* This handles BestComm transmit task interrupts + */ +static irqreturn_t fec_tx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct fec_priv *priv = netdev_priv(dev); + + spin_lock(&priv->lock); + + while (bcom_buffer_done(priv->tx_dmatsk)) { + struct sk_buff *skb; + skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL, NULL); + + priv->tx_full = 0; + dev_kfree_skb_irq(skb); + } + + if (netif_queue_stopped(dev) && !priv->tx_full) + netif_wake_queue(dev); + + spin_unlock(&priv->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t fec_rx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct fec_priv *priv = netdev_priv(dev); + + while (bcom_buffer_done(priv->rx_dmatsk)) { + struct sk_buff *skb; + struct sk_buff *rskb; + struct bcom_fec_bd *bd; + u32 status; + + rskb = bcom_retrieve_buffer(priv->rx_dmatsk, &status, NULL); + + /* Test for errors in received frame */ + if (status & BCOM_FEC_RX_BD_ERRORS) { + /* Drop packet and reuse the buffer */ + bd = (struct bcom_fec_bd *) + bcom_prepare_next_buffer(priv->rx_dmatsk); + + bd->status = FEC_RX_BUFFER_SIZE; + bd->skb_pa = virt_to_phys(rskb->data); + + bcom_submit_next_buffer(priv->rx_dmatsk, rskb); + + priv->stats.rx_dropped++; + + continue; + } + + /* skbs are allocated on open, so now we allocate a new one, + * and remove the old (with the packet) */ + skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); + if (skb) { + /* Process the received skb */ + int length = status & BCOM_FEC_RX_BD_LEN_MASK; + + skb_put(rskb, length - 4); /* length without CRC32 */ + + rskb->dev = dev; + rskb->protocol = eth_type_trans(rskb, dev); + + netif_rx(rskb); + dev->last_rx = jiffies; + } else { + /* Can't get a new one : reuse the same & drop pkt */ + dev_notice(&dev->dev, "Memory squeeze, dropping packet.\n"); + priv->stats.rx_dropped++; + + skb = rskb; + } + + bd = (struct bcom_fec_bd *) + bcom_prepare_next_buffer(priv->rx_dmatsk); + + bd->status = FEC_RX_BUFFER_SIZE; + bd->skb_pa = virt_to_phys(skb->data); + + bcom_submit_next_buffer(priv->rx_dmatsk, skb); + } + + return IRQ_HANDLED; +} + +static irqreturn_t fec_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + u32 ievent; + + ievent = in_be32(&fec->ievent); + + ievent &= ~FEC_IEVENT_MII; /* mii is handled separately */ + if (!ievent) + return IRQ_NONE; + + out_be32(&fec->ievent, ievent); /* clear pending events */ + + if (ievent & ~(FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) { + if (ievent & ~FEC_IEVENT_TFINT) + dev_dbg(&dev->dev, "ievent: %08x\n", ievent); + return IRQ_HANDLED; + } + + if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR)) + dev_warn(&dev->dev, "FEC_IEVENT_RFIFO_ERROR\n"); + if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR)) + dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n"); + + fec_stop(dev); + fec_hw_init(dev); + fec_start(dev); + + netif_wake_queue(dev); + return IRQ_HANDLED; +} + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ +static struct net_device_stats *fec_get_stats(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &priv->stats; + struct mpc52xx_fec __iomem *fec = priv->fec; + +/* printk(KERN_ALERT "%s: %i, rmon_r_octets: %i, rmon_r_packets: %i, " + "ieee_r_octets_ok: %i, ieee_r_frame_ok: %i, " + "%i\n", + __func__, __LINE__, + in_be32(&fec->rmon_r_octets), in_be32(&fec->rmon_r_packets), + in_be32(&fec->ieee_r_octets_ok), in_be32(&fec->ieee_r_frame_ok), + 0); +*/ + stats->rx_bytes = in_be32(&fec->rmon_r_octets); + stats->rx_packets = in_be32(&fec->rmon_r_packets); + stats->rx_errors = in_be32(&fec->rmon_r_crc_align) + + in_be32(&fec->rmon_r_undersize) + + in_be32(&fec->rmon_r_oversize) + + in_be32(&fec->rmon_r_frag) + + in_be32(&fec->rmon_r_jab); + + stats->tx_bytes = in_be32(&fec->rmon_t_octets); + stats->tx_packets = in_be32(&fec->rmon_t_packets); + stats->tx_errors = in_be32(&fec->rmon_t_crc_align) + + in_be32(&fec->rmon_t_undersize) + + in_be32(&fec->rmon_t_oversize) + + in_be32(&fec->rmon_t_frag) + + in_be32(&fec->rmon_t_jab); + + stats->multicast = in_be32(&fec->rmon_r_mc_pkt); + stats->collisions = in_be32(&fec->rmon_t_col); + + /* detailed rx_errors: */ + stats->rx_length_errors = in_be32(&fec->rmon_r_undersize) + + in_be32(&fec->rmon_r_oversize) + + in_be32(&fec->rmon_r_frag) + + in_be32(&fec->rmon_r_jab); + stats->rx_over_errors = in_be32(&fec->r_macerr); + stats->rx_crc_errors = in_be32(&fec->ieee_r_crc); + stats->rx_frame_errors = in_be32(&fec->ieee_r_align); + stats->rx_fifo_errors = in_be32(&fec->rmon_r_drop); + stats->rx_missed_errors = in_be32(&fec->rmon_r_drop); + + /* detailed tx_errors: */ + stats->tx_aborted_errors = 0; + stats->tx_carrier_errors = in_be32(&fec->ieee_t_cserr); + stats->tx_fifo_errors = in_be32(&fec->rmon_t_drop); + stats->tx_heartbeat_errors = in_be32(&fec->ieee_t_sqe); + stats->tx_window_errors = in_be32(&fec->ieee_t_lcol); + + return stats; +} + +/* + * Read MIB counters in order to reset them, + * then zero all the stats fields in memory + */ +static void fec_reset_stats(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + + out_be32(&fec->mib_control, FEC_MIB_DISABLE); + memset_io(&fec->rmon_t_drop, 0, + (u32)&fec->reserved10 - (u32)&fec->rmon_t_drop); + out_be32(&fec->mib_control, 0); + + memset(&priv->stats, 0, sizeof(priv->stats)); +} + +/* + * Set or clear the multicast filter for this adaptor. + */ +static void fec_set_multicast_list(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + u32 rx_control; + + rx_control = in_be32(&fec->r_cntrl); + + if (dev->flags & IFF_PROMISC) { + rx_control |= FEC_RCNTRL_PROM; + out_be32(&fec->r_cntrl, rx_control); + } else { + rx_control &= ~FEC_RCNTRL_PROM; + out_be32(&fec->r_cntrl, rx_control); + + if (dev->flags & IFF_ALLMULTI) { + out_be32(&fec->gaddr1, 0xffffffff); + out_be32(&fec->gaddr2, 0xffffffff); + } else { + u32 crc; + int i; + struct dev_mc_list *dmi; + u32 gaddr1 = 0x00000000; + u32 gaddr2 = 0x00000000; + + dmi = dev->mc_list; + for (i=0; i<dev->mc_count; i++) { + crc = ether_crc_le(6, dmi->dmi_addr) >> 26; + if (crc >= 32) + gaddr1 |= 1 << (crc-32); + else + gaddr2 |= 1 << crc; + dmi = dmi->next; + } + out_be32(&fec->gaddr1, gaddr1); + out_be32(&fec->gaddr2, gaddr2); + } + } +} + +static void __init fec_str2mac(char *str, unsigned char *mac) +{ + int i; + u64 val64; + + val64 = simple_strtoull(str, NULL, 16); + + for (i = 0; i < 6; i++) + mac[5-i] = val64 >> (i*8); +} + +static int __init mpc52xx_fec_mac_setup(char *mac_address) +{ + fec_str2mac(mac_address, mpc52xx_fec_mac_addr); + return 0; +} + +/* XXX do we need this? */ +__setup("mpc52xx-mac=", mpc52xx_fec_mac_setup); + +/** + * fec_hw_init + * @dev: network device + * + * Setup various hardware setting, only needed once on start + */ +static void fec_hw_init(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + int i; + + /* Whack a reset. We should wait for this. */ + out_be32(&fec->ecntrl, FEC_ECNTRL_RESET); + for (i = 0; i < FEC_RESET_DELAY; ++i) { + if ((in_be32(&fec->ecntrl) & FEC_ECNTRL_RESET) == 0) + break; + udelay(1); + } + if (i == FEC_RESET_DELAY) + dev_err(&dev->dev, "FEC Reset timeout!\n"); + + /* set pause to 0x20 frames */ + out_be32(&fec->op_pause, FEC_OP_PAUSE_OPCODE | 0x20); + + /* high service request will be deasserted when there's < 7 bytes in fifo + * low service request will be deasserted when there's < 4*7 bytes in fifo + */ + out_be32(&fec->rfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7); + out_be32(&fec->tfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7); + + /* alarm when <= x bytes in FIFO */ + out_be32(&fec->rfifo_alarm, 0x0000030c); + out_be32(&fec->tfifo_alarm, 0x00000100); + + /* begin transmittion when 256 bytes are in FIFO (or EOF or FIFO full) */ + out_be32(&fec->x_wmrk, FEC_FIFO_WMRK_256B); + + /* enable crc generation */ + out_be32(&fec->xmit_fsm, FEC_XMIT_FSM_APPEND_CRC | FEC_XMIT_FSM_ENABLE_CRC); + out_be32(&fec->iaddr1, 0x00000000); /* No individual filter */ + out_be32(&fec->iaddr2, 0x00000000); /* No individual filter */ + + /* set phy speed and enable MII interrupt + * this can't be done in phy driver, since it needs to be called + * before fec stuff (even on resume) */ + set_phy_speed(fec, priv->phy_speed); + out_be32(&fec->imask, in_be32(&fec->imask) | FEC_IMASK_MII); +} + +/** + * fec_start + * @dev: network device + * + * This function is called to start or restart the FEC during a link + * change. This happens on fifo errors or when switching between half + * and full duplex. + */ +static void fec_start(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + u32 rcntrl; + u32 tcntrl; + u32 tmp; + + /* clear sticky error bits */ + tmp = FEC_FIFO_STATUS_ERR | FEC_FIFO_STATUS_UF | FEC_FIFO_STATUS_OF; + out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & tmp); + out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & tmp); + + /* FIFOs will reset on fec_enable */ + out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_ENABLE_IS_RESET); + + /* Set station address. */ + fec_set_paddr(dev, dev->dev_addr); + + fec_set_multicast_list(dev); + + /* set max frame len, enable flow control, select mii mode */ + rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */ + rcntrl |= FEC_RCNTRL_FCE; + rcntrl |= MII_RCNTL_MODE; + if (priv->duplex == DUPLEX_FULL) + tcntrl = FEC_TCNTRL_FDEN; /* FD enable */ + else { + rcntrl |= FEC_RCNTRL_DRT; /* disable Rx on Tx (HD) */ + tcntrl = 0; + } + out_be32(&fec->r_cntrl, rcntrl); + out_be32(&fec->x_cntrl, tcntrl); + + /* Clear any outstanding interrupt. */ + out_be32(&fec->ievent, 0xffffffff); + + /* Enable interrupts we wish to service. */ + out_be32(&fec->imask, FEC_IMASK_ENABLE); + + /* And last, enable the transmit and receive processing. */ + out_be32(&fec->ecntrl, FEC_ECNTRL_ETHER_EN); + out_be32(&fec->r_des_active, 0x01000000); + + priv->tx_full = 0; +} + +/** + * fec_stop + * @dev: network device + * + * stop all activity on fec and empty dma buffers + */ +static void fec_stop(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + unsigned long timeout; + + out_be32(&fec->imask, FEC_IMASK_MII); /* disable all but MII interrupt */ + + /* Disable the rx and tx tasks. */ + bcom_disable(priv->rx_dmatsk); + + /* Wait for queues to drain, but only if we're in process context */ + if (!in_interrupt()) { + timeout = jiffies + 2*HZ; + while (time_before(jiffies, timeout) && + (!bcom_queue_empty(priv->tx_dmatsk) || + !bcom_queue_empty(priv->rx_dmatsk))) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/10); + } + if (time_after_eq(jiffies, timeout)) + dev_err(&dev->dev, "queues didn't drain\n"); + } + + bcom_disable(priv->tx_dmatsk); + + /* Stop FEC */ + out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~FEC_ECNTRL_ETHER_EN); + + return; +} + +static int fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&rq->ifr_data; + + return phy_mii_ioctl(priv->phydev, mii, cmd); +} + +/* ======================================================================== */ +/* OF Driver */ +/* ======================================================================== */ + +static int __devinit +mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match) +{ + int rv; + struct net_device *ndev; + struct fec_priv *priv = NULL; + struct resource mem; + + phys_addr_t rx_fifo; + phys_addr_t tx_fifo; + + /* Get the ether ndev & it's private zone */ + ndev = alloc_etherdev(sizeof(struct fec_priv)); + if (!ndev) + return -ENOMEM; + + priv = netdev_priv(ndev); + + priv->ofdev = op; + + /* Reserve FEC control zone */ + rv = of_address_to_resource(op->node, 0, &mem); + if (rv) { + printk(KERN_ERR DRIVER_NAME ": " + "Error while parsing device node resource\n" ); + return rv; + } + if ((mem.end - mem.start + 1) != sizeof(struct mpc52xx_fec)) { + printk(KERN_ERR DRIVER_NAME + " - invalid resource size (%lx != %x), check mpc52xx_devices.c\n", + (unsigned long)(mem.end - mem.start + 1), sizeof(struct mpc52xx_fec)); + return -EINVAL; + } + + if (!request_mem_region(mem.start, sizeof(struct mpc52xx_fec), DRIVER_NAME)) + return -EBUSY; + + /* Init ether ndev with what we have */ + ndev->open = fec_open; + ndev->stop = fec_close; + ndev->hard_start_xmit = fec_hard_start_xmit; + ndev->do_ioctl = fec_ioctl; + ndev->get_stats = fec_get_stats; + ndev->set_mac_address = fec_set_mac_address; + ndev->set_multicast_list = fec_set_multicast_list; + ndev->tx_timeout = fec_tx_timeout; + ndev->watchdog_timeo = FEC_WATCHDOG_TIMEOUT; + ndev->flags &= ~IFF_RUNNING; + ndev->base_addr = mem.start; + + priv->t_irq = priv->r_irq = ndev->irq = NO_IRQ; /* IRQ are free for now */ + + spin_lock_init(&priv->lock); + + /* ioremap the zones */ + priv->fec = ioremap(mem.start, sizeof(struct mpc52xx_fec)); + + if (!priv->fec) { + rv = -ENOMEM; + goto probe_error; + } + + /* Bestcomm init */ + rx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, rfifo_data); + tx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, tfifo_data); + + priv->rx_dmatsk = bcom_fec_rx_init(FEC_RX_NUM_BD, rx_fifo, FEC_RX_BUFFER_SIZE); + priv->tx_dmatsk = bcom_fec_tx_init(FEC_TX_NUM_BD, tx_fifo); + + if (!priv->rx_dmatsk || !priv->tx_dmatsk) { + printk(KERN_ERR DRIVER_NAME ": Can not init SDMA tasks\n" ); + rv = -ENOMEM; + goto probe_error; + } + + /* Get the IRQ we need one by one */ + /* Control */ + ndev->irq = irq_of_parse_and_map(op->node, 0); + + /* RX */ + priv->r_irq = bcom_get_task_irq(priv->rx_dmatsk); + + /* TX */ + priv->t_irq = bcom_get_task_irq(priv->tx_dmatsk); + + /* MAC address init */ + if (memcmp(mpc52xx_fec_mac_addr, null_mac, 6) != 0) + memcpy(ndev->dev_addr, mpc52xx_fec_mac_addr, 6); + else + fec_get_paddr(ndev, ndev->dev_addr); + + /* Phy speed */ + priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1; + + priv->msg_enable = (NETIF_MSG_IFUP << 1) - 1; + priv->duplex = DUPLEX_HALF; + + /* Hardware init */ + fec_hw_init(ndev); + + fec_reset_stats(ndev); + + /* Register the new network device */ + rv = register_netdev(ndev); + if (rv < 0) + goto probe_error; + + /* We're done ! */ + dev_set_drvdata(&op->dev, ndev); + + return 0; + + + /* Error handling - free everything that might be allocated */ +probe_error: + + irq_dispose_mapping(ndev->irq); + + if (priv->rx_dmatsk) + bcom_fec_rx_release(priv->rx_dmatsk); + if (priv->tx_dmatsk) + bcom_fec_tx_release(priv->tx_dmatsk); + + if (priv->fec) + iounmap(priv->fec); + + release_mem_region(mem.start, sizeof(struct mpc52xx_fec)); + + free_netdev(ndev); + + return rv; +} + +static int +mpc52xx_fec_remove(struct of_device *op) +{ + struct net_device *ndev; + struct fec_priv *priv; + + ndev = dev_get_drvdata(&op->dev); + if (!ndev) + return 0; + priv = netdev_priv(ndev); + + unregister_netdev(ndev); + + irq_dispose_mapping(ndev->irq); + + bcom_fec_rx_release(priv->rx_dmatsk); + bcom_fec_tx_release(priv->tx_dmatsk); + + iounmap(priv->fec); + + release_mem_region(ndev->base_addr, sizeof(struct mpc52xx_fec)); + + free_netdev(ndev); + + dev_set_drvdata(&op->dev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state) +{ + struct net_device *dev = dev_get_drvdata(&op->dev); + + if (netif_running(dev)) + fec_close(dev); + + return 0; +} + +static int mpc52xx_fec_of_resume(struct of_device *op) +{ + struct net_device *dev = dev_get_drvdata(&op->dev); + + fec_hw_init(dev); + fec_reset_stats(dev); + + if (netif_running(dev)) + fec_open(dev); + + return 0; +} +#endif + +static struct of_device_id mpc52xx_fec_match[] = { + { + .type = "network", + .compatible = "mpc5200-fec", + }, + { } +}; + +MODULE_DEVICE_TABLE(of, mpc52xx_fec_match); + +static struct of_platform_driver mpc52xx_fec_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = mpc52xx_fec_match, + .probe = mpc52xx_fec_probe, + .remove = mpc52xx_fec_remove, +#ifdef CONFIG_PM + .suspend = mpc52xx_fec_of_suspend, + .resume = mpc52xx_fec_of_resume, +#endif +}; + + +/* ======================================================================== */ +/* Module */ +/* ======================================================================== */ + +static int __init +mpc52xx_fec_init(void) +{ + int ret; + if ((ret = fec_mdio_init())) { + printk(KERN_ERR "%s: %i fec_mdio_init failed\n", __func__, __LINE__); + return ret; + } + + return of_register_platform_driver(&mpc52xx_fec_driver); +} + +static void __exit +mpc52xx_fec_exit(void) +{ + of_unregister_platform_driver(&mpc52xx_fec_driver); + fec_mdio_exit(); +} + + +module_init(mpc52xx_fec_init); +module_exit(mpc52xx_fec_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dale Farnsworth"); +MODULE_DESCRIPTION("Ethernet driver for the Freescale MPC52xx FEC"); + diff -pruN dummy/fec.h ./drivers/net/fec_mpc52xx/fec.h --- dummy/fec.h 1970-01-01 01:00:00.000000000 +0100 +++ ./drivers/net/fec_mpc52xx/fec.h 2007-08-10 11:24:37.000000000 +0200 @@ -0,0 +1,299 @@ +/* + * drivers/net/fec_mpc52xx/fec.h + * + * Driver for the MPC5200 Fast Ethernet Controller + * + * Author: Dale Farnsworth <dfarnsworth@mvista.com> + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __DRIVERS_NET_MPC52XX_FEC_H__ +#define __DRIVERS_NET_MPC52XX_FEC_H__ + +#include <linux/mii.h> // XXX, still needed? +#include <linux/phy.h> + +/* Tunable constant */ +/* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */ +#define FEC_RX_BUFFER_SIZE 1522 /* max receive packet size */ +#define FEC_RX_NUM_BD 64 +#define FEC_TX_NUM_BD 64 + +#define FEC_RESET_DELAY 50 /* uS */ + +#define FEC_WATCHDOG_TIMEOUT ((400*HZ)/1000) + +struct fec_priv { + int duplex; + int tx_full; + int r_irq; + int t_irq; + struct mpc52xx_fec __iomem *fec; + struct bcom_task *rx_dmatsk; + struct bcom_task *tx_dmatsk; + spinlock_t lock; + struct net_device_stats stats; + int msg_enable; +#ifdef CONFIG_FEC_MPC52xx_MDIO + uint phy_speed; + + struct phy_device *phydev; + enum phy_state link; + int speed; + + struct of_device *ofdev; +#endif /* CONFIG_FEC_MPC52xx_MDIO */ +}; + + +/* ======================================================================== */ +/* Hardware register sets & bits */ +/* ======================================================================== */ + +struct mpc52xx_fec { + u32 fec_id; /* FEC + 0x000 */ + u32 ievent; /* FEC + 0x004 */ + u32 imask; /* FEC + 0x008 */ + + u32 reserved0[1]; /* FEC + 0x00C */ + u32 r_des_active; /* FEC + 0x010 */ + u32 x_des_active; /* FEC + 0x014 */ + u32 r_des_active_cl; /* FEC + 0x018 */ + u32 x_des_active_cl; /* FEC + 0x01C */ + u32 ivent_set; /* FEC + 0x020 */ + u32 ecntrl; /* FEC + 0x024 */ + + u32 reserved1[6]; /* FEC + 0x028-03C */ + u32 mii_data; /* FEC + 0x040 */ + u32 mii_speed; /* FEC + 0x044 */ + u32 mii_status; /* FEC + 0x048 */ + + u32 reserved2[5]; /* FEC + 0x04C-05C */ + u32 mib_data; /* FEC + 0x060 */ + u32 mib_control; /* FEC + 0x064 */ + + u32 reserved3[6]; /* FEC + 0x068-7C */ + u32 r_activate; /* FEC + 0x080 */ + u32 r_cntrl; /* FEC + 0x084 */ + u32 r_hash; /* FEC + 0x088 */ + u32 r_data; /* FEC + 0x08C */ + u32 ar_done; /* FEC + 0x090 */ + u32 r_test; /* FEC + 0x094 */ + u32 r_mib; /* FEC + 0x098 */ + u32 r_da_low; /* FEC + 0x09C */ + u32 r_da_high; /* FEC + 0x0A0 */ + + u32 reserved4[7]; /* FEC + 0x0A4-0BC */ + u32 x_activate; /* FEC + 0x0C0 */ + u32 x_cntrl; /* FEC + 0x0C4 */ + u32 backoff; /* FEC + 0x0C8 */ + u32 x_data; /* FEC + 0x0CC */ + u32 x_status; /* FEC + 0x0D0 */ + u32 x_mib; /* FEC + 0x0D4 */ + u32 x_test; /* FEC + 0x0D8 */ + u32 fdxfc_da1; /* FEC + 0x0DC */ + u32 fdxfc_da2; /* FEC + 0x0E0 */ + u32 paddr1; /* FEC + 0x0E4 */ + u32 paddr2; /* FEC + 0x0E8 */ + u32 op_pause; /* FEC + 0x0EC */ + + u32 reserved5[4]; /* FEC + 0x0F0-0FC */ + u32 instr_reg; /* FEC + 0x100 */ + u32 context_reg; /* FEC + 0x104 */ + u32 test_cntrl; /* FEC + 0x108 */ + u32 acc_reg; /* FEC + 0x10C */ + u32 ones; /* FEC + 0x110 */ + u32 zeros; /* FEC + 0x114 */ + u32 iaddr1; /* FEC + 0x118 */ + u32 iaddr2; /* FEC + 0x11C */ + u32 gaddr1; /* FEC + 0x120 */ + u32 gaddr2; /* FEC + 0x124 */ + u32 random; /* FEC + 0x128 */ + u32 rand1; /* FEC + 0x12C */ + u32 tmp; /* FEC + 0x130 */ + + u32 reserved6[3]; /* FEC + 0x134-13C */ + u32 fifo_id; /* FEC + 0x140 */ + u32 x_wmrk; /* FEC + 0x144 */ + u32 fcntrl; /* FEC + 0x148 */ + u32 r_bound; /* FEC + 0x14C */ + u32 r_fstart; /* FEC + 0x150 */ + u32 r_count; /* FEC + 0x154 */ + u32 r_lag; /* FEC + 0x158 */ + u32 r_read; /* FEC + 0x15C */ + u32 r_write; /* FEC + 0x160 */ + u32 x_count; /* FEC + 0x164 */ + u32 x_lag; /* FEC + 0x168 */ + u32 x_retry; /* FEC + 0x16C */ + u32 x_write; /* FEC + 0x170 */ + u32 x_read; /* FEC + 0x174 */ + + u32 reserved7[2]; /* FEC + 0x178-17C */ + u32 fm_cntrl; /* FEC + 0x180 */ + u32 rfifo_data; /* FEC + 0x184 */ + u32 rfifo_status; /* FEC + 0x188 */ + u32 rfifo_cntrl; /* FEC + 0x18C */ + u32 rfifo_lrf_ptr; /* FEC + 0x190 */ + u32 rfifo_lwf_ptr; /* FEC + 0x194 */ + u32 rfifo_alarm; /* FEC + 0x198 */ + u32 rfifo_rdptr; /* FEC + 0x19C */ + u32 rfifo_wrptr; /* FEC + 0x1A0 */ + u32 tfifo_data; /* FEC + 0x1A4 */ + u32 tfifo_status; /* FEC + 0x1A8 */ + u32 tfifo_cntrl; /* FEC + 0x1AC */ + u32 tfifo_lrf_ptr; /* FEC + 0x1B0 */ + u32 tfifo_lwf_ptr; /* FEC + 0x1B4 */ + u32 tfifo_alarm; /* FEC + 0x1B8 */ + u32 tfifo_rdptr; /* FEC + 0x1BC */ + u32 tfifo_wrptr; /* FEC + 0x1C0 */ + + u32 reset_cntrl; /* FEC + 0x1C4 */ + u32 xmit_fsm; /* FEC + 0x1C8 */ + + u32 reserved8[3]; /* FEC + 0x1CC-1D4 */ + u32 rdes_data0; /* FEC + 0x1D8 */ + u32 rdes_data1; /* FEC + 0x1DC */ + u32 r_length; /* FEC + 0x1E0 */ + u32 x_length; /* FEC + 0x1E4 */ + u32 x_addr; /* FEC + 0x1E8 */ + u32 cdes_data; /* FEC + 0x1EC */ + u32 status; /* FEC + 0x1F0 */ + u32 dma_control; /* FEC + 0x1F4 */ + u32 des_cmnd; /* FEC + 0x1F8 */ + u32 data; /* FEC + 0x1FC */ + + u32 rmon_t_drop; /* FEC + 0x200 */ + u32 rmon_t_packets; /* FEC + 0x204 */ + u32 rmon_t_bc_pkt; /* FEC + 0x208 */ + u32 rmon_t_mc_pkt; /* FEC + 0x20C */ + u32 rmon_t_crc_align; /* FEC + 0x210 */ + u32 rmon_t_undersize; /* FEC + 0x214 */ + u32 rmon_t_oversize; /* FEC + 0x218 */ + u32 rmon_t_frag; /* FEC + 0x21C */ + u32 rmon_t_jab; /* FEC + 0x220 */ + u32 rmon_t_col; /* FEC + 0x224 */ + u32 rmon_t_p64; /* FEC + 0x228 */ + u32 rmon_t_p65to127; /* FEC + 0x22C */ + u32 rmon_t_p128to255; /* FEC + 0x230 */ + u32 rmon_t_p256to511; /* FEC + 0x234 */ + u32 rmon_t_p512to1023; /* FEC + 0x238 */ + u32 rmon_t_p1024to2047; /* FEC + 0x23C */ + u32 rmon_t_p_gte2048; /* FEC + 0x240 */ + u32 rmon_t_octets; /* FEC + 0x244 */ + u32 ieee_t_drop; /* FEC + 0x248 */ + u32 ieee_t_frame_ok; /* FEC + 0x24C */ + u32 ieee_t_1col; /* FEC + 0x250 */ + u32 ieee_t_mcol; /* FEC + 0x254 */ + u32 ieee_t_def; /* FEC + 0x258 */ + u32 ieee_t_lcol; /* FEC + 0x25C */ + u32 ieee_t_excol; /* FEC + 0x260 */ + u32 ieee_t_macerr; /* FEC + 0x264 */ + u32 ieee_t_cserr; /* FEC + 0x268 */ + u32 ieee_t_sqe; /* FEC + 0x26C */ + u32 t_fdxfc; /* FEC + 0x270 */ + u32 ieee_t_octets_ok; /* FEC + 0x274 */ + + u32 reserved9[2]; /* FEC + 0x278-27C */ + u32 rmon_r_drop; /* FEC + 0x280 */ + u32 rmon_r_packets; /* FEC + 0x284 */ + u32 rmon_r_bc_pkt; /* FEC + 0x288 */ + u32 rmon_r_mc_pkt; /* FEC + 0x28C */ + u32 rmon_r_crc_align; /* FEC + 0x290 */ + u32 rmon_r_undersize; /* FEC + 0x294 */ + u32 rmon_r_oversize; /* FEC + 0x298 */ + u32 rmon_r_frag; /* FEC + 0x29C */ + u32 rmon_r_jab; /* FEC + 0x2A0 */ + + u32 rmon_r_resvd_0; /* FEC + 0x2A4 */ + + u32 rmon_r_p64; /* FEC + 0x2A8 */ + u32 rmon_r_p65to127; /* FEC + 0x2AC */ + u32 rmon_r_p128to255; /* FEC + 0x2B0 */ + u32 rmon_r_p256to511; /* FEC + 0x2B4 */ + u32 rmon_r_p512to1023; /* FEC + 0x2B8 */ + u32 rmon_r_p1024to2047; /* FEC + 0x2BC */ + u32 rmon_r_p_gte2048; /* FEC + 0x2C0 */ + u32 rmon_r_octets; /* FEC + 0x2C4 */ + u32 ieee_r_drop; /* FEC + 0x2C8 */ + u32 ieee_r_frame_ok; /* FEC + 0x2CC */ + u32 ieee_r_crc; /* FEC + 0x2D0 */ + u32 ieee_r_align; /* FEC + 0x2D4 */ + u32 r_macerr; /* FEC + 0x2D8 */ + u32 r_fdxfc; /* FEC + 0x2DC */ + u32 ieee_r_octets_ok; /* FEC + 0x2E0 */ + + u32 reserved10[7]; /* FEC + 0x2E4-2FC */ + + u32 reserved11[64]; /* FEC + 0x300-3FF */ +}; + +#define FEC_MIB_DISABLE 0x80000000 + +#define FEC_IEVENT_HBERR 0x80000000 +#define FEC_IEVENT_BABR 0x40000000 +#define FEC_IEVENT_BABT 0x20000000 +#define FEC_IEVENT_GRA 0x10000000 +#define FEC_IEVENT_TFINT 0x08000000 +#define FEC_IEVENT_MII 0x00800000 +#define FEC_IEVENT_LATE_COL 0x00200000 +#define FEC_IEVENT_COL_RETRY_LIM 0x00100000 +#define FEC_IEVENT_XFIFO_UN 0x00080000 +#define FEC_IEVENT_XFIFO_ERROR 0x00040000 +#define FEC_IEVENT_RFIFO_ERROR 0x00020000 + +#define FEC_IMASK_HBERR 0x80000000 +#define FEC_IMASK_BABR 0x40000000 +#define FEC_IMASK_BABT 0x20000000 +#define FEC_IMASK_GRA 0x10000000 +#define FEC_IMASK_MII 0x00800000 +#define FEC_IMASK_LATE_COL 0x00200000 +#define FEC_IMASK_COL_RETRY_LIM 0x00100000 +#define FEC_IMASK_XFIFO_UN 0x00080000 +#define FEC_IMASK_XFIFO_ERROR 0x00040000 +#define FEC_IMASK_RFIFO_ERROR 0x00020000 + +#define FEC_RCNTRL_MAX_FL_SHIFT 16 +#define FEC_RCNTRL_LOOP 0x01 +#define FEC_RCNTRL_DRT 0x02 +#define FEC_RCNTRL_MII_MODE 0x04 +#define FEC_RCNTRL_PROM 0x08 +#define FEC_RCNTRL_BC_REJ 0x10 +#define FEC_RCNTRL_FCE 0x20 + +#define FEC_TCNTRL_GTS 0x00000001 +#define FEC_TCNTRL_HBC 0x00000002 +#define FEC_TCNTRL_FDEN 0x00000004 +#define FEC_TCNTRL_TFC_PAUSE 0x00000008 +#define FEC_TCNTRL_RFC_PAUSE 0x00000010 + +#define FEC_ECNTRL_RESET 0x00000001 +#define FEC_ECNTRL_ETHER_EN 0x00000002 + +#define FEC_PADDR2_TYPE 0x8808 + +#define FEC_OP_PAUSE_OPCODE 0x00010000 + +#define FEC_FIFO_WMRK_256B 0x3 + +#define FEC_FIFO_STATUS_ERR 0x00400000 +#define FEC_FIFO_STATUS_UF 0x00200000 +#define FEC_FIFO_STATUS_OF 0x00100000 + +#define FEC_FIFO_CNTRL_FRAME 0x08000000 +#define FEC_FIFO_CNTRL_LTG_7 0x07000000 + +#define FEC_RESET_CNTRL_RESET_FIFO 0x02000000 +#define FEC_RESET_CNTRL_ENABLE_IS_RESET 0x01000000 + +#define FEC_XMIT_FSM_APPEND_CRC 0x02000000 +#define FEC_XMIT_FSM_ENABLE_CRC 0x01000000 + + +int __init fec_mdio_init(void); +void __exit fec_mdio_exit(void); + +#endif /* __DRIVERS_NET_MPC52XX_FEC_H__ */ diff -pruN dummy/fec_phy.c ./drivers/net/fec_mpc52xx/fec_phy.c --- dummy/fec_phy.c 1970-01-01 01:00:00.000000000 +0100 +++ ./drivers/net/fec_mpc52xx/fec_phy.c 2007-08-10 10:59:53.000000000 +0200 @@ -0,0 +1,229 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/phy.h> +#include <asm/io.h> +#include <asm/mpc52xx.h> +#include <asm/of_platform.h> +#include "fec_phy.h" +#include "fec.h" + +struct fec_mdio_priv { + int completed; + wait_queue_head_t wq; + struct mpc52xx_fec __iomem *regs; + int irq; +}; + +static int fec_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct fec_mdio_priv *priv = bus->priv; + int tries = 100; + + u32 request = FEC_MII_READ_FRAME; + request |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; + request |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; + + out_be32(&priv->regs->mii_data, request); + + /* wait for it to finish, this takes about 23 us on lite5200b */ + while (priv->completed == 0 && tries--) + udelay(5); + + priv->completed = 0; + + if (tries == 0) + return -ETIMEDOUT; + + return in_be32(&priv->regs->mii_data) & FEC_MII_DATA_DATAMSK; +} + +static int fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data) +{ + struct fec_mdio_priv *priv = bus->priv; + u32 value = data; + int tries = 100; + + value |= FEC_MII_WRITE_FRAME; + value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; + value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; + + out_be32(&priv->regs->mii_data, value); + + /* wait for request to finish */ + while (priv->completed == 0 && tries--) + udelay(5); + + priv->completed = 0; + + if (tries == 0) + return -ETIMEDOUT; + + return 0; +} + +static irqreturn_t fec_mdio_interrupt(int irq, void *dev_id) +{ + struct fec_mdio_priv *priv = dev_id; + struct mpc52xx_fec __iomem *fec; + int ievent; + + fec = priv->regs; + ievent = in_be32(&fec->ievent); + + ievent &= FEC_IEVENT_MII; + if (!ievent) + return IRQ_NONE; + + out_be32(&fec->ievent, ievent); + + priv->completed = 1; + wake_up(&priv->wq); + + return IRQ_HANDLED; +} + +static int fec_mdio_probe(struct of_device *of, const struct of_device_id *match) +{ + struct device *dev = &of->dev; + struct device_node *np = of->node; + struct device_node *child = NULL; + struct mii_bus *bus; + struct fec_mdio_priv *priv; + struct resource res = {}; + int err; + int i; + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (bus == NULL) + return -ENOMEM; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) { + err = -ENOMEM; + goto out_free; + } + + bus->name = "mpc52xx MII bus"; + bus->read = fec_mdio_read; + bus->write = fec_mdio_write; + + /* setup irqs */ + bus->irq = kcalloc(sizeof(bus->irq[0]), PHY_MAX_ADDR, GFP_KERNEL); + if (bus->irq == NULL) { + err = -ENOMEM; + goto out_free; + } + for (i=0; i<PHY_MAX_ADDR; i++) + bus->irq[i] = PHY_POLL; + + while ((child = of_get_next_child(np, child)) != NULL) { + int irq = irq_of_parse_and_map(child, 0); + if (irq != NO_IRQ) { + const u32 *id = of_get_property(child, "reg", NULL); + bus->irq[*id] = irq; + } + } + + /* setup registers */ + err = of_address_to_resource(np, 0, &res); + if (err) + goto out_free; + priv->regs = ioremap(res.start, res.end - res.start + 1); + if (priv->regs == NULL) { + err = -ENOMEM; + goto out_free; + } + + priv->irq = irq_of_parse_and_map(np, 0); + err = request_irq(priv->irq, &fec_mdio_interrupt, IRQF_DISABLED | IRQF_SHARED, + "fec_mdio", priv); + if (err) { + printk(KERN_ERR "%s: interrupt request failed with %i\n", __func__, err); + goto out_unmap; + } + + bus->id = res.start; + bus->priv = priv; + + bus->dev = dev; + dev_set_drvdata(dev, bus); + + init_waitqueue_head(&priv->wq); + + /* set MII speed */ + out_be32(&priv->regs->mii_speed, ((mpc52xx_find_ipb_freq(of->node) >> 20) / 5) << 1); + + /* enable MII interrupt */ + out_be32(&priv->regs->imask, in_be32(&priv->regs->imask) | FEC_IMASK_MII); + + err = mdiobus_register(bus); + if (err) + goto out_free_irq; + + return 0; + + out_free_irq: + free_irq(priv->irq, dev); + irq_dispose_mapping(priv->irq); + out_unmap: + iounmap(priv->regs); + out_free: + for (i=0; i<PHY_MAX_ADDR; i++) + if (bus->irq[i]) + irq_dispose_mapping(bus->irq[i]); + kfree(bus->irq); + kfree(priv); + kfree(bus); + + return err; +} + +static int fec_mdio_remove(struct of_device *of) +{ + struct device *dev = &of->dev; + struct mii_bus *bus = dev_get_drvdata(dev); + struct fec_mdio_priv *priv = bus->priv; + int i; + + mdiobus_unregister(bus); + dev_set_drvdata(dev, NULL); + + free_irq(priv->irq, dev); + irq_dispose_mapping(priv->irq); + iounmap(priv->regs); + for (i=0; i<PHY_MAX_ADDR; i++) + if (bus->irq[i]) + irq_dispose_mapping(bus->irq[i]); + kfree(priv); + kfree(bus->irq); + kfree(bus); + + return 0; +} + + +static struct of_device_id fec_mdio_match[] = { + { + .type = "mdio", + .compatible = "mpc5200b-fec-phy", + }, + {}, +}; + +static struct of_platform_driver fec_mdio_driver = { + .name = "mpc5200b-fec-phy", + .probe = fec_mdio_probe, + .remove = fec_mdio_remove, + .match_table = fec_mdio_match, +}; + + +int __init fec_mdio_init(void) +{ + return of_register_platform_driver(&fec_mdio_driver); +} + +void __exit fec_mdio_exit(void) +{ + of_unregister_platform_driver(&fec_mdio_driver); +} diff -pruN dummy/fec_phy.h ./drivers/net/fec_mpc52xx/fec_phy.h --- dummy/fec_phy.h 1970-01-01 01:00:00.000000000 +0100 +++ ./drivers/net/fec_mpc52xx/fec_phy.h 2007-08-10 11:22:54.000000000 +0200 @@ -0,0 +1,49 @@ +/* + * arch/ppc/52xx_io/fec_phy.h + * + * Driver for the MPC5200 Fast Ethernet Controller + * Based heavily on the MII support for the MPC8xx by Dan Malek + * + * Author: Dale Farnsworth <dfarnsworth@mvista.com> + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#define FEC_IMASK_ALL (FEC_IMASK_HBERR | FEC_IMASK_BABR | \ + FEC_IMASK_BABT | FEC_IMASK_GRA | FEC_IMASK_MII | \ + FEC_IMASK_LATE_COL | FEC_IMASK_COL_RETRY_LIM | \ + FEC_IMASK_XFIFO_UN | FEC_IMASK_XFIFO_ERROR | \ + FEC_IMASK_RFIFO_ERROR) + +#ifdef CONFIG_FEC_MPC52xx_MDIO +#define MII_RCNTL_MODE FEC_RCNTRL_MII_MODE +#define FEC_IMASK_ENABLE FEC_IMASK_ALL +#define set_phy_speed(fec, s) out_be32(&fec->mii_speed, s) +#else +#define MII_RCNTL_MODE 0 +#define FEC_IMASK_ENABLE (FEC_IMASK_ALL & ~FEC_IMASK_MII) +#define set_phy_speed(fec, s) do { } while (0) +#define fec_mii_start(dev) do { } while (0) +#define fec_mii(dev) printk(KERN_WARNING "unexpected FEC_IEVENT_MII\n") +#define fec_mii_init(dev) do { } while (0) +#define fec_mii_suspend(dev) do { } while (0) +#define fec_mii_resume(dev) do { } while (0) +#endif /* CONFIG_FEC_MPC52xx_MDIO */ + +/* MII-related definitions */ +#define FEC_MII_DATA_ST 0x40000000 /* Start frame */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform read */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform write */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data mask */ + +#define FEC_MII_READ_FRAME (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA) +#define FEC_MII_WRITE_FRAME (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | FEC_MII_DATA_TA) + +#define FEC_MII_DATA_RA_SHIFT 0x12 /* MII reg addr bits */ +#define FEC_MII_DATA_PA_SHIFT 0x17 /* MII PHY addr bits */ diff -pruN dummy/Kconfig ./drivers/net/fec_mpc52xx/Kconfig --- dummy/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ ./drivers/net/fec_mpc52xx/Kconfig 2007-08-08 10:50:04.000000000 +0200 @@ -0,0 +1,24 @@ +menu "MPC5200 Networking Options" + depends PPC_MPC52xx && NET_ETHERNET + +config FEC_MPC52xx + tristate "FEC Ethernet" + depends on NET_ETHERNET + select PPC_BESTCOMM + select PPC_BESTCOMM_FEC + select CRC32 + ---help--- + This option enables support for the MPC5200's on-chip + Fast Ethernet Controller + +config USE_MDIO + bool "Use external Ethernet MII PHY" + select MII + depends FEC_MPC52xx + ---help--- + The MPC5200's FEC can connect to the Ethernet either with + an external MII PHY chip or 10 Mbps 7-wire interface + (Motorola? industry standard). + If your board uses an external PHY, say y, else n. + +endmenu diff -pruN dummy/Makefile ./drivers/net/fec_mpc52xx/Makefile --- dummy/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ ./drivers/net/fec_mpc52xx/Makefile 2007-08-08 10:50:04.000000000 +0200 @@ -0,0 +1,7 @@ +obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o + +fec_mpc52xx-objs := fec.o + +ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) +fec_mpc52xx-objs += fec_phy.o +endif Index: work-powerpc.git/arch/powerpc/boot/dts/lite5200b.dts =================================================================== --- work-powerpc.git.orig/arch/powerpc/boot/dts/lite5200b.dts +++ work-powerpc.git/arch/powerpc/boot/dts/lite5200b.dts @@ -365,10 +365,26 @@ ethernet@3000 { device_type = "network"; compatible = "mpc5200b-fec\0mpc5200-fec"; - reg = <3000 800>; + reg = <3000 400>; mac-address = [ 02 03 04 05 06 07 ]; // Bad! interrupts = <2 5 0>; interrupt-parent = <&mpc5200_pic>; + phy-handle = <&phy0>; + }; + + mdio@3000 { + #address-cells = <1>; + #size-cells = <0>; + device_type = "mdio"; + compatible = "mpc5200b-fec-phy"; + reg = <3000 400>; // fec range, since we need to setup fec interrupts + interrupts = <2 5 0>; // these are for "mii command finished", not link changes & co. + interrupt-parent = <&mpc5200_pic>; + + phy0:ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + }; }; ata@3a00 { Index: work-powerpc.git/arch/powerpc/sysdev/bestcomm/fec.h =================================================================== --- work-powerpc.git.orig/arch/powerpc/sysdev/bestcomm/fec.h +++ work-powerpc.git/arch/powerpc/sysdev/bestcomm/fec.h @@ -22,6 +22,20 @@ struct bcom_fec_bd { #define BCOM_FEC_TX_BD_TFD 0x08000000ul /* transmit frame done */ #define BCOM_FEC_TX_BD_INT 0x04000000ul /* interrupt */ +#define BCOM_FEC_TX_BD_TC 0x04000000ul /* transmit CRC XXX same as ^? */ +#define BCOM_FEC_TX_BD_ABC 0x02000000ul /* append bad CRC */ + +#define BCOM_FEC_RX_BD_L 0x08000000ul /* buffer is last in frame */ +#define BCOM_FEC_RX_BD_BC 0x00800000ul /* DA is broadcast */ +#define BCOM_FEC_RX_BD_MC 0x00400000ul /* DA is multicast and not broadcast */ +#define BCOM_FEC_RX_BD_LG 0x00200000ul /* Rx frame length violation */ +#define BCOM_FEC_RX_BD_NO 0x00100000ul /* Rx non-octet aligned frame */ +#define BCOM_FEC_RX_BD_CR 0x00040000ul /* Rx CRC error */ +#define BCOM_FEC_RX_BD_OV 0x00020000ul /* overrun */ +#define BCOM_FEC_RX_BD_TR 0x00010000ul /* Rx frame truncated */ +#define BCOM_FEC_RX_BD_LEN_MASK 0x000007fful /* mask for length of received frame */ +#define BCOM_FEC_RX_BD_ERRORS (BCOM_FEC_RX_BD_LG | BCOM_FEC_RX_BD_NO | \ + BCOM_FEC_RX_BD_CR | BCOM_FEC_RX_BD_OV | BCOM_FEC_RX_BD_TR) extern struct bcom_task * ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-08-10 9:51 [RFC PATCH v0.1] net driver: mpc52xx fec Domen Puncer @ 2007-08-10 13:02 ` Arnaldo Carvalho de Melo 2007-08-13 7:21 ` Domen Puncer 2007-08-18 10:06 ` Domen Puncer ` (2 subsequent siblings) 3 siblings, 1 reply; 47+ messages in thread From: Arnaldo Carvalho de Melo @ 2007-08-10 13:02 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, linuxppc-embedded Em Fri, Aug 10, 2007 at 11:51:53AM +0200, Domen Puncer escreveu: > Hi! > > Not for merge (yet)! But please do review. > > fec_mpc52xx driver (not in-tree, but floating around) isn't in very > good shape, so I tried to change that. > Diff against original is quite big (fec_phy.c is completely rewritten) > and confuzing, so I'm including whole drivers/net/fec_mpc52xx/ . > > I still have 'make CONFIG_FEC_MPC52xx_MDIO=n compile and work' on my > TODO, maybe even ethtool support. > > > Domen > > > arch/powerpc/boot/dts/lite5200b.dts | 18 > arch/powerpc/sysdev/bestcomm/fec.h | 14 > drivers/net/fec_mpc52xx/Kconfig | 24 > drivers/net/fec_mpc52xx/Makefile | 7 > drivers/net/fec_mpc52xx/fec.c | 1002 ++++++++++++++++++++++++++++++++++++ > drivers/net/fec_mpc52xx/fec.h | 299 ++++++++++ > drivers/net/fec_mpc52xx/fec_phy.c | 229 ++++++++ > drivers/net/fec_mpc52xx/fec_phy.h | 49 + > 8 files changed, 1641 insertions(+), 1 deletion(-) > > diff -pruN dummy/fec.c ./drivers/net/fec_mpc52xx/fec.c > --- dummy/fec.c 1970-01-01 01:00:00.000000000 +0100 > +++ ./drivers/net/fec_mpc52xx/fec.c 2007-08-10 10:59:00.000000000 +0200 > @@ -0,0 +1,1002 @@ > +/* > + * drivers/net/fec_mpc52xx/fec.c > + * > + * Driver for the MPC5200 Fast Ethernet Controller > + * > + * Originally written by Dale Farnsworth <dfarnsworth@mvista.com> and > + * now maintained by Sylvain Munaut <tnt@246tNt.com> > + * > + * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com> > + * Copyrigth (C) 2003-2004 MontaVista, Software, Inc. > + * > + * This file is licensed under the terms of the GNU General Public License > + * version 2. This program is licensed "as is" without any warranty of any > + * kind, whether express or implied. > + * > + */ > + > +#include <linux/module.h> > + > +#include <linux/kernel.h> > +#include <linux/types.h> > +#include <linux/spinlock.h> > +#include <linux/errno.h> > +#include <linux/init.h> > +#include <linux/crc32.h> > +#include <linux/hardirq.h> > + > +#include <linux/netdevice.h> > +#include <linux/etherdevice.h> > +#include <linux/ethtool.h> > +#include <linux/skbuff.h> > + > +#include <asm/of_device.h> > +#include <asm/of_platform.h> > +#include <asm/io.h> > +#include <asm/delay.h> > +#include <asm/mpc52xx.h> > + > +#include <sysdev/bestcomm/bestcomm.h> > +#include <sysdev/bestcomm/fec.h> > + > +#include "fec_phy.h" > +#include "fec.h" > + > +#define DRIVER_NAME "mpc52xx-fec" > + > +static irqreturn_t fec_interrupt(int, void *); > +static irqreturn_t fec_rx_interrupt(int, void *); > +static irqreturn_t fec_tx_interrupt(int, void *); > +static struct net_device_stats *fec_get_stats(struct net_device *); > +static void fec_set_multicast_list(struct net_device *dev); > +static void fec_hw_init(struct net_device *dev); > +static void fec_stop(struct net_device *dev); > +static void fec_start(struct net_device *dev); > + > +static u8 mpc52xx_fec_mac_addr[6]; > +static u8 null_mac[6]; const > + > +static void fec_tx_timeout(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + > + dev_warn(&dev->dev, "transmit timed out\n"); > + > + fec_stop(dev); > + fec_start(dev); > + > + priv->stats.tx_errors++; > + > + if (!priv->tx_full) > + netif_wake_queue(dev); > +} > + > +static void fec_set_paddr(struct net_device *dev, u8 *mac) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct mpc52xx_fec __iomem *fec = priv->fec; > + > + out_be32(&fec->paddr1, *(u32*)(&mac[0])); > + out_be32(&fec->paddr2, (*(u16*)(&mac[4]) << 16) | FEC_PADDR2_TYPE); spaces after the types on casts to pointers > +} > + > +static void fec_get_paddr(struct net_device *dev, u8 *mac) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct mpc52xx_fec __iomem *fec = priv->fec; > + > + *(u32*)(&mac[0]) = in_be32(&fec->paddr1); > + *(u16*)(&mac[4]) = in_be32(&fec->paddr2) >> 16; ditto > +} > + > +static int fec_set_mac_address(struct net_device *dev, void *addr) > +{ > + struct sockaddr *sock = (struct sockaddr *)addr; no need for a cast, addr is a void pointer > + > + memcpy(dev->dev_addr, sock->sa_data, dev->addr_len); > + > + fec_set_paddr(dev, sock->sa_data); > + return 0; Why always return 0? make it void > +} > + > +static void fec_free_rx_buffers(struct bcom_task *s) > +{ > + struct sk_buff *skb; > + > + while (!bcom_queue_empty(s)) { > + skb = bcom_retrieve_buffer(s, NULL, NULL); > + kfree_skb(skb); > + } > +} > + > +static int fec_alloc_rx_buffers(struct bcom_task *rxtsk) > +{ > + while (!bcom_queue_full(rxtsk)) { > + struct sk_buff *skb; > + struct bcom_fec_bd *bd; > + > + skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); > + if (skb == 0) Test against NULL > + return -EAGAIN; > + > + /* zero out the initial receive buffers to aid debugging */ > + memset(skb->data, 0, FEC_RX_BUFFER_SIZE); > + > + bd = (struct bcom_fec_bd *)bcom_prepare_next_buffer(rxtsk); > + > + bd->status = FEC_RX_BUFFER_SIZE; > + bd->skb_pa = virt_to_phys(skb->data); > + > + bcom_submit_next_buffer(rxtsk, skb); > + } > + > + return 0; > +} > + > +/* based on generic_adjust_link - fs_enet-main.c */ > +static void fec_adjust_link(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct phy_device *phydev = priv->phydev; > + int new_state = 0; > + > + if (phydev->link != PHY_DOWN) { > + if (phydev->duplex != priv->duplex) { > + new_state = 1; > + priv->duplex = phydev->duplex; > + } > + > + if (phydev->speed != priv->speed) { > + new_state = 1; > + priv->speed = phydev->speed; > + } > + > + if (priv->link == PHY_DOWN) { > + new_state = 1; > + priv->link = phydev->link; > + netif_schedule(dev); > + netif_carrier_on(dev); > + netif_start_queue(dev); > + } > + > + } else if (priv->link) { > + new_state = 1; > + priv->link = PHY_DOWN; > + priv->speed = 0; > + priv->duplex = -1; > + netif_stop_queue(dev); > + netif_carrier_off(dev); > + } > + > + if (new_state && netif_msg_link(priv)) { > + phy_print_status(phydev); > + } No need for {}, this if has only one statement > +} > + > +static int fec_init_phy(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct phy_device *phydev; > + char phy_id[BUS_ID_SIZE]; > + > + struct device_node *dn, *phy_dn; > + unsigned int phy_addr; > + const phandle *ph; > + const unsigned int *prop; > + struct resource res; > + int ret; > + > + dn = priv->ofdev->node; > + ph = of_get_property(dn, "phy-handle", NULL); > + if (!ph) { > + dev_err(&dev->dev, "can't find \"phy-handle\" in device tree\n"); > + return -ENODEV; > + } > + phy_dn = of_find_node_by_phandle(*ph); > + > + prop = of_get_property(phy_dn, "reg", NULL); > + ret = of_address_to_resource(phy_dn->parent, 0, &res); > + if (ret) { > + dev_err(&dev->dev, "of_address_to_resource failed\n"); > + return ret; > + } > + > + phy_addr = *prop; > + of_node_put(phy_dn); > + > + snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, res.start, phy_addr); > + > + priv->link = PHY_DOWN; > + priv->speed = 0; > + priv->duplex = -1; > + > + phydev = phy_connect(dev, phy_id, &fec_adjust_link, 0, PHY_INTERFACE_MODE_MII); > + if (IS_ERR(phydev)) { > + printk(KERN_ERR "%s: phy_connect failed\n", dev->name); > + return PTR_ERR(phydev); > + } > + > + phydev->advertising &= ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half; > + > + priv->phydev = phydev; > + > + return 0; > +} > + > +static int fec_open(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + int err = -EBUSY; > + > + if (request_irq(dev->irq, &fec_interrupt, IRQF_DISABLED | IRQF_SHARED, > + DRIVER_NAME "_ctrl", dev)) { > + dev_err(&dev->dev, "ctrl interrupt request failed\n"); > + goto out; > + } > + if (request_irq(priv->r_irq, &fec_rx_interrupt, IRQF_DISABLED, > + DRIVER_NAME "_rx", dev)) { > + dev_err(&dev->dev, "rx interrupt request failed\n"); > + goto free_ctrl_irq; > + } > + if (request_irq(priv->t_irq, &fec_tx_interrupt, IRQF_DISABLED, > + DRIVER_NAME "_tx", dev)) { > + dev_err(&dev->dev, "tx interrupt request failed\n"); > + goto free_2irqs; > + } > + > + bcom_fec_rx_reset(priv->rx_dmatsk); > + bcom_fec_tx_reset(priv->tx_dmatsk); > + > + err = fec_alloc_rx_buffers(priv->rx_dmatsk); > + if (err) { > + dev_err(&dev->dev, "fec_alloc_rx_buffers failed\n"); > + goto free_irqs; > + } > + > + err = fec_init_phy(dev); > + if (err) { > + dev_err(&dev->dev, "fec_init_phy failed\n"); > + goto free_skbs; > + } > + bcom_enable(priv->rx_dmatsk); > + bcom_enable(priv->tx_dmatsk); > + > + /* reset phy - this also wakes it from PDOWN */ > + phy_write(priv->phydev, MII_BMCR, BMCR_RESET); > + phy_start(priv->phydev); > + > + fec_start(dev); > + > + netif_start_queue(dev); > + > + return 0; > + > + free_skbs: > + fec_free_rx_buffers(priv->rx_dmatsk); > + > + free_irqs: > + free_irq(priv->t_irq, dev); > + free_2irqs: > + free_irq(priv->r_irq, dev); > + free_ctrl_irq: > + free_irq(dev->irq, dev); > + out: > + > + return err; > +} > + > +static int fec_close(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + > + netif_stop_queue(dev); > + > + fec_stop(dev); > + > + fec_free_rx_buffers(priv->rx_dmatsk); > + > + phy_disconnect(priv->phydev); > + > + free_irq(dev->irq, dev); > + free_irq(priv->r_irq, dev); > + free_irq(priv->t_irq, dev); > + > + /* power down phy */ > + phy_stop(priv->phydev); > + phy_write(priv->phydev, MII_BMCR, BMCR_PDOWN); > + > + return 0; > +} > + > +/* This will only be invoked if your driver is _not_ in XOFF state. > + * What this means is that you need not check it, and that this > + * invariant will hold if you make sure that the netif_*_queue() > + * calls are done at the proper times. > + */ > +static int fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct bcom_fec_bd *bd; > + > + if (bcom_queue_full(priv->tx_dmatsk)) { > + if (net_ratelimit()) > + dev_err(&dev->dev, "transmit queue overrun\n"); > + return 1; > + } > + > + spin_lock_irq(&priv->lock); > + dev->trans_start = jiffies; > + > + bd = (struct bcom_fec_bd *) > + bcom_prepare_next_buffer(priv->tx_dmatsk); > + > + bd->status = skb->len | BCOM_FEC_TX_BD_TFD | BCOM_FEC_TX_BD_INT; > + bd->skb_pa = virt_to_phys(skb->data); > + > + bcom_submit_next_buffer(priv->tx_dmatsk, skb); > + > + if (bcom_queue_full(priv->tx_dmatsk)) { > + priv->tx_full = 1; > + netif_stop_queue(dev); > + } > + > + spin_unlock_irq(&priv->lock); > + > + return 0; > +} > + > +/* This handles BestComm transmit task interrupts > + */ > +static irqreturn_t fec_tx_interrupt(int irq, void *dev_id) > +{ > + struct net_device *dev = dev_id; > + struct fec_priv *priv = netdev_priv(dev); > + > + spin_lock(&priv->lock); > + > + while (bcom_buffer_done(priv->tx_dmatsk)) { > + struct sk_buff *skb; > + skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL, NULL); > + > + priv->tx_full = 0; > + dev_kfree_skb_irq(skb); > + } > + > + if (netif_queue_stopped(dev) && !priv->tx_full) > + netif_wake_queue(dev); > + > + spin_unlock(&priv->lock); > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t fec_rx_interrupt(int irq, void *dev_id) > +{ > + struct net_device *dev = dev_id; > + struct fec_priv *priv = netdev_priv(dev); > + > + while (bcom_buffer_done(priv->rx_dmatsk)) { > + struct sk_buff *skb; > + struct sk_buff *rskb; > + struct bcom_fec_bd *bd; > + u32 status; > + > + rskb = bcom_retrieve_buffer(priv->rx_dmatsk, &status, NULL); > + > + /* Test for errors in received frame */ > + if (status & BCOM_FEC_RX_BD_ERRORS) { > + /* Drop packet and reuse the buffer */ > + bd = (struct bcom_fec_bd *) > + bcom_prepare_next_buffer(priv->rx_dmatsk); > + > + bd->status = FEC_RX_BUFFER_SIZE; > + bd->skb_pa = virt_to_phys(rskb->data); > + > + bcom_submit_next_buffer(priv->rx_dmatsk, rskb); > + > + priv->stats.rx_dropped++; > + > + continue; > + } > + > + /* skbs are allocated on open, so now we allocate a new one, > + * and remove the old (with the packet) */ > + skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); > + if (skb) { > + /* Process the received skb */ > + int length = status & BCOM_FEC_RX_BD_LEN_MASK; > + > + skb_put(rskb, length - 4); /* length without CRC32 */ > + > + rskb->dev = dev; > + rskb->protocol = eth_type_trans(rskb, dev); > + > + netif_rx(rskb); > + dev->last_rx = jiffies; > + } else { > + /* Can't get a new one : reuse the same & drop pkt */ > + dev_notice(&dev->dev, "Memory squeeze, dropping packet.\n"); > + priv->stats.rx_dropped++; > + > + skb = rskb; > + } > + > + bd = (struct bcom_fec_bd *) > + bcom_prepare_next_buffer(priv->rx_dmatsk); > + > + bd->status = FEC_RX_BUFFER_SIZE; > + bd->skb_pa = virt_to_phys(skb->data); > + > + bcom_submit_next_buffer(priv->rx_dmatsk, skb); > + } > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t fec_interrupt(int irq, void *dev_id) > +{ > + struct net_device *dev = dev_id; > + struct fec_priv *priv = netdev_priv(dev); > + struct mpc52xx_fec __iomem *fec = priv->fec; > + u32 ievent; > + > + ievent = in_be32(&fec->ievent); > + > + ievent &= ~FEC_IEVENT_MII; /* mii is handled separately */ > + if (!ievent) > + return IRQ_NONE; > + > + out_be32(&fec->ievent, ievent); /* clear pending events */ > + > + if (ievent & ~(FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) { > + if (ievent & ~FEC_IEVENT_TFINT) > + dev_dbg(&dev->dev, "ievent: %08x\n", ievent); > + return IRQ_HANDLED; > + } > + > + if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR)) > + dev_warn(&dev->dev, "FEC_IEVENT_RFIFO_ERROR\n"); > + if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR)) > + dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n"); > + > + fec_stop(dev); > + fec_hw_init(dev); > + fec_start(dev); > + > + netif_wake_queue(dev); > + return IRQ_HANDLED; > +} > + > +/* > + * Get the current statistics. > + * This may be called with the card open or closed. > + */ > +static struct net_device_stats *fec_get_stats(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct net_device_stats *stats = &priv->stats; > + struct mpc52xx_fec __iomem *fec = priv->fec; > + > +/* printk(KERN_ALERT "%s: %i, rmon_r_octets: %i, rmon_r_packets: %i, " > + "ieee_r_octets_ok: %i, ieee_r_frame_ok: %i, " > + "%i\n", > + __func__, __LINE__, > + in_be32(&fec->rmon_r_octets), in_be32(&fec->rmon_r_packets), > + in_be32(&fec->ieee_r_octets_ok), in_be32(&fec->ieee_r_frame_ok), > + 0); > +*/ > + stats->rx_bytes = in_be32(&fec->rmon_r_octets); > + stats->rx_packets = in_be32(&fec->rmon_r_packets); > + stats->rx_errors = in_be32(&fec->rmon_r_crc_align) + > + in_be32(&fec->rmon_r_undersize) + > + in_be32(&fec->rmon_r_oversize) + > + in_be32(&fec->rmon_r_frag) + > + in_be32(&fec->rmon_r_jab); > + > + stats->tx_bytes = in_be32(&fec->rmon_t_octets); > + stats->tx_packets = in_be32(&fec->rmon_t_packets); > + stats->tx_errors = in_be32(&fec->rmon_t_crc_align) + > + in_be32(&fec->rmon_t_undersize) + > + in_be32(&fec->rmon_t_oversize) + > + in_be32(&fec->rmon_t_frag) + > + in_be32(&fec->rmon_t_jab); > + > + stats->multicast = in_be32(&fec->rmon_r_mc_pkt); > + stats->collisions = in_be32(&fec->rmon_t_col); > + > + /* detailed rx_errors: */ > + stats->rx_length_errors = in_be32(&fec->rmon_r_undersize) > + + in_be32(&fec->rmon_r_oversize) > + + in_be32(&fec->rmon_r_frag) > + + in_be32(&fec->rmon_r_jab); > + stats->rx_over_errors = in_be32(&fec->r_macerr); > + stats->rx_crc_errors = in_be32(&fec->ieee_r_crc); > + stats->rx_frame_errors = in_be32(&fec->ieee_r_align); > + stats->rx_fifo_errors = in_be32(&fec->rmon_r_drop); > + stats->rx_missed_errors = in_be32(&fec->rmon_r_drop); > + > + /* detailed tx_errors: */ > + stats->tx_aborted_errors = 0; > + stats->tx_carrier_errors = in_be32(&fec->ieee_t_cserr); > + stats->tx_fifo_errors = in_be32(&fec->rmon_t_drop); > + stats->tx_heartbeat_errors = in_be32(&fec->ieee_t_sqe); > + stats->tx_window_errors = in_be32(&fec->ieee_t_lcol); > + > + return stats; > +} > + > +/* > + * Read MIB counters in order to reset them, > + * then zero all the stats fields in memory > + */ > +static void fec_reset_stats(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct mpc52xx_fec __iomem *fec = priv->fec; > + > + out_be32(&fec->mib_control, FEC_MIB_DISABLE); > + memset_io(&fec->rmon_t_drop, 0, > + (u32)&fec->reserved10 - (u32)&fec->rmon_t_drop); > + out_be32(&fec->mib_control, 0); > + > + memset(&priv->stats, 0, sizeof(priv->stats)); > +} > + > +/* > + * Set or clear the multicast filter for this adaptor. > + */ > +static void fec_set_multicast_list(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct mpc52xx_fec __iomem *fec = priv->fec; > + u32 rx_control; > + > + rx_control = in_be32(&fec->r_cntrl); > + > + if (dev->flags & IFF_PROMISC) { > + rx_control |= FEC_RCNTRL_PROM; > + out_be32(&fec->r_cntrl, rx_control); > + } else { > + rx_control &= ~FEC_RCNTRL_PROM; > + out_be32(&fec->r_cntrl, rx_control); > + > + if (dev->flags & IFF_ALLMULTI) { > + out_be32(&fec->gaddr1, 0xffffffff); > + out_be32(&fec->gaddr2, 0xffffffff); > + } else { > + u32 crc; > + int i; > + struct dev_mc_list *dmi; > + u32 gaddr1 = 0x00000000; > + u32 gaddr2 = 0x00000000; > + > + dmi = dev->mc_list; > + for (i=0; i<dev->mc_count; i++) { > + crc = ether_crc_le(6, dmi->dmi_addr) >> 26; > + if (crc >= 32) > + gaddr1 |= 1 << (crc-32); > + else > + gaddr2 |= 1 << crc; > + dmi = dmi->next; > + } > + out_be32(&fec->gaddr1, gaddr1); > + out_be32(&fec->gaddr2, gaddr2); > + } > + } > +} > + > +static void __init fec_str2mac(char *str, unsigned char *mac) > +{ > + int i; > + u64 val64; > + > + val64 = simple_strtoull(str, NULL, 16); > + > + for (i = 0; i < 6; i++) > + mac[5-i] = val64 >> (i*8); > +} > + > +static int __init mpc52xx_fec_mac_setup(char *mac_address) > +{ > + fec_str2mac(mac_address, mpc52xx_fec_mac_addr); > + return 0; > +} > + > +/* XXX do we need this? */ > +__setup("mpc52xx-mac=", mpc52xx_fec_mac_setup); > + > +/** > + * fec_hw_init > + * @dev: network device > + * > + * Setup various hardware setting, only needed once on start > + */ > +static void fec_hw_init(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct mpc52xx_fec __iomem *fec = priv->fec; > + int i; > + > + /* Whack a reset. We should wait for this. */ > + out_be32(&fec->ecntrl, FEC_ECNTRL_RESET); > + for (i = 0; i < FEC_RESET_DELAY; ++i) { > + if ((in_be32(&fec->ecntrl) & FEC_ECNTRL_RESET) == 0) > + break; > + udelay(1); > + } > + if (i == FEC_RESET_DELAY) > + dev_err(&dev->dev, "FEC Reset timeout!\n"); > + > + /* set pause to 0x20 frames */ > + out_be32(&fec->op_pause, FEC_OP_PAUSE_OPCODE | 0x20); > + > + /* high service request will be deasserted when there's < 7 bytes in fifo > + * low service request will be deasserted when there's < 4*7 bytes in fifo > + */ > + out_be32(&fec->rfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7); > + out_be32(&fec->tfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7); > + > + /* alarm when <= x bytes in FIFO */ > + out_be32(&fec->rfifo_alarm, 0x0000030c); > + out_be32(&fec->tfifo_alarm, 0x00000100); > + > + /* begin transmittion when 256 bytes are in FIFO (or EOF or FIFO full) */ > + out_be32(&fec->x_wmrk, FEC_FIFO_WMRK_256B); > + > + /* enable crc generation */ > + out_be32(&fec->xmit_fsm, FEC_XMIT_FSM_APPEND_CRC | FEC_XMIT_FSM_ENABLE_CRC); > + out_be32(&fec->iaddr1, 0x00000000); /* No individual filter */ > + out_be32(&fec->iaddr2, 0x00000000); /* No individual filter */ > + > + /* set phy speed and enable MII interrupt > + * this can't be done in phy driver, since it needs to be called > + * before fec stuff (even on resume) */ > + set_phy_speed(fec, priv->phy_speed); > + out_be32(&fec->imask, in_be32(&fec->imask) | FEC_IMASK_MII); > +} > + > +/** > + * fec_start > + * @dev: network device > + * > + * This function is called to start or restart the FEC during a link > + * change. This happens on fifo errors or when switching between half > + * and full duplex. > + */ > +static void fec_start(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct mpc52xx_fec __iomem *fec = priv->fec; > + u32 rcntrl; > + u32 tcntrl; > + u32 tmp; > + > + /* clear sticky error bits */ > + tmp = FEC_FIFO_STATUS_ERR | FEC_FIFO_STATUS_UF | FEC_FIFO_STATUS_OF; > + out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & tmp); > + out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & tmp); > + > + /* FIFOs will reset on fec_enable */ > + out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_ENABLE_IS_RESET); > + > + /* Set station address. */ > + fec_set_paddr(dev, dev->dev_addr); > + > + fec_set_multicast_list(dev); > + > + /* set max frame len, enable flow control, select mii mode */ > + rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */ > + rcntrl |= FEC_RCNTRL_FCE; > + rcntrl |= MII_RCNTL_MODE; > + if (priv->duplex == DUPLEX_FULL) > + tcntrl = FEC_TCNTRL_FDEN; /* FD enable */ > + else { > + rcntrl |= FEC_RCNTRL_DRT; /* disable Rx on Tx (HD) */ > + tcntrl = 0; > + } > + out_be32(&fec->r_cntrl, rcntrl); > + out_be32(&fec->x_cntrl, tcntrl); > + > + /* Clear any outstanding interrupt. */ > + out_be32(&fec->ievent, 0xffffffff); > + > + /* Enable interrupts we wish to service. */ > + out_be32(&fec->imask, FEC_IMASK_ENABLE); > + > + /* And last, enable the transmit and receive processing. */ > + out_be32(&fec->ecntrl, FEC_ECNTRL_ETHER_EN); > + out_be32(&fec->r_des_active, 0x01000000); > + > + priv->tx_full = 0; > +} > + > +/** > + * fec_stop > + * @dev: network device > + * > + * stop all activity on fec and empty dma buffers > + */ > +static void fec_stop(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct mpc52xx_fec __iomem *fec = priv->fec; > + unsigned long timeout; > + > + out_be32(&fec->imask, FEC_IMASK_MII); /* disable all but MII interrupt */ > + > + /* Disable the rx and tx tasks. */ > + bcom_disable(priv->rx_dmatsk); > + > + /* Wait for queues to drain, but only if we're in process context */ > + if (!in_interrupt()) { > + timeout = jiffies + 2*HZ; > + while (time_before(jiffies, timeout) && > + (!bcom_queue_empty(priv->tx_dmatsk) || > + !bcom_queue_empty(priv->rx_dmatsk))) { > + set_current_state(TASK_INTERRUPTIBLE); > + schedule_timeout(HZ/10); > + } > + if (time_after_eq(jiffies, timeout)) > + dev_err(&dev->dev, "queues didn't drain\n"); > + } > + > + bcom_disable(priv->tx_dmatsk); > + > + /* Stop FEC */ > + out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~FEC_ECNTRL_ETHER_EN); > + > + return; > +} > + > +static int fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&rq->ifr_data; > + > + return phy_mii_ioctl(priv->phydev, mii, cmd); > +} > + > +/* ======================================================================== */ > +/* OF Driver */ > +/* ======================================================================== */ > + > +static int __devinit > +mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match) > +{ > + int rv; > + struct net_device *ndev; > + struct fec_priv *priv = NULL; > + struct resource mem; > + > + phys_addr_t rx_fifo; > + phys_addr_t tx_fifo; > + > + /* Get the ether ndev & it's private zone */ > + ndev = alloc_etherdev(sizeof(struct fec_priv)); > + if (!ndev) > + return -ENOMEM; > + > + priv = netdev_priv(ndev); > + > + priv->ofdev = op; > + > + /* Reserve FEC control zone */ > + rv = of_address_to_resource(op->node, 0, &mem); > + if (rv) { > + printk(KERN_ERR DRIVER_NAME ": " > + "Error while parsing device node resource\n" ); > + return rv; > + } > + if ((mem.end - mem.start + 1) != sizeof(struct mpc52xx_fec)) { > + printk(KERN_ERR DRIVER_NAME > + " - invalid resource size (%lx != %x), check mpc52xx_devices.c\n", > + (unsigned long)(mem.end - mem.start + 1), sizeof(struct mpc52xx_fec)); > + return -EINVAL; > + } > + > + if (!request_mem_region(mem.start, sizeof(struct mpc52xx_fec), DRIVER_NAME)) > + return -EBUSY; > + > + /* Init ether ndev with what we have */ > + ndev->open = fec_open; > + ndev->stop = fec_close; > + ndev->hard_start_xmit = fec_hard_start_xmit; > + ndev->do_ioctl = fec_ioctl; > + ndev->get_stats = fec_get_stats; > + ndev->set_mac_address = fec_set_mac_address; > + ndev->set_multicast_list = fec_set_multicast_list; > + ndev->tx_timeout = fec_tx_timeout; > + ndev->watchdog_timeo = FEC_WATCHDOG_TIMEOUT; > + ndev->flags &= ~IFF_RUNNING; > + ndev->base_addr = mem.start; > + > + priv->t_irq = priv->r_irq = ndev->irq = NO_IRQ; /* IRQ are free for now */ > + > + spin_lock_init(&priv->lock); > + > + /* ioremap the zones */ > + priv->fec = ioremap(mem.start, sizeof(struct mpc52xx_fec)); > + > + if (!priv->fec) { > + rv = -ENOMEM; > + goto probe_error; > + } > + > + /* Bestcomm init */ > + rx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, rfifo_data); > + tx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, tfifo_data); > + > + priv->rx_dmatsk = bcom_fec_rx_init(FEC_RX_NUM_BD, rx_fifo, FEC_RX_BUFFER_SIZE); > + priv->tx_dmatsk = bcom_fec_tx_init(FEC_TX_NUM_BD, tx_fifo); > + > + if (!priv->rx_dmatsk || !priv->tx_dmatsk) { > + printk(KERN_ERR DRIVER_NAME ": Can not init SDMA tasks\n" ); > + rv = -ENOMEM; > + goto probe_error; > + } > + > + /* Get the IRQ we need one by one */ > + /* Control */ > + ndev->irq = irq_of_parse_and_map(op->node, 0); > + > + /* RX */ > + priv->r_irq = bcom_get_task_irq(priv->rx_dmatsk); > + > + /* TX */ > + priv->t_irq = bcom_get_task_irq(priv->tx_dmatsk); > + > + /* MAC address init */ > + if (memcmp(mpc52xx_fec_mac_addr, null_mac, 6) != 0) > + memcpy(ndev->dev_addr, mpc52xx_fec_mac_addr, 6); > + else > + fec_get_paddr(ndev, ndev->dev_addr); > + > + /* Phy speed */ > + priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1; > + > + priv->msg_enable = (NETIF_MSG_IFUP << 1) - 1; > + priv->duplex = DUPLEX_HALF; > + > + /* Hardware init */ > + fec_hw_init(ndev); > + > + fec_reset_stats(ndev); > + > + /* Register the new network device */ > + rv = register_netdev(ndev); > + if (rv < 0) > + goto probe_error; > + > + /* We're done ! */ > + dev_set_drvdata(&op->dev, ndev); > + > + return 0; > + > + > + /* Error handling - free everything that might be allocated */ > +probe_error: > + > + irq_dispose_mapping(ndev->irq); > + > + if (priv->rx_dmatsk) > + bcom_fec_rx_release(priv->rx_dmatsk); > + if (priv->tx_dmatsk) > + bcom_fec_tx_release(priv->tx_dmatsk); > + > + if (priv->fec) > + iounmap(priv->fec); > + > + release_mem_region(mem.start, sizeof(struct mpc52xx_fec)); > + > + free_netdev(ndev); > + > + return rv; > +} > + > +static int > +mpc52xx_fec_remove(struct of_device *op) > +{ > + struct net_device *ndev; > + struct fec_priv *priv; > + > + ndev = dev_get_drvdata(&op->dev); > + if (!ndev) > + return 0; > + priv = netdev_priv(ndev); > + > + unregister_netdev(ndev); > + > + irq_dispose_mapping(ndev->irq); > + > + bcom_fec_rx_release(priv->rx_dmatsk); > + bcom_fec_tx_release(priv->tx_dmatsk); > + > + iounmap(priv->fec); > + > + release_mem_region(ndev->base_addr, sizeof(struct mpc52xx_fec)); > + > + free_netdev(ndev); > + > + dev_set_drvdata(&op->dev, NULL); > + return 0; > +} > + > +#ifdef CONFIG_PM > +static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state) > +{ > + struct net_device *dev = dev_get_drvdata(&op->dev); > + > + if (netif_running(dev)) > + fec_close(dev); > + > + return 0; > +} > + > +static int mpc52xx_fec_of_resume(struct of_device *op) > +{ > + struct net_device *dev = dev_get_drvdata(&op->dev); > + > + fec_hw_init(dev); > + fec_reset_stats(dev); > + > + if (netif_running(dev)) > + fec_open(dev); > + > + return 0; > +} > +#endif > + > +static struct of_device_id mpc52xx_fec_match[] = { > + { > + .type = "network", > + .compatible = "mpc5200-fec", > + }, > + { } > +}; > + > +MODULE_DEVICE_TABLE(of, mpc52xx_fec_match); > + > +static struct of_platform_driver mpc52xx_fec_driver = { > + .owner = THIS_MODULE, > + .name = DRIVER_NAME, > + .match_table = mpc52xx_fec_match, > + .probe = mpc52xx_fec_probe, > + .remove = mpc52xx_fec_remove, > +#ifdef CONFIG_PM > + .suspend = mpc52xx_fec_of_suspend, > + .resume = mpc52xx_fec_of_resume, > +#endif > +}; > + > + > +/* ======================================================================== */ > +/* Module */ > +/* ======================================================================== */ > + > +static int __init > +mpc52xx_fec_init(void) > +{ > + int ret; > + if ((ret = fec_mdio_init())) { Why not: int ret = fec_mdio_init(); if (ret) { Less parenthesis, looks more clear > + printk(KERN_ERR "%s: %i fec_mdio_init failed\n", __func__, __LINE__); > + return ret; > + } > + > + return of_register_platform_driver(&mpc52xx_fec_driver); > +} > + > +static void __exit > +mpc52xx_fec_exit(void) > +{ > + of_unregister_platform_driver(&mpc52xx_fec_driver); > + fec_mdio_exit(); > +} > + > + > +module_init(mpc52xx_fec_init); > +module_exit(mpc52xx_fec_exit); > + > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Dale Farnsworth"); > +MODULE_DESCRIPTION("Ethernet driver for the Freescale MPC52xx FEC"); > + > diff -pruN dummy/fec.h ./drivers/net/fec_mpc52xx/fec.h > --- dummy/fec.h 1970-01-01 01:00:00.000000000 +0100 > +++ ./drivers/net/fec_mpc52xx/fec.h 2007-08-10 11:24:37.000000000 +0200 > @@ -0,0 +1,299 @@ > +/* > + * drivers/net/fec_mpc52xx/fec.h > + * > + * Driver for the MPC5200 Fast Ethernet Controller > + * > + * Author: Dale Farnsworth <dfarnsworth@mvista.com> > + * > + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under > + * the terms of the GNU General Public License version 2. This program > + * is licensed "as is" without any warranty of any kind, whether express > + * or implied. > + */ > + > +#ifndef __DRIVERS_NET_MPC52XX_FEC_H__ > +#define __DRIVERS_NET_MPC52XX_FEC_H__ > + > +#include <linux/mii.h> // XXX, still needed? > +#include <linux/phy.h> > + > +/* Tunable constant */ > +/* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */ > +#define FEC_RX_BUFFER_SIZE 1522 /* max receive packet size */ > +#define FEC_RX_NUM_BD 64 > +#define FEC_TX_NUM_BD 64 > + > +#define FEC_RESET_DELAY 50 /* uS */ > + > +#define FEC_WATCHDOG_TIMEOUT ((400*HZ)/1000) > + > +struct fec_priv { > + int duplex; > + int tx_full; > + int r_irq; > + int t_irq; > + struct mpc52xx_fec __iomem *fec; > + struct bcom_task *rx_dmatsk; > + struct bcom_task *tx_dmatsk; > + spinlock_t lock; > + struct net_device_stats stats; > + int msg_enable; > +#ifdef CONFIG_FEC_MPC52xx_MDIO > + uint phy_speed; > + > + struct phy_device *phydev; > + enum phy_state link; > + int speed; > + > + struct of_device *ofdev; > +#endif /* CONFIG_FEC_MPC52xx_MDIO */ > +}; > + > + > +/* ======================================================================== */ > +/* Hardware register sets & bits */ > +/* ======================================================================== */ > + > +struct mpc52xx_fec { > + u32 fec_id; /* FEC + 0x000 */ > + u32 ievent; /* FEC + 0x004 */ > + u32 imask; /* FEC + 0x008 */ > + > + u32 reserved0[1]; /* FEC + 0x00C */ > + u32 r_des_active; /* FEC + 0x010 */ > + u32 x_des_active; /* FEC + 0x014 */ > + u32 r_des_active_cl; /* FEC + 0x018 */ > + u32 x_des_active_cl; /* FEC + 0x01C */ > + u32 ivent_set; /* FEC + 0x020 */ > + u32 ecntrl; /* FEC + 0x024 */ > + > + u32 reserved1[6]; /* FEC + 0x028-03C */ > + u32 mii_data; /* FEC + 0x040 */ > + u32 mii_speed; /* FEC + 0x044 */ > + u32 mii_status; /* FEC + 0x048 */ > + > + u32 reserved2[5]; /* FEC + 0x04C-05C */ > + u32 mib_data; /* FEC + 0x060 */ > + u32 mib_control; /* FEC + 0x064 */ > + > + u32 reserved3[6]; /* FEC + 0x068-7C */ > + u32 r_activate; /* FEC + 0x080 */ > + u32 r_cntrl; /* FEC + 0x084 */ > + u32 r_hash; /* FEC + 0x088 */ > + u32 r_data; /* FEC + 0x08C */ > + u32 ar_done; /* FEC + 0x090 */ > + u32 r_test; /* FEC + 0x094 */ > + u32 r_mib; /* FEC + 0x098 */ > + u32 r_da_low; /* FEC + 0x09C */ > + u32 r_da_high; /* FEC + 0x0A0 */ > + > + u32 reserved4[7]; /* FEC + 0x0A4-0BC */ > + u32 x_activate; /* FEC + 0x0C0 */ > + u32 x_cntrl; /* FEC + 0x0C4 */ > + u32 backoff; /* FEC + 0x0C8 */ > + u32 x_data; /* FEC + 0x0CC */ > + u32 x_status; /* FEC + 0x0D0 */ > + u32 x_mib; /* FEC + 0x0D4 */ > + u32 x_test; /* FEC + 0x0D8 */ > + u32 fdxfc_da1; /* FEC + 0x0DC */ > + u32 fdxfc_da2; /* FEC + 0x0E0 */ > + u32 paddr1; /* FEC + 0x0E4 */ > + u32 paddr2; /* FEC + 0x0E8 */ > + u32 op_pause; /* FEC + 0x0EC */ > + > + u32 reserved5[4]; /* FEC + 0x0F0-0FC */ > + u32 instr_reg; /* FEC + 0x100 */ > + u32 context_reg; /* FEC + 0x104 */ > + u32 test_cntrl; /* FEC + 0x108 */ > + u32 acc_reg; /* FEC + 0x10C */ > + u32 ones; /* FEC + 0x110 */ > + u32 zeros; /* FEC + 0x114 */ > + u32 iaddr1; /* FEC + 0x118 */ > + u32 iaddr2; /* FEC + 0x11C */ > + u32 gaddr1; /* FEC + 0x120 */ > + u32 gaddr2; /* FEC + 0x124 */ > + u32 random; /* FEC + 0x128 */ > + u32 rand1; /* FEC + 0x12C */ > + u32 tmp; /* FEC + 0x130 */ > + > + u32 reserved6[3]; /* FEC + 0x134-13C */ > + u32 fifo_id; /* FEC + 0x140 */ > + u32 x_wmrk; /* FEC + 0x144 */ > + u32 fcntrl; /* FEC + 0x148 */ > + u32 r_bound; /* FEC + 0x14C */ > + u32 r_fstart; /* FEC + 0x150 */ > + u32 r_count; /* FEC + 0x154 */ > + u32 r_lag; /* FEC + 0x158 */ > + u32 r_read; /* FEC + 0x15C */ > + u32 r_write; /* FEC + 0x160 */ > + u32 x_count; /* FEC + 0x164 */ > + u32 x_lag; /* FEC + 0x168 */ > + u32 x_retry; /* FEC + 0x16C */ > + u32 x_write; /* FEC + 0x170 */ > + u32 x_read; /* FEC + 0x174 */ > + > + u32 reserved7[2]; /* FEC + 0x178-17C */ > + u32 fm_cntrl; /* FEC + 0x180 */ > + u32 rfifo_data; /* FEC + 0x184 */ > + u32 rfifo_status; /* FEC + 0x188 */ > + u32 rfifo_cntrl; /* FEC + 0x18C */ > + u32 rfifo_lrf_ptr; /* FEC + 0x190 */ > + u32 rfifo_lwf_ptr; /* FEC + 0x194 */ > + u32 rfifo_alarm; /* FEC + 0x198 */ > + u32 rfifo_rdptr; /* FEC + 0x19C */ > + u32 rfifo_wrptr; /* FEC + 0x1A0 */ > + u32 tfifo_data; /* FEC + 0x1A4 */ > + u32 tfifo_status; /* FEC + 0x1A8 */ > + u32 tfifo_cntrl; /* FEC + 0x1AC */ > + u32 tfifo_lrf_ptr; /* FEC + 0x1B0 */ > + u32 tfifo_lwf_ptr; /* FEC + 0x1B4 */ > + u32 tfifo_alarm; /* FEC + 0x1B8 */ > + u32 tfifo_rdptr; /* FEC + 0x1BC */ > + u32 tfifo_wrptr; /* FEC + 0x1C0 */ > + > + u32 reset_cntrl; /* FEC + 0x1C4 */ > + u32 xmit_fsm; /* FEC + 0x1C8 */ > + > + u32 reserved8[3]; /* FEC + 0x1CC-1D4 */ > + u32 rdes_data0; /* FEC + 0x1D8 */ > + u32 rdes_data1; /* FEC + 0x1DC */ > + u32 r_length; /* FEC + 0x1E0 */ > + u32 x_length; /* FEC + 0x1E4 */ > + u32 x_addr; /* FEC + 0x1E8 */ > + u32 cdes_data; /* FEC + 0x1EC */ > + u32 status; /* FEC + 0x1F0 */ > + u32 dma_control; /* FEC + 0x1F4 */ > + u32 des_cmnd; /* FEC + 0x1F8 */ > + u32 data; /* FEC + 0x1FC */ > + > + u32 rmon_t_drop; /* FEC + 0x200 */ > + u32 rmon_t_packets; /* FEC + 0x204 */ > + u32 rmon_t_bc_pkt; /* FEC + 0x208 */ > + u32 rmon_t_mc_pkt; /* FEC + 0x20C */ > + u32 rmon_t_crc_align; /* FEC + 0x210 */ > + u32 rmon_t_undersize; /* FEC + 0x214 */ > + u32 rmon_t_oversize; /* FEC + 0x218 */ > + u32 rmon_t_frag; /* FEC + 0x21C */ > + u32 rmon_t_jab; /* FEC + 0x220 */ > + u32 rmon_t_col; /* FEC + 0x224 */ > + u32 rmon_t_p64; /* FEC + 0x228 */ > + u32 rmon_t_p65to127; /* FEC + 0x22C */ > + u32 rmon_t_p128to255; /* FEC + 0x230 */ > + u32 rmon_t_p256to511; /* FEC + 0x234 */ > + u32 rmon_t_p512to1023; /* FEC + 0x238 */ > + u32 rmon_t_p1024to2047; /* FEC + 0x23C */ > + u32 rmon_t_p_gte2048; /* FEC + 0x240 */ > + u32 rmon_t_octets; /* FEC + 0x244 */ > + u32 ieee_t_drop; /* FEC + 0x248 */ > + u32 ieee_t_frame_ok; /* FEC + 0x24C */ > + u32 ieee_t_1col; /* FEC + 0x250 */ > + u32 ieee_t_mcol; /* FEC + 0x254 */ > + u32 ieee_t_def; /* FEC + 0x258 */ > + u32 ieee_t_lcol; /* FEC + 0x25C */ > + u32 ieee_t_excol; /* FEC + 0x260 */ > + u32 ieee_t_macerr; /* FEC + 0x264 */ > + u32 ieee_t_cserr; /* FEC + 0x268 */ > + u32 ieee_t_sqe; /* FEC + 0x26C */ > + u32 t_fdxfc; /* FEC + 0x270 */ > + u32 ieee_t_octets_ok; /* FEC + 0x274 */ > + > + u32 reserved9[2]; /* FEC + 0x278-27C */ > + u32 rmon_r_drop; /* FEC + 0x280 */ > + u32 rmon_r_packets; /* FEC + 0x284 */ > + u32 rmon_r_bc_pkt; /* FEC + 0x288 */ > + u32 rmon_r_mc_pkt; /* FEC + 0x28C */ > + u32 rmon_r_crc_align; /* FEC + 0x290 */ > + u32 rmon_r_undersize; /* FEC + 0x294 */ > + u32 rmon_r_oversize; /* FEC + 0x298 */ > + u32 rmon_r_frag; /* FEC + 0x29C */ > + u32 rmon_r_jab; /* FEC + 0x2A0 */ > + > + u32 rmon_r_resvd_0; /* FEC + 0x2A4 */ > + > + u32 rmon_r_p64; /* FEC + 0x2A8 */ > + u32 rmon_r_p65to127; /* FEC + 0x2AC */ > + u32 rmon_r_p128to255; /* FEC + 0x2B0 */ > + u32 rmon_r_p256to511; /* FEC + 0x2B4 */ > + u32 rmon_r_p512to1023; /* FEC + 0x2B8 */ > + u32 rmon_r_p1024to2047; /* FEC + 0x2BC */ > + u32 rmon_r_p_gte2048; /* FEC + 0x2C0 */ > + u32 rmon_r_octets; /* FEC + 0x2C4 */ > + u32 ieee_r_drop; /* FEC + 0x2C8 */ > + u32 ieee_r_frame_ok; /* FEC + 0x2CC */ > + u32 ieee_r_crc; /* FEC + 0x2D0 */ > + u32 ieee_r_align; /* FEC + 0x2D4 */ > + u32 r_macerr; /* FEC + 0x2D8 */ > + u32 r_fdxfc; /* FEC + 0x2DC */ > + u32 ieee_r_octets_ok; /* FEC + 0x2E0 */ > + > + u32 reserved10[7]; /* FEC + 0x2E4-2FC */ > + > + u32 reserved11[64]; /* FEC + 0x300-3FF */ > +}; > + > +#define FEC_MIB_DISABLE 0x80000000 > + > +#define FEC_IEVENT_HBERR 0x80000000 > +#define FEC_IEVENT_BABR 0x40000000 > +#define FEC_IEVENT_BABT 0x20000000 > +#define FEC_IEVENT_GRA 0x10000000 > +#define FEC_IEVENT_TFINT 0x08000000 > +#define FEC_IEVENT_MII 0x00800000 > +#define FEC_IEVENT_LATE_COL 0x00200000 > +#define FEC_IEVENT_COL_RETRY_LIM 0x00100000 > +#define FEC_IEVENT_XFIFO_UN 0x00080000 > +#define FEC_IEVENT_XFIFO_ERROR 0x00040000 > +#define FEC_IEVENT_RFIFO_ERROR 0x00020000 > + > +#define FEC_IMASK_HBERR 0x80000000 > +#define FEC_IMASK_BABR 0x40000000 > +#define FEC_IMASK_BABT 0x20000000 > +#define FEC_IMASK_GRA 0x10000000 > +#define FEC_IMASK_MII 0x00800000 > +#define FEC_IMASK_LATE_COL 0x00200000 > +#define FEC_IMASK_COL_RETRY_LIM 0x00100000 > +#define FEC_IMASK_XFIFO_UN 0x00080000 > +#define FEC_IMASK_XFIFO_ERROR 0x00040000 > +#define FEC_IMASK_RFIFO_ERROR 0x00020000 > + > +#define FEC_RCNTRL_MAX_FL_SHIFT 16 > +#define FEC_RCNTRL_LOOP 0x01 > +#define FEC_RCNTRL_DRT 0x02 > +#define FEC_RCNTRL_MII_MODE 0x04 > +#define FEC_RCNTRL_PROM 0x08 > +#define FEC_RCNTRL_BC_REJ 0x10 > +#define FEC_RCNTRL_FCE 0x20 > + > +#define FEC_TCNTRL_GTS 0x00000001 > +#define FEC_TCNTRL_HBC 0x00000002 > +#define FEC_TCNTRL_FDEN 0x00000004 > +#define FEC_TCNTRL_TFC_PAUSE 0x00000008 > +#define FEC_TCNTRL_RFC_PAUSE 0x00000010 > + > +#define FEC_ECNTRL_RESET 0x00000001 > +#define FEC_ECNTRL_ETHER_EN 0x00000002 > + > +#define FEC_PADDR2_TYPE 0x8808 > + > +#define FEC_OP_PAUSE_OPCODE 0x00010000 > + > +#define FEC_FIFO_WMRK_256B 0x3 > + > +#define FEC_FIFO_STATUS_ERR 0x00400000 > +#define FEC_FIFO_STATUS_UF 0x00200000 > +#define FEC_FIFO_STATUS_OF 0x00100000 > + > +#define FEC_FIFO_CNTRL_FRAME 0x08000000 > +#define FEC_FIFO_CNTRL_LTG_7 0x07000000 > + > +#define FEC_RESET_CNTRL_RESET_FIFO 0x02000000 > +#define FEC_RESET_CNTRL_ENABLE_IS_RESET 0x01000000 > + > +#define FEC_XMIT_FSM_APPEND_CRC 0x02000000 > +#define FEC_XMIT_FSM_ENABLE_CRC 0x01000000 > + > + > +int __init fec_mdio_init(void); > +void __exit fec_mdio_exit(void); > + > +#endif /* __DRIVERS_NET_MPC52XX_FEC_H__ */ > diff -pruN dummy/fec_phy.c ./drivers/net/fec_mpc52xx/fec_phy.c > --- dummy/fec_phy.c 1970-01-01 01:00:00.000000000 +0100 > +++ ./drivers/net/fec_mpc52xx/fec_phy.c 2007-08-10 10:59:53.000000000 +0200 > @@ -0,0 +1,229 @@ > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/netdevice.h> > +#include <linux/phy.h> > +#include <asm/io.h> > +#include <asm/mpc52xx.h> > +#include <asm/of_platform.h> > +#include "fec_phy.h" > +#include "fec.h" > + > +struct fec_mdio_priv { > + int completed; > + wait_queue_head_t wq; > + struct mpc52xx_fec __iomem *regs; > + int irq; > +}; > + > +static int fec_mdio_read(struct mii_bus *bus, int phy_id, int reg) > +{ > + struct fec_mdio_priv *priv = bus->priv; > + int tries = 100; > + > + u32 request = FEC_MII_READ_FRAME; > + request |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; > + request |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; > + > + out_be32(&priv->regs->mii_data, request); > + > + /* wait for it to finish, this takes about 23 us on lite5200b */ > + while (priv->completed == 0 && tries--) > + udelay(5); > + > + priv->completed = 0; > + > + if (tries == 0) > + return -ETIMEDOUT; > + > + return in_be32(&priv->regs->mii_data) & FEC_MII_DATA_DATAMSK; > +} > + > +static int fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data) > +{ > + struct fec_mdio_priv *priv = bus->priv; > + u32 value = data; > + int tries = 100; > + > + value |= FEC_MII_WRITE_FRAME; > + value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; > + value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; > + > + out_be32(&priv->regs->mii_data, value); > + > + /* wait for request to finish */ > + while (priv->completed == 0 && tries--) > + udelay(5); > + > + priv->completed = 0; > + > + if (tries == 0) > + return -ETIMEDOUT; > + > + return 0; > +} > + > +static irqreturn_t fec_mdio_interrupt(int irq, void *dev_id) > +{ > + struct fec_mdio_priv *priv = dev_id; > + struct mpc52xx_fec __iomem *fec; > + int ievent; > + > + fec = priv->regs; > + ievent = in_be32(&fec->ievent); > + > + ievent &= FEC_IEVENT_MII; > + if (!ievent) > + return IRQ_NONE; > + > + out_be32(&fec->ievent, ievent); > + > + priv->completed = 1; > + wake_up(&priv->wq); > + > + return IRQ_HANDLED; > +} > + > +static int fec_mdio_probe(struct of_device *of, const struct of_device_id *match) > +{ > + struct device *dev = &of->dev; > + struct device_node *np = of->node; > + struct device_node *child = NULL; > + struct mii_bus *bus; > + struct fec_mdio_priv *priv; > + struct resource res = {}; > + int err; > + int i; > + > + bus = kzalloc(sizeof(*bus), GFP_KERNEL); > + if (bus == NULL) > + return -ENOMEM; > + priv = kzalloc(sizeof(*priv), GFP_KERNEL); > + if (priv == NULL) { > + err = -ENOMEM; > + goto out_free; > + } > + > + bus->name = "mpc52xx MII bus"; > + bus->read = fec_mdio_read; > + bus->write = fec_mdio_write; > + > + /* setup irqs */ > + bus->irq = kcalloc(sizeof(bus->irq[0]), PHY_MAX_ADDR, GFP_KERNEL); > + if (bus->irq == NULL) { > + err = -ENOMEM; > + goto out_free; > + } > + for (i=0; i<PHY_MAX_ADDR; i++) > + bus->irq[i] = PHY_POLL; > + > + while ((child = of_get_next_child(np, child)) != NULL) { > + int irq = irq_of_parse_and_map(child, 0); > + if (irq != NO_IRQ) { > + const u32 *id = of_get_property(child, "reg", NULL); > + bus->irq[*id] = irq; > + } > + } > + > + /* setup registers */ > + err = of_address_to_resource(np, 0, &res); > + if (err) > + goto out_free; > + priv->regs = ioremap(res.start, res.end - res.start + 1); > + if (priv->regs == NULL) { > + err = -ENOMEM; > + goto out_free; > + } > + > + priv->irq = irq_of_parse_and_map(np, 0); > + err = request_irq(priv->irq, &fec_mdio_interrupt, IRQF_DISABLED | IRQF_SHARED, > + "fec_mdio", priv); > + if (err) { > + printk(KERN_ERR "%s: interrupt request failed with %i\n", __func__, err); > + goto out_unmap; > + } > + > + bus->id = res.start; > + bus->priv = priv; > + > + bus->dev = dev; > + dev_set_drvdata(dev, bus); > + > + init_waitqueue_head(&priv->wq); > + > + /* set MII speed */ > + out_be32(&priv->regs->mii_speed, ((mpc52xx_find_ipb_freq(of->node) >> 20) / 5) << 1); > + > + /* enable MII interrupt */ > + out_be32(&priv->regs->imask, in_be32(&priv->regs->imask) | FEC_IMASK_MII); > + > + err = mdiobus_register(bus); > + if (err) > + goto out_free_irq; > + > + return 0; > + > + out_free_irq: > + free_irq(priv->irq, dev); > + irq_dispose_mapping(priv->irq); > + out_unmap: > + iounmap(priv->regs); > + out_free: > + for (i=0; i<PHY_MAX_ADDR; i++) > + if (bus->irq[i]) > + irq_dispose_mapping(bus->irq[i]); > + kfree(bus->irq); > + kfree(priv); > + kfree(bus); > + > + return err; > +} > + > +static int fec_mdio_remove(struct of_device *of) > +{ > + struct device *dev = &of->dev; > + struct mii_bus *bus = dev_get_drvdata(dev); > + struct fec_mdio_priv *priv = bus->priv; > + int i; > + > + mdiobus_unregister(bus); > + dev_set_drvdata(dev, NULL); > + > + free_irq(priv->irq, dev); > + irq_dispose_mapping(priv->irq); > + iounmap(priv->regs); > + for (i=0; i<PHY_MAX_ADDR; i++) > + if (bus->irq[i]) > + irq_dispose_mapping(bus->irq[i]); > + kfree(priv); > + kfree(bus->irq); > + kfree(bus); > + > + return 0; > +} > + > + > +static struct of_device_id fec_mdio_match[] = { > + { > + .type = "mdio", > + .compatible = "mpc5200b-fec-phy", > + }, > + {}, > +}; > + > +static struct of_platform_driver fec_mdio_driver = { > + .name = "mpc5200b-fec-phy", > + .probe = fec_mdio_probe, > + .remove = fec_mdio_remove, > + .match_table = fec_mdio_match, > +}; > + > + > +int __init fec_mdio_init(void) > +{ > + return of_register_platform_driver(&fec_mdio_driver); > +} > + > +void __exit fec_mdio_exit(void) > +{ > + of_unregister_platform_driver(&fec_mdio_driver); > +} > diff -pruN dummy/fec_phy.h ./drivers/net/fec_mpc52xx/fec_phy.h > --- dummy/fec_phy.h 1970-01-01 01:00:00.000000000 +0100 > +++ ./drivers/net/fec_mpc52xx/fec_phy.h 2007-08-10 11:22:54.000000000 +0200 > @@ -0,0 +1,49 @@ > +/* > + * arch/ppc/52xx_io/fec_phy.h > + * > + * Driver for the MPC5200 Fast Ethernet Controller > + * Based heavily on the MII support for the MPC8xx by Dan Malek > + * > + * Author: Dale Farnsworth <dfarnsworth@mvista.com> > + * > + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under > + * the terms of the GNU General Public License version 2. This program > + * is licensed "as is" without any warranty of any kind, whether express > + * or implied. > + */ > + > +#define FEC_IMASK_ALL (FEC_IMASK_HBERR | FEC_IMASK_BABR | \ > + FEC_IMASK_BABT | FEC_IMASK_GRA | FEC_IMASK_MII | \ > + FEC_IMASK_LATE_COL | FEC_IMASK_COL_RETRY_LIM | \ > + FEC_IMASK_XFIFO_UN | FEC_IMASK_XFIFO_ERROR | \ > + FEC_IMASK_RFIFO_ERROR) > + > +#ifdef CONFIG_FEC_MPC52xx_MDIO > +#define MII_RCNTL_MODE FEC_RCNTRL_MII_MODE > +#define FEC_IMASK_ENABLE FEC_IMASK_ALL > +#define set_phy_speed(fec, s) out_be32(&fec->mii_speed, s) > +#else > +#define MII_RCNTL_MODE 0 > +#define FEC_IMASK_ENABLE (FEC_IMASK_ALL & ~FEC_IMASK_MII) > +#define set_phy_speed(fec, s) do { } while (0) > +#define fec_mii_start(dev) do { } while (0) > +#define fec_mii(dev) printk(KERN_WARNING "unexpected FEC_IEVENT_MII\n") > +#define fec_mii_init(dev) do { } while (0) > +#define fec_mii_suspend(dev) do { } while (0) > +#define fec_mii_resume(dev) do { } while (0) > +#endif /* CONFIG_FEC_MPC52xx_MDIO */ > + > +/* MII-related definitions */ > +#define FEC_MII_DATA_ST 0x40000000 /* Start frame */ > +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform read */ > +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform write */ > +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address mask */ > +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register mask */ > +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ > +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data mask */ > + > +#define FEC_MII_READ_FRAME (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA) > +#define FEC_MII_WRITE_FRAME (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | FEC_MII_DATA_TA) > + > +#define FEC_MII_DATA_RA_SHIFT 0x12 /* MII reg addr bits */ > +#define FEC_MII_DATA_PA_SHIFT 0x17 /* MII PHY addr bits */ > diff -pruN dummy/Kconfig ./drivers/net/fec_mpc52xx/Kconfig > --- dummy/Kconfig 1970-01-01 01:00:00.000000000 +0100 > +++ ./drivers/net/fec_mpc52xx/Kconfig 2007-08-08 10:50:04.000000000 +0200 > @@ -0,0 +1,24 @@ > +menu "MPC5200 Networking Options" > + depends PPC_MPC52xx && NET_ETHERNET > + > +config FEC_MPC52xx > + tristate "FEC Ethernet" > + depends on NET_ETHERNET > + select PPC_BESTCOMM > + select PPC_BESTCOMM_FEC > + select CRC32 > + ---help--- > + This option enables support for the MPC5200's on-chip > + Fast Ethernet Controller > + > +config USE_MDIO > + bool "Use external Ethernet MII PHY" > + select MII > + depends FEC_MPC52xx > + ---help--- > + The MPC5200's FEC can connect to the Ethernet either with > + an external MII PHY chip or 10 Mbps 7-wire interface > + (Motorola? industry standard). > + If your board uses an external PHY, say y, else n. > + > +endmenu > diff -pruN dummy/Makefile ./drivers/net/fec_mpc52xx/Makefile > --- dummy/Makefile 1970-01-01 01:00:00.000000000 +0100 > +++ ./drivers/net/fec_mpc52xx/Makefile 2007-08-08 10:50:04.000000000 +0200 > @@ -0,0 +1,7 @@ > +obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o > + > +fec_mpc52xx-objs := fec.o > + > +ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) > +fec_mpc52xx-objs += fec_phy.o > +endif > Index: work-powerpc.git/arch/powerpc/boot/dts/lite5200b.dts > =================================================================== > --- work-powerpc.git.orig/arch/powerpc/boot/dts/lite5200b.dts > +++ work-powerpc.git/arch/powerpc/boot/dts/lite5200b.dts > @@ -365,10 +365,26 @@ > ethernet@3000 { > device_type = "network"; > compatible = "mpc5200b-fec\0mpc5200-fec"; > - reg = <3000 800>; > + reg = <3000 400>; > mac-address = [ 02 03 04 05 06 07 ]; // Bad! > interrupts = <2 5 0>; > interrupt-parent = <&mpc5200_pic>; > + phy-handle = <&phy0>; > + }; > + > + mdio@3000 { > + #address-cells = <1>; > + #size-cells = <0>; > + device_type = "mdio"; > + compatible = "mpc5200b-fec-phy"; > + reg = <3000 400>; // fec range, since we need to setup fec interrupts > + interrupts = <2 5 0>; // these are for "mii command finished", not link changes & co. > + interrupt-parent = <&mpc5200_pic>; > + > + phy0:ethernet-phy@0 { > + device_type = "ethernet-phy"; > + reg = <0>; > + }; > }; > > ata@3a00 { > Index: work-powerpc.git/arch/powerpc/sysdev/bestcomm/fec.h > =================================================================== > --- work-powerpc.git.orig/arch/powerpc/sysdev/bestcomm/fec.h > +++ work-powerpc.git/arch/powerpc/sysdev/bestcomm/fec.h > @@ -22,6 +22,20 @@ struct bcom_fec_bd { > > #define BCOM_FEC_TX_BD_TFD 0x08000000ul /* transmit frame done */ > #define BCOM_FEC_TX_BD_INT 0x04000000ul /* interrupt */ > +#define BCOM_FEC_TX_BD_TC 0x04000000ul /* transmit CRC XXX same as ^? */ > +#define BCOM_FEC_TX_BD_ABC 0x02000000ul /* append bad CRC */ > + > +#define BCOM_FEC_RX_BD_L 0x08000000ul /* buffer is last in frame */ > +#define BCOM_FEC_RX_BD_BC 0x00800000ul /* DA is broadcast */ > +#define BCOM_FEC_RX_BD_MC 0x00400000ul /* DA is multicast and not broadcast */ > +#define BCOM_FEC_RX_BD_LG 0x00200000ul /* Rx frame length violation */ > +#define BCOM_FEC_RX_BD_NO 0x00100000ul /* Rx non-octet aligned frame */ > +#define BCOM_FEC_RX_BD_CR 0x00040000ul /* Rx CRC error */ > +#define BCOM_FEC_RX_BD_OV 0x00020000ul /* overrun */ > +#define BCOM_FEC_RX_BD_TR 0x00010000ul /* Rx frame truncated */ > +#define BCOM_FEC_RX_BD_LEN_MASK 0x000007fful /* mask for length of received frame */ > +#define BCOM_FEC_RX_BD_ERRORS (BCOM_FEC_RX_BD_LG | BCOM_FEC_RX_BD_NO | \ > + BCOM_FEC_RX_BD_CR | BCOM_FEC_RX_BD_OV | BCOM_FEC_RX_BD_TR) > > > extern struct bcom_task * > - > To unsubscribe from this list: send the line "unsubscribe netdev" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-08-10 13:02 ` Arnaldo Carvalho de Melo @ 2007-08-13 7:21 ` Domen Puncer 0 siblings, 0 replies; 47+ messages in thread From: Domen Puncer @ 2007-08-13 7:21 UTC (permalink / raw) To: Arnaldo Carvalho de Melo, linuxppc-embedded, netdev On 10/08/07 10:02 -0300, Arnaldo Carvalho de Melo wrote: > Em Fri, Aug 10, 2007 at 11:51:53AM +0200, Domen Puncer escreveu: > > +static u8 null_mac[6]; > > const OK. ... > > +static void fec_set_paddr(struct net_device *dev, u8 *mac) > > +{ > > + struct fec_priv *priv = netdev_priv(dev); > > + struct mpc52xx_fec __iomem *fec = priv->fec; > > + > > + out_be32(&fec->paddr1, *(u32*)(&mac[0])); > > + out_be32(&fec->paddr2, (*(u16*)(&mac[4]) << 16) | FEC_PADDR2_TYPE); > > spaces after the types on casts to pointers I assume you mean: out_be32(&fec->paddr1, *(u32 *)(&mac[0])); > > > +} > > + > > +static void fec_get_paddr(struct net_device *dev, u8 *mac) > > +{ > > + struct fec_priv *priv = netdev_priv(dev); > > + struct mpc52xx_fec __iomem *fec = priv->fec; > > + > > + *(u32*)(&mac[0]) = in_be32(&fec->paddr1); > > + *(u16*)(&mac[4]) = in_be32(&fec->paddr2) >> 16; > > ditto OK. > > > +} > > + > > +static int fec_set_mac_address(struct net_device *dev, void *addr) > > +{ > > + struct sockaddr *sock = (struct sockaddr *)addr; > > no need for a cast, addr is a void pointer Right, missed this one. > > > + > > + memcpy(dev->dev_addr, sock->sa_data, dev->addr_len); > > + > > + fec_set_paddr(dev, sock->sa_data); > > + return 0; > > Why always return 0? make it void Because struct net_device->set_mac_address's prototype is like that. > > > +} > > + > > +static void fec_free_rx_buffers(struct bcom_task *s) > > +{ > > + struct sk_buff *skb; > > + > > + while (!bcom_queue_empty(s)) { > > + skb = bcom_retrieve_buffer(s, NULL, NULL); > > + kfree_skb(skb); > > + } > > +} > > + > > +static int fec_alloc_rx_buffers(struct bcom_task *rxtsk) > > +{ > > + while (!bcom_queue_full(rxtsk)) { > > + struct sk_buff *skb; > > + struct bcom_fec_bd *bd; > > + > > + skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); > > + if (skb == 0) > > Test against NULL Right. I also fixed other stuff sparse reports. > > > + return -EAGAIN; > > + ... > > + > > + if (new_state && netif_msg_link(priv)) { > > + phy_print_status(phydev); > > + } > > No need for {}, this if has only one statement OK. ... > > +static int __init > > +mpc52xx_fec_init(void) > > +{ > > + int ret; > > + if ((ret = fec_mdio_init())) { > > Why not: > > int ret = fec_mdio_init(); > if (ret) { > > Less parenthesis, looks more clear I prefer it like that too. Thanks Arnaldo! Any other comments on code functionality, design? Domen ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-08-10 9:51 [RFC PATCH v0.1] net driver: mpc52xx fec Domen Puncer 2007-08-10 13:02 ` Arnaldo Carvalho de Melo @ 2007-08-18 10:06 ` Domen Puncer 2007-08-19 15:39 ` Matt Sealey 2007-09-02 7:41 ` [RFC PATCH v0.2] " Domen Puncer 2007-09-27 17:07 ` [RFC PATCH v0.1] " Juergen Beisert 3 siblings, 1 reply; 47+ messages in thread From: Domen Puncer @ 2007-08-18 10:06 UTC (permalink / raw) To: Matt Sealey; +Cc: linuxppc-embedded Hi! On 10/08/07 11:51 +0200, Domen Puncer wrote: > Index: work-powerpc.git/arch/powerpc/boot/dts/lite5200b.dts > =================================================================== > --- work-powerpc.git.orig/arch/powerpc/boot/dts/lite5200b.dts > +++ work-powerpc.git/arch/powerpc/boot/dts/lite5200b.dts > + mdio@3000 { > + #address-cells = <1>; > + #size-cells = <0>; > + device_type = "mdio"; > + compatible = "mpc5200b-fec-phy"; > + reg = <3000 400>; // fec range, since we need to setup fec interrupts > + interrupts = <2 5 0>; // these are for "mii command finished", not link changes & co. > + interrupt-parent = <&mpc5200_pic>; > + > + phy0:ethernet-phy@0 { > + device_type = "ethernet-phy"; > + reg = <0>; > + }; > }; I am struggling with this part on Efika. I would like to add this to the device tree from fixup_device_tree_efika() (arch/powerpc/kernel/prom_init.c). AFAICS client-services doesn't offer anything like new-device, so I guess "interpret" or "call-method" will have to be used. I have read some docs, but I'm still wandering in the dark. Can I please get an example? Pretty please with a cherry on top? Domen ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-08-18 10:06 ` Domen Puncer @ 2007-08-19 15:39 ` Matt Sealey 2007-08-20 8:31 ` Domen Puncer 0 siblings, 1 reply; 47+ messages in thread From: Matt Sealey @ 2007-08-19 15:39 UTC (permalink / raw) To: Domen Puncer; +Cc: linuxppc-embedded Domen, Do it in a Forth script, or in nvramrc (after probe-all). Don't clutter Linux with more fixups. The Efika PHY isn't going to change to something else and it's a bog standard no-frills MII PHY anyway. I think it is a distinction that the OF docs forgot to make, that the client interface is *all those Forth words* and not just the 6 or 7 distinct, special callable functions like claim (they exist because of the simple fact that claiming memory shouldn't involve claiming memory and such other paradoxes) and call-method. Call-method is a perfectly valid way of doing things. But, I'd really recommend you please think of a different way.. if you want to spec out a device tree entry for it I'll update my script which I am probably going to stick as an 'official' Genesi support file in the next week. If you insist on using prom_init and fixups, yaboot has the best examples of call-method and interpret, both readable and fairly easily available. -- Matt Sealey <matt@genesi-usa.com> Genesi, Manager, Developer Relations Domen Puncer wrote: > Hi! > > On 10/08/07 11:51 +0200, Domen Puncer wrote: >> Index: work-powerpc.git/arch/powerpc/boot/dts/lite5200b.dts >> =================================================================== >> --- work-powerpc.git.orig/arch/powerpc/boot/dts/lite5200b.dts >> +++ work-powerpc.git/arch/powerpc/boot/dts/lite5200b.dts >> + mdio@3000 { >> + #address-cells = <1>; >> + #size-cells = <0>; >> + device_type = "mdio"; >> + compatible = "mpc5200b-fec-phy"; >> + reg = <3000 400>; // fec range, since we need to setup fec interrupts >> + interrupts = <2 5 0>; // these are for "mii command finished", not link changes & co. >> + interrupt-parent = <&mpc5200_pic>; >> + >> + phy0:ethernet-phy@0 { >> + device_type = "ethernet-phy"; >> + reg = <0>; >> + }; >> }; > > I am struggling with this part on Efika. > I would like to add this to the device tree from > fixup_device_tree_efika() (arch/powerpc/kernel/prom_init.c). > > AFAICS client-services doesn't offer anything like new-device, > so I guess "interpret" or "call-method" will have to be used. > > I have read some docs, but I'm still wandering in the dark. > Can I please get an example? > Pretty please with a cherry on top? > > > Domen ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-08-19 15:39 ` Matt Sealey @ 2007-08-20 8:31 ` Domen Puncer 2007-08-20 13:13 ` Domen Puncer 0 siblings, 1 reply; 47+ messages in thread From: Domen Puncer @ 2007-08-20 8:31 UTC (permalink / raw) To: Matt Sealey; +Cc: Domen Puncer, linuxppc-embedded On 19/08/07 16:39 +0100, Matt Sealey wrote: > Domen, > > Do it in a Forth script, or in nvramrc (after probe-all). Don't clutter > Linux with more fixups. The Efika PHY isn't going to change to something > else and it's a bog standard no-frills MII PHY anyway. Fine with me, but I'm worried people won't update nvramrc. Currently I have this: --- start --- s" /builtin" find-device new-device 1 encode-int s" #address-cells" property 0 encode-int s" #size-cells" property s" mdio" 2dup device-name device-type s" mpc5200b-fec-phy" s" compatible" property 0xf0003000 0x400 reg 0x2 encode-int 0x5 encode-int 0x3 encode-int encode+ encode+ s" interupts" property new-device s" ethernet-phy@0" device-name s" ethernet-phy" device-type 0 encode-int s" reg" property my-self \ save our phandle to stack ihandle>phandle finish-device finish-device s" /builtin/ethernet" find-device encode-int \ phy's phandle s" phy-handle" property device-end --- end --- But I have a problem with it, possibly due to my not-knowledge of Forth. Compatible keep getting set to: compatible "/builtin/etherne" <spin> > If you insist on using prom_init and fixups, yaboot has the best > examples of call-method and interpret, both readable and fairly > easily available. Thanks. Domen > > On 10/08/07 11:51 +0200, Domen Puncer wrote: > >> Index: work-powerpc.git/arch/powerpc/boot/dts/lite5200b.dts > >> =================================================================== > >> --- work-powerpc.git.orig/arch/powerpc/boot/dts/lite5200b.dts > >> +++ work-powerpc.git/arch/powerpc/boot/dts/lite5200b.dts > >> + mdio@3000 { > >> + #address-cells = <1>; > >> + #size-cells = <0>; > >> + device_type = "mdio"; > >> + compatible = "mpc5200b-fec-phy"; > >> + reg = <3000 400>; // fec range, since we need to setup fec interrupts > >> + interrupts = <2 5 0>; // these are for "mii command finished", not link changes & co. > >> + interrupt-parent = <&mpc5200_pic>; > >> + > >> + phy0:ethernet-phy@0 { > >> + device_type = "ethernet-phy"; > >> + reg = <0>; > >> + }; > >> }; > > > > I am struggling with this part on Efika. > > I would like to add this to the device tree from > > fixup_device_tree_efika() (arch/powerpc/kernel/prom_init.c). > > > > AFAICS client-services doesn't offer anything like new-device, > > so I guess "interpret" or "call-method" will have to be used. > > > > I have read some docs, but I'm still wandering in the dark. > > Can I please get an example? > > Pretty please with a cherry on top? > > > > > > Domen > _______________________________________________ > Linuxppc-embedded mailing list > Linuxppc-embedded@ozlabs.org > https://ozlabs.org/mailman/listinfo/linuxppc-embedded ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-08-20 8:31 ` Domen Puncer @ 2007-08-20 13:13 ` Domen Puncer 2007-08-20 19:02 ` Matt Sealey 0 siblings, 1 reply; 47+ messages in thread From: Domen Puncer @ 2007-08-20 13:13 UTC (permalink / raw) To: Domen Puncer; +Cc: linuxppc-embedded [-- Attachment #1: Type: text/plain, Size: 647 bytes --] On 20/08/07 10:31 +0200, Domen Puncer wrote: > On 19/08/07 16:39 +0100, Matt Sealey wrote: > > Domen, > > > > Do it in a Forth script, or in nvramrc (after probe-all). Don't clutter > > Linux with more fixups. The Efika PHY isn't going to change to something > > else and it's a bog standard no-frills MII PHY anyway. > > Fine with me, but I'm worried people won't update nvramrc. > ... > But I have a problem with it, possibly due to my not-knowledge of Forth. > Compatible keep getting set to: > compatible "/builtin/etherne" I missed the encode-string. Matt, can you please add attached Forth script to Efika updates. Domen [-- Attachment #2: smart_firmware_phy --] [-- Type: text/plain, Size: 613 bytes --] s" /builtin" find-device new-device 1 encode-int s" #address-cells" property 0 encode-int s" #size-cells" property s" mdio" 2dup device-name device-type s" mpc5200b-fec-phy" encode-string s" compatible" property 0xf0003000 0x400 reg 0x2 encode-int 0x5 encode-int 0x3 encode-int encode+ encode+ s" interrupts" property new-device s" ethernet-phy" 2dup device-name device-type 0x10 encode-int s" reg" property my-self \ save our phandle to stack ihandle>phandle finish-device finish-device s" /builtin/ethernet" find-device encode-int \ phy's phandle s" phy-handle" property device-end ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-08-20 13:13 ` Domen Puncer @ 2007-08-20 19:02 ` Matt Sealey 2007-08-21 5:49 ` Domen Puncer 0 siblings, 1 reply; 47+ messages in thread From: Matt Sealey @ 2007-08-20 19:02 UTC (permalink / raw) To: Domen Puncer; +Cc: Domen Puncer, linuxppc-embedded Are you sure this is correct for the Efika? The MPC5200B manual makes a decent distinction between MII operation and straight MDIO? -- Matt Sealey <matt@genesi-usa.com> Genesi, Manager, Developer Relations Domen Puncer wrote: > On 20/08/07 10:31 +0200, Domen Puncer wrote: >> On 19/08/07 16:39 +0100, Matt Sealey wrote: >>> Domen, >>> >>> Do it in a Forth script, or in nvramrc (after probe-all). Don't clutter >>> Linux with more fixups. The Efika PHY isn't going to change to something >>> else and it's a bog standard no-frills MII PHY anyway. >> Fine with me, but I'm worried people won't update nvramrc. >> > ... >> But I have a problem with it, possibly due to my not-knowledge of Forth. >> Compatible keep getting set to: >> compatible "/builtin/etherne" > > I missed the encode-string. > > Matt, can you please add attached Forth script to Efika updates. > > > Domen ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-08-20 19:02 ` Matt Sealey @ 2007-08-21 5:49 ` Domen Puncer 0 siblings, 0 replies; 47+ messages in thread From: Domen Puncer @ 2007-08-21 5:49 UTC (permalink / raw) To: Matt Sealey; +Cc: Domen Puncer, linuxppc-embedded On 20/08/07 20:02 +0100, Matt Sealey wrote: > Are you sure this is correct for the Efika? It works (tm). > > The MPC5200B manual makes a decent distinction between MII operation and > straight MDIO? Uh? From what I read MDIO are the lines of MII. Domen > > -- > Matt Sealey <matt@genesi-usa.com> > Genesi, Manager, Developer Relations > > Domen Puncer wrote: > >On 20/08/07 10:31 +0200, Domen Puncer wrote: > >>On 19/08/07 16:39 +0100, Matt Sealey wrote: > >>>Domen, > >>> > >>>Do it in a Forth script, or in nvramrc (after probe-all). Don't clutter > >>>Linux with more fixups. The Efika PHY isn't going to change to something > >>>else and it's a bog standard no-frills MII PHY anyway. > >>Fine with me, but I'm worried people won't update nvramrc. > >> > >... > >>But I have a problem with it, possibly due to my not-knowledge of Forth. > >>Compatible keep getting set to: > >> compatible "/builtin/etherne" > > > >I missed the encode-string. > > > >Matt, can you please add attached Forth script to Efika updates. > > > > > > Domen ^ permalink raw reply [flat|nested] 47+ messages in thread
* [RFC PATCH v0.2] net driver: mpc52xx fec 2007-08-10 9:51 [RFC PATCH v0.1] net driver: mpc52xx fec Domen Puncer 2007-08-10 13:02 ` Arnaldo Carvalho de Melo 2007-08-18 10:06 ` Domen Puncer @ 2007-09-02 7:41 ` Domen Puncer 2007-09-03 15:57 ` Grant Likely 2007-10-02 12:49 ` [RFC PATCH v0.2] net driver: mpc52xx fec Sascha Hauer 2007-09-27 17:07 ` [RFC PATCH v0.1] " Juergen Beisert 3 siblings, 2 replies; 47+ messages in thread From: Domen Puncer @ 2007-09-02 7:41 UTC (permalink / raw) To: linuxppc-embedded; +Cc: netdev Hi! new in this version: - fixed stuff that was commented on. - added 7-wire support (compile at least, if someone has the hardware, please test!) - ethtool support (this obsoletes Sylvain's patch that's floating around: 0008-drivers-net-Add-support-for-Freescale-MPC5200-SoC-i.patch) If there are no objections, I would like this to be merged after bestcomm (mpc52xx dma engine) patches hit mainline, and that will hopefully be at the beginning of the 2.6.24 merge window. -- Driver for ethernet on mpc5200/mpc5200b SoCs (FEC). Signed-off-by: Domen Puncer <domen.puncer@telargo.com> --- arch/powerpc/boot/dts/lite5200b.dts | 18 arch/powerpc/sysdev/bestcomm/fec.h | 15 drivers/net/Kconfig | 1 drivers/net/Makefile | 1 drivers/net/fec_mpc52xx/Kconfig | 25 drivers/net/fec_mpc52xx/Makefile | 7 drivers/net/fec_mpc52xx/fec.c | 1127 ++++++++++++++++++++++++++++++++++++ drivers/net/fec_mpc52xx/fec.h | 329 ++++++++++ drivers/net/fec_mpc52xx/fec_phy.c | 238 +++++++ 9 files changed, 1759 insertions(+), 2 deletions(-) Index: linux.git/drivers/net/Kconfig =================================================================== --- linux.git.orig/drivers/net/Kconfig +++ linux.git/drivers/net/Kconfig @@ -1956,6 +1956,7 @@ config NE_H8300 controller on the Renesas H8/300 processor. source "drivers/net/fec_8xx/Kconfig" +source "drivers/net/fec_mpc52xx/Kconfig" source "drivers/net/fs_enet/Kconfig" endif # NET_ETHERNET Index: linux.git/drivers/net/Makefile =================================================================== --- linux.git.orig/drivers/net/Makefile +++ linux.git/drivers/net/Makefile @@ -204,6 +204,7 @@ obj-$(CONFIG_SMC911X) += smc911x.o obj-$(CONFIG_BFIN_MAC) += bfin_mac.o obj-$(CONFIG_DM9000) += dm9000.o obj-$(CONFIG_FEC_8XX) += fec_8xx/ +obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx/ obj-$(CONFIG_PASEMI_MAC) += pasemi_mac.o obj-$(CONFIG_MLX4_CORE) += mlx4/ Index: linux.git/arch/powerpc/boot/dts/lite5200b.dts =================================================================== --- linux.git.orig/arch/powerpc/boot/dts/lite5200b.dts +++ linux.git/arch/powerpc/boot/dts/lite5200b.dts @@ -306,10 +306,26 @@ ethernet@3000 { device_type = "network"; compatible = "mpc5200b-fec\0mpc5200-fec"; - reg = <3000 800>; + reg = <3000 400>; mac-address = [ 02 03 04 05 06 07 ]; // Bad! interrupts = <2 5 0>; interrupt-parent = <&mpc5200_pic>; + phy-handle = <&phy0>; + }; + + mdio@3000 { + #address-cells = <1>; + #size-cells = <0>; + device_type = "mdio"; + compatible = "mpc5200b-fec-phy"; + reg = <3000 400>; // fec range, since we need to setup fec interrupts + interrupts = <2 5 0>; // these are for "mii command finished", not link changes & co. + interrupt-parent = <&mpc5200_pic>; + + phy0:ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + }; }; ata@3a00 { Index: linux.git/arch/powerpc/sysdev/bestcomm/fec.h =================================================================== --- linux.git.orig/arch/powerpc/sysdev/bestcomm/fec.h +++ linux.git/arch/powerpc/sysdev/bestcomm/fec.h @@ -21,7 +21,20 @@ struct bcom_fec_bd { }; #define BCOM_FEC_TX_BD_TFD 0x08000000ul /* transmit frame done */ -#define BCOM_FEC_TX_BD_INT 0x04000000ul /* interrupt */ +#define BCOM_FEC_TX_BD_TC 0x04000000ul /* transmit CRC */ +#define BCOM_FEC_TX_BD_ABC 0x02000000ul /* append bad CRC */ + +#define BCOM_FEC_RX_BD_L 0x08000000ul /* buffer is last in frame */ +#define BCOM_FEC_RX_BD_BC 0x00800000ul /* DA is broadcast */ +#define BCOM_FEC_RX_BD_MC 0x00400000ul /* DA is multicast and not broadcast */ +#define BCOM_FEC_RX_BD_LG 0x00200000ul /* Rx frame length violation */ +#define BCOM_FEC_RX_BD_NO 0x00100000ul /* Rx non-octet aligned frame */ +#define BCOM_FEC_RX_BD_CR 0x00040000ul /* Rx CRC error */ +#define BCOM_FEC_RX_BD_OV 0x00020000ul /* overrun */ +#define BCOM_FEC_RX_BD_TR 0x00010000ul /* Rx frame truncated */ +#define BCOM_FEC_RX_BD_LEN_MASK 0x000007fful /* mask for length of received frame */ +#define BCOM_FEC_RX_BD_ERRORS (BCOM_FEC_RX_BD_LG | BCOM_FEC_RX_BD_NO | \ + BCOM_FEC_RX_BD_CR | BCOM_FEC_RX_BD_OV | BCOM_FEC_RX_BD_TR) extern struct bcom_task * Index: linux.git/drivers/net/fec_mpc52xx/Kconfig =================================================================== --- /dev/null +++ linux.git/drivers/net/fec_mpc52xx/Kconfig @@ -0,0 +1,25 @@ +menu "MPC5200 Networking Options" + depends PPC_MPC52xx && NET_ETHERNET + +config FEC_MPC52xx + tristate "FEC Ethernet" + depends on NET_ETHERNET + select PPC_BESTCOMM + select PPC_BESTCOMM_FEC + select CRC32 + ---help--- + This option enables support for the MPC5200's on-chip + Fast Ethernet Controller + +config FEC_MPC52xx_MDIO + bool "Use external Ethernet MII PHY" + depends on FEC_MPC52xx + select PHYLIB + default y + ---help--- + The MPC5200's FEC can connect to the Ethernet either with + an external MII PHY chip or 10 Mbps 7-wire interface + (Motorola? industry standard). + If your board uses an external PHY, say y, else n. + +endmenu Index: linux.git/drivers/net/fec_mpc52xx/Makefile =================================================================== --- /dev/null +++ linux.git/drivers/net/fec_mpc52xx/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o + +fec_mpc52xx-objs := fec.o + +ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) +fec_mpc52xx-objs += fec_phy.o +endif Index: linux.git/drivers/net/fec_mpc52xx/fec.c =================================================================== --- /dev/null +++ linux.git/drivers/net/fec_mpc52xx/fec.c @@ -0,0 +1,1127 @@ +/* + * drivers/drivers/net/fec_mpc52xx/fec.c + * + * Driver for the MPC5200 Fast Ethernet Controller + * + * Originally written by Dale Farnsworth <dfarnsworth@mvista.com> and + * now maintained by Sylvain Munaut <tnt@246tNt.com> + * + * Copyright (C) 2007 Domen Puncer, Telargo, Inc. + * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com> + * Copyright (C) 2003-2004 MontaVista, Software, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/crc32.h> +#include <linux/hardirq.h> +#include <linux/delay.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/skbuff.h> + +#include <asm/of_device.h> +#include <asm/of_platform.h> +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/mpc52xx.h> + +#include <sysdev/bestcomm/bestcomm.h> +#include <sysdev/bestcomm/fec.h> + +#include "fec.h" + +#define DRIVER_NAME "mpc52xx-fec" + +static irqreturn_t fec_interrupt(int, void *); +static irqreturn_t fec_rx_interrupt(int, void *); +static irqreturn_t fec_tx_interrupt(int, void *); +static struct net_device_stats *fec_get_stats(struct net_device *); +static void fec_set_multicast_list(struct net_device *dev); +static void fec_hw_init(struct net_device *dev); +static void fec_stop(struct net_device *dev); +static void fec_start(struct net_device *dev); +static void fec_reset(struct net_device *dev); + +static u8 mpc52xx_fec_mac_addr[6]; +static const u8 null_mac[6]; + +static void fec_tx_timeout(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + + dev_warn(&dev->dev, "transmit timed out\n"); + + fec_reset(dev); + + priv->stats.tx_errors++; + + if (!priv->tx_full) + netif_wake_queue(dev); +} + +static void fec_set_paddr(struct net_device *dev, u8 *mac) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + + out_be32(&fec->paddr1, *(u32 *)(&mac[0])); + out_be32(&fec->paddr2, (*(u16 *)(&mac[4]) << 16) | FEC_PADDR2_TYPE); +} + +static void fec_get_paddr(struct net_device *dev, u8 *mac) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + + *(u32 *)(&mac[0]) = in_be32(&fec->paddr1); + *(u16 *)(&mac[4]) = in_be32(&fec->paddr2) >> 16; +} + +static int fec_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sock = addr; + + memcpy(dev->dev_addr, sock->sa_data, dev->addr_len); + + fec_set_paddr(dev, sock->sa_data); + return 0; +} + +static void fec_free_rx_buffers(struct bcom_task *s) +{ + struct sk_buff *skb; + + while (!bcom_queue_empty(s)) { + skb = bcom_retrieve_buffer(s, NULL, NULL); + kfree_skb(skb); + } +} + +static int fec_alloc_rx_buffers(struct bcom_task *rxtsk) +{ + while (!bcom_queue_full(rxtsk)) { + struct sk_buff *skb; + struct bcom_fec_bd *bd; + + skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); + if (skb == NULL) + return -EAGAIN; + + /* zero out the initial receive buffers to aid debugging */ + memset(skb->data, 0, FEC_RX_BUFFER_SIZE); + + bd = (struct bcom_fec_bd *)bcom_prepare_next_buffer(rxtsk); + + bd->status = FEC_RX_BUFFER_SIZE; + bd->skb_pa = virt_to_phys(skb->data); + + bcom_submit_next_buffer(rxtsk, skb); + } + + return 0; +} + +#ifdef CONFIG_FEC_MPC52xx_MDIO +/* based on generic_adjust_link from fs_enet-main.c */ +static void fec_adjust_link(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct phy_device *phydev = priv->phydev; + int new_state = 0; + + if (phydev->link != PHY_DOWN) { + if (phydev->duplex != priv->duplex) { + struct mpc52xx_fec __iomem *fec = priv->fec; + u32 rcntrl; + u32 tcntrl; + + new_state = 1; + priv->duplex = phydev->duplex; + + rcntrl = in_be32(&fec->r_cntrl); + tcntrl = in_be32(&fec->x_cntrl); + + rcntrl &= ~FEC_RCNTRL_DRT; + tcntrl &= ~FEC_TCNTRL_FDEN; + if (phydev->duplex == DUPLEX_FULL) + tcntrl |= FEC_TCNTRL_FDEN; /* FD enable */ + else + rcntrl |= FEC_RCNTRL_DRT; /* disable Rx on Tx (HD) */ + + out_be32(&fec->r_cntrl, rcntrl); + out_be32(&fec->x_cntrl, tcntrl); + } + + if (phydev->speed != priv->speed) { + new_state = 1; + priv->speed = phydev->speed; + } + + if (priv->link == PHY_DOWN) { + new_state = 1; + priv->link = phydev->link; + netif_schedule(dev); + netif_carrier_on(dev); + netif_start_queue(dev); + } + + } else if (priv->link) { + new_state = 1; + priv->link = PHY_DOWN; + priv->speed = 0; + priv->duplex = -1; + netif_stop_queue(dev); + netif_carrier_off(dev); + } + + if (new_state && netif_msg_link(priv)) + phy_print_status(phydev); +} + +static int fec_init_phy(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct phy_device *phydev; + char phy_id[BUS_ID_SIZE]; + + struct device_node *dn, *phy_dn; + unsigned int phy_addr; + const phandle *ph; + const unsigned int *prop; + struct resource res; + int ret; + + dn = priv->ofdev->node; + ph = of_get_property(dn, "phy-handle", NULL); + if (!ph) { + dev_err(&dev->dev, "can't find \"phy-handle\" in device tree\n"); + return -ENODEV; + } + phy_dn = of_find_node_by_phandle(*ph); + + prop = of_get_property(phy_dn, "reg", NULL); + ret = of_address_to_resource(phy_dn->parent, 0, &res); + if (ret) { + dev_err(&dev->dev, "of_address_to_resource failed\n"); + return ret; + } + + phy_addr = *prop; + of_node_put(phy_dn); + + snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, res.start, phy_addr); + + priv->link = PHY_DOWN; + priv->speed = 0; + priv->duplex = -1; + + phydev = phy_connect(dev, phy_id, &fec_adjust_link, 0, PHY_INTERFACE_MODE_MII); + if (IS_ERR(phydev)) { + dev_err(&dev->dev, "phy_connect failed\n"); + return PTR_ERR(phydev); + } + dev_info(&dev->dev, "attached phy %i to driver %s\n", + phydev->addr, phydev->drv->name); + + priv->phydev = phydev; + + return 0; +} + +static int fec_phy_start(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + int err; + + err = fec_init_phy(dev); + if (err) { + dev_err(&dev->dev, "fec_init_phy failed\n"); + return err; + } + + /* reset phy - this also wakes it from PDOWN */ + phy_write(priv->phydev, MII_BMCR, BMCR_RESET); + phy_start(priv->phydev); + + return 0; +} + +static void fec_phy_stop(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + + phy_disconnect(priv->phydev); + /* power down phy */ + phy_stop(priv->phydev); + phy_write(priv->phydev, MII_BMCR, BMCR_PDOWN); +} + +static int fec_phy_mii_ioctl(struct fec_priv *priv, + struct mii_ioctl_data *mii_data, int cmd) +{ + return phy_mii_ioctl(priv->phydev, mii_data, cmd); +} + +static void fec_phy_hw_init(struct fec_priv *priv) +{ + struct mpc52xx_fec __iomem *fec = priv->fec; + + out_be32(&fec->mii_speed, priv->phy_speed); + out_be32(&fec->imask, in_be32(&fec->imask) | FEC_IMASK_MII); +} +#else +static inline int fec_phy_start(struct net_device *dev) { return 0; } +static inline void fec_phy_stop(struct net_device *dev) { } +static inline int fec_phy_mii_ioctl(struct fec_priv *priv, + struct mii_ioctl_data *mii_data, int cmd) { return -ENOTSUPP; } +static inline void fec_phy_hw_init(struct fec_priv *priv) { } +#endif + +static int fec_open(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + int err = -EBUSY; + + if (request_irq(dev->irq, &fec_interrupt, IRQF_DISABLED | IRQF_SHARED, + DRIVER_NAME "_ctrl", dev)) { + dev_err(&dev->dev, "ctrl interrupt request failed\n"); + goto out; + } + if (request_irq(priv->r_irq, &fec_rx_interrupt, IRQF_DISABLED, + DRIVER_NAME "_rx", dev)) { + dev_err(&dev->dev, "rx interrupt request failed\n"); + goto free_ctrl_irq; + } + if (request_irq(priv->t_irq, &fec_tx_interrupt, IRQF_DISABLED, + DRIVER_NAME "_tx", dev)) { + dev_err(&dev->dev, "tx interrupt request failed\n"); + goto free_2irqs; + } + + bcom_fec_rx_reset(priv->rx_dmatsk); + bcom_fec_tx_reset(priv->tx_dmatsk); + + err = fec_alloc_rx_buffers(priv->rx_dmatsk); + if (err) { + dev_err(&dev->dev, "fec_alloc_rx_buffers failed\n"); + goto free_irqs; + } + + err = fec_phy_start(dev); + if (err) + goto free_skbs; + + bcom_enable(priv->rx_dmatsk); + bcom_enable(priv->tx_dmatsk); + + fec_start(dev); + + netif_start_queue(dev); + + return 0; + + free_skbs: + fec_free_rx_buffers(priv->rx_dmatsk); + + free_irqs: + free_irq(priv->t_irq, dev); + free_2irqs: + free_irq(priv->r_irq, dev); + free_ctrl_irq: + free_irq(dev->irq, dev); + out: + + return err; +} + +static int fec_close(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + + fec_stop(dev); + + fec_free_rx_buffers(priv->rx_dmatsk); + + free_irq(dev->irq, dev); + free_irq(priv->r_irq, dev); + free_irq(priv->t_irq, dev); + + fec_phy_stop(dev); + + return 0; +} + +/* This will only be invoked if your driver is _not_ in XOFF state. + * What this means is that you need not check it, and that this + * invariant will hold if you make sure that the netif_*_queue() + * calls are done at the proper times. + */ +static int fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct bcom_fec_bd *bd; + + if (bcom_queue_full(priv->tx_dmatsk)) { + if (net_ratelimit()) + dev_err(&dev->dev, "transmit queue overrun\n"); + return 1; + } + + spin_lock_irq(&priv->lock); + dev->trans_start = jiffies; + + bd = (struct bcom_fec_bd *) + bcom_prepare_next_buffer(priv->tx_dmatsk); + + bd->status = skb->len | BCOM_FEC_TX_BD_TFD | BCOM_FEC_TX_BD_TC; + bd->skb_pa = virt_to_phys(skb->data); + + bcom_submit_next_buffer(priv->tx_dmatsk, skb); + + if (bcom_queue_full(priv->tx_dmatsk)) { + priv->tx_full = 1; + netif_stop_queue(dev); + } + + spin_unlock_irq(&priv->lock); + + return 0; +} + +/* This handles BestComm transmit task interrupts + */ +static irqreturn_t fec_tx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct fec_priv *priv = netdev_priv(dev); + + spin_lock(&priv->lock); + + while (bcom_buffer_done(priv->tx_dmatsk)) { + struct sk_buff *skb; + skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL, NULL); + + priv->tx_full = 0; + dev_kfree_skb_irq(skb); + } + + if (netif_queue_stopped(dev) && !priv->tx_full) + netif_wake_queue(dev); + + spin_unlock(&priv->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t fec_rx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct fec_priv *priv = netdev_priv(dev); + + while (bcom_buffer_done(priv->rx_dmatsk)) { + struct sk_buff *skb; + struct sk_buff *rskb; + struct bcom_fec_bd *bd; + u32 status; + + rskb = bcom_retrieve_buffer(priv->rx_dmatsk, &status, NULL); + + /* Test for errors in received frame */ + if (status & BCOM_FEC_RX_BD_ERRORS) { + /* Drop packet and reuse the buffer */ + bd = (struct bcom_fec_bd *) + bcom_prepare_next_buffer(priv->rx_dmatsk); + + bd->status = FEC_RX_BUFFER_SIZE; + bd->skb_pa = virt_to_phys(rskb->data); + + bcom_submit_next_buffer(priv->rx_dmatsk, rskb); + + priv->stats.rx_dropped++; + + continue; + } + + /* skbs are allocated on open, so now we allocate a new one, + * and remove the old (with the packet) */ + skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); + if (skb) { + /* Process the received skb */ + int length = status & BCOM_FEC_RX_BD_LEN_MASK; + + skb_put(rskb, length - 4); /* length without CRC32 */ + + rskb->dev = dev; + rskb->protocol = eth_type_trans(rskb, dev); + + netif_rx(rskb); + dev->last_rx = jiffies; + } else { + /* Can't get a new one : reuse the same & drop pkt */ + dev_notice(&dev->dev, "Memory squeeze, dropping packet.\n"); + priv->stats.rx_dropped++; + + skb = rskb; + } + + bd = (struct bcom_fec_bd *) + bcom_prepare_next_buffer(priv->rx_dmatsk); + + bd->status = FEC_RX_BUFFER_SIZE; + bd->skb_pa = virt_to_phys(skb->data); + + bcom_submit_next_buffer(priv->rx_dmatsk, skb); + } + + return IRQ_HANDLED; +} + +static irqreturn_t fec_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + u32 ievent; + + ievent = in_be32(&fec->ievent); + + ievent &= ~FEC_IEVENT_MII; /* mii is handled separately */ + if (!ievent) + return IRQ_NONE; + + out_be32(&fec->ievent, ievent); /* clear pending events */ + + if (ievent & ~(FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) { + if (ievent & ~FEC_IEVENT_TFINT) + dev_dbg(&dev->dev, "ievent: %08x\n", ievent); + return IRQ_HANDLED; + } + + if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR)) + dev_warn(&dev->dev, "FEC_IEVENT_RFIFO_ERROR\n"); + if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR)) + dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n"); + + fec_reset(dev); + + netif_wake_queue(dev); + return IRQ_HANDLED; +} + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ +static struct net_device_stats *fec_get_stats(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &priv->stats; + struct mpc52xx_fec __iomem *fec = priv->fec; + + stats->rx_bytes = in_be32(&fec->rmon_r_octets); + stats->rx_packets = in_be32(&fec->rmon_r_packets); + stats->rx_errors = in_be32(&fec->rmon_r_crc_align) + + in_be32(&fec->rmon_r_undersize) + + in_be32(&fec->rmon_r_oversize) + + in_be32(&fec->rmon_r_frag) + + in_be32(&fec->rmon_r_jab); + + stats->tx_bytes = in_be32(&fec->rmon_t_octets); + stats->tx_packets = in_be32(&fec->rmon_t_packets); + stats->tx_errors = in_be32(&fec->rmon_t_crc_align) + + in_be32(&fec->rmon_t_undersize) + + in_be32(&fec->rmon_t_oversize) + + in_be32(&fec->rmon_t_frag) + + in_be32(&fec->rmon_t_jab); + + stats->multicast = in_be32(&fec->rmon_r_mc_pkt); + stats->collisions = in_be32(&fec->rmon_t_col); + + /* detailed rx_errors: */ + stats->rx_length_errors = in_be32(&fec->rmon_r_undersize) + + in_be32(&fec->rmon_r_oversize) + + in_be32(&fec->rmon_r_frag) + + in_be32(&fec->rmon_r_jab); + stats->rx_over_errors = in_be32(&fec->r_macerr); + stats->rx_crc_errors = in_be32(&fec->ieee_r_crc); + stats->rx_frame_errors = in_be32(&fec->ieee_r_align); + stats->rx_fifo_errors = in_be32(&fec->rmon_r_drop); + stats->rx_missed_errors = in_be32(&fec->rmon_r_drop); + + /* detailed tx_errors: */ + stats->tx_aborted_errors = 0; + stats->tx_carrier_errors = in_be32(&fec->ieee_t_cserr); + stats->tx_fifo_errors = in_be32(&fec->rmon_t_drop); + stats->tx_heartbeat_errors = in_be32(&fec->ieee_t_sqe); + stats->tx_window_errors = in_be32(&fec->ieee_t_lcol); + + return stats; +} + +/* + * Read MIB counters in order to reset them, + * then zero all the stats fields in memory + */ +static void fec_reset_stats(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + + out_be32(&fec->mib_control, FEC_MIB_DISABLE); + memset_io(&fec->rmon_t_drop, 0, (__force u32)&fec->reserved10 - + (__force u32)&fec->rmon_t_drop); + out_be32(&fec->mib_control, 0); + + memset(&priv->stats, 0, sizeof(priv->stats)); +} + +/* + * Set or clear the multicast filter for this adaptor. + */ +static void fec_set_multicast_list(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + u32 rx_control; + + rx_control = in_be32(&fec->r_cntrl); + + if (dev->flags & IFF_PROMISC) { + rx_control |= FEC_RCNTRL_PROM; + out_be32(&fec->r_cntrl, rx_control); + } else { + rx_control &= ~FEC_RCNTRL_PROM; + out_be32(&fec->r_cntrl, rx_control); + + if (dev->flags & IFF_ALLMULTI) { + out_be32(&fec->gaddr1, 0xffffffff); + out_be32(&fec->gaddr2, 0xffffffff); + } else { + u32 crc; + int i; + struct dev_mc_list *dmi; + u32 gaddr1 = 0x00000000; + u32 gaddr2 = 0x00000000; + + dmi = dev->mc_list; + for (i=0; i<dev->mc_count; i++) { + crc = ether_crc_le(6, dmi->dmi_addr) >> 26; + if (crc >= 32) + gaddr1 |= 1 << (crc-32); + else + gaddr2 |= 1 << crc; + dmi = dmi->next; + } + out_be32(&fec->gaddr1, gaddr1); + out_be32(&fec->gaddr2, gaddr2); + } + } +} + +static void __init fec_str2mac(char *str, unsigned char *mac) +{ + int i; + u64 val64; + + val64 = simple_strtoull(str, NULL, 16); + + for (i = 0; i < 6; i++) + mac[5-i] = val64 >> (i*8); +} + +static int __init mpc52xx_fec_mac_setup(char *mac_address) +{ + fec_str2mac(mac_address, mpc52xx_fec_mac_addr); + return 0; +} + +__setup("mpc52xx-mac=", mpc52xx_fec_mac_setup); + +/** + * fec_hw_init + * @dev: network device + * + * Setup various hardware setting, only needed once on start + */ +static void fec_hw_init(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + int i; + + /* Whack a reset. We should wait for this. */ + out_be32(&fec->ecntrl, FEC_ECNTRL_RESET); + for (i = 0; i < FEC_RESET_DELAY; ++i) { + if ((in_be32(&fec->ecntrl) & FEC_ECNTRL_RESET) == 0) + break; + udelay(1); + } + if (i == FEC_RESET_DELAY) + dev_err(&dev->dev, "FEC Reset timeout!\n"); + + /* set pause to 0x20 frames */ + out_be32(&fec->op_pause, FEC_OP_PAUSE_OPCODE | 0x20); + + /* high service request will be deasserted when there's < 7 bytes in fifo + * low service request will be deasserted when there's < 4*7 bytes in fifo + */ + out_be32(&fec->rfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7); + out_be32(&fec->tfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7); + + /* alarm when <= x bytes in FIFO */ + out_be32(&fec->rfifo_alarm, 0x0000030c); + out_be32(&fec->tfifo_alarm, 0x00000100); + + /* begin transmittion when 256 bytes are in FIFO (or EOF or FIFO full) */ + out_be32(&fec->x_wmrk, FEC_FIFO_WMRK_256B); + + /* enable crc generation */ + out_be32(&fec->xmit_fsm, FEC_XMIT_FSM_APPEND_CRC | FEC_XMIT_FSM_ENABLE_CRC); + out_be32(&fec->iaddr1, 0x00000000); /* No individual filter */ + out_be32(&fec->iaddr2, 0x00000000); /* No individual filter */ + + /* set phy speed and enable MII interrupt + * this can't be done in phy driver, since it needs to be called + * before fec stuff (even on resume) */ + fec_phy_hw_init(priv); +} + +/** + * fec_start + * @dev: network device + * + * This function is called to start or restart the FEC during a link + * change. This happens on fifo errors or when switching between half + * and full duplex. + */ +static void fec_start(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + u32 rcntrl; + u32 tcntrl; + u32 tmp; + + /* clear sticky error bits */ + tmp = FEC_FIFO_STATUS_ERR | FEC_FIFO_STATUS_UF | FEC_FIFO_STATUS_OF; + out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & tmp); + out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & tmp); + + /* FIFOs will reset on fec_enable */ + out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_ENABLE_IS_RESET); + + /* Set station address. */ + fec_set_paddr(dev, dev->dev_addr); + + fec_set_multicast_list(dev); + + /* set max frame len, enable flow control, select mii mode */ + rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */ + rcntrl |= FEC_RCNTRL_FCE; + rcntrl |= MII_RCNTL_MODE; + if (priv->duplex == DUPLEX_FULL) + tcntrl = FEC_TCNTRL_FDEN; /* FD enable */ + else { + rcntrl |= FEC_RCNTRL_DRT; /* disable Rx on Tx (HD) */ + tcntrl = 0; + } + out_be32(&fec->r_cntrl, rcntrl); + out_be32(&fec->x_cntrl, tcntrl); + + /* Clear any outstanding interrupt. */ + out_be32(&fec->ievent, 0xffffffff); + + /* Enable interrupts we wish to service. */ + out_be32(&fec->imask, FEC_IMASK_ENABLE); + + /* And last, enable the transmit and receive processing. */ + out_be32(&fec->ecntrl, FEC_ECNTRL_ETHER_EN); + out_be32(&fec->r_des_active, 0x01000000); + + priv->tx_full = 0; +} + +/** + * fec_stop + * @dev: network device + * + * stop all activity on fec and empty dma buffers + */ +static void fec_stop(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + unsigned long timeout; + + /* disable all but MII interrupt */ + out_be32(&fec->imask, in_be32(&fec->imask) & FEC_IMASK_MII); + + /* Disable the rx task. */ + bcom_disable(priv->rx_dmatsk); + + /* Wait for tx queue to drain, but only if we're in process context */ + if (!in_interrupt()) { + timeout = jiffies + msecs_to_jiffies(2000); + while (time_before(jiffies, timeout) && + !bcom_queue_empty(priv->tx_dmatsk)) + msleep(100); + + if (time_after_eq(jiffies, timeout)) + dev_err(&dev->dev, "queues didn't drain\n"); +#if 1 + if (time_after_eq(jiffies, timeout)) { + dev_err(&dev->dev, " tx: index: %i, outdex: %i\n", + priv->tx_dmatsk->index, + priv->tx_dmatsk->outdex); + dev_err(&dev->dev, " rx: index: %i, outdex: %i\n", + priv->rx_dmatsk->index, + priv->rx_dmatsk->outdex); + } +#endif + } + + bcom_disable(priv->tx_dmatsk); + + /* Stop FEC */ + out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~FEC_ECNTRL_ETHER_EN); + + return; +} + +/* reset fec and bestcomm tasks */ +static void fec_reset(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct mpc52xx_fec __iomem *fec = priv->fec; + + fec_stop(dev); + + out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status)); + out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_RESET_FIFO); + + fec_free_rx_buffers(priv->rx_dmatsk); + + fec_hw_init(dev); + + phy_stop(priv->phydev); + phy_write(priv->phydev, MII_BMCR, BMCR_RESET); + phy_start(priv->phydev); + + bcom_fec_rx_reset(priv->rx_dmatsk); + bcom_fec_tx_reset(priv->tx_dmatsk); + + fec_alloc_rx_buffers(priv->rx_dmatsk); + + fec_start(dev); +} + + +/* ethtool interface */ +static void fec_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRIVER_NAME); +} + +static int fec_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct fec_priv *priv = netdev_priv(dev); + return phy_ethtool_gset(priv->phydev, cmd); +} + +static int fec_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct fec_priv *priv = netdev_priv(dev); + return phy_ethtool_sset(priv->phydev, cmd); +} + +static u32 fec_get_msglevel(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + return priv->msg_enable; +} + +static void fec_set_msglevel(struct net_device *dev, u32 level) +{ + struct fec_priv *priv = netdev_priv(dev); + priv->msg_enable = level; +} + +static const struct ethtool_ops fec_ethtool_ops = { + .get_drvinfo = fec_get_drvinfo, + .get_settings = fec_get_settings, + .set_settings = fec_set_settings, + .get_link = ethtool_op_get_link, + .get_msglevel = fec_get_msglevel, + .set_msglevel = fec_set_msglevel, +}; + + +static int fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct fec_priv *priv = netdev_priv(dev); + + return fec_phy_mii_ioctl(priv, if_mii(rq), cmd); +} + +/* ======================================================================== */ +/* OF Driver */ +/* ======================================================================== */ + +static int __devinit +mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match) +{ + int rv; + struct net_device *ndev; + struct fec_priv *priv = NULL; + struct resource mem; + + phys_addr_t rx_fifo; + phys_addr_t tx_fifo; + + /* Get the ether ndev & it's private zone */ + ndev = alloc_etherdev(sizeof(struct fec_priv)); + if (!ndev) + return -ENOMEM; + + priv = netdev_priv(ndev); + + priv->ofdev = op; + + /* Reserve FEC control zone */ + rv = of_address_to_resource(op->node, 0, &mem); + if (rv) { + printk(KERN_ERR DRIVER_NAME ": " + "Error while parsing device node resource\n" ); + return rv; + } + if ((mem.end - mem.start + 1) != sizeof(struct mpc52xx_fec)) { + printk(KERN_ERR DRIVER_NAME + " - invalid resource size (%lx != %x), check mpc52xx_devices.c\n", + (unsigned long)(mem.end - mem.start + 1), sizeof(struct mpc52xx_fec)); + return -EINVAL; + } + + if (!request_mem_region(mem.start, sizeof(struct mpc52xx_fec), DRIVER_NAME)) + return -EBUSY; + + /* Init ether ndev with what we have */ + ndev->open = fec_open; + ndev->stop = fec_close; + ndev->hard_start_xmit = fec_hard_start_xmit; + ndev->do_ioctl = fec_ioctl; + ndev->ethtool_ops = &fec_ethtool_ops; + ndev->get_stats = fec_get_stats; + ndev->set_mac_address = fec_set_mac_address; + ndev->set_multicast_list = fec_set_multicast_list; + ndev->tx_timeout = fec_tx_timeout; + ndev->watchdog_timeo = FEC_WATCHDOG_TIMEOUT; + ndev->flags &= ~IFF_RUNNING; + ndev->base_addr = mem.start; + + priv->t_irq = priv->r_irq = ndev->irq = NO_IRQ; /* IRQ are free for now */ + + spin_lock_init(&priv->lock); + + /* ioremap the zones */ + priv->fec = ioremap(mem.start, sizeof(struct mpc52xx_fec)); + + if (!priv->fec) { + rv = -ENOMEM; + goto probe_error; + } + + /* Bestcomm init */ + rx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, rfifo_data); + tx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, tfifo_data); + + priv->rx_dmatsk = bcom_fec_rx_init(FEC_RX_NUM_BD, rx_fifo, FEC_RX_BUFFER_SIZE); + priv->tx_dmatsk = bcom_fec_tx_init(FEC_TX_NUM_BD, tx_fifo); + + if (!priv->rx_dmatsk || !priv->tx_dmatsk) { + printk(KERN_ERR DRIVER_NAME ": Can not init SDMA tasks\n" ); + rv = -ENOMEM; + goto probe_error; + } + + /* Get the IRQ we need one by one */ + /* Control */ + ndev->irq = irq_of_parse_and_map(op->node, 0); + + /* RX */ + priv->r_irq = bcom_get_task_irq(priv->rx_dmatsk); + + /* TX */ + priv->t_irq = bcom_get_task_irq(priv->tx_dmatsk); + + /* MAC address init */ + if (memcmp(mpc52xx_fec_mac_addr, null_mac, 6) != 0) + memcpy(ndev->dev_addr, mpc52xx_fec_mac_addr, 6); + else + fec_get_paddr(ndev, ndev->dev_addr); + + /* Phy speed */ + priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1; + + priv->msg_enable = (NETIF_MSG_IFUP << 1) - 1; + priv->duplex = DUPLEX_FULL; + + /* Hardware init */ + fec_hw_init(ndev); + + fec_reset_stats(ndev); + + /* Register the new network device */ + rv = register_netdev(ndev); + if (rv < 0) + goto probe_error; + + /* We're done ! */ + dev_set_drvdata(&op->dev, ndev); + + return 0; + + + /* Error handling - free everything that might be allocated */ +probe_error: + + irq_dispose_mapping(ndev->irq); + + if (priv->rx_dmatsk) + bcom_fec_rx_release(priv->rx_dmatsk); + if (priv->tx_dmatsk) + bcom_fec_tx_release(priv->tx_dmatsk); + + if (priv->fec) + iounmap(priv->fec); + + release_mem_region(mem.start, sizeof(struct mpc52xx_fec)); + + free_netdev(ndev); + + return rv; +} + +static int +mpc52xx_fec_remove(struct of_device *op) +{ + struct net_device *ndev; + struct fec_priv *priv; + + ndev = dev_get_drvdata(&op->dev); + if (!ndev) + return 0; + priv = netdev_priv(ndev); + + unregister_netdev(ndev); + + irq_dispose_mapping(ndev->irq); + + bcom_fec_rx_release(priv->rx_dmatsk); + bcom_fec_tx_release(priv->tx_dmatsk); + + iounmap(priv->fec); + + release_mem_region(ndev->base_addr, sizeof(struct mpc52xx_fec)); + + free_netdev(ndev); + + dev_set_drvdata(&op->dev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state) +{ + struct net_device *dev = dev_get_drvdata(&op->dev); + + if (netif_running(dev)) + fec_close(dev); + + return 0; +} + +static int mpc52xx_fec_of_resume(struct of_device *op) +{ + struct net_device *dev = dev_get_drvdata(&op->dev); + + fec_hw_init(dev); + fec_reset_stats(dev); + + if (netif_running(dev)) + fec_open(dev); + + return 0; +} +#endif + +static struct of_device_id mpc52xx_fec_match[] = { + { + .type = "network", + .compatible = "mpc5200-fec", + }, + { } +}; + +MODULE_DEVICE_TABLE(of, mpc52xx_fec_match); + +static struct of_platform_driver mpc52xx_fec_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = mpc52xx_fec_match, + .probe = mpc52xx_fec_probe, + .remove = mpc52xx_fec_remove, +#ifdef CONFIG_PM + .suspend = mpc52xx_fec_of_suspend, + .resume = mpc52xx_fec_of_resume, +#endif +}; + + +/* ======================================================================== */ +/* Module */ +/* ======================================================================== */ + +static int __init +mpc52xx_fec_init(void) +{ + int ret; + ret = fec_mdio_init(); + if (ret) { + printk(KERN_ERR DRIVER_NAME ": fec_mdio_init failed\n"); + return ret; + } + + return of_register_platform_driver(&mpc52xx_fec_driver); +} + +static void __exit +mpc52xx_fec_exit(void) +{ + of_unregister_platform_driver(&mpc52xx_fec_driver); + fec_mdio_exit(); +} + + +module_init(mpc52xx_fec_init); +module_exit(mpc52xx_fec_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dale Farnsworth"); +MODULE_DESCRIPTION("Ethernet driver for the Freescale MPC52xx FEC"); + Index: linux.git/drivers/net/fec_mpc52xx/fec.h =================================================================== --- /dev/null +++ linux.git/drivers/net/fec_mpc52xx/fec.h @@ -0,0 +1,329 @@ +/* + * drivers/drivers/net/fec_mpc52xx/fec.h + * + * Driver for the MPC5200 Fast Ethernet Controller + * + * Author: Dale Farnsworth <dfarnsworth@mvista.com> + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __DRIVERS_NET_MPC52XX_FEC_H__ +#define __DRIVERS_NET_MPC52XX_FEC_H__ + +#include <linux/phy.h> + +/* Tunable constant */ +/* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */ +#define FEC_RX_BUFFER_SIZE 1522 /* max receive packet size */ +#define FEC_RX_NUM_BD 64 +#define FEC_TX_NUM_BD 64 + +#define FEC_RESET_DELAY 50 /* uS */ + +#define FEC_WATCHDOG_TIMEOUT ((400*HZ)/1000) + +struct fec_priv { + int duplex; + int tx_full; + int r_irq; + int t_irq; + struct mpc52xx_fec __iomem *fec; + struct bcom_task *rx_dmatsk; + struct bcom_task *tx_dmatsk; + spinlock_t lock; + struct net_device_stats stats; + int msg_enable; + struct of_device *ofdev; + uint phy_speed; +#ifdef CONFIG_FEC_MPC52xx_MDIO + struct phy_device *phydev; + enum phy_state link; + int speed; +#endif /* CONFIG_FEC_MPC52xx_MDIO */ +}; + + +/* ======================================================================== */ +/* Hardware register sets & bits */ +/* ======================================================================== */ + +struct mpc52xx_fec { + u32 fec_id; /* FEC + 0x000 */ + u32 ievent; /* FEC + 0x004 */ + u32 imask; /* FEC + 0x008 */ + + u32 reserved0[1]; /* FEC + 0x00C */ + u32 r_des_active; /* FEC + 0x010 */ + u32 x_des_active; /* FEC + 0x014 */ + u32 r_des_active_cl; /* FEC + 0x018 */ + u32 x_des_active_cl; /* FEC + 0x01C */ + u32 ivent_set; /* FEC + 0x020 */ + u32 ecntrl; /* FEC + 0x024 */ + + u32 reserved1[6]; /* FEC + 0x028-03C */ + u32 mii_data; /* FEC + 0x040 */ + u32 mii_speed; /* FEC + 0x044 */ + u32 mii_status; /* FEC + 0x048 */ + + u32 reserved2[5]; /* FEC + 0x04C-05C */ + u32 mib_data; /* FEC + 0x060 */ + u32 mib_control; /* FEC + 0x064 */ + + u32 reserved3[6]; /* FEC + 0x068-7C */ + u32 r_activate; /* FEC + 0x080 */ + u32 r_cntrl; /* FEC + 0x084 */ + u32 r_hash; /* FEC + 0x088 */ + u32 r_data; /* FEC + 0x08C */ + u32 ar_done; /* FEC + 0x090 */ + u32 r_test; /* FEC + 0x094 */ + u32 r_mib; /* FEC + 0x098 */ + u32 r_da_low; /* FEC + 0x09C */ + u32 r_da_high; /* FEC + 0x0A0 */ + + u32 reserved4[7]; /* FEC + 0x0A4-0BC */ + u32 x_activate; /* FEC + 0x0C0 */ + u32 x_cntrl; /* FEC + 0x0C4 */ + u32 backoff; /* FEC + 0x0C8 */ + u32 x_data; /* FEC + 0x0CC */ + u32 x_status; /* FEC + 0x0D0 */ + u32 x_mib; /* FEC + 0x0D4 */ + u32 x_test; /* FEC + 0x0D8 */ + u32 fdxfc_da1; /* FEC + 0x0DC */ + u32 fdxfc_da2; /* FEC + 0x0E0 */ + u32 paddr1; /* FEC + 0x0E4 */ + u32 paddr2; /* FEC + 0x0E8 */ + u32 op_pause; /* FEC + 0x0EC */ + + u32 reserved5[4]; /* FEC + 0x0F0-0FC */ + u32 instr_reg; /* FEC + 0x100 */ + u32 context_reg; /* FEC + 0x104 */ + u32 test_cntrl; /* FEC + 0x108 */ + u32 acc_reg; /* FEC + 0x10C */ + u32 ones; /* FEC + 0x110 */ + u32 zeros; /* FEC + 0x114 */ + u32 iaddr1; /* FEC + 0x118 */ + u32 iaddr2; /* FEC + 0x11C */ + u32 gaddr1; /* FEC + 0x120 */ + u32 gaddr2; /* FEC + 0x124 */ + u32 random; /* FEC + 0x128 */ + u32 rand1; /* FEC + 0x12C */ + u32 tmp; /* FEC + 0x130 */ + + u32 reserved6[3]; /* FEC + 0x134-13C */ + u32 fifo_id; /* FEC + 0x140 */ + u32 x_wmrk; /* FEC + 0x144 */ + u32 fcntrl; /* FEC + 0x148 */ + u32 r_bound; /* FEC + 0x14C */ + u32 r_fstart; /* FEC + 0x150 */ + u32 r_count; /* FEC + 0x154 */ + u32 r_lag; /* FEC + 0x158 */ + u32 r_read; /* FEC + 0x15C */ + u32 r_write; /* FEC + 0x160 */ + u32 x_count; /* FEC + 0x164 */ + u32 x_lag; /* FEC + 0x168 */ + u32 x_retry; /* FEC + 0x16C */ + u32 x_write; /* FEC + 0x170 */ + u32 x_read; /* FEC + 0x174 */ + + u32 reserved7[2]; /* FEC + 0x178-17C */ + u32 fm_cntrl; /* FEC + 0x180 */ + u32 rfifo_data; /* FEC + 0x184 */ + u32 rfifo_status; /* FEC + 0x188 */ + u32 rfifo_cntrl; /* FEC + 0x18C */ + u32 rfifo_lrf_ptr; /* FEC + 0x190 */ + u32 rfifo_lwf_ptr; /* FEC + 0x194 */ + u32 rfifo_alarm; /* FEC + 0x198 */ + u32 rfifo_rdptr; /* FEC + 0x19C */ + u32 rfifo_wrptr; /* FEC + 0x1A0 */ + u32 tfifo_data; /* FEC + 0x1A4 */ + u32 tfifo_status; /* FEC + 0x1A8 */ + u32 tfifo_cntrl; /* FEC + 0x1AC */ + u32 tfifo_lrf_ptr; /* FEC + 0x1B0 */ + u32 tfifo_lwf_ptr; /* FEC + 0x1B4 */ + u32 tfifo_alarm; /* FEC + 0x1B8 */ + u32 tfifo_rdptr; /* FEC + 0x1BC */ + u32 tfifo_wrptr; /* FEC + 0x1C0 */ + + u32 reset_cntrl; /* FEC + 0x1C4 */ + u32 xmit_fsm; /* FEC + 0x1C8 */ + + u32 reserved8[3]; /* FEC + 0x1CC-1D4 */ + u32 rdes_data0; /* FEC + 0x1D8 */ + u32 rdes_data1; /* FEC + 0x1DC */ + u32 r_length; /* FEC + 0x1E0 */ + u32 x_length; /* FEC + 0x1E4 */ + u32 x_addr; /* FEC + 0x1E8 */ + u32 cdes_data; /* FEC + 0x1EC */ + u32 status; /* FEC + 0x1F0 */ + u32 dma_control; /* FEC + 0x1F4 */ + u32 des_cmnd; /* FEC + 0x1F8 */ + u32 data; /* FEC + 0x1FC */ + + u32 rmon_t_drop; /* FEC + 0x200 */ + u32 rmon_t_packets; /* FEC + 0x204 */ + u32 rmon_t_bc_pkt; /* FEC + 0x208 */ + u32 rmon_t_mc_pkt; /* FEC + 0x20C */ + u32 rmon_t_crc_align; /* FEC + 0x210 */ + u32 rmon_t_undersize; /* FEC + 0x214 */ + u32 rmon_t_oversize; /* FEC + 0x218 */ + u32 rmon_t_frag; /* FEC + 0x21C */ + u32 rmon_t_jab; /* FEC + 0x220 */ + u32 rmon_t_col; /* FEC + 0x224 */ + u32 rmon_t_p64; /* FEC + 0x228 */ + u32 rmon_t_p65to127; /* FEC + 0x22C */ + u32 rmon_t_p128to255; /* FEC + 0x230 */ + u32 rmon_t_p256to511; /* FEC + 0x234 */ + u32 rmon_t_p512to1023; /* FEC + 0x238 */ + u32 rmon_t_p1024to2047; /* FEC + 0x23C */ + u32 rmon_t_p_gte2048; /* FEC + 0x240 */ + u32 rmon_t_octets; /* FEC + 0x244 */ + u32 ieee_t_drop; /* FEC + 0x248 */ + u32 ieee_t_frame_ok; /* FEC + 0x24C */ + u32 ieee_t_1col; /* FEC + 0x250 */ + u32 ieee_t_mcol; /* FEC + 0x254 */ + u32 ieee_t_def; /* FEC + 0x258 */ + u32 ieee_t_lcol; /* FEC + 0x25C */ + u32 ieee_t_excol; /* FEC + 0x260 */ + u32 ieee_t_macerr; /* FEC + 0x264 */ + u32 ieee_t_cserr; /* FEC + 0x268 */ + u32 ieee_t_sqe; /* FEC + 0x26C */ + u32 t_fdxfc; /* FEC + 0x270 */ + u32 ieee_t_octets_ok; /* FEC + 0x274 */ + + u32 reserved9[2]; /* FEC + 0x278-27C */ + u32 rmon_r_drop; /* FEC + 0x280 */ + u32 rmon_r_packets; /* FEC + 0x284 */ + u32 rmon_r_bc_pkt; /* FEC + 0x288 */ + u32 rmon_r_mc_pkt; /* FEC + 0x28C */ + u32 rmon_r_crc_align; /* FEC + 0x290 */ + u32 rmon_r_undersize; /* FEC + 0x294 */ + u32 rmon_r_oversize; /* FEC + 0x298 */ + u32 rmon_r_frag; /* FEC + 0x29C */ + u32 rmon_r_jab; /* FEC + 0x2A0 */ + + u32 rmon_r_resvd_0; /* FEC + 0x2A4 */ + + u32 rmon_r_p64; /* FEC + 0x2A8 */ + u32 rmon_r_p65to127; /* FEC + 0x2AC */ + u32 rmon_r_p128to255; /* FEC + 0x2B0 */ + u32 rmon_r_p256to511; /* FEC + 0x2B4 */ + u32 rmon_r_p512to1023; /* FEC + 0x2B8 */ + u32 rmon_r_p1024to2047; /* FEC + 0x2BC */ + u32 rmon_r_p_gte2048; /* FEC + 0x2C0 */ + u32 rmon_r_octets; /* FEC + 0x2C4 */ + u32 ieee_r_drop; /* FEC + 0x2C8 */ + u32 ieee_r_frame_ok; /* FEC + 0x2CC */ + u32 ieee_r_crc; /* FEC + 0x2D0 */ + u32 ieee_r_align; /* FEC + 0x2D4 */ + u32 r_macerr; /* FEC + 0x2D8 */ + u32 r_fdxfc; /* FEC + 0x2DC */ + u32 ieee_r_octets_ok; /* FEC + 0x2E0 */ + + u32 reserved10[7]; /* FEC + 0x2E4-2FC */ + + u32 reserved11[64]; /* FEC + 0x300-3FF */ +}; + +#define FEC_MIB_DISABLE 0x80000000 + +#define FEC_IEVENT_HBERR 0x80000000 +#define FEC_IEVENT_BABR 0x40000000 +#define FEC_IEVENT_BABT 0x20000000 +#define FEC_IEVENT_GRA 0x10000000 +#define FEC_IEVENT_TFINT 0x08000000 +#define FEC_IEVENT_MII 0x00800000 +#define FEC_IEVENT_LATE_COL 0x00200000 +#define FEC_IEVENT_COL_RETRY_LIM 0x00100000 +#define FEC_IEVENT_XFIFO_UN 0x00080000 +#define FEC_IEVENT_XFIFO_ERROR 0x00040000 +#define FEC_IEVENT_RFIFO_ERROR 0x00020000 + +#define FEC_IMASK_HBERR 0x80000000 +#define FEC_IMASK_BABR 0x40000000 +#define FEC_IMASK_BABT 0x20000000 +#define FEC_IMASK_GRA 0x10000000 +#define FEC_IMASK_MII 0x00800000 +#define FEC_IMASK_LATE_COL 0x00200000 +#define FEC_IMASK_COL_RETRY_LIM 0x00100000 +#define FEC_IMASK_XFIFO_UN 0x00080000 +#define FEC_IMASK_XFIFO_ERROR 0x00040000 +#define FEC_IMASK_RFIFO_ERROR 0x00020000 + +/* all but MII, which is enabled separately */ +#define FEC_IMASK_ENABLE (FEC_IMASK_HBERR | FEC_IMASK_BABR | \ + FEC_IMASK_BABT | FEC_IMASK_GRA | FEC_IMASK_LATE_COL | \ + FEC_IMASK_COL_RETRY_LIM | FEC_IMASK_XFIFO_UN | \ + FEC_IMASK_XFIFO_ERROR | FEC_IMASK_RFIFO_ERROR) + +#define FEC_RCNTRL_MAX_FL_SHIFT 16 +#define FEC_RCNTRL_LOOP 0x01 +#define FEC_RCNTRL_DRT 0x02 +#define FEC_RCNTRL_MII_MODE 0x04 +#define FEC_RCNTRL_PROM 0x08 +#define FEC_RCNTRL_BC_REJ 0x10 +#define FEC_RCNTRL_FCE 0x20 + +#define FEC_TCNTRL_GTS 0x00000001 +#define FEC_TCNTRL_HBC 0x00000002 +#define FEC_TCNTRL_FDEN 0x00000004 +#define FEC_TCNTRL_TFC_PAUSE 0x00000008 +#define FEC_TCNTRL_RFC_PAUSE 0x00000010 + +#define FEC_ECNTRL_RESET 0x00000001 +#define FEC_ECNTRL_ETHER_EN 0x00000002 + +#define FEC_MII_DATA_ST 0x40000000 /* Start frame */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform read */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform write */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data mask */ + +#define FEC_MII_READ_FRAME (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA) +#define FEC_MII_WRITE_FRAME (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | FEC_MII_DATA_TA) + +#define FEC_MII_DATA_RA_SHIFT 0x12 /* MII reg addr bits */ +#define FEC_MII_DATA_PA_SHIFT 0x17 /* MII PHY addr bits */ + +#define FEC_PADDR2_TYPE 0x8808 + +#define FEC_OP_PAUSE_OPCODE 0x00010000 + +#define FEC_FIFO_WMRK_256B 0x3 + +#define FEC_FIFO_STATUS_ERR 0x00400000 +#define FEC_FIFO_STATUS_UF 0x00200000 +#define FEC_FIFO_STATUS_OF 0x00100000 + +#define FEC_FIFO_CNTRL_FRAME 0x08000000 +#define FEC_FIFO_CNTRL_LTG_7 0x07000000 + +#define FEC_RESET_CNTRL_RESET_FIFO 0x02000000 +#define FEC_RESET_CNTRL_ENABLE_IS_RESET 0x01000000 + +#define FEC_XMIT_FSM_APPEND_CRC 0x02000000 +#define FEC_XMIT_FSM_ENABLE_CRC 0x01000000 + + +#ifdef CONFIG_FEC_MPC52xx_MDIO /* 18-wire MII mode */ + +#define MII_RCNTL_MODE FEC_RCNTRL_MII_MODE + +int __init fec_mdio_init(void); +void __exit fec_mdio_exit(void); + +#else /* 7-wire 10 Mbps mode */ + +#define MII_RCNTL_MODE 0 + +static inline int __init fec_mdio_init(void) { return 0; } +static inline void __exit fec_mdio_exit(void) { } + +#endif /* CONFIG_FEC_MPC52xx_MDIO */ + +#endif /* __DRIVERS_NET_MPC52XX_FEC_H__ */ Index: linux.git/drivers/net/fec_mpc52xx/fec_phy.c =================================================================== --- /dev/null +++ linux.git/drivers/net/fec_mpc52xx/fec_phy.c @@ -0,0 +1,238 @@ +/* + * Driver for the MPC5200 Fast Ethernet Controller - PHY/MII part + * + * Copyright (C) 2007 Domen Puncer, Telargo, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/phy.h> +#include <asm/io.h> +#include <asm/mpc52xx.h> +#include <asm/of_platform.h> +#include "fec.h" + +struct fec_mdio_priv { + int completed; + wait_queue_head_t wq; + struct mpc52xx_fec __iomem *regs; + int irq; +}; + +static int fec_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct fec_mdio_priv *priv = bus->priv; + int tries = 100; + + u32 request = FEC_MII_READ_FRAME; + request |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; + request |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; + + out_be32(&priv->regs->mii_data, request); + + /* wait for it to finish, this takes about 23 us on lite5200b */ + while (priv->completed == 0 && tries--) + udelay(5); + + priv->completed = 0; + + if (tries == 0) + return -ETIMEDOUT; + + return in_be32(&priv->regs->mii_data) & FEC_MII_DATA_DATAMSK; +} + +static int fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data) +{ + struct fec_mdio_priv *priv = bus->priv; + u32 value = data; + int tries = 100; + + value |= FEC_MII_WRITE_FRAME; + value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; + value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; + + out_be32(&priv->regs->mii_data, value); + + /* wait for request to finish */ + while (priv->completed == 0 && tries--) + udelay(5); + + priv->completed = 0; + + if (tries == 0) + return -ETIMEDOUT; + + return 0; +} + +static irqreturn_t fec_mdio_interrupt(int irq, void *dev_id) +{ + struct fec_mdio_priv *priv = dev_id; + struct mpc52xx_fec __iomem *fec; + int ievent; + + fec = priv->regs; + ievent = in_be32(&fec->ievent); + + ievent &= FEC_IEVENT_MII; + if (!ievent) + return IRQ_NONE; + + out_be32(&fec->ievent, ievent); + + priv->completed = 1; + wake_up(&priv->wq); + + return IRQ_HANDLED; +} + +static int fec_mdio_probe(struct of_device *of, const struct of_device_id *match) +{ + struct device *dev = &of->dev; + struct device_node *np = of->node; + struct device_node *child = NULL; + struct mii_bus *bus; + struct fec_mdio_priv *priv; + struct resource res = {}; + int err; + int i; + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (bus == NULL) + return -ENOMEM; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) { + err = -ENOMEM; + goto out_free; + } + + bus->name = "mpc52xx MII bus"; + bus->read = fec_mdio_read; + bus->write = fec_mdio_write; + + /* setup irqs */ + bus->irq = kmalloc(sizeof(bus->irq[0]) * PHY_MAX_ADDR, GFP_KERNEL); + if (bus->irq == NULL) { + err = -ENOMEM; + goto out_free; + } + for (i=0; i<PHY_MAX_ADDR; i++) + bus->irq[i] = PHY_POLL; + + while ((child = of_get_next_child(np, child)) != NULL) { + int irq = irq_of_parse_and_map(child, 0); + if (irq != NO_IRQ) { + const u32 *id = of_get_property(child, "reg", NULL); + bus->irq[*id] = irq; + } + } + + /* setup registers */ + err = of_address_to_resource(np, 0, &res); + if (err) + goto out_free; + priv->regs = ioremap(res.start, res.end - res.start + 1); + if (priv->regs == NULL) { + err = -ENOMEM; + goto out_free; + } + + priv->irq = irq_of_parse_and_map(np, 0); + err = request_irq(priv->irq, &fec_mdio_interrupt, IRQF_DISABLED | IRQF_SHARED, + "fec_mdio", priv); + if (err) { + printk(KERN_ERR "%s: interrupt request failed with %i\n", __func__, err); + goto out_unmap; + } + + bus->id = res.start; + bus->priv = priv; + + bus->dev = dev; + dev_set_drvdata(dev, bus); + + init_waitqueue_head(&priv->wq); + + /* set MII speed */ + out_be32(&priv->regs->mii_speed, ((mpc52xx_find_ipb_freq(of->node) >> 20) / 5) << 1); + + /* enable MII interrupt */ + out_be32(&priv->regs->imask, in_be32(&priv->regs->imask) | FEC_IMASK_MII); + + err = mdiobus_register(bus); + if (err) + goto out_free_irq; + + return 0; + + out_free_irq: + free_irq(priv->irq, dev); + irq_dispose_mapping(priv->irq); + out_unmap: + iounmap(priv->regs); + out_free: + for (i=0; i<PHY_MAX_ADDR; i++) + if (bus->irq[i] != PHY_POLL) + irq_dispose_mapping(bus->irq[i]); + kfree(bus->irq); + kfree(priv); + kfree(bus); + + return err; +} + +static int fec_mdio_remove(struct of_device *of) +{ + struct device *dev = &of->dev; + struct mii_bus *bus = dev_get_drvdata(dev); + struct fec_mdio_priv *priv = bus->priv; + int i; + + mdiobus_unregister(bus); + dev_set_drvdata(dev, NULL); + + free_irq(priv->irq, dev); + irq_dispose_mapping(priv->irq); + iounmap(priv->regs); + for (i=0; i<PHY_MAX_ADDR; i++) + if (bus->irq[i]) + irq_dispose_mapping(bus->irq[i]); + kfree(priv); + kfree(bus->irq); + kfree(bus); + + return 0; +} + + +static struct of_device_id fec_mdio_match[] = { + { + .type = "mdio", + .compatible = "mpc5200b-fec-phy", + }, + {}, +}; + +static struct of_platform_driver fec_mdio_driver = { + .name = "mpc5200b-fec-phy", + .probe = fec_mdio_probe, + .remove = fec_mdio_remove, + .match_table = fec_mdio_match, +}; + + +int __init fec_mdio_init(void) +{ + return of_register_platform_driver(&fec_mdio_driver); +} + +void __exit fec_mdio_exit(void) +{ + of_unregister_platform_driver(&fec_mdio_driver); +} ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.2] net driver: mpc52xx fec 2007-09-02 7:41 ` [RFC PATCH v0.2] " Domen Puncer @ 2007-09-03 15:57 ` Grant Likely 2007-09-03 16:09 ` Jon Smirl 2007-09-15 12:14 ` Domen Puncer 2007-10-02 12:49 ` [RFC PATCH v0.2] net driver: mpc52xx fec Sascha Hauer 1 sibling, 2 replies; 47+ messages in thread From: Grant Likely @ 2007-09-03 15:57 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, linuxppc-embedded On 9/2/07, Domen Puncer <domen@coderock.org> wrote: > Hi! > > new in this version: > - fixed stuff that was commented on. > - added 7-wire support (compile at least, if someone has the hardware, > please test!) > - ethtool support Thanks for this work Domen, comments below... This is a large patch, and it should be broken up into logical changes. ie. split into dts changes, bestcomm changes, fec driver and mdio driver. Easier to review that way. The bestcomm and dts changes don't need to go to the netdev list. > --- /dev/null > +++ linux.git/drivers/net/fec_mpc52xx/Kconfig > @@ -0,0 +1,25 @@ > +menu "MPC5200 Networking Options" > + depends PPC_MPC52xx && NET_ETHERNET > + > +config FEC_MPC52xx > + tristate "FEC Ethernet" > + depends on NET_ETHERNET > + select PPC_BESTCOMM > + select PPC_BESTCOMM_FEC > + select CRC32 > + ---help--- > + This option enables support for the MPC5200's on-chip > + Fast Ethernet Controller > + > +config FEC_MPC52xx_MDIO > + bool "Use external Ethernet MII PHY" > + depends on FEC_MPC52xx > + select PHYLIB > + default y > + ---help--- > + The MPC5200's FEC can connect to the Ethernet either with > + an external MII PHY chip or 10 Mbps 7-wire interface > + (Motorola? industry standard). > + If your board uses an external PHY, say y, else n. This option should change. Either build the MDIO driver into the FEC driver unconditionally and drop this option, or make the MDIO driver independent from the FEC driver (it does use the MDIO bus infrastructure after all). Either way the FEC driver should detect the phy type at runtime (possibly based on the presence/absence of a phy-handle property) instead of being hard compiled. 5200 support is now multiplatform after all. If you drop the MDIO config option, then I'd also consider eliminating driver/net/fec_mpc52xx/Kconfig entirely and rolling the single MPC52xx_FEC option into drivers/net/Kconfig. > + > +endmenu > Index: linux.git/drivers/net/fec_mpc52xx/Makefile > =================================================================== > --- /dev/null > +++ linux.git/drivers/net/fec_mpc52xx/Makefile > @@ -0,0 +1,7 @@ > +obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o > + > +fec_mpc52xx-objs := fec.o > + > +ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) > +fec_mpc52xx-objs += fec_phy.o > +endif Hmm, is the phy driver separate or not? You've got the logic in place to probe MDIO separately from the FEC driver, yet they are linked as a single driver. > Index: linux.git/drivers/net/fec_mpc52xx/fec.c > =================================================================== > --- /dev/null > +++ linux.git/drivers/net/fec_mpc52xx/fec.c > @@ -0,0 +1,1127 @@ > +/* > + * drivers/drivers/net/fec_mpc52xx/fec.c > + * > + * Driver for the MPC5200 Fast Ethernet Controller > + * > + * Originally written by Dale Farnsworth <dfarnsworth@mvista.com> and > + * now maintained by Sylvain Munaut <tnt@246tNt.com> > + * > + * Copyright (C) 2007 Domen Puncer, Telargo, Inc. > + * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com> > + * Copyright (C) 2003-2004 MontaVista, Software, Inc. > + * > + * This file is licensed under the terms of the GNU General Public License > + * version 2. This program is licensed "as is" without any warranty of any > + * kind, whether express or implied. > + * > + */ > + > +#include <linux/module.h> > + > +#include <linux/kernel.h> > +#include <linux/types.h> > +#include <linux/spinlock.h> > +#include <linux/errno.h> > +#include <linux/init.h> > +#include <linux/crc32.h> > +#include <linux/hardirq.h> > +#include <linux/delay.h> > + > +#include <linux/netdevice.h> > +#include <linux/etherdevice.h> > +#include <linux/ethtool.h> > +#include <linux/skbuff.h> > + > +#include <asm/of_device.h> > +#include <asm/of_platform.h> > +#include <asm/io.h> > +#include <asm/delay.h> > +#include <asm/mpc52xx.h> > + > +#include <sysdev/bestcomm/bestcomm.h> > +#include <sysdev/bestcomm/fec.h> > + > +#include "fec.h" > + > +#define DRIVER_NAME "mpc52xx-fec" > + > +static irqreturn_t fec_interrupt(int, void *); > +static irqreturn_t fec_rx_interrupt(int, void *); > +static irqreturn_t fec_tx_interrupt(int, void *); > +static struct net_device_stats *fec_get_stats(struct net_device *); > +static void fec_set_multicast_list(struct net_device *dev); > +static void fec_hw_init(struct net_device *dev); > +static void fec_stop(struct net_device *dev); > +static void fec_start(struct net_device *dev); > +static void fec_reset(struct net_device *dev); Nit: Are all these forward decls needed? > + > +static u8 mpc52xx_fec_mac_addr[6]; Why isn't this part of struct fec_priv? > +static const u8 null_mac[6]; null_mac?!? Just for comparing a mac addr against 0? <snip> > +#ifdef CONFIG_FEC_MPC52xx_MDIO Once again; don't make this a conditional compile; detect at runtime. <snip> > +static void __init fec_str2mac(char *str, unsigned char *mac) > +{ > + int i; > + u64 val64; > + > + val64 = simple_strtoull(str, NULL, 16); > + > + for (i = 0; i < 6; i++) > + mac[5-i] = val64 >> (i*8); > +} > + > +static int __init mpc52xx_fec_mac_setup(char *mac_address) > +{ > + fec_str2mac(mac_address, mpc52xx_fec_mac_addr); > + return 0; > +} fec_str2mac is called in *1* place. I'd roll it into mpc52xx_fec_mac_setup. > + > +__setup("mpc52xx-mac=", mpc52xx_fec_mac_setup); > + -- Grant Likely, B.Sc., P.Eng. Secret Lab Technologies Ltd. grant.likely@secretlab.ca (403) 399-0195 ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.2] net driver: mpc52xx fec 2007-09-03 15:57 ` Grant Likely @ 2007-09-03 16:09 ` Jon Smirl 2007-09-03 16:41 ` Grant Likely 2007-09-15 12:14 ` Domen Puncer 1 sibling, 1 reply; 47+ messages in thread From: Jon Smirl @ 2007-09-03 16:09 UTC (permalink / raw) To: Grant Likely; +Cc: netdev, Domen Puncer, linuxppc-embedded On 9/3/07, Grant Likely <grant.likely@secretlab.ca> wrote: > On 9/2/07, Domen Puncer <domen@coderock.org> wrote: > > Hi! > > > > new in this version: > > - fixed stuff that was commented on. > > - added 7-wire support (compile at least, if someone has the hardware, > > please test!) > > - ethtool support > > Thanks for this work Domen, comments below... > > This is a large patch, and it should be broken up into logical > changes. ie. split into dts changes, bestcomm changes, fec driver and > mdio driver. Easier to review that way. The bestcomm and dts changes > don't need to go to the netdev list. A similar patch that is already broken up is available for Efika. http://dev.gentoo.org/~nixnut/efika/efika-patches-2.6.22.tar.bz2 -- Jon Smirl jonsmirl@gmail.com ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.2] net driver: mpc52xx fec 2007-09-03 16:09 ` Jon Smirl @ 2007-09-03 16:41 ` Grant Likely 0 siblings, 0 replies; 47+ messages in thread From: Grant Likely @ 2007-09-03 16:41 UTC (permalink / raw) To: Jon Smirl; +Cc: netdev, Domen Puncer, linuxppc-embedded On 9/3/07, Jon Smirl <jonsmirl@gmail.com> wrote: > On 9/3/07, Grant Likely <grant.likely@secretlab.ca> wrote: > > On 9/2/07, Domen Puncer <domen@coderock.org> wrote: > > > Hi! > > > > > > new in this version: > > > - fixed stuff that was commented on. > > > - added 7-wire support (compile at least, if someone has the hardware, > > > please test!) > > > - ethtool support > > > > Thanks for this work Domen, comments below... > > > > This is a large patch, and it should be broken up into logical > > changes. ie. split into dts changes, bestcomm changes, fec driver and > > mdio driver. Easier to review that way. The bestcomm and dts changes > > don't need to go to the netdev list. > > A similar patch that is already broken up is available for Efika. > http://dev.gentoo.org/~nixnut/efika/efika-patches-2.6.22.tar.bz2 No. That series is Sylvain's full patchset including the old FEC driver. Domen's patch is a reworked FEC driver, and it only replaces patch 0008 from that series. However, this patch should be split up further because it makes changes to both the device tree and the bestcomm code. Cheers, g. -- Grant Likely, B.Sc., P.Eng. Secret Lab Technologies Ltd. grant.likely@secretlab.ca (403) 399-0195 ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.2] net driver: mpc52xx fec 2007-09-03 15:57 ` Grant Likely 2007-09-03 16:09 ` Jon Smirl @ 2007-09-15 12:14 ` Domen Puncer 2007-09-17 9:53 ` Sven Luther 1 sibling, 1 reply; 47+ messages in thread From: Domen Puncer @ 2007-09-15 12:14 UTC (permalink / raw) To: Grant Likely; +Cc: netdev, linuxppc-embedded On 03/09/07 09:57 -0600, Grant Likely wrote: > On 9/2/07, Domen Puncer <domen@coderock.org> wrote: > > Hi! > > > > new in this version: > > - fixed stuff that was commented on. > > - added 7-wire support (compile at least, if someone has the hardware, > > please test!) > > - ethtool support > > Thanks for this work Domen, comments below... Thanks for reviewing and sorry for not replying sooner, I lost the mail. > > This is a large patch, and it should be broken up into logical > changes. ie. split into dts changes, bestcomm changes, fec driver and > mdio driver. Easier to review that way. The bestcomm and dts changes > don't need to go to the netdev list. OK. > > +config FEC_MPC52xx > > + tristate "FEC Ethernet" > > + depends on NET_ETHERNET > > + select PPC_BESTCOMM > > + select PPC_BESTCOMM_FEC > > + select CRC32 > > + ---help--- > > + This option enables support for the MPC5200's on-chip > > + Fast Ethernet Controller > > + > > +config FEC_MPC52xx_MDIO > > + bool "Use external Ethernet MII PHY" > > + depends on FEC_MPC52xx > > + select PHYLIB > > + default y > > + ---help--- > > + The MPC5200's FEC can connect to the Ethernet either with > > + an external MII PHY chip or 10 Mbps 7-wire interface > > + (Motorola? industry standard). > > + If your board uses an external PHY, say y, else n. > > This option should change. Either build the MDIO driver into the FEC > driver unconditionally and drop this option, or make the MDIO driver > independent from the FEC driver (it does use the MDIO bus > infrastructure after all). Either way the FEC driver should detect > the phy type at runtime (possibly based on the presence/absence of a > phy-handle property) instead of being hard compiled. 5200 support is > now multiplatform after all. > > If you drop the MDIO config option, then I'd also consider eliminating > driver/net/fec_mpc52xx/Kconfig entirely and rolling the single > MPC52xx_FEC option into drivers/net/Kconfig. Right. I separated it. > > +static irqreturn_t fec_interrupt(int, void *); > > +static irqreturn_t fec_rx_interrupt(int, void *); > > +static irqreturn_t fec_tx_interrupt(int, void *); > > +static struct net_device_stats *fec_get_stats(struct net_device *); > > +static void fec_set_multicast_list(struct net_device *dev); > > +static void fec_hw_init(struct net_device *dev); > > +static void fec_stop(struct net_device *dev); > > +static void fec_start(struct net_device *dev); > > +static void fec_reset(struct net_device *dev); > > Nit: Are all these forward decls needed? Some aren't - cleaned. > > > + > > +static u8 mpc52xx_fec_mac_addr[6]; > > Why isn't this part of struct fec_priv? Because at __setup time, there's no fec_priv instance. OTOH, does anyone even use mpc52xx-mac=? > > > +static const u8 null_mac[6]; > > null_mac?!? Just for comparing a mac addr against 0? right, is_zero_ether_addr is the right thing. > > <snip> > > > +#ifdef CONFIG_FEC_MPC52xx_MDIO > > Once again; don't make this a conditional compile; detect at runtime. > > <snip> > > > +static void __init fec_str2mac(char *str, unsigned char *mac) > > +{ > > + int i; > > + u64 val64; > > + > > + val64 = simple_strtoull(str, NULL, 16); > > + > > + for (i = 0; i < 6; i++) > > + mac[5-i] = val64 >> (i*8); > > +} > > + > > +static int __init mpc52xx_fec_mac_setup(char *mac_address) > > +{ > > + fec_str2mac(mac_address, mpc52xx_fec_mac_addr); > > + return 0; > > +} > > fec_str2mac is called in *1* place. I'd roll it into mpc52xx_fec_mac_setup. > > > + > > +__setup("mpc52xx-mac=", mpc52xx_fec_mac_setup); > > + > OK. Updated and split version at: http://coderock.org/tmp/fec-v3rc1/ I'll repost to lists once I run-test them. Domen > > > -- > Grant Likely, B.Sc., P.Eng. > Secret Lab Technologies Ltd. > grant.likely@secretlab.ca > (403) 399-0195 > _______________________________________________ > Linuxppc-embedded mailing list > Linuxppc-embedded@ozlabs.org > https://ozlabs.org/mailman/listinfo/linuxppc-embedded ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.2] net driver: mpc52xx fec 2007-09-15 12:14 ` Domen Puncer @ 2007-09-17 9:53 ` Sven Luther 2007-09-17 20:21 ` [PATCH] phy: export phy_mii_ioctl Domen Puncer 0 siblings, 1 reply; 47+ messages in thread From: Sven Luther @ 2007-09-17 9:53 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, linuxppc-embedded On Sat, Sep 15, 2007 at 02:14:44PM +0200, Domen Puncer wrote: > Updated and split version at: > http://coderock.org/tmp/fec-v3rc1/ > > I'll repost to lists once I run-test them. When applying those patches, the build did die with : ERROR: "phy_mii_ioctl" [drivers/net/fec_mpc52xx/fec_mpc52xx.ko] undefined! Apparently, phy_mii_ioctl is not an exported symbol. Domen, did you maybe forget a little snipplet when you cut the patches in different pieces ? Or did i mess up applying them ? Friendly, Sven Luther ^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH] phy: export phy_mii_ioctl 2007-09-17 9:53 ` Sven Luther @ 2007-09-17 20:21 ` Domen Puncer 2007-09-17 22:08 ` Jon Smirl 2007-09-20 6:36 ` Jeff Garzik 0 siblings, 2 replies; 47+ messages in thread From: Domen Puncer @ 2007-09-17 20:21 UTC (permalink / raw) To: Jeff Garzik; +Cc: netdev, linuxppc-embedded Export phy_mii_ioctl, so network drivers can use it when built as modules too. Signed-off-by: Domen Puncer <domen@coderock.org> --- On 17/09/07 11:53 +0200, Sven Luther wrote: > On Sat, Sep 15, 2007 at 02:14:44PM +0200, Domen Puncer wrote: > > Updated and split version at: > > http://coderock.org/tmp/fec-v3rc1/ > > > > I'll repost to lists once I run-test them. > > When applying those patches, the build did die with : > > > ERROR: "phy_mii_ioctl" [drivers/net/fec_mpc52xx/fec_mpc52xx.ko] undefined! > > Apparently, phy_mii_ioctl is not an exported symbol. > > Domen, did you maybe forget a little snipplet when you cut the patches > in different pieces ? Or did i mess up applying them ? > > Friendly, > > Sven Luther drivers/net/phy/phy.c | 1 + 1 files changed, 1 insertion(+) Index: linux.git/drivers/net/phy/phy.c =================================================================== --- linux.git.orig/drivers/net/phy/phy.c +++ linux.git/drivers/net/phy/phy.c @@ -409,6 +409,7 @@ int phy_mii_ioctl(struct phy_device *phy return 0; } +EXPORT_SYMBOL(phy_mii_ioctl); /** * phy_start_aneg - start auto-negotiation for this PHY device ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-17 20:21 ` [PATCH] phy: export phy_mii_ioctl Domen Puncer @ 2007-09-17 22:08 ` Jon Smirl 2007-09-18 15:16 ` Domen Puncer 2007-09-20 6:36 ` Jeff Garzik 1 sibling, 1 reply; 47+ messages in thread From: Jon Smirl @ 2007-09-17 22:08 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, Jeff Garzik, linuxppc-embedded On 9/17/07, Domen Puncer <domen@coderock.org> wrote: > Export phy_mii_ioctl, so network drivers can use it when built > as modules too. Domen, do you want to collect all of these changes for MPC5200 FEC in to a single patch series? The code is getting scattered around, I'll check it over to make sure it is all working. I have these patches applied individually and they all work. It builds on this series: [PATCH 0/7] MPC52xx Bestcomm submission for 2.6.24 If you can put this together is a clean series, I should be able to layer support for the Phytec pcm030 on top of it. It would be these three combined... http://coderock.org/tmp/fec-v3rc1/ diff --git a/drivers/net/fec_mpc52xx/fec.c b/drivers/net/fec_mpc52xx/fec.c index 922e9a8..c4442e0 100644 --- a/drivers/net/fec_mpc52xx/fec.c +++ b/drivers/net/fec_mpc52xx/fec.c @@ -1087,11 +1087,13 @@ static struct of_platform_driver mpc52xx_fec_driver = { /* ======================================================================== */ /* Module */ /* ======================================================================== */ +extern int fec_mdio_init(void); +void fec_mdio_exit(void); static int __init mpc52xx_fec_init(void) { -#ifdef FEC_MPC52xx_MDIO +#ifdef CONFIG_FEC_MPC52xx_MDIO int ret; ret = fec_mdio_init(); if (ret) { @@ -1106,7 +1108,7 @@ static void __exit mpc52xx_fec_exit(void) { of_unregister_platform_driver(&mpc52xx_fec_driver); -#ifdef FEC_MPC52xx_MDIO +#ifdef CONFIG_FEC_MPC52xx_MDIO fec_mdio_exit(); #endif } > > Signed-off-by: Domen Puncer <domen@coderock.org> > > --- > On 17/09/07 11:53 +0200, Sven Luther wrote: > > On Sat, Sep 15, 2007 at 02:14:44PM +0200, Domen Puncer wrote: > > > Updated and split version at: > > > http://coderock.org/tmp/fec-v3rc1/ > > > > > > I'll repost to lists once I run-test them. > > > > When applying those patches, the build did die with : > > > > > > ERROR: "phy_mii_ioctl" [drivers/net/fec_mpc52xx/fec_mpc52xx.ko] undefined! > > > > Apparently, phy_mii_ioctl is not an exported symbol. > > > > Domen, did you maybe forget a little snipplet when you cut the patches > > in different pieces ? Or did i mess up applying them ? > > > > Friendly, > > > > Sven Luther > > > drivers/net/phy/phy.c | 1 + > 1 files changed, 1 insertion(+) > > Index: linux.git/drivers/net/phy/phy.c > =================================================================== > --- linux.git.orig/drivers/net/phy/phy.c > +++ linux.git/drivers/net/phy/phy.c > @@ -409,6 +409,7 @@ int phy_mii_ioctl(struct phy_device *phy > > return 0; > } > +EXPORT_SYMBOL(phy_mii_ioctl); > > /** > * phy_start_aneg - start auto-negotiation for this PHY device > > _______________________________________________ > Linuxppc-embedded mailing list > Linuxppc-embedded@ozlabs.org > https://ozlabs.org/mailman/listinfo/linuxppc-embedded > -- Jon Smirl jonsmirl@gmail.com ^ permalink raw reply related [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-17 22:08 ` Jon Smirl @ 2007-09-18 15:16 ` Domen Puncer 2007-09-18 19:17 ` Jon Smirl 2007-09-18 19:29 ` Jon Smirl 0 siblings, 2 replies; 47+ messages in thread From: Domen Puncer @ 2007-09-18 15:16 UTC (permalink / raw) To: Jon Smirl; +Cc: netdev, linuxppc-embedded (I edited Cc: -jeff, +sven, hope you don't mind) On 17/09/07 18:08 -0400, Jon Smirl wrote: > On 9/17/07, Domen Puncer <domen@coderock.org> wrote: > > Export phy_mii_ioctl, so network drivers can use it when built > > as modules too. > > Domen, do you want to collect all of these changes for MPC5200 FEC in > to a single patch series? The code is getting scattered around, I'll > check it over to make sure it is all working. I have these patches > applied individually and they all work. > > It builds on this series: > [PATCH 0/7] MPC52xx Bestcomm submission for 2.6.24 > > If you can put this together is a clean series, I should be able to > layer support for the Phytec pcm030 on top of it. > > It would be these three combined... > > http://coderock.org/tmp/fec-v3rc1/ http://coderock.org/tmp/fec-v3rc2/ export_phy_mii_ioctl fec_driver-bestcomm fec_driver-dts fec_driver-fec fec_driver-phy Built (on top of 7 bestcomm patches) and ran it built-in and as module on Efika. Order of applying only matters for phy part, which has to be after the fec driver. More testing and getting it to work properly on Phytec pcm030 would be great. Domen ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-18 15:16 ` Domen Puncer @ 2007-09-18 19:17 ` Jon Smirl 2007-09-19 11:56 ` Domen Puncer 2007-09-18 19:29 ` Jon Smirl 1 sibling, 1 reply; 47+ messages in thread From: Jon Smirl @ 2007-09-18 19:17 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, linuxppc-embedded On 9/18/07, Domen Puncer <domen@coderock.org> wrote: > More testing and getting it to work properly on Phytec pcm030 would > be great. I compiled it as a module: CC [M] drivers/net/fec_mpc52xx/fec.o drivers/net/fec_mpc52xx/fec.c:613: warning: 'mpc52xx_fec_mac_setup' defined but not used This code needs to be enclosed in "#ifndef MODULE". But why aren't you using module_param() to make a string parameter and then copy it into mpc52xx_fec_mac_addr[] if the parameter is not null? If it is a module param you need to use fec_mpc52xx_phy.mpc52xx-mac="xxxx" instead of just mpc52xx-mac. The way it is not you can't use mpc52xx-mac when built as a module. static int __init mpc52xx_fec_mac_setup(char *mac_address) { int i; u64 val64; val64 = simple_strtoull(mac_address, NULL, 16); for (i = 0; i < 6; i++) mpc52xx_fec_mac_addr[5-i] = val64 >> (i*8); return 0; } __setup("mpc52xx-mac=", mpc52xx_fec_mac_setup); -- Jon Smirl jonsmirl@gmail.com ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-18 19:17 ` Jon Smirl @ 2007-09-19 11:56 ` Domen Puncer 2007-09-19 18:44 ` Jon Smirl 0 siblings, 1 reply; 47+ messages in thread From: Domen Puncer @ 2007-09-19 11:56 UTC (permalink / raw) To: Jon Smirl; +Cc: netdev, linuxppc-embedded On 18/09/07 15:17 -0400, Jon Smirl wrote: > On 9/18/07, Domen Puncer <domen@coderock.org> wrote: > > More testing and getting it to work properly on Phytec pcm030 would > > be great. > > I compiled it as a module: > CC [M] drivers/net/fec_mpc52xx/fec.o > drivers/net/fec_mpc52xx/fec.c:613: warning: 'mpc52xx_fec_mac_setup' > defined but not used > > This code needs to be enclosed in "#ifndef MODULE". But why aren't you > using module_param() to make a string parameter and then copy it into > mpc52xx_fec_mac_addr[] if the parameter is not null? Right, Patch at the end. When compiled as module use "modprobe fec_mpc52xx mac=foo", when built-in add to boot line: "fec_mpc52xx.mac=foo" As for link-up-printk in the middle of DHCP requests... is it really that big of a problem? This sort of things happen when printk doesn't have the whole line... getting rid of link-up message would just hide it (it can show ie. when an usb device is bound to scsi layer). Domen --- drivers/net/fec_mpc52xx/fec.c | 18 ++---------------- 1 files changed, 2 insertions(+), 16 deletions(-) Index: linux.git/drivers/net/fec_mpc52xx/fec.c =================================================================== --- linux.git.orig/drivers/net/fec_mpc52xx/fec.c +++ linux.git/drivers/net/fec_mpc52xx/fec.c @@ -53,6 +53,8 @@ static void fec_start(struct net_device static void fec_reset(struct net_device *dev); static u8 mpc52xx_fec_mac_addr[6]; +module_param_array_named(mac, mpc52xx_fec_mac_addr, byte, NULL, 0); +MODULE_PARM_DESC(mac, "six hex digits, ie. 0x1,0x2,0xc0,0x01,0xba,0xbe"); static void fec_tx_timeout(struct net_device *dev) { @@ -609,22 +611,6 @@ static void fec_set_multicast_list(struc } } -static int __init mpc52xx_fec_mac_setup(char *mac_address) -{ - int i; - u64 val64; - - val64 = simple_strtoull(mac_address, NULL, 16); - - for (i = 0; i < 6; i++) - mpc52xx_fec_mac_addr[5-i] = val64 >> (i*8); - - return 0; -} - -__setup("mpc52xx-mac=",mpc52xx_fec_mac_setup); - - /** * fec_hw_init * @dev: network device ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-19 11:56 ` Domen Puncer @ 2007-09-19 18:44 ` Jon Smirl 2007-09-19 21:18 ` Jon Smirl 0 siblings, 1 reply; 47+ messages in thread From: Jon Smirl @ 2007-09-19 18:44 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, linuxppc-embedded On 9/19/07, Domen Puncer <domen@coderock.org> wrote: > Patch at the end. > When compiled as module use "modprobe fec_mpc52xx mac=foo", > when built-in add to boot line: "fec_mpc52xx.mac=foo" This patch series is working for me now. This needs a cleanup too, but it is unrelated.... CC drivers/serial/mpc52xx_uart.o drivers/serial/mpc52xx_uart.c: In function 'mpc52xx_console_setup': drivers/serial/mpc52xx_uart.c:760: warning: format '%lx' expects type 'long unsigned int', but argument 2 has type 'resource_size_t' drivers/serial/mpc52xx_uart.c: In function 'mpc52xx_uart_of_probe': drivers/serial/mpc52xx_uart.c:978: warning: format '%lx' expects type 'long unsigned int', but argument 3 has type 'resource_size_t' -- Jon Smirl jonsmirl@gmail.com ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-19 18:44 ` Jon Smirl @ 2007-09-19 21:18 ` Jon Smirl 0 siblings, 0 replies; 47+ messages in thread From: Jon Smirl @ 2007-09-19 21:18 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, linuxppc-embedded On 9/19/07, Jon Smirl <jonsmirl@gmail.com> wrote: > This needs a cleanup too, but it is unrelated.... Another set of related warnings that need clean up.... CC drivers/spi/mpc52xx_psc_spi.o drivers/spi/mpc52xx_psc_spi.c: In function 'mpc52xx_psc_spi_activate_cs': drivers/spi/mpc52xx_psc_spi.c:110: warning: passing argument 1 of 'in_be16' from incompatible pointer type drivers/spi/mpc52xx_psc_spi.c:116: warning: passing argument 1 of 'out_be16' from incompatible pointer type drivers/spi/mpc52xx_psc_spi.c: In function 'mpc52xx_psc_spi_port_config': drivers/spi/mpc52xx_psc_spi.c:417: warning: passing argument 1 of 'out_be16' from incompatible pointer type -- Jon Smirl jonsmirl@gmail.com ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-18 15:16 ` Domen Puncer 2007-09-18 19:17 ` Jon Smirl @ 2007-09-18 19:29 ` Jon Smirl 2007-09-19 8:54 ` Pedro Luis D. L. 2007-09-19 8:54 ` Pedro Luis D. L. 1 sibling, 2 replies; 47+ messages in thread From: Jon Smirl @ 2007-09-18 19:29 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, linuxppc-embedded On 9/18/07, Domen Puncer <domen@coderock.org> wrote: > More testing and getting it to work properly on Phytec pcm030 would > be great. Do we want to do anything about this? [ 1.569657] net eth0: attached phy 0 to driver Generic PHY [ 2.576013] Sending DHCP requests .<6>PHY: f0003000:00 - Link is Up - 100/Full [ 4.612000] ., OK [ 6.764005] IP-Config: Got DHCP answer from 192.168.1.200, my address is 192.168.1.5 What is happening is the printk for "<6>PHY: f0003000:00 - Link is Up - 100/Full" is done in an interrupt and it comes in the middle of the kernel doing DHCP and printing ... without a CR. Two possible solutions, get rid of the link-up message or wait in in the initial driver load until the link is up. Or we could leave it the way it is, but some people may report this as a bug. -- Jon Smirl jonsmirl@gmail.com ^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH] phy: export phy_mii_ioctl 2007-09-18 19:29 ` Jon Smirl @ 2007-09-19 8:54 ` Pedro Luis D. L. 2007-09-19 10:37 ` Juergen Beisert 2007-09-19 13:56 ` Jon Smirl 2007-09-19 8:54 ` Pedro Luis D. L. 1 sibling, 2 replies; 47+ messages in thread From: Pedro Luis D. L. @ 2007-09-19 8:54 UTC (permalink / raw) To: Jon Smirl, Domen Puncer; +Cc: netdev, linuxppc-embedded Hello Jon, I=B4m also working with a Phytec pcm030, but I can=B4t get it booted... Which kernel are you using? I tried to apply the 7 bestcomm patches from Sylvain and patch over these w= ith this new ones that Domen released. The base kernel I=B4m using is 2.6.22.6 from kernel.org. Although I used the patch that creates pcm030.c in arch/platforms/52xx/ and= compiled using this file, it gets halted at booting time. Bytes transferred =3D 5091 (13e3 hex) ## Booting image at 00500000 ... Image Name: Linux-2.6.22.6 Created: 2007-09-19 8:53:02 UTC Image Type: PowerPC Linux Kernel Image (gzip compressed) Data Size: 1196911 Bytes =3D 1.1 MB Load Address: 00000000 Entry Point: 00000000 Verifying Checksum ... OK Uncompressing Kernel Image ... OK Booting using flat device tree at 0x400000 (No more output and boot is halted) Are you using any other patch for the platform or any other kernel, because= I tried to apply these patches to a 2.6.20 kernel and are not successful. Bests, Pedro. > Date: Tue, 18 Sep 2007 15:29:09 -0400> From: jonsmirl@gmail.com=20 > To: domen@coderock.org> Subject: Re: [PATCH] phy: export phy_mii_ioctl=20 > CC: netdev@vger.kernel.org; linuxppc-embedded@ozlabs.org=20 >> On 9/18/07, Domen Puncer wrote:=20 >> More testing and getting it to work properly on Phytec pcm030 would=20 >> be great.>> Do we want to do anything about this?=20 >> [ 1.569657] net eth0: attached phy 0 to driver Generic PHY=20 > [ 2.576013] Sending DHCP requests .PHY: f0003000:00 - Link is Up=20 > - 100/Full> [ 4.612000] ., OK=20 > [ 6.764005] IP-Config: Got DHCP answer from 192.168.1.200, my=20 > address is 192.168.1.5 >> What is happening is the printk for "PHY: f0003000:00 - Link is Up=20 > - 100/Full" is done in an interrupt and it comes in the middle of the> ke= rnel doing DHCP and printing ... without a CR.=20 >> Two possible solutions, get rid of the link-up message or wait in in=20 > the initial driver load until the link is up. Or we could leave it the=20 > way it is, but some people may report this as a bug. >>> --> Jon Smirl> jonsmirl@gmail.com=20 > _______________________________________________=20 > Linuxppc-embedded mailing list> Linuxppc-embedded@ozlabs.org> https://ozl= abs.org/mailman/listinfo/linuxppc-embedded _________________________________________________________________ Busca desde cualquier p=E1gina Web con una protecci=F3n excepcional. Consig= ue la Barra de herramientas de Windows Live hoy mismo y GRATUITAMENTE. http://www.toolbar.live.com= ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-19 8:54 ` Pedro Luis D. L. @ 2007-09-19 10:37 ` Juergen Beisert 2007-09-19 11:38 ` Pedro Luis D. L. 2007-09-19 13:56 ` Jon Smirl 1 sibling, 1 reply; 47+ messages in thread From: Juergen Beisert @ 2007-09-19 10:37 UTC (permalink / raw) To: linuxppc-embedded; +Cc: netdev, Domen Puncer Pedro, On Wednesday 19 September 2007 10:54, Pedro Luis D. L. wrote: > I=B4m also working with a Phytec pcm030, but I can=B4t get it booted... > Which kernel are you using? > I tried to apply the 7 bestcomm patches from Sylvain and patch over these > with this new ones that Domen released. The base kernel I=B4m using is > 2.6.22.6 from kernel.org. > Although I used the patch that creates pcm030.c in arch/platforms/52xx/ a= nd > compiled using this file, it gets halted at booting time. > > Bytes transferred =3D 5091 (13e3 hex) > ## Booting image at 00500000 ... > Image Name: Linux-2.6.22.6 > Created: 2007-09-19 8:53:02 UTC > Image Type: PowerPC Linux Kernel Image (gzip compressed) > Data Size: 1196911 Bytes =3D 1.1 MB > Load Address: 00000000 > Entry Point: 00000000 > Verifying Checksum ... OK > Uncompressing Kernel Image ... OK > Booting using flat device tree at 0x400000 > > (No more output and boot is halted) Check your oftree! Most of the time this behaviour means its a wrong oftree= in=20 use. Juergen =2D-=20 Dipl.-Ing. Juergen Beisert | http://www.pengutronix.de =A0Pengutronix - Linux Solutions for Science and Industry =A0 Handelsregister: Amtsgericht Hildesheim, HRA 2686 =A0 =A0 =A0 Vertretung Sued/Muenchen, Germany Phone: +49-8766-939 228 | Fax: +49-5121-206917-9 ^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH] phy: export phy_mii_ioctl 2007-09-19 10:37 ` Juergen Beisert @ 2007-09-19 11:38 ` Pedro Luis D. L. 2007-09-19 14:51 ` Juergen Beisert 0 siblings, 1 reply; 47+ messages in thread From: Pedro Luis D. L. @ 2007-09-19 11:38 UTC (permalink / raw) To: linuxppc-embedded Juergen wrote: > >Pedro, > >On Wednesday 19 September 2007 10:54, Pedro Luis D. L. wrote: >> I=B4m also working with a Phytec pcm030, but I can=B4t get it booted... >> Which kernel are you using? >> I tried to apply the 7 bestcomm patches from Sylvain and patch over thes= e >> with this new ones that Domen released. The base kernel I=B4m using is >> 2.6.22.6 from kernel.org. >> Although I used the patch that creates pcm030.c in arch/platforms/52xx/ = and >> compiled using this file, it gets halted at booting time. >> >> Bytes transferred =3D 5091 (13e3 hex) >> ## Booting image at 00500000 ... >> Image Name: Linux-2.6.22.6 >> Created: 2007-09-19 8:53:02 UTC >> Image Type: PowerPC Linux Kernel Image (gzip compressed) >> Data Size: 1196911 Bytes =3D 1.1 MB >> Load Address: 00000000 >> Entry Point: 00000000 >> Verifying Checksum ... OK >> Uncompressing Kernel Image ... OK >> Booting using flat device tree at 0x400000 >> >> (No more output and boot is halted) >=20 >Check your oftree! Most of the time this behaviour means its a wrong oftre= e in=20 >use. I=B4m using an specific pcm030.dts oftree that works for the 2.6.20 kernel.= I=B4m not quite familiar with the oftree stuff, but I thought it should wo= rk also for the 2.6.22.6. Is there any other dts file? Where can I find it? Pedro. PD: Sorry. I sent the previous message to Jon, Domen and someone else too b= esides the list. I had some problems with the browser... Even sent twice th= e message :-( >Juergen >=20 >--=20 >Dipl.-Ing. Juergen Beisert | http://www.pengutronix.de >Pengutronix - Linux Solutions for Science and Industry > Handelsregister: Amtsgericht Hildesheim, HRA 2686 > Vertretung Sued/Muenchen, Germany > Phone: +49-8766-939 228 | Fax: +49-5121-206917-9 > _________________________________________________________________ Llama a tus amigos de PC a PC: =A1Es GRATIS! http://get.live.com/messenger/overview= ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-19 11:38 ` Pedro Luis D. L. @ 2007-09-19 14:51 ` Juergen Beisert 2007-09-19 15:11 ` Pedro Luis D. L. 0 siblings, 1 reply; 47+ messages in thread From: Juergen Beisert @ 2007-09-19 14:51 UTC (permalink / raw) To: linuxppc-embedded Pedro, On Wednesday 19 September 2007 13:38, Pedro Luis D. L. wrote: > I=B4m using an specific pcm030.dts oftree that works for the 2.6.20 kerne= l. > I=B4m not quite familiar with the oftree stuff, but I thought it should w= ork > also for the 2.6.22.6. Is there any other dts file? Where can I find it? dts patch sent offline. Juergen =2D-=20 Dipl.-Ing. Juergen Beisert | http://www.pengutronix.de =A0Pengutronix - Linux Solutions for Science and Industry =A0 Handelsregister: Amtsgericht Hildesheim, HRA 2686 =A0 =A0 =A0 Vertretung Sued/Muenchen, Germany Phone: +49-8766-939 228 | Fax: +49-5121-206917-9 ^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH] phy: export phy_mii_ioctl 2007-09-19 14:51 ` Juergen Beisert @ 2007-09-19 15:11 ` Pedro Luis D. L. 0 siblings, 0 replies; 47+ messages in thread From: Pedro Luis D. L. @ 2007-09-19 15:11 UTC (permalink / raw) To: linuxppc-embedded >Pedro, >=20 >On Wednesday 19 September 2007 13:38, Pedro Luis D. L. wrote: >> I=B4m using an specific pcm030.dts oftree that works for the 2.6.20 kern= el. >> I=B4m not quite familiar with the oftree stuff, but I thought it should = work >> also for the 2.6.22.6. Is there any other dts file? Where can I find it? >=20 >dts patch sent offline. Thanks for the patch, Juergen. Received and applied, but still doesn't boot= . I=B4m starting to suspect there should be something else... Pedro. >Juergen >--=20 >Dipl.-Ing. Juergen Beisert | http://www.pengutronix.de > Pengutronix - Linux Solutions for Science and Industry > Handelsregister: Amtsgericht Hildesheim, HRA 2686 > Vertretung Sued/Muenchen, Germany > Phone: +49-8766-939 228 | Fax: +49-5121-206917-9 _________________________________________________________________ Consigue el nuevo Windows Live Messenger http://get.live.com/messenger/overview= ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-19 8:54 ` Pedro Luis D. L. 2007-09-19 10:37 ` Juergen Beisert @ 2007-09-19 13:56 ` Jon Smirl 2007-09-19 14:31 ` Pedro Luis D. L. 1 sibling, 1 reply; 47+ messages in thread From: Jon Smirl @ 2007-09-19 13:56 UTC (permalink / raw) To: Pedro Luis D. L.; +Cc: netdev, Domen Puncer, linuxppc-embedded On 9/19/07, Pedro Luis D. L. <carcadiz@hotmail.com> wrote: > > Hello Jon, > I=B4m also working with a Phytec pcm030, but I can=B4t get it booted... > Which kernel are you using? > I tried to apply the 7 bestcomm patches from Sylvain and patch over these= with this new ones that Domen released. > The base kernel I=B4m using is 2.6.22.6 from kernel.org. > Although I used the patch that creates pcm030.c in arch/platforms/52xx/ a= nd compiled using this file, it gets halted at booting time. > > Bytes transferred =3D 5091 (13e3 hex) > ## Booting image at 00500000 ... > Image Name: Linux-2.6.22.6 > Created: 2007-09-19 8:53:02 UTC > Image Type: PowerPC Linux Kernel Image (gzip compressed) > Data Size: 1196911 Bytes =3D 1.1 MB > Load Address: 00000000 > Entry Point: 00000000 > Verifying Checksum ... OK > Uncompressing Kernel Image ... OK > Booting using flat device tree at 0x400000 > > (No more output and boot is halted) The root name of your device tree needs to match the name in pcm030.c pcm030_probe(void). If they don't match this happens. --=20 Jon Smirl jonsmirl@gmail.com ^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH] phy: export phy_mii_ioctl 2007-09-19 13:56 ` Jon Smirl @ 2007-09-19 14:31 ` Pedro Luis D. L. 0 siblings, 0 replies; 47+ messages in thread From: Pedro Luis D. L. @ 2007-09-19 14:31 UTC (permalink / raw) To: linuxppc-embedded On 9/19/07, Jon Smirl wrote: >On 9/19/07, Pedro Luis D. L. wrote: >> >> Hello Jon, >> I=B4m also working with a Phytec pcm030, but I can=B4t get it booted... >> Which kernel are you using? >> I tried to apply the 7 bestcomm patches from Sylvain and patch over thes= e with this new ones that Domen released. >> The base kernel I=B4m using is 2.6.22.6 from kernel.org. >> Although I used the patch that creates pcm030.c in arch/platforms/52xx/ = and compiled using this file, it gets halted at booting time. >> >> Bytes transferred =3D 5091 (13e3 hex) >> ## Booting image at 00500000 ... >> Image Name: Linux-2.6.22.6 >> Created: 2007-09-19 8:53:02 UTC >> Image Type: PowerPC Linux Kernel Image (gzip compressed) >> Data Size: 1196911 Bytes =3D 1.1 MB >> Load Address: 00000000 >> Entry Point: 00000000 >> Verifying Checksum ... OK >> Uncompressing Kernel Image ... OK >> Booting using flat device tree at 0x400000 >> >> (No more output and boot is halted) >=20 =20 I checked that and both are the same: In pcm030.c: static int __init pcm030_probe(void) { unsigned long node =3D of_get_flat_dt_root(); if (!of_flat_dt_is_compatible(node, "pcm030")) return 0; return 1; } define_machine(pcm030) { .name =3D "pcm030", .probe =3D pcm030_probe, .setup_arch =3D pcm030_setup_arch, .restart =3D mpc52xx_restart, .init =3D pcm030_init, .init_IRQ =3D mpc52xx_init_irq, .get_irq =3D mpc52xx_get_irq, .show_cpuinfo =3D pcm030_show_cpuinfo, .calibrate_decr =3D generic_calibrate_decr, }; in pcm030.dts: model =3D "pcm030"; compatible =3D "pcm030\0mpc5200b\0mpc52xx"; #address-cells =3D ; #size-cells =3D ; And it still doesn=B4t boot... I know it sounds hard, but... Can I skip the "if (!of_flat_dt_is_compatible= (node, "pcm030"))" line? This pcm030.c and pcm030.dts files work and boot with 2.6.20 kernel... Pedro Dominguez =20 _________________________________________________________________ Consigue el nuevo Windows Live Messenger http://get.live.com/messenger/overview= ^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH] phy: export phy_mii_ioctl 2007-09-18 19:29 ` Jon Smirl 2007-09-19 8:54 ` Pedro Luis D. L. @ 2007-09-19 8:54 ` Pedro Luis D. L. 1 sibling, 0 replies; 47+ messages in thread From: Pedro Luis D. L. @ 2007-09-19 8:54 UTC (permalink / raw) To: Jon Smirl, Domen Puncer; +Cc: netdev, linuxppc-embedded Hello Jon, I=B4m also working with a Phytec pcm030, but I can=B4t get it booted... Which kernel are you using? I tried to apply the 7 bestcomm patches from Sylvain and patch over these w= ith this new ones that Domen released. The base kernel I=B4m using is 2.6.22.6 from kernel.org. Although I used the patch that creates pcm030.c in arch/platforms/52xx/ and= compiled using this file, it gets halted at booting time. Bytes transferred =3D 5091 (13e3 hex) ## Booting image at 00500000 ... Image Name: Linux-2.6.22.6 Created: 2007-09-19 8:53:02 UTC Image Type: PowerPC Linux Kernel Image (gzip compressed) Data Size: 1196911 Bytes =3D 1.1 MB Load Address: 00000000 Entry Point: 00000000 Verifying Checksum ... OK Uncompressing Kernel Image ... OK Booting using flat device tree at 0x400000 (No more output and boot is halted) Are you using any other patch for the platform or any other kernel, because= I tried to apply these patches to a 2.6.20 kernel and are not successful. Bests, Pedro. > Date: Tue, 18 Sep 2007 15:29:09 -0400> From: jonsmirl@gmail.com=20 > To: domen@coderock.org> Subject: Re: [PATCH] phy: export phy_mii_ioctl=20 > CC: netdev@vger.kernel.org; linuxppc-embedded@ozlabs.org=20 >> On 9/18/07, Domen Puncer wrote:=20 >> More testing and getting it to work properly on Phytec pcm030 would=20 >> be great.>> Do we want to do anything about this?=20 >> [ 1.569657] net eth0: attached phy 0 to driver Generic PHY=20 > [ 2.576013] Sending DHCP requests .PHY: f0003000:00 - Link is Up=20 > - 100/Full> [ 4.612000] ., OK=20 > [ 6.764005] IP-Config: Got DHCP answer from 192.168.1.200, my=20 > address is 192.168.1.5 >> What is happening is the printk for "PHY: f0003000:00 - Link is Up=20 > - 100/Full" is done in an interrupt and it comes in the middle of the> ke= rnel doing DHCP and printing ... without a CR.=20 >> Two possible solutions, get rid of the link-up message or wait in in=20 > the initial driver load until the link is up. Or we could leave it the=20 > way it is, but some people may report this as a bug. >>> --> Jon Smirl> jonsmirl@gmail.com=20 > _______________________________________________=20 > Linuxppc-embedded mailing list> Linuxppc-embedded@ozlabs.org> https://ozl= abs.org/mailman/listinfo/linuxppc-embedded _________________________________________________________________ Busca desde cualquier p=E1gina Web con una protecci=F3n excepcional. Consig= ue la Barra de herramientas de Windows Live hoy mismo y GRATUITAMENTE. http://www.toolbar.live.com= ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH] phy: export phy_mii_ioctl 2007-09-17 20:21 ` [PATCH] phy: export phy_mii_ioctl Domen Puncer 2007-09-17 22:08 ` Jon Smirl @ 2007-09-20 6:36 ` Jeff Garzik 1 sibling, 0 replies; 47+ messages in thread From: Jeff Garzik @ 2007-09-20 6:36 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, linuxppc-embedded Domen Puncer wrote: > Export phy_mii_ioctl, so network drivers can use it when built > as modules too. > > Signed-off-by: Domen Puncer <domen@coderock.org> applied ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.2] net driver: mpc52xx fec 2007-09-02 7:41 ` [RFC PATCH v0.2] " Domen Puncer 2007-09-03 15:57 ` Grant Likely @ 2007-10-02 12:49 ` Sascha Hauer 2007-10-02 14:32 ` Domen Puncer 1 sibling, 1 reply; 47+ messages in thread From: Sascha Hauer @ 2007-10-02 12:49 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, linuxppc-embedded Hi Domen, On Sun, Sep 02, 2007 at 09:41:43AM +0200, Domen Puncer wrote: + */ > +static void fec_start(struct net_device *dev) > +{ > + struct fec_priv *priv = netdev_priv(dev); > + struct mpc52xx_fec __iomem *fec = priv->fec; > + u32 rcntrl; > + u32 tcntrl; > + u32 tmp; > + > + /* clear sticky error bits */ > + tmp = FEC_FIFO_STATUS_ERR | FEC_FIFO_STATUS_UF | FEC_FIFO_STATUS_OF; > + out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & tmp); > + out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & tmp); > + > + /* FIFOs will reset on fec_enable */ > + out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_ENABLE_IS_RESET); > + > + /* Set station address. */ > + fec_set_paddr(dev, dev->dev_addr); > + > + fec_set_multicast_list(dev); > + > + /* set max frame len, enable flow control, select mii mode */ > + rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */ > + rcntrl |= FEC_RCNTRL_FCE; > + rcntrl |= MII_RCNTL_MODE; > + if (priv->duplex == DUPLEX_FULL) > + tcntrl = FEC_TCNTRL_FDEN; /* FD enable */ > + else { > + rcntrl |= FEC_RCNTRL_DRT; /* disable Rx on Tx (HD) */ > + tcntrl = 0; > + } > + out_be32(&fec->r_cntrl, rcntrl); > + out_be32(&fec->x_cntrl, tcntrl); > + > + /* Clear any outstanding interrupt. */ > + out_be32(&fec->ievent, 0xffffffff); > + > + /* Enable interrupts we wish to service. */ > + out_be32(&fec->imask, FEC_IMASK_ENABLE); This disables phy interrupts. > +static int fec_mdio_read(struct mii_bus *bus, int phy_id, int reg) > +{ > + struct fec_mdio_priv *priv = bus->priv; > + int tries = 100; > + > + u32 request = FEC_MII_READ_FRAME; > + request |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; > + request |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; > + > + out_be32(&priv->regs->mii_data, request); > + > + /* wait for it to finish, this takes about 23 us on lite5200b */ > + while (priv->completed == 0 && tries--) > + udelay(5); > + > + priv->completed = 0; > + > + if (tries == 0) > + return -ETIMEDOUT; This does not work as expected. When a timeout occurs tries is -1 not 0, so the test above will never trigger. Using --tries instead of tries-- reveals another bug. We get a timeout everytime now, because MII interrupts are accidently disabled in fec_start(). We cannot use a waitqueue or similar for waiting for the mii transfer because we are atomic here. A simple fix is provided below. It removes the need for the interrupt handler in the phy handling routines. Anyway, it might be better to fix the phy layer not to use atomic contexts, so this patch might not be the way to go. Regards, Sascha + > +static int fec_mdio_probe(struct of_device *of, const struct of_device_id *match) > +{ > + struct device *dev = &of->dev; > > [...] > > + init_waitqueue_head(&priv->wq); This waitqueue is never used. wake_up() is called in the interrupt handler, but noone ever sleeps on the queue. --- drivers/net/fec_mpc52xx/fec.c | 7 +--- drivers/net/fec_mpc52xx/fec_phy.c | 59 +++++++------------------------------- 2 files changed, 15 insertions(+), 51 deletions(-) Index: linux-2.6.23-rc8/drivers/net/fec_mpc52xx/fec.c =================================================================== --- linux-2.6.23-rc8.orig/drivers/net/fec_mpc52xx/fec.c +++ linux-2.6.23-rc8/drivers/net/fec_mpc52xx/fec.c @@ -265,7 +265,6 @@ static void fec_phy_hw_init(struct fec_p return; out_be32(&fec->mii_speed, priv->phy_speed); - out_be32(&fec->imask, in_be32(&fec->imask) | FEC_IMASK_MII); } static int fec_open(struct net_device *dev) @@ -654,7 +653,7 @@ static void fec_hw_init(struct net_devic out_be32(&fec->iaddr1, 0x00000000); /* No individual filter */ out_be32(&fec->iaddr2, 0x00000000); /* No individual filter */ - /* set phy speed and enable MII interrupt + /* set phy speed. * this can't be done in phy driver, since it needs to be called * before fec stuff (even on resume) */ fec_phy_hw_init(priv); @@ -730,8 +729,8 @@ static void fec_stop(struct net_device * struct mpc52xx_fec __iomem *fec = priv->fec; unsigned long timeout; - /* disable all but MII interrupt */ - out_be32(&fec->imask, in_be32(&fec->imask) & FEC_IMASK_MII); + /* disable all interrupts */ + out_be32(&fec->imask, 0); /* Disable the rx task. */ bcom_disable(priv->rx_dmatsk); Index: linux-2.6.23-rc8/drivers/net/fec_mpc52xx/fec_phy.c =================================================================== --- linux-2.6.23-rc8.orig/drivers/net/fec_mpc52xx/fec_phy.c +++ linux-2.6.23-rc8/drivers/net/fec_mpc52xx/fec_phy.c @@ -18,29 +18,28 @@ #include "fec.h" struct fec_mdio_priv { - int completed; - wait_queue_head_t wq; struct mpc52xx_fec __iomem *regs; - int irq; }; static int fec_mdio_read(struct mii_bus *bus, int phy_id, int reg) { struct fec_mdio_priv *priv = bus->priv; + struct mpc52xx_fec __iomem *fec; int tries = 100; - u32 request = FEC_MII_READ_FRAME; + + fec = priv->regs; + out_be32(&fec->ievent, FEC_IEVENT_MII); + request |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; request |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; out_be32(&priv->regs->mii_data, request); /* wait for it to finish, this takes about 23 us on lite5200b */ - while (priv->completed == 0 && tries--) + while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries) udelay(5); - priv->completed = 0; - if (tries == 0) return -ETIMEDOUT; @@ -50,9 +49,13 @@ static int fec_mdio_read(struct mii_bus static int fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data) { struct fec_mdio_priv *priv = bus->priv; + struct mpc52xx_fec __iomem *fec; u32 value = data; int tries = 100; + fec = priv->regs; + out_be32(&fec->ievent, FEC_IEVENT_MII); + value |= FEC_MII_WRITE_FRAME; value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; @@ -60,38 +63,15 @@ static int fec_mdio_write(struct mii_bus out_be32(&priv->regs->mii_data, value); /* wait for request to finish */ - while (priv->completed == 0 && tries--) + while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries) udelay(5); - priv->completed = 0; - if (tries == 0) return -ETIMEDOUT; return 0; } -static irqreturn_t fec_mdio_interrupt(int irq, void *dev_id) -{ - struct fec_mdio_priv *priv = dev_id; - struct mpc52xx_fec __iomem *fec; - int ievent; - - fec = priv->regs; - ievent = in_be32(&fec->ievent); - - ievent &= FEC_IEVENT_MII; - if (!ievent) - return IRQ_NONE; - - out_be32(&fec->ievent, ievent); - - priv->completed = 1; - wake_up(&priv->wq); - - return IRQ_HANDLED; -} - static int fec_mdio_probe(struct of_device *of, const struct of_device_id *match) { struct device *dev = &of->dev; @@ -143,22 +123,12 @@ static int fec_mdio_probe(struct of_devi goto out_free; } - priv->irq = irq_of_parse_and_map(np, 0); - err = request_irq(priv->irq, &fec_mdio_interrupt, IRQF_DISABLED | IRQF_SHARED, - "fec_mdio", priv); - if (err) { - printk(KERN_ERR "%s: interrupt request failed with %i\n", __func__, err); - goto out_unmap; - } - bus->id = res.start; bus->priv = priv; bus->dev = dev; dev_set_drvdata(dev, bus); - init_waitqueue_head(&priv->wq); - /* set MII speed */ out_be32(&priv->regs->mii_speed, ((mpc52xx_find_ipb_freq(of->node) >> 20) / 5) << 1); @@ -167,13 +137,10 @@ static int fec_mdio_probe(struct of_devi err = mdiobus_register(bus); if (err) - goto out_free_irq; + goto out_unmap; return 0; - out_free_irq: - free_irq(priv->irq, dev); - irq_dispose_mapping(priv->irq); out_unmap: iounmap(priv->regs); out_free: @@ -197,8 +164,6 @@ static int fec_mdio_remove(struct of_dev mdiobus_unregister(bus); dev_set_drvdata(dev, NULL); - free_irq(priv->irq, dev); - irq_dispose_mapping(priv->irq); iounmap(priv->regs); for (i=0; i<PHY_MAX_ADDR; i++) if (bus->irq[i]) -- Pengutronix - Linux Solutions for Science and Industry Entwicklungszentrum Nord http://www.pengutronix.de ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.2] net driver: mpc52xx fec 2007-10-02 12:49 ` [RFC PATCH v0.2] net driver: mpc52xx fec Sascha Hauer @ 2007-10-02 14:32 ` Domen Puncer 2007-10-02 15:46 ` Robert Schwebel 0 siblings, 1 reply; 47+ messages in thread From: Domen Puncer @ 2007-10-02 14:32 UTC (permalink / raw) To: Sascha Hauer; +Cc: netdev, linuxppc-embedded On 02/10/07 14:49 +0200, Sascha Hauer wrote: > > Hi Domen, Hi Sascha! > > On Sun, Sep 02, 2007 at 09:41:43AM +0200, Domen Puncer wrote: > + */ > > +static void fec_start(struct net_device *dev) > > +{ > > + struct fec_priv *priv = netdev_priv(dev); > > + struct mpc52xx_fec __iomem *fec = priv->fec; > > + u32 rcntrl; > > + u32 tcntrl; > > + u32 tmp; > > + > > + /* clear sticky error bits */ > > + tmp = FEC_FIFO_STATUS_ERR | FEC_FIFO_STATUS_UF | FEC_FIFO_STATUS_OF; > > + out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & tmp); > > + out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & tmp); > > + > > + /* FIFOs will reset on fec_enable */ > > + out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_ENABLE_IS_RESET); > > + > > + /* Set station address. */ > > + fec_set_paddr(dev, dev->dev_addr); > > + > > + fec_set_multicast_list(dev); > > + > > + /* set max frame len, enable flow control, select mii mode */ > > + rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */ > > + rcntrl |= FEC_RCNTRL_FCE; > > + rcntrl |= MII_RCNTL_MODE; > > + if (priv->duplex == DUPLEX_FULL) > > + tcntrl = FEC_TCNTRL_FDEN; /* FD enable */ > > + else { > > + rcntrl |= FEC_RCNTRL_DRT; /* disable Rx on Tx (HD) */ > > + tcntrl = 0; > > + } > > + out_be32(&fec->r_cntrl, rcntrl); > > + out_be32(&fec->x_cntrl, tcntrl); > > + > > + /* Clear any outstanding interrupt. */ > > + out_be32(&fec->ievent, 0xffffffff); > > + > > + /* Enable interrupts we wish to service. */ > > + out_be32(&fec->imask, FEC_IMASK_ENABLE); > > > This disables phy interrupts. Right, oops. > > > > +static int fec_mdio_read(struct mii_bus *bus, int phy_id, int reg) > > +{ > > + struct fec_mdio_priv *priv = bus->priv; > > + int tries = 100; > > + > > + u32 request = FEC_MII_READ_FRAME; > > + request |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK; > > + request |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK; > > + > > + out_be32(&priv->regs->mii_data, request); > > + > > + /* wait for it to finish, this takes about 23 us on lite5200b */ > > + while (priv->completed == 0 && tries--) > > + udelay(5); > > + > > + priv->completed = 0; > > + > > + if (tries == 0) > > + return -ETIMEDOUT; > > This does not work as expected. When a timeout occurs tries is -1 not 0, > so the test above will never trigger. > Using --tries instead of tries-- reveals another bug. We get a timeout > everytime now, because MII interrupts are accidently disabled in > fec_start(). Oh, double bug made it work! ;-) > > We cannot use a waitqueue or similar for waiting for the mii transfer > because we are atomic here. > A simple fix is provided below. It removes the need for the interrupt > handler in the phy handling routines. Anyway, it might be better to fix > the phy layer not to use atomic contexts, so this patch might not be the > way to go. Doh, looks like this was the problem with wq's, but I forgot to remove them, when I "fixed" the code. > > > Regards, > Sascha > > + > > +static int fec_mdio_probe(struct of_device *of, const struct of_device_id *match) > > +{ > > + struct device *dev = &of->dev; > > > > [...] > > > > + init_waitqueue_head(&priv->wq); > > This waitqueue is never used. wake_up() is called in the interrupt > handler, but noone ever sleeps on the queue. > > > --- > drivers/net/fec_mpc52xx/fec.c | 7 +--- > drivers/net/fec_mpc52xx/fec_phy.c | 59 +++++++------------------------------- > 2 files changed, 15 insertions(+), 51 deletions(-) The patch looks ok to me. Domen ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.2] net driver: mpc52xx fec 2007-10-02 14:32 ` Domen Puncer @ 2007-10-02 15:46 ` Robert Schwebel 0 siblings, 0 replies; 47+ messages in thread From: Robert Schwebel @ 2007-10-02 15:46 UTC (permalink / raw) To: Domen Puncer; +Cc: netdev, linuxppc-embedded On Tue, Oct 02, 2007 at 04:32:02PM +0200, Domen Puncer wrote: > The patch looks ok to me. Short update: even with the patch, the driver doesn't work on an rt-preempt enabled kernel, or at least not reliable. It survives normal traffic and ping -f, but dies when running nmap against the box, with a set RFIFO_ERROR flag. More research needs to be done. Robert -- Pengutronix - Linux Solutions for Science and Industry Entwicklungszentrum Nord http://www.pengutronix.de ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-08-10 9:51 [RFC PATCH v0.1] net driver: mpc52xx fec Domen Puncer ` (2 preceding siblings ...) 2007-09-02 7:41 ` [RFC PATCH v0.2] " Domen Puncer @ 2007-09-27 17:07 ` Juergen Beisert 2007-09-27 18:12 ` Jon Smirl 2007-09-28 15:07 ` Juergen Beisert 3 siblings, 2 replies; 47+ messages in thread From: Juergen Beisert @ 2007-09-27 17:07 UTC (permalink / raw) To: linuxppc-embedded On Friday 10 August 2007 11:51, Domen Puncer wrote: > Not for merge (yet)! But please do review. > > fec_mpc52xx driver (not in-tree, but floating around) isn't in very > good shape, so I tried to change that. > Diff against original is quite big (fec_phy.c is completely rewritten) > and confuzing, so I'm including whole drivers/net/fec_mpc52xx/ . > > I still have 'make CONFIG_FEC_MPC52xx_MDIO=3Dn compile and work' on my > TODO, maybe even ethtool support. Currently I'm trying with your fec driver and Sylvain Munaut's bestcomm dri= ver=20 *and* rt-preemt 2.6.23-rc8-rt1 and now I'm getting this error while stress= =20 test the network: BUG: scheduling while atomic: softirq-timer/0/0x00000002/5, CPU#0 Call Trace: [c0309e00] [c0007ddc] show_stack+0x3c/0x194 (unreliable) [c0309e30] [c0017934] __schedule_bug+0x38/0x48 [c0309e40] [c01c8f24] __schedule+0x3e8/0x428 [c0309e70] [c01c96d4] schedule+0x54/0xf0 [c0309e80] [c01c9e8c] schedule_timeout+0x68/0xe4 [c0309ec0] [c00282dc] msleep+0x1c/0x34 [c0309ed0] [c0125fb8] fec_stop+0xbc/0x1a8 [c0309ef0] [c0126530] fec_reset+0x20/0xb0 [c0309f10] [c0127840] fec_tx_timeout+0x3c/0xa4 [c0309f30] [c016b5dc] dev_watchdog+0x13c/0x14c [c0309f50] [c0027c90] run_timer_softirq+0x2e4/0x444 [c0309f90] [c00239a4] ksoftirqd+0x134/0x214 [c0309fd0] [c0034d94] kthread+0x48/0x84 [c0309ff0] [c000f828] kernel_thread+0x44/0x60 Do you have an idea what happens? Juergen =2D-=20 Dipl.-Ing. Juergen Beisert | http://www.pengutronix.de =A0Pengutronix - Linux Solutions for Science and Industry =A0 Handelsregister: Amtsgericht Hildesheim, HRA 2686 =A0 =A0 =A0 Vertretung Sued/Muenchen, Germany Phone: +49-8766-939 228 | Fax: +49-5121-206917-9 ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-09-27 17:07 ` [RFC PATCH v0.1] " Juergen Beisert @ 2007-09-27 18:12 ` Jon Smirl 2007-09-27 18:43 ` Scott Wood 2007-09-28 15:07 ` Juergen Beisert 1 sibling, 1 reply; 47+ messages in thread From: Jon Smirl @ 2007-09-27 18:12 UTC (permalink / raw) To: Juergen Beisert; +Cc: linuxppc-embedded On 9/27/07, Juergen Beisert <jbe@pengutronix.de> wrote: > On Friday 10 August 2007 11:51, Domen Puncer wrote: > > Not for merge (yet)! But please do review. > > > > fec_mpc52xx driver (not in-tree, but floating around) isn't in very > > good shape, so I tried to change that. > > Diff against original is quite big (fec_phy.c is completely rewritten) > > and confuzing, so I'm including whole drivers/net/fec_mpc52xx/ . > > > > I still have 'make CONFIG_FEC_MPC52xx_MDIO=n compile and work' on my > > TODO, maybe even ethtool support. > > Currently I'm trying with your fec driver and Sylvain Munaut's bestcomm driver > *and* rt-preemt 2.6.23-rc8-rt1 and now I'm getting this error while stress > test the network: > > BUG: scheduling while atomic: softirq-timer/0/0x00000002/5, CPU#0 > Call Trace: > [c0309e00] [c0007ddc] show_stack+0x3c/0x194 (unreliable) > [c0309e30] [c0017934] __schedule_bug+0x38/0x48 > [c0309e40] [c01c8f24] __schedule+0x3e8/0x428 > [c0309e70] [c01c96d4] schedule+0x54/0xf0 > [c0309e80] [c01c9e8c] schedule_timeout+0x68/0xe4 > [c0309ec0] [c00282dc] msleep+0x1c/0x34 > [c0309ed0] [c0125fb8] fec_stop+0xbc/0x1a8 > [c0309ef0] [c0126530] fec_reset+0x20/0xb0 > [c0309f10] [c0127840] fec_tx_timeout+0x3c/0xa4 > [c0309f30] [c016b5dc] dev_watchdog+0x13c/0x14c > [c0309f50] [c0027c90] run_timer_softirq+0x2e4/0x444 > [c0309f90] [c00239a4] ksoftirqd+0x134/0x214 > [c0309fd0] [c0034d94] kthread+0x48/0x84 > [c0309ff0] [c000f828] kernel_thread+0x44/0x60 > > Do you have an idea what happens? The call to msleep() is inside a block protected with :#define in_interrupt() (irq_count()) if (!in_interrupt) The stack trace looks like it is in a timer interrupt so shouldn't irq_count be non-zero? Could there be some lack of coordination on irq_count and the timer tick with the preempt patch applied? Or does irq_count() not count soft irqs? (!in_interrupt) may be the wrong way to protect this code. -- Jon Smirl jonsmirl@gmail.com ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-09-27 18:12 ` Jon Smirl @ 2007-09-27 18:43 ` Scott Wood 2007-09-28 9:12 ` Juergen Beisert 0 siblings, 1 reply; 47+ messages in thread From: Scott Wood @ 2007-09-27 18:43 UTC (permalink / raw) To: Jon Smirl; +Cc: linuxppc-embedded Jon Smirl wrote: > The call to msleep() is inside a block protected with > :#define in_interrupt() (irq_count()) > if (!in_interrupt) > > The stack trace looks like it is in a timer interrupt so shouldn't > irq_count be non-zero? > Could there be some lack of coordination on irq_count and the timer > tick with the preempt patch applied? Or does irq_count() not count > soft irqs? > > (!in_interrupt) may be the wrong way to protect this code. I think in_atomic() is what you want. -Scott ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-09-27 18:43 ` Scott Wood @ 2007-09-28 9:12 ` Juergen Beisert 2007-09-28 15:40 ` Scott Wood ` (2 more replies) 0 siblings, 3 replies; 47+ messages in thread From: Juergen Beisert @ 2007-09-28 9:12 UTC (permalink / raw) To: linuxppc-embedded On Thursday 27 September 2007 20:43, Scott Wood wrote: > Jon Smirl wrote: > > The call to msleep() is inside a block protected with > > > > :#define in_interrupt() (irq_count()) > > > > if (!in_interrupt) > > > > The stack trace looks like it is in a timer interrupt so shouldn't > > irq_count be non-zero? > > Could there be some lack of coordination on irq_count and the timer > > tick with the preempt patch applied? Or does irq_count() not count > > soft irqs? > > > > (!in_interrupt) may be the wrong way to protect this code. > > I think in_atomic() is what you want. I tried with in_atomic(). The BUG report is gone, but the problem still=20 exists.=20 While network stress testing:=20 [...] NETDEV WATCHDOG: eth0: transmit timed out net eth0: transmit timed out net eth0: queues didn't drain net eth0: tx: index: 35, outdex: 36 net eth0: rx: index: 24, outdex: 25 PHY: f0003000:00 - Link is Down PHY: f0003000:00 - Link is Up - 100/Full The link is up again, but any connection is dead (no answers to ping etc.).= =20 But the serial console is still working. I'm not sure if the RT-Preempt pat= ch=20 *causes* this behavior or only *discover* it. Any idea? Juergen =2D-=20 Dipl.-Ing. Juergen Beisert | http://www.pengutronix.de =C2=A0Pengutronix - Linux Solutions for Science and Industry =C2=A0 Handelsregister: Amtsgericht Hildesheim, HRA 2686 =C2=A0 =C2=A0 =C2=A0 Vertretung Sued/Muenchen, Germany Phone: +49-8766-939 228 | Fax: +49-5121-206917-9 ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-09-28 9:12 ` Juergen Beisert @ 2007-09-28 15:40 ` Scott Wood 2007-10-08 8:48 ` Sascha Hauer 2007-10-08 9:01 ` Sascha Hauer 2 siblings, 0 replies; 47+ messages in thread From: Scott Wood @ 2007-09-28 15:40 UTC (permalink / raw) To: Juergen Beisert; +Cc: linuxppc-embedded Juergen Beisert wrote: > I tried with in_atomic(). The BUG report is gone, but the problem still > exists. > > While network stress testing: > > [...] > NETDEV WATCHDOG: eth0: transmit timed out > net eth0: transmit timed out > net eth0: queues didn't drain > net eth0: tx: index: 35, outdex: 36 > net eth0: rx: index: 24, outdex: 25 > PHY: f0003000:00 - Link is Down > PHY: f0003000:00 - Link is Up - 100/Full > > The link is up again, but any connection is dead (no answers to ping etc.). > But the serial console is still working. I'm not sure if the RT-Preempt patch > *causes* this behavior or only *discover* it. Any idea? I'd try looking at the driver's locking to make sure that it's correct. -Scott ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-09-28 9:12 ` Juergen Beisert 2007-09-28 15:40 ` Scott Wood @ 2007-10-08 8:48 ` Sascha Hauer 2007-10-08 9:01 ` Sascha Hauer 2 siblings, 0 replies; 47+ messages in thread From: Sascha Hauer @ 2007-10-08 8:48 UTC (permalink / raw) To: Juergen Beisert; +Cc: linuxppc-embedded Hi, On Fri, Sep 28, 2007 at 11:12:17AM +0200, Juergen Beisert wrote: > I tried with in_atomic(). The BUG report is gone, but the problem still > exists. > > While network stress testing: > > [...] > NETDEV WATCHDOG: eth0: transmit timed out > net eth0: transmit timed out > net eth0: queues didn't drain > net eth0: tx: index: 35, outdex: 36 > net eth0: rx: index: 24, outdex: 25 > PHY: f0003000:00 - Link is Down > PHY: f0003000:00 - Link is Up - 100/Full > > The link is up again, but any connection is dead (no answers to ping etc.). > But the serial console is still working. I'm not sure if the RT-Preempt patch > *causes* this behavior or only *discover* it. Any idea? We finally solved this problem. It has nothing to do with locking though. The problem is that the bcom engine was not reenabled after resetting the fec. The following patch solves this. Reenable the bestcom engine after resetting the mpc52xx fec controller. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/net/fec_mpc52xx/fec.c | 3 +++ 1 file changed, 3 insertions(+) Index: linux-2.6/drivers/net/fec_mpc52xx/fec.c =================================================================== --- linux-2.6.orig/drivers/net/fec_mpc52xx/fec.c +++ linux-2.6/drivers/net/fec_mpc52xx/fec.c @@ -788,6 +788,9 @@ static void fec_reset(struct net_device fec_alloc_rx_buffers(priv->rx_dmatsk); + bcom_enable(priv->rx_dmatsk); + bcom_enable(priv->tx_dmatsk); + fec_start(dev); } > > Juergen > -- > Dipl.-Ing. Juergen Beisert | http://www.pengutronix.de > Pengutronix - Linux Solutions for Science and Industry > Handelsregister: Amtsgericht Hildesheim, HRA 2686 > Vertretung Sued/Muenchen, Germany > Phone: +49-8766-939 228 | Fax: +49-5121-206917-9 > _______________________________________________ > Linuxppc-embedded mailing list > Linuxppc-embedded@ozlabs.org > https://ozlabs.org/mailman/listinfo/linuxppc-embedded -- Pengutronix - Linux Solutions for Science and Industry Entwicklungszentrum Nord http://www.pengutronix.de ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-09-28 9:12 ` Juergen Beisert 2007-09-28 15:40 ` Scott Wood 2007-10-08 8:48 ` Sascha Hauer @ 2007-10-08 9:01 ` Sascha Hauer 2007-10-08 16:46 ` Jon Smirl 2 siblings, 1 reply; 47+ messages in thread From: Sascha Hauer @ 2007-10-08 9:01 UTC (permalink / raw) To: Juergen Beisert; +Cc: linuxppc-embedded On Fri, Sep 28, 2007 at 11:12:17AM +0200, Juergen Beisert wrote: > > While network stress testing: > > [...] > NETDEV WATCHDOG: eth0: transmit timed out > net eth0: transmit timed out > net eth0: queues didn't drain > net eth0: tx: index: 35, outdex: 36 > net eth0: rx: index: 24, outdex: 25 > PHY: f0003000:00 - Link is Down > PHY: f0003000:00 - Link is Up - 100/Full > > The link is up again, but any connection is dead (no answers to ping etc.). > But the serial console is still working. I'm not sure if the RT-Preempt patch > *causes* this behavior or only *discover* it. Any idea? While the previous patch I sent fixed the reset path for the fec controller this patch makes sure the chip does not have to be resetted. Problem was that we ran out of receive buffers when we nmapped our board (nmap sends lots of small packages in a short period of time). By increasing the number of rx buffers this problem does not appear anymore. This patch produces a significant memory overhead to the driver of about 340k, so we might want to have this configurable as a module parameter. Let me know what the preferred way is, I can update the patch accordingly. Sascha Increase the number of RX packets in the fec_mpc52xx driver. This is necessary to no run out of rx packets when lots of small packets arrive in short time (for example when nmapping the board) Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/net/fec_mpc52xx/fec.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) Index: linux-2.6/drivers/net/fec_mpc52xx/fec.h =================================================================== --- linux-2.6.orig/drivers/net/fec_mpc52xx/fec.h +++ linux-2.6/drivers/net/fec_mpc52xx/fec.h @@ -19,7 +19,7 @@ /* Tunable constant */ /* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */ #define FEC_RX_BUFFER_SIZE 1522 /* max receive packet size */ -#define FEC_RX_NUM_BD 64 +#define FEC_RX_NUM_BD 256 #define FEC_TX_NUM_BD 64 #define FEC_RESET_DELAY 50 /* uS */ -- Pengutronix - Linux Solutions for Science and Industry Entwicklungszentrum Nord http://www.pengutronix.de ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-10-08 9:01 ` Sascha Hauer @ 2007-10-08 16:46 ` Jon Smirl 0 siblings, 0 replies; 47+ messages in thread From: Jon Smirl @ 2007-10-08 16:46 UTC (permalink / raw) To: Sascha Hauer; +Cc: linuxppc-embedded On 10/8/07, Sascha Hauer <s.hauer@pengutronix.de> wrote: > While the previous patch I sent fixed the reset path for the fec > controller this patch makes sure the chip does not have to be resetted. > Problem was that we ran out of receive buffers when we nmapped our > board (nmap sends lots of small packages in a short period of time). > By increasing the number of rx buffers this problem does not appear > anymore. > This patch produces a significant memory overhead to the driver of about > 340k, so we might want to have this configurable as a module parameter. > Let me know what the preferred way is, I can update the patch > accordingly. Is nmap sending UPD packets, why does the chip need to receive everything without dropping packets? It we do get into receive overrun, is everything recovering correctly? As another test you could slow everything down by forcing the net to 10Mb. It may be interesting to explore why the small packets aren't being processed fast enough, there could be other bugs lurking. -- Jon Smirl jonsmirl@gmail.com ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-09-27 17:07 ` [RFC PATCH v0.1] " Juergen Beisert 2007-09-27 18:12 ` Jon Smirl @ 2007-09-28 15:07 ` Juergen Beisert 2007-09-28 15:38 ` Jon Smirl 1 sibling, 1 reply; 47+ messages in thread From: Juergen Beisert @ 2007-09-28 15:07 UTC (permalink / raw) To: linuxppc-embedded On Thursday 27 September 2007 19:07, Juergen Beisert wrote: > On Friday 10 August 2007 11:51, Domen Puncer wrote: > > Not for merge (yet)! But please do review. > > > > fec_mpc52xx driver (not in-tree, but floating around) isn't in very > > good shape, so I tried to change that. > > Diff against original is quite big (fec_phy.c is completely rewritten) > > and confuzing, so I'm including whole drivers/net/fec_mpc52xx/ . > > > > I still have 'make CONFIG_FEC_MPC52xx_MDIO=3Dn compile and work' on my > > TODO, maybe even ethtool support. I add a few more debug outputs and now with this driver I can run a $ nmap <ip> from my host against the target and target's network stops always at the sa= me=20 point. The last output from the driver is (with DEBUG macro defined): net eth0: ievent: 08020000 and no further interrupt occurs anymore (I checked all three interrupt entr= y=20 functions) nmap on host's side outputs: Starting Nmap 4.20 ( http://insecure.org ) at 2007-09-28 16:56 CEST Interesting ports on 192.168.23.226: Not shown: 852 filtered ports, 843 closed ports PORT STATE SERVICE 22/tcp open ssh 23/tcp open telnet Nmap finished: 1 IP address (1 host up) scanned in 14.120 seconds But I can't run it a second time, as the network on target's side doesn't=20 respond. Any idea? Juergen =2D-=20 Dipl.-Ing. Juergen Beisert | http://www.pengutronix.de =A0Pengutronix - Linux Solutions for Science and Industry =A0 Handelsregister: Amtsgericht Hildesheim, HRA 2686 =A0 =A0 =A0 Vertretung Sued/Muenchen, Germany Phone: +49-8766-939 228 | Fax: +49-5121-206917-9 ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-09-28 15:07 ` Juergen Beisert @ 2007-09-28 15:38 ` Jon Smirl 2007-10-01 8:35 ` Juergen Beisert 0 siblings, 1 reply; 47+ messages in thread From: Jon Smirl @ 2007-09-28 15:38 UTC (permalink / raw) To: Juergen Beisert; +Cc: linuxppc-embedded On 9/28/07, Juergen Beisert <jbe@pengutronix.de> wrote: > But I can't run it a second time, as the network on target's side doesn't > respond. Any idea? Do the stress tests complete on a non-rt kernel? That will narrow down the type of bug being looked for. -- Jon Smirl jonsmirl@gmail.com ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-09-28 15:38 ` Jon Smirl @ 2007-10-01 8:35 ` Juergen Beisert 2007-10-01 16:24 ` Juergen Beisert 0 siblings, 1 reply; 47+ messages in thread From: Juergen Beisert @ 2007-10-01 8:35 UTC (permalink / raw) To: linuxppc-embedded On Friday 28 September 2007 17:38, Jon Smirl wrote: > On 9/28/07, Juergen Beisert <jbe@pengutronix.de> wrote: > > But I can't run it a second time, as the network on target's side doesn= 't > > respond. Any idea? > > Do the stress tests complete on a non-rt kernel? I tried it again: 1) Target runs 2.6.23-rc8 without rt-preempt: @host$ nmap 192.168.23.226 Starting Nmap 4.20 ( http://insecure.org ) at 2007-10-01 10:20 CEST Interesting ports on 192.168.23.226: Not shown: 1695 closed ports PORT STATE SERVICE 22/tcp open ssh 23/tcp open telnet Nmap finished: 1 IP address (1 host up) scanned in 0.581 seconds Target continues to work. Does not make a difference if the root filesystem= is=20 jffs2 or nfs. 2) Same target runs 2.6.23-rc8-rt1 @host$ nmap 192.168.23.226 Starting Nmap 4.20 ( http://insecure.org ) at 2007-10-01 10:15 CEST Interesting ports on 192.168.23.226: Not shown: 871 filtered ports, 824 closed ports PORT STATE SERVICE 22/tcp open ssh 23/tcp open telnet Nmap finished: 1 IP address (1 host up) scanned in 14.116 seconds Network on target dies. But can be reactivated by an "ifconfig eth0 down;=20 ifconfig eth0 up". I included some printk statements into the fec.c source = to=20 see what interrupts are happen. "r" means fec_rx_interrupt was entered, "t" means fec_tx_interrupt was ente= red=20 and "p" means fec_interrupt was entered. This is the output of the=20 nmap "attack" above: rtrtrrr at this point: fec_hard_start_xmit, stop queue rrt at this point: fec_tx_interrupt, wake queue ttrr at this point: fec_hard_start_xmit, stop queue rrt at this point: fec_tx_interrupt, wake queue ttrr at this point: fec_hard_start_xmit, stop queue rrt at this point: fec_tx_interrupt, wake queue ttrr at this point: fec_hard_start_xmit, stop queue rrt at this point: fec_tx_interrupt, wake queue ttrr at this point: fec_hard_start_xmit, stop queue rrt at this point: fec_tx_interrupt, wake queue ttrr at this point: fec_hard_start_xmit, stop queue rrt at this point: fec_tx_interrupt, wake queue ttr at this point: fec_hard_start_xmit, stop queue rrt at this point: fec_tx_interrupt, wake queue at this point: fec_hard_start_xmit, stop queue t at this point: fec_tx_interrupt, wake queue tp <7>net eth0: ievent: 08020000 =2E..at this point the network is dead. BTW: Without rt-preempt none of the wake/stop queue events and no=20 fec_interrupt occurs. I only see a long list of "r"s and "t"s... Juergen =2D-=20 Dipl.-Ing. Juergen Beisert | http://www.pengutronix.de =A0Pengutronix - Linux Solutions for Science and Industry =A0 Handelsregister: Amtsgericht Hildesheim, HRA 2686 =A0 =A0 =A0 Vertretung Sued/Muenchen, Germany Phone: +49-8766-939 228 | Fax: +49-5121-206917-9 ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [RFC PATCH v0.1] net driver: mpc52xx fec 2007-10-01 8:35 ` Juergen Beisert @ 2007-10-01 16:24 ` Juergen Beisert 0 siblings, 0 replies; 47+ messages in thread From: Juergen Beisert @ 2007-10-01 16:24 UTC (permalink / raw) To: linuxppc-embedded; +Cc: Daniel Walker On Monday 01 October 2007 10:35, Juergen Beisert wrote: > 2) Same target runs 2.6.23-rc8-rt1 > > @host$ nmap 192.168.23.226 > > Starting Nmap 4.20 ( http://insecure.org ) at 2007-10-01 10:15 CEST > Interesting ports on 192.168.23.226: > Not shown: 871 filtered ports, 824 closed ports > PORT STATE SERVICE > 22/tcp open ssh > 23/tcp open telnet > > Nmap finished: 1 IP address (1 host up) scanned in 14.116 seconds > > Network on target dies. But can be reactivated by an "ifconfig eth0 down; > ifconfig eth0 up". I included some printk statements into the fec.c source > to see what interrupts are happen. > > "r" means fec_rx_interrupt was entered, "t" means fec_tx_interrupt was > entered and "p" means fec_interrupt was entered. This is the output of the > nmap "attack" above: > > rtrtrrr > at this point: fec_hard_start_xmit, stop queue > rrt > at this point: fec_tx_interrupt, wake queue > ttrr > at this point: fec_hard_start_xmit, stop queue > rrt > at this point: fec_tx_interrupt, wake queue > ttrr > at this point: fec_hard_start_xmit, stop queue > rrt > at this point: fec_tx_interrupt, wake queue > ttrr > at this point: fec_hard_start_xmit, stop queue > rrt > at this point: fec_tx_interrupt, wake queue > ttrr > at this point: fec_hard_start_xmit, stop queue > rrt > at this point: fec_tx_interrupt, wake queue > ttrr > at this point: fec_hard_start_xmit, stop queue > rrt > at this point: fec_tx_interrupt, wake queue > ttr > at this point: fec_hard_start_xmit, stop queue > rrt > at this point: fec_tx_interrupt, wake queue > at this point: fec_hard_start_xmit, stop queue > t > at this point: fec_tx_interrupt, wake queue > tp > <7>net eth0: ievent: 08020000 > > ...at this point the network is dead. > > BTW: Without rt-preempt none of the wake/stop queue events and no > fec_interrupt occurs. I only see a long list of "r"s and "t"s... We tried again with rt-preempt and increased the priority of FEC's three=20 interrupts: And now it survives the nmap "attack". But we don't know now if= =20 we only changed the behavior or fixed the bug? BTW: Is it possible that fec_interrupt(() doesn' handle FEC_IEVENT_RFIFO_ER= ROR =20 and FEC_IEVENT_XFIFO_ERROR)) incorrectly? The lines makes more sense with t= he=20 following patch (but we are not sure about authors real intention). @@ -506,7 +484,7 @@ static irqreturn_t fec_interrupt(int irq out_be32(&fec->ievent, ievent); /* clear pending events */ =2D if (ievent & ~(FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) { + if (!(ievent & (FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR))) { if (ievent & ~FEC_IEVENT_TFINT) dev_dbg(&dev->dev, "ievent: %08x\n", ievent); return IRQ_HANDLED; Juergen =2D-=20 Dipl.-Ing. Juergen Beisert | http://www.pengutronix.de =A0Pengutronix - Linux Solutions for Science and Industry =A0 Handelsregister: Amtsgericht Hildesheim, HRA 2686 =A0 =A0 =A0 Vertretung Sued/Muenchen, Germany Phone: +49-8766-939 228 | Fax: +49-5121-206917-9 ^ permalink raw reply [flat|nested] 47+ messages in thread
end of thread, other threads:[~2007-10-08 16:46 UTC | newest] Thread overview: 47+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2007-08-10 9:51 [RFC PATCH v0.1] net driver: mpc52xx fec Domen Puncer 2007-08-10 13:02 ` Arnaldo Carvalho de Melo 2007-08-13 7:21 ` Domen Puncer 2007-08-18 10:06 ` Domen Puncer 2007-08-19 15:39 ` Matt Sealey 2007-08-20 8:31 ` Domen Puncer 2007-08-20 13:13 ` Domen Puncer 2007-08-20 19:02 ` Matt Sealey 2007-08-21 5:49 ` Domen Puncer 2007-09-02 7:41 ` [RFC PATCH v0.2] " Domen Puncer 2007-09-03 15:57 ` Grant Likely 2007-09-03 16:09 ` Jon Smirl 2007-09-03 16:41 ` Grant Likely 2007-09-15 12:14 ` Domen Puncer 2007-09-17 9:53 ` Sven Luther 2007-09-17 20:21 ` [PATCH] phy: export phy_mii_ioctl Domen Puncer 2007-09-17 22:08 ` Jon Smirl 2007-09-18 15:16 ` Domen Puncer 2007-09-18 19:17 ` Jon Smirl 2007-09-19 11:56 ` Domen Puncer 2007-09-19 18:44 ` Jon Smirl 2007-09-19 21:18 ` Jon Smirl 2007-09-18 19:29 ` Jon Smirl 2007-09-19 8:54 ` Pedro Luis D. L. 2007-09-19 10:37 ` Juergen Beisert 2007-09-19 11:38 ` Pedro Luis D. L. 2007-09-19 14:51 ` Juergen Beisert 2007-09-19 15:11 ` Pedro Luis D. L. 2007-09-19 13:56 ` Jon Smirl 2007-09-19 14:31 ` Pedro Luis D. L. 2007-09-19 8:54 ` Pedro Luis D. L. 2007-09-20 6:36 ` Jeff Garzik 2007-10-02 12:49 ` [RFC PATCH v0.2] net driver: mpc52xx fec Sascha Hauer 2007-10-02 14:32 ` Domen Puncer 2007-10-02 15:46 ` Robert Schwebel 2007-09-27 17:07 ` [RFC PATCH v0.1] " Juergen Beisert 2007-09-27 18:12 ` Jon Smirl 2007-09-27 18:43 ` Scott Wood 2007-09-28 9:12 ` Juergen Beisert 2007-09-28 15:40 ` Scott Wood 2007-10-08 8:48 ` Sascha Hauer 2007-10-08 9:01 ` Sascha Hauer 2007-10-08 16:46 ` Jon Smirl 2007-09-28 15:07 ` Juergen Beisert 2007-09-28 15:38 ` Jon Smirl 2007-10-01 8:35 ` Juergen Beisert 2007-10-01 16:24 ` Juergen Beisert
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).