* describes connections using the internal parallel port I/O, which
* is basically all of Port D.
*
- * Right now, I am very watseful with the buffers. I allocate memory
+ * Right now, I am very wasteful with the buffers. I allocate memory
* pages and then divide them into 2K frame buffers. This way I know I
* have buffers large enough to hold one frame within one buffer descriptor.
* Once I get this working, I will use 64 or 128 byte CPM buffers, which
* Much better multiple PHY support by Magnus Damm.
* Copyright (c) 2000 Ericsson Radio Systems AB.
*
- * Support for FEC controller of ColdFire/5272.
- * Copyrught (c) 2001-2002 Greg Ungerer (gerg@snapgear.com)
+ * Support for FEC controller of ColdFire processors.
+ * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com)
+ *
+ * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
+ * Copyright (c) 2004-2005 Macq Electronique SA.
*/
#include <linux/config.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
+#include <linux/bitops.h>
#include <asm/irq.h>
-#include <asm/bitops.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/pgtable.h>
-#ifdef CONFIG_M5272
+#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || \
+ defined(CONFIG_M5272) || defined(CONFIG_M528x) || \
+ defined(CONFIG_M520x)
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#include "fec.h"
#include "commproc.h"
#endif
-static int opened = 0;
-static int found = 0;
+#if defined(CONFIG_FEC2)
+#define FEC_MAX_PORTS 2
+#else
+#define FEC_MAX_PORTS 1
+#endif
/*
* Define the fixed address of the FEC hardware.
*/
-#ifdef CONFIG_M5272
-static volatile fec_t *fec_hwp = (volatile fec_t *) (MCF_MBAR + 0x840);
-static ushort my_enet_addr[] = { 0x00d0, 0xcf00, 0x0072 };
+static unsigned int fec_hw[] = {
+#if defined(CONFIG_M5272)
+ (MCF_MBAR + 0x840),
+#elif defined(CONFIG_M527x)
+ (MCF_MBAR + 0x1000),
+ (MCF_MBAR + 0x1800),
+#elif defined(CONFIG_M523x) || defined(CONFIG_M528x)
+ (MCF_MBAR + 0x1000),
+#elif defined(CONFIG_M520x)
+ (MCF_MBAR+0x30000),
#else
-static volatile fec_t *fec_hwp = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec)
-static ushort my_enet_addr[3];
-#endif /* CONFIG_M5272 */
+ &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec),
+#endif
+};
+
+static unsigned char fec_mac_default[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
/*
* Some hardware gets it MAC address out of local flash memory.
*/
#if defined(CONFIG_NETtel)
#define FEC_FLASHMAC 0xf0006006
-#elif defined(CONFIG_GILBARCONAP)
+#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES)
#define FEC_FLASHMAC 0xf0006000
#elif defined (CONFIG_MTD_KeyTechnology)
#define FEC_FLASHMAC 0xffe04000
+#elif defined(CONFIG_CANCam)
+#define FEC_FLASHMAC 0xf0020000
+#elif defined (CONFIG_M5272C3)
+#define FEC_FLASHMAC (0xffe04000 + 4)
+#elif defined(CONFIG_MOD5272)
+#define FEC_FLASHMAC 0xffc0406b
#else
#define FEC_FLASHMAC 0
#endif
-unsigned char *fec_flashmac = (unsigned char *) FEC_FLASHMAC;
-
/* Forward declarations of some structures to support different PHYs
*/
* We don't need to allocate pages for the transmitter. We just use
* the skbuffer directly.
*/
-#if 1
-#define FEC_ENET_RX_PAGES 4
-#define FEC_ENET_RX_FRSIZE 2048
-#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE)
-#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
-#define TX_RING_SIZE 8 /* Must be power of two */
-#define TX_RING_MOD_MASK 7 /* for this to work */
-#else
-#define FEC_ENET_RX_PAGES 16
+#define FEC_ENET_RX_PAGES 8
#define FEC_ENET_RX_FRSIZE 2048
#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE)
#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
+#define FEC_ENET_TX_FRSIZE 2048
+#define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE)
#define TX_RING_SIZE 16 /* Must be power of two */
#define TX_RING_MOD_MASK 15 /* for this to work */
+
+#if (((RX_RING_SIZE + TX_RING_SIZE) * 8) > PAGE_SIZE)
+#error "FEC: descriptor ring size contants too large"
#endif
/* Interrupt events/masks.
#define PKT_MINBUF_SIZE 64
#define PKT_MAXBLR_SIZE 1520
+
+/*
+ * The 5270/5271/5280/5282 RX control register also contains maximum frame
+ * size bits. Other FEC hardware does not, so we need to take that into
+ * account when setting it.
+ */
+#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
+ defined(CONFIG_M520x)
+#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16)
+#else
+#define OPT_FRAME_SIZE 0
+#endif
+
/* The FEC buffer descriptors track the ring buffers. The rx_bd_base and
* tx_bd_base always point to the base of the buffer descriptors. The
* cur_rx and cur_tx point to the currently available buffer.
* the buffer descriptor determines the actual condition.
*/
struct fec_enet_private {
+ /* Hardware registers of the FEC device */
+ volatile fec_t *hwp;
+
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ unsigned char *tx_bounce[TX_RING_SIZE];
struct sk_buff* tx_skbuff[TX_RING_SIZE];
ushort skb_cur;
ushort skb_dirty;
uint phy_id_done;
uint phy_status;
uint phy_speed;
- phy_info_t *phy;
+ phy_info_t const *phy;
struct work_struct phy_task;
uint sequence_done;
+ uint mii_phy_task_queued;
uint phy_addr;
+ int index;
+ int opened;
int link;
int old_link;
int full_duplex;
} mii_list_t;
#define NMII 20
-mii_list_t mii_cmds[NMII];
-mii_list_t *mii_free;
-mii_list_t *mii_head;
-mii_list_t *mii_tail;
+static mii_list_t mii_cmds[NMII];
+static mii_list_t *mii_free;
+static mii_list_t *mii_head;
+static mii_list_t *mii_tail;
static int mii_queue(struct net_device *dev, int request,
void (*func)(uint, struct net_device *));
bdp->cbd_bufaddr = __pa(skb->data);
bdp->cbd_datlen = skb->len;
+ /*
+ * On some FEC implementations data must be aligned on
+ * 4-byte boundaries. Use bounce buffers to copy data
+ * and get it aligned. Ugh.
+ */
+ if (bdp->cbd_bufaddr & 0x3) {
+ unsigned int index;
+ index = bdp - fep->tx_bd_base;
+ memcpy(fep->tx_bounce[index], (void *) bdp->cbd_bufaddr, bdp->cbd_datlen);
+ bdp->cbd_bufaddr = __pa(fep->tx_bounce[index]);
+ }
+
/* Save skb pointer.
*/
fep->tx_skbuff[fep->skb_cur] = skb;
}
}
#endif
- fec_restart(dev, 0);
+ fec_restart(dev, fep->full_duplex);
netif_wake_queue(dev);
}
*/
while ((int_events = fecp->fec_ievent) != 0) {
fecp->fec_ievent = int_events;
- if ((int_events & (FEC_ENET_HBERR | FEC_ENET_BABR |
- FEC_ENET_BABT | FEC_ENET_EBERR)) != 0) {
- printk("FEC ERROR %x\n", int_events);
- }
/* Handle receive event in its own function.
*/
printk("FEC ENET: rcv is not +last\n");
#endif
- if (!opened)
+ if (!fep->opened)
goto rx_processing_done;
/* Check for errors. */
uint mii_reg;
fep = netdev_priv(dev);
- ep = fec_hwp;
+ ep = fep->hwp;
mii_reg = ep->fec_mii_data;
if ((mip = mii_head) == NULL) {
}
else {
mii_head = mii_tail = mip;
- fec_hwp->fec_mii_data = regval;
+ fep->hwp->fec_mii_data = regval;
}
}
else {
{
struct fec_enet_private *fep = netdev_priv(dev);
volatile uint *s = &(fep->phy_status);
+ uint status;
- *s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
+ status = *s & ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
if (mii_reg & 0x0004)
- *s |= PHY_STAT_LINK;
+ status |= PHY_STAT_LINK;
if (mii_reg & 0x0010)
- *s |= PHY_STAT_FAULT;
+ status |= PHY_STAT_FAULT;
if (mii_reg & 0x0020)
- *s |= PHY_STAT_ANC;
+ status |= PHY_STAT_ANC;
+
+ *s = status;
}
static void mii_parse_cr(uint mii_reg, struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
volatile uint *s = &(fep->phy_status);
+ uint status;
- *s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP);
+ status = *s & ~(PHY_CONF_ANE | PHY_CONF_LOOP);
if (mii_reg & 0x1000)
- *s |= PHY_CONF_ANE;
+ status |= PHY_CONF_ANE;
if (mii_reg & 0x4000)
- *s |= PHY_CONF_LOOP;
+ status |= PHY_CONF_LOOP;
+ *s = status;
}
static void mii_parse_anar(uint mii_reg, struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
volatile uint *s = &(fep->phy_status);
+ uint status;
- *s &= ~(PHY_CONF_SPMASK);
+ status = *s & ~(PHY_CONF_SPMASK);
if (mii_reg & 0x0020)
- *s |= PHY_CONF_10HDX;
+ status |= PHY_CONF_10HDX;
if (mii_reg & 0x0040)
- *s |= PHY_CONF_10FDX;
+ status |= PHY_CONF_10FDX;
if (mii_reg & 0x0080)
- *s |= PHY_CONF_100HDX;
+ status |= PHY_CONF_100HDX;
if (mii_reg & 0x00100)
- *s |= PHY_CONF_100FDX;
+ status |= PHY_CONF_100FDX;
+ *s = status;
}
/* ------------------------------------------------------------------------- */
{
struct fec_enet_private *fep = netdev_priv(dev);
volatile uint *s = &(fep->phy_status);
+ uint status;
- *s &= ~(PHY_STAT_SPMASK);
-
+ status = *s & ~(PHY_STAT_SPMASK);
if (mii_reg & 0x0800) {
if (mii_reg & 0x1000)
- *s |= PHY_STAT_100FDX;
+ status |= PHY_STAT_100FDX;
else
- *s |= PHY_STAT_100HDX;
+ status |= PHY_STAT_100HDX;
} else {
if (mii_reg & 0x1000)
- *s |= PHY_STAT_10FDX;
+ status |= PHY_STAT_10FDX;
else
- *s |= PHY_STAT_10HDX;
+ status |= PHY_STAT_10HDX;
}
+ *s = status;
}
-static phy_info_t phy_info_lxt970 = {
- 0x07810000,
- "LXT970",
-
- (const phy_cmd_t []) { /* config */
+static phy_cmd_t const phy_cmd_lxt970_config[] = {
{ mk_mii_read(MII_REG_CR), mii_parse_cr },
{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* startup - enable interrupts */
+ };
+static phy_cmd_t const phy_cmd_lxt970_startup[] = { /* enable interrupts */
{ mk_mii_write(MII_LXT970_IER, 0x0002), NULL },
{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* ack_int */
+ };
+static phy_cmd_t const phy_cmd_lxt970_ack_int[] = {
/* read SR and ISR to acknowledge */
{ mk_mii_read(MII_REG_SR), mii_parse_sr },
{ mk_mii_read(MII_LXT970_ISR), NULL },
/* find out the current status */
{ mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr },
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* shutdown - disable interrupts */
+ };
+static phy_cmd_t const phy_cmd_lxt970_shutdown[] = { /* disable interrupts */
{ mk_mii_write(MII_LXT970_IER, 0x0000), NULL },
{ mk_mii_end, }
- },
+ };
+static phy_info_t const phy_info_lxt970 = {
+ .id = 0x07810000,
+ .name = "LXT970",
+ .config = phy_cmd_lxt970_config,
+ .startup = phy_cmd_lxt970_startup,
+ .ack_int = phy_cmd_lxt970_ack_int,
+ .shutdown = phy_cmd_lxt970_shutdown
};
/* ------------------------------------------------------------------------- */
{
struct fec_enet_private *fep = netdev_priv(dev);
volatile uint *s = &(fep->phy_status);
+ uint status;
- *s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
+ status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
if (mii_reg & 0x0400) {
fep->link = 1;
- *s |= PHY_STAT_LINK;
+ status |= PHY_STAT_LINK;
} else {
fep->link = 0;
}
if (mii_reg & 0x0080)
- *s |= PHY_STAT_ANC;
+ status |= PHY_STAT_ANC;
if (mii_reg & 0x4000) {
if (mii_reg & 0x0200)
- *s |= PHY_STAT_100FDX;
+ status |= PHY_STAT_100FDX;
else
- *s |= PHY_STAT_100HDX;
+ status |= PHY_STAT_100HDX;
} else {
if (mii_reg & 0x0200)
- *s |= PHY_STAT_10FDX;
+ status |= PHY_STAT_10FDX;
else
- *s |= PHY_STAT_10HDX;
+ status |= PHY_STAT_10HDX;
}
if (mii_reg & 0x0008)
- *s |= PHY_STAT_FAULT;
-}
+ status |= PHY_STAT_FAULT;
-static phy_info_t phy_info_lxt971 = {
- 0x0001378e,
- "LXT971",
+ *s = status;
+}
- (const phy_cmd_t []) { /* config */
- /* limit to 10MBit because my protorype board
+static phy_cmd_t const phy_cmd_lxt971_config[] = {
+ /* limit to 10MBit because my prototype board
* doesn't work with 100. */
{ mk_mii_read(MII_REG_CR), mii_parse_cr },
{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
{ mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* startup - enable interrupts */
+ };
+static phy_cmd_t const phy_cmd_lxt971_startup[] = { /* enable interrupts */
{ mk_mii_write(MII_LXT971_IER, 0x00f2), NULL },
{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
{ mk_mii_write(MII_LXT971_LCR, 0xd422), NULL }, /* LED config */
* read here to get a valid value in ack_int */
{ mk_mii_read(MII_REG_SR), mii_parse_sr },
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* ack_int */
+ };
+static phy_cmd_t const phy_cmd_lxt971_ack_int[] = {
+ /* acknowledge the int before reading status ! */
+ { mk_mii_read(MII_LXT971_ISR), NULL },
/* find out the current status */
{ mk_mii_read(MII_REG_SR), mii_parse_sr },
{ mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
- /* we only need to read ISR to acknowledge */
- { mk_mii_read(MII_LXT971_ISR), NULL },
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* shutdown - disable interrupts */
+ };
+static phy_cmd_t const phy_cmd_lxt971_shutdown[] = { /* disable interrupts */
{ mk_mii_write(MII_LXT971_IER, 0x0000), NULL },
{ mk_mii_end, }
- },
+ };
+static phy_info_t const phy_info_lxt971 = {
+ .id = 0x0001378e,
+ .name = "LXT971",
+ .config = phy_cmd_lxt971_config,
+ .startup = phy_cmd_lxt971_startup,
+ .ack_int = phy_cmd_lxt971_ack_int,
+ .shutdown = phy_cmd_lxt971_shutdown
};
/* ------------------------------------------------------------------------- */
{
struct fec_enet_private *fep = netdev_priv(dev);
volatile uint *s = &(fep->phy_status);
+ uint status;
- *s &= ~(PHY_STAT_SPMASK);
+ status = *s & ~(PHY_STAT_SPMASK);
switch((mii_reg >> 2) & 7) {
- case 1: *s |= PHY_STAT_10HDX; break;
- case 2: *s |= PHY_STAT_100HDX; break;
- case 5: *s |= PHY_STAT_10FDX; break;
- case 6: *s |= PHY_STAT_100FDX; break;
- }
+ case 1: status |= PHY_STAT_10HDX; break;
+ case 2: status |= PHY_STAT_100HDX; break;
+ case 5: status |= PHY_STAT_10FDX; break;
+ case 6: status |= PHY_STAT_100FDX; break;
}
-static phy_info_t phy_info_qs6612 = {
- 0x00181440,
- "QS6612",
-
- (const phy_cmd_t []) { /* config */
+ *s = status;
+}
+
+static phy_cmd_t const phy_cmd_qs6612_config[] = {
/* The PHY powers up isolated on the RPX,
* so send a command to allow operation.
*/
{ mk_mii_read(MII_REG_CR), mii_parse_cr },
{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* startup - enable interrupts */
+ };
+static phy_cmd_t const phy_cmd_qs6612_startup[] = { /* enable interrupts */
{ mk_mii_write(MII_QS6612_IMR, 0x003a), NULL },
{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* ack_int */
+ };
+static phy_cmd_t const phy_cmd_qs6612_ack_int[] = {
/* we need to read ISR, SR and ANER to acknowledge */
{ mk_mii_read(MII_QS6612_ISR), NULL },
{ mk_mii_read(MII_REG_SR), mii_parse_sr },
/* read pcr to get info */
{ mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr },
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* shutdown - disable interrupts */
+ };
+static phy_cmd_t const phy_cmd_qs6612_shutdown[] = { /* disable interrupts */
{ mk_mii_write(MII_QS6612_IMR, 0x0000), NULL },
{ mk_mii_end, }
- },
+ };
+static phy_info_t const phy_info_qs6612 = {
+ .id = 0x00181440,
+ .name = "QS6612",
+ .config = phy_cmd_qs6612_config,
+ .startup = phy_cmd_qs6612_startup,
+ .ack_int = phy_cmd_qs6612_ack_int,
+ .shutdown = phy_cmd_qs6612_shutdown
};
/* ------------------------------------------------------------------------- */
{
struct fec_enet_private *fep = netdev_priv(dev);
volatile uint *s = &(fep->phy_status);
+ uint status;
- *s &= ~(PHY_STAT_SPMASK | PHY_STAT_ANC);
+ status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_ANC);
if (mii_reg & 0x0080)
- *s |= PHY_STAT_ANC;
+ status |= PHY_STAT_ANC;
if (mii_reg & 0x0400)
- *s |= ((mii_reg & 0x0800) ? PHY_STAT_100FDX : PHY_STAT_100HDX);
+ status |= ((mii_reg & 0x0800) ? PHY_STAT_100FDX : PHY_STAT_100HDX);
else
- *s |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX);
+ status |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX);
+
+ *s = status;
}
-static phy_info_t phy_info_am79c874 = {
- 0x00022561,
- "AM79C874",
-
- (const phy_cmd_t []) { /* config */
- /* limit to 10MBit because my protorype board
- * doesn't work with 100. */
+static phy_cmd_t const phy_cmd_am79c874_config[] = {
{ mk_mii_read(MII_REG_CR), mii_parse_cr },
{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
{ mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* startup - enable interrupts */
+ };
+static phy_cmd_t const phy_cmd_am79c874_startup[] = { /* enable interrupts */
{ mk_mii_write(MII_AM79C874_ICSR, 0xff00), NULL },
{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
{ mk_mii_read(MII_REG_SR), mii_parse_sr },
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* ack_int */
+ };
+static phy_cmd_t const phy_cmd_am79c874_ack_int[] = {
/* find out the current status */
{ mk_mii_read(MII_REG_SR), mii_parse_sr },
{ mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
/* we only need to read ISR to acknowledge */
{ mk_mii_read(MII_AM79C874_ICSR), NULL },
{ mk_mii_end, }
- },
- (const phy_cmd_t []) { /* shutdown - disable interrupts */
+ };
+static phy_cmd_t const phy_cmd_am79c874_shutdown[] = { /* disable interrupts */
{ mk_mii_write(MII_AM79C874_ICSR, 0x0000), NULL },
{ mk_mii_end, }
+ };
+static phy_info_t const phy_info_am79c874 = {
+ .id = 0x00022561,
+ .name = "AM79C874",
+ .config = phy_cmd_am79c874_config,
+ .startup = phy_cmd_am79c874_startup,
+ .ack_int = phy_cmd_am79c874_ack_int,
+ .shutdown = phy_cmd_am79c874_shutdown
+};
+
+
+/* ------------------------------------------------------------------------- */
+/* Kendin KS8721BL phy */
+
+/* register definitions for the 8721 */
+
+#define MII_KS8721BL_RXERCR 21
+#define MII_KS8721BL_ICSR 22
+#define MII_KS8721BL_PHYCR 31
+
+static phy_cmd_t const phy_cmd_ks8721bl_config[] = {
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_end, }
+ };
+static phy_cmd_t const phy_cmd_ks8721bl_startup[] = { /* enable interrupts */
+ { mk_mii_write(MII_KS8721BL_ICSR, 0xff00), NULL },
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_end, }
+ };
+static phy_cmd_t const phy_cmd_ks8721bl_ack_int[] = {
+ /* find out the current status */
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ /* we only need to read ISR to acknowledge */
+ { mk_mii_read(MII_KS8721BL_ICSR), NULL },
+ { mk_mii_end, }
+ };
+static phy_cmd_t const phy_cmd_ks8721bl_shutdown[] = { /* disable interrupts */
+ { mk_mii_write(MII_KS8721BL_ICSR, 0x0000), NULL },
+ { mk_mii_end, }
+ };
+static phy_info_t const phy_info_ks8721bl = {
+ .id = 0x00022161,
+ .name = "KS8721BL",
+ .config = phy_cmd_ks8721bl_config,
+ .startup = phy_cmd_ks8721bl_startup,
+ .ack_int = phy_cmd_ks8721bl_ack_int,
+ .shutdown = phy_cmd_ks8721bl_shutdown
+};
+
+/* ------------------------------------------------------------------------- */
+/* register definitions for the DP83848 */
+
+#define MII_DP8384X_PHYSTST 16 /* PHY Status Register */
+
+static void mii_parse_dp8384x_sr2(uint mii_reg, struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+ volatile uint *s = &(fep->phy_status);
+
+ *s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
+
+ /* Link up */
+ if (mii_reg & 0x0001) {
+ fep->link = 1;
+ *s |= PHY_STAT_LINK;
+ } else
+ fep->link = 0;
+ /* Status of link */
+ if (mii_reg & 0x0010) /* Autonegotioation complete */
+ *s |= PHY_STAT_ANC;
+ if (mii_reg & 0x0002) { /* 10MBps? */
+ if (mii_reg & 0x0004) /* Full Duplex? */
+ *s |= PHY_STAT_10FDX;
+ else
+ *s |= PHY_STAT_10HDX;
+ } else { /* 100 Mbps? */
+ if (mii_reg & 0x0004) /* Full Duplex? */
+ *s |= PHY_STAT_100FDX;
+ else
+ *s |= PHY_STAT_100HDX;
+ }
+ if (mii_reg & 0x0008)
+ *s |= PHY_STAT_FAULT;
+}
+
+static phy_info_t phy_info_dp83848= {
+ 0x020005c9,
+ "DP83848",
+
+ (const phy_cmd_t []) { /* config */
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_read(MII_DP8384X_PHYSTST), mii_parse_dp8384x_sr2 },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup - enable interrupts */
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* ack_int - never happens, no interrupt */
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown */
+ { mk_mii_end, }
},
};
/* ------------------------------------------------------------------------- */
-static phy_info_t *phy_info[] = {
+static phy_info_t const * const phy_info[] = {
&phy_info_lxt970,
&phy_info_lxt971,
&phy_info_qs6612,
&phy_info_am79c874,
+ &phy_info_ks8721bl,
+ &phy_info_dp83848,
NULL
};
/* ------------------------------------------------------------------------- */
-static void
#ifdef CONFIG_RPXCLASSIC
+static void
mii_link_interrupt(void *dev_id);
#else
+static irqreturn_t
mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs);
#endif
-#ifdef CONFIG_M5272
+#if defined(CONFIG_M5272)
/*
* Code specific to Coldfire 5272 setup.
*/
-static void __inline__ fec_request_intrs(struct net_device *dev, volatile fec_t *fecp)
+static void __inline__ fec_request_intrs(struct net_device *dev)
{
volatile unsigned long *icrp;
+ static const struct idesc {
+ char *name;
+ unsigned short irq;
+ irqreturn_t (*handler)(int, void *, struct pt_regs *);
+ } *idp, id[] = {
+ { "fec(RX)", 86, fec_enet_interrupt },
+ { "fec(TX)", 87, fec_enet_interrupt },
+ { "fec(OTHER)", 88, fec_enet_interrupt },
+ { "fec(MII)", 66, mii_link_interrupt },
+ { NULL },
+ };
/* Setup interrupt handlers. */
- if (request_irq(86, fec_enet_interrupt, 0, "fec(RX)", dev) != 0)
- printk("FEC: Could not allocate FEC(RC) IRQ(86)!\n");
- if (request_irq(87, fec_enet_interrupt, 0, "fec(TX)", dev) != 0)
- printk("FEC: Could not allocate FEC(RC) IRQ(87)!\n");
- if (request_irq(88, fec_enet_interrupt, 0, "fec(OTHER)", dev) != 0)
- printk("FEC: Could not allocate FEC(OTHER) IRQ(88)!\n");
- if (request_irq(66, mii_link_interrupt, 0, "fec(MII)", dev) != 0)
- printk("FEC: Could not allocate MII IRQ(66)!\n");
+ for (idp = id; idp->name; idp++) {
+ if (request_irq(idp->irq, idp->handler, 0, idp->name, dev) != 0)
+ printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, idp->irq);
+ }
/* Unmask interrupt at ColdFire 5272 SIM */
icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR3);
static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
{
volatile fec_t *fecp;
- fecp = fec_hwp;
- fecp->fec_r_cntrl = 0x04;
+ fecp = fep->hwp;
+ fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
fecp->fec_x_cntrl = 0x00;
- /* Set MII speed to 2.5 MHz
- */
- fecp->fec_mii_speed = fep->phy_speed = 0x0e;
+ /*
+ * Set MII speed to 2.5 MHz
+ * See 5272 manual section 11.5.8: MSCR
+ */
+ fep->phy_speed = ((((MCF_CLK / 4) / (2500000 / 10)) + 5) / 10) * 2;
+ fecp->fec_mii_speed = fep->phy_speed;
fec_restart(dev, 0);
}
-static void __inline__ fec_get_mac(struct net_device *dev, struct fec_enet_private *fep)
+static void __inline__ fec_get_mac(struct net_device *dev)
{
+ struct fec_enet_private *fep = netdev_priv(dev);
volatile fec_t *fecp;
- unsigned char *eap, *iap, tmpaddr[6];
- int i;
+ unsigned char *iap, tmpaddr[ETH_ALEN];
- fecp = fec_hwp;
- eap = (unsigned char *) my_enet_addr;
+ fecp = fep->hwp;
- if (fec_flashmac) {
+ if (FEC_FLASHMAC) {
/*
* Get MAC address from FLASH.
* If it is all 1's or 0's, use the default.
*/
- iap = fec_flashmac;
+ iap = (unsigned char *)FEC_FLASHMAC;
if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
(iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
- iap = eap;
+ iap = fec_mac_default;
if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
(iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
- iap = eap;
+ iap = fec_mac_default;
} else {
*((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low;
*((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16);
iap = &tmpaddr[0];
}
- for (i=0; i<6; i++)
- dev->dev_addr[i] = *eap++ = *iap++;
+ memcpy(dev->dev_addr, iap, ETH_ALEN);
+
+ /* Adjust MAC if using default MAC address */
+ if (iap == fec_mac_default)
+ dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
}
static void __inline__ fec_enable_phy_intr(void)
/* ------------------------------------------------------------------------- */
+#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x)
+
+/*
+ * Code specific to Coldfire 5230/5231/5232/5234/5235,
+ * the 5270/5271/5274/5275 and 5280/5282 setups.
+ */
+static void __inline__ fec_request_intrs(struct net_device *dev)
+{
+ struct fec_enet_private *fep;
+ int b;
+ static const struct idesc {
+ char *name;
+ unsigned short irq;
+ } *idp, id[] = {
+ { "fec(TXF)", 23 },
+ { "fec(TXB)", 24 },
+ { "fec(TXFIFO)", 25 },
+ { "fec(TXCR)", 26 },
+ { "fec(RXF)", 27 },
+ { "fec(RXB)", 28 },
+ { "fec(MII)", 29 },
+ { "fec(LC)", 30 },
+ { "fec(HBERR)", 31 },
+ { "fec(GRA)", 32 },
+ { "fec(EBERR)", 33 },
+ { "fec(BABT)", 34 },
+ { "fec(BABR)", 35 },
+ { NULL },
+ };
+
+ fep = netdev_priv(dev);
+ b = (fep->index) ? 128 : 64;
+
+ /* Setup interrupt handlers. */
+ for (idp = id; idp->name; idp++) {
+ if (request_irq(b+idp->irq, fec_enet_interrupt, 0, idp->name, dev) != 0)
+ printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, b+idp->irq);
+ }
+
+ /* Unmask interrupts at ColdFire 5280/5282 interrupt controller */
+ {
+ volatile unsigned char *icrp;
+ volatile unsigned long *imrp;
+ int i;
+
+ b = (fep->index) ? MCFICM_INTC1 : MCFICM_INTC0;
+ icrp = (volatile unsigned char *) (MCF_IPSBAR + b +
+ MCFINTC_ICR0);
+ for (i = 23; (i < 36); i++)
+ icrp[i] = 0x23;
+
+ imrp = (volatile unsigned long *) (MCF_IPSBAR + b +
+ MCFINTC_IMRH);
+ *imrp &= ~0x0000000f;
+ imrp = (volatile unsigned long *) (MCF_IPSBAR + b +
+ MCFINTC_IMRL);
+ *imrp &= ~0xff800001;
+ }
+
+#if defined(CONFIG_M528x)
+ /* Set up gpio outputs for MII lines */
+ {
+ volatile u16 *gpio_paspar;
+ volatile u8 *gpio_pehlpar;
+
+ gpio_paspar = (volatile u16 *) (MCF_IPSBAR + 0x100056);
+ gpio_pehlpar = (volatile u16 *) (MCF_IPSBAR + 0x100058);
+ *gpio_paspar |= 0x0f00;
+ *gpio_pehlpar = 0xc0;
+ }
+#endif
+}
+
+static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
+{
+ volatile fec_t *fecp;
+
+ fecp = fep->hwp;
+ fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
+ fecp->fec_x_cntrl = 0x00;
+
+ /*
+ * Set MII speed to 2.5 MHz
+ * See 5282 manual section 17.5.4.7: MSCR
+ */
+ fep->phy_speed = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2;
+ fecp->fec_mii_speed = fep->phy_speed;
+
+ fec_restart(dev, 0);
+}
+
+static void __inline__ fec_get_mac(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ volatile fec_t *fecp;
+ unsigned char *iap, tmpaddr[ETH_ALEN];
+
+ fecp = fep->hwp;
+
+ if (FEC_FLASHMAC) {
+ /*
+ * Get MAC address from FLASH.
+ * If it is all 1's or 0's, use the default.
+ */
+ iap = FEC_FLASHMAC;
+ if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
+ (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
+ iap = fec_mac_default;
+ if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
+ (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
+ iap = fec_mac_default;
+ } else {
+ *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low;
+ *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16);
+ iap = &tmpaddr[0];
+ }
+
+ memcpy(dev->dev_addr, iap, ETH_ALEN);
+
+ /* Adjust MAC if using default MAC address */
+ if (iap == fec_mac_default)
+ dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
+}
+
+static void __inline__ fec_enable_phy_intr(void)
+{
+}
+
+static void __inline__ fec_disable_phy_intr(void)
+{
+}
+
+static void __inline__ fec_phy_ack_intr(void)
+{
+}
+
+static void __inline__ fec_localhw_setup(void)
+{
+}
+
+/*
+ * Do not need to make region uncached on 5272.
+ */
+static void __inline__ fec_uncache(unsigned long addr)
+{
+}
+
+/* ------------------------------------------------------------------------- */
+
+#elif defined(CONFIG_M520x)
+
+/*
+ * Code specific to Coldfire 520x
+ */
+static void __inline__ fec_request_intrs(struct net_device *dev)
+{
+ struct fec_enet_private *fep;
+ int b;
+ static const struct idesc {
+ char *name;
+ unsigned short irq;
+ } *idp, id[] = {
+ { "fec(TXF)", 23 },
+ { "fec(TXB)", 24 },
+ { "fec(TXFIFO)", 25 },
+ { "fec(TXCR)", 26 },
+ { "fec(RXF)", 27 },
+ { "fec(RXB)", 28 },
+ { "fec(MII)", 29 },
+ { "fec(LC)", 30 },
+ { "fec(HBERR)", 31 },
+ { "fec(GRA)", 32 },
+ { "fec(EBERR)", 33 },
+ { "fec(BABT)", 34 },
+ { "fec(BABR)", 35 },
+ { NULL },
+ };
+
+ fep = netdev_priv(dev);
+ b = 64 + 13;
+
+ /* Setup interrupt handlers. */
+ for (idp = id; idp->name; idp++) {
+ if (request_irq(b+idp->irq,fec_enet_interrupt,0,idp->name,dev)!=0)
+ printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, b+idp->irq);
+ }
+
+ /* Unmask interrupts at ColdFire interrupt controller */
+ {
+ volatile unsigned char *icrp;
+ volatile unsigned long *imrp;
+
+ icrp = (volatile unsigned char *) (MCF_IPSBAR + MCFICM_INTC0 +
+ MCFINTC_ICR0);
+ for (b = 36; (b < 49); b++)
+ icrp[b] = 0x04;
+ imrp = (volatile unsigned long *) (MCF_IPSBAR + MCFICM_INTC0 +
+ MCFINTC_IMRH);
+ *imrp &= ~0x0001FFF0;
+ }
+ *(volatile unsigned char *)(MCF_IPSBAR + MCF_GPIO_PAR_FEC) |= 0xf0;
+ *(volatile unsigned char *)(MCF_IPSBAR + MCF_GPIO_PAR_FECI2C) |= 0x0f;
+}
+
+static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
+{
+ volatile fec_t *fecp;
+
+ fecp = fep->hwp;
+ fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
+ fecp->fec_x_cntrl = 0x00;
+
+ /*
+ * Set MII speed to 2.5 MHz
+ * See 5282 manual section 17.5.4.7: MSCR
+ */
+ fep->phy_speed = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2;
+ fecp->fec_mii_speed = fep->phy_speed;
+
+ fec_restart(dev, 0);
+}
+
+static void __inline__ fec_get_mac(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ volatile fec_t *fecp;
+ unsigned char *iap, tmpaddr[ETH_ALEN];
+
+ fecp = fep->hwp;
+
+ if (FEC_FLASHMAC) {
+ /*
+ * Get MAC address from FLASH.
+ * If it is all 1's or 0's, use the default.
+ */
+ iap = FEC_FLASHMAC;
+ if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
+ (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
+ iap = fec_mac_default;
+ if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
+ (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
+ iap = fec_mac_default;
+ } else {
+ *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low;
+ *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16);
+ iap = &tmpaddr[0];
+ }
+
+ memcpy(dev->dev_addr, iap, ETH_ALEN);
+
+ /* Adjust MAC if using default MAC address */
+ if (iap == fec_mac_default)
+ dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
+}
+
+static void __inline__ fec_enable_phy_intr(void)
+{
+}
+
+static void __inline__ fec_disable_phy_intr(void)
+{
+}
+
+static void __inline__ fec_phy_ack_intr(void)
+{
+}
+
+static void __inline__ fec_localhw_setup(void)
+{
+}
+
+static void __inline__ fec_uncache(unsigned long addr)
+{
+}
+
+/* ------------------------------------------------------------------------- */
+
#else
/*
- * Code sepcific to the MPC860T setup.
+ * Code specific to the MPC860T setup.
*/
-static void __inline__ fec_request_intrs(struct net_device *dev, volatile fec_t *fecp)
+static void __inline__ fec_request_intrs(struct net_device *dev)
{
volatile immap_t *immap;
#endif
}
-static void __inline__ fec_get_mac(struct net_device *dev, struct fec_enet_private *fep)
+static void __inline__ fec_get_mac(struct net_device *dev)
{
- unsigned char *eap, *iap, tmpaddr[6];
bd_t *bd;
- int i;
- eap = (unsigned char *)my_enet_addr;
- iap = bd->bi_enetaddr;
bd = (bd_t *)__res;
+ memcpy(dev->dev_addr, bd->bi_enetaddr, ETH_ALEN);
#ifdef CONFIG_RPXCLASSIC
/* The Embedded Planet boards have only one MAC address in
* the address bits above something that would have (up to
* now) been allocated.
*/
- for (i=0; i<6; i++)
- tmpaddr[i] = *iap++;
- tmpaddr[3] |= 0x80;
- iap = tmpaddr;
+ dev->dev_adrd[3] |= 0x80;
#endif
-
- for (i=0; i<6; i++)
- dev->dev_addr[i] = *eap++ = *iap++;
}
static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
volatile immap_t *immap;
volatile fec_t *fecp;
- fecp = fec_hwp;
+ fecp = fep->hwp;
immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */
/* Configure all of port D for MII.
static void __inline__ fec_enable_phy_intr(void)
{
volatile fec_t *fecp;
- fecp = fec_hwp;
+
+ fecp = fep->hwp;
/* Enable MII command finished interrupt
*/
static void __inline__ fec_localhw_setup(void)
{
volatile fec_t *fecp;
- fecp = fec_hwp;
+ fecp = fep->hwp;
fecp->fec_r_hash = PKT_MAXBUF_SIZE;
/* Enable big endian and don't care about SDMA FC.
*/
static void mii_display_config(struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
- volatile uint *s = &(fep->phy_status);
+ uint status = fep->phy_status;
+ /*
+ ** When we get here, phy_task is already removed from
+ ** the workqueue. It is thus safe to allow to reuse it.
+ */
+ fep->mii_phy_task_queued = 0;
printk("%s: config: auto-negotiation ", dev->name);
- if (*s & PHY_CONF_ANE)
+ if (status & PHY_CONF_ANE)
printk("on");
else
printk("off");
- if (*s & PHY_CONF_100FDX)
+ if (status & PHY_CONF_100FDX)
printk(", 100FDX");
- if (*s & PHY_CONF_100HDX)
+ if (status & PHY_CONF_100HDX)
printk(", 100HDX");
- if (*s & PHY_CONF_10FDX)
+ if (status & PHY_CONF_10FDX)
printk(", 10FDX");
- if (*s & PHY_CONF_10HDX)
+ if (status & PHY_CONF_10HDX)
printk(", 10HDX");
- if (!(*s & PHY_CONF_SPMASK))
+ if (!(status & PHY_CONF_SPMASK))
printk(", No speed/duplex selected?");
- if (*s & PHY_CONF_LOOP)
+ if (status & PHY_CONF_LOOP)
printk(", loopback enabled");
printk(".\n");
struct fec_enet_private *fep = netdev_priv(dev);
int duplex;
+ /*
+ ** When we get here, phy_task is already removed from
+ ** the workqueue. It is thus safe to allow to reuse it.
+ */
+ fep->mii_phy_task_queued = 0;
fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;
mii_display_status(dev);
fep->old_link = fep->link;
}
+/* mii_queue_relink is called in interrupt context from mii_link_interrupt */
static void mii_queue_relink(uint mii_reg, struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
+ /*
+ ** We cannot queue phy_task twice in the workqueue. It
+ ** would cause an endless loop in the workqueue.
+ ** Fortunately, if the last mii_relink entry has not yet been
+ ** executed now, it will do the job for the current interrupt,
+ ** which is just what we want.
+ */
+ if (fep->mii_phy_task_queued)
+ return;
+
+ fep->mii_phy_task_queued = 1;
INIT_WORK(&fep->phy_task, (void*)mii_relink, dev);
schedule_work(&fep->phy_task);
}
+/* mii_queue_config is called in interrupt context from fec_enet_mii */
static void mii_queue_config(uint mii_reg, struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
+ if (fep->mii_phy_task_queued)
+ return;
+
+ fep->mii_phy_task_queued = 1;
INIT_WORK(&fep->phy_task, (void*)mii_display_config, dev);
schedule_work(&fep->phy_task);
}
-
-
-phy_cmd_t phy_cmd_relink[] = { { mk_mii_read(MII_REG_CR), mii_queue_relink },
- { mk_mii_end, } };
-phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config },
- { mk_mii_end, } };
-
-
+phy_cmd_t const phy_cmd_relink[] = {
+ { mk_mii_read(MII_REG_CR), mii_queue_relink },
+ { mk_mii_end, }
+ };
+phy_cmd_t const phy_cmd_config[] = {
+ { mk_mii_read(MII_REG_CR), mii_queue_config },
+ { mk_mii_end, }
+ };
/* Read remainder of PHY ID.
*/
mii_discover_phy3(uint mii_reg, struct net_device *dev)
{
struct fec_enet_private *fep;
- int i;
+ int i;
fep = netdev_priv(dev);
fep->phy_id |= (mii_reg & 0xffff);
uint phytype;
fep = netdev_priv(dev);
- fecp = fec_hwp;
+ fecp = fep->hwp;
if (fep->phy_addr < 32) {
if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) {
/* This interrupt occurs when the PHY detects a link change.
*/
-static void
#ifdef CONFIG_RPXCLASSIC
+static void
mii_link_interrupt(void *dev_id)
#else
+static irqreturn_t
mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs)
#endif
{
mii_do_cmd(dev, fep->phy->ack_int);
mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */
+ return IRQ_HANDLED;
}
static int
/* I should reset the ring buffers here, but I don't yet know
* a simple way to do that.
*/
-
fec_set_mac_address(dev);
fep->sequence_done = 0;
schedule();
mii_do_cmd(dev, fep->phy->startup);
+
+ /* Set the initial link state to true. A lot of hardware
+ * based on this device does not implement a PHY interrupt,
+ * so we are never notified of link change.
+ */
+ fep->link = 1;
} else {
fep->link = 1; /* lets just try it and see */
/* no phy, go full duplex, it's most likely a hub chip */
}
netif_start_queue(dev);
- opened = 1;
+ fep->opened = 1;
return 0; /* Success */
}
static int
fec_enet_close(struct net_device *dev)
{
+ struct fec_enet_private *fep = netdev_priv(dev);
+
/* Don't know what to do yet.
*/
- opened = 0;
+ fep->opened = 0;
netif_stop_queue(dev);
fec_stop(dev);
unsigned char hash;
fep = netdev_priv(dev);
- ep = fec_hwp;
+ ep = fep->hwp;
if (dev->flags&IFF_PROMISC) {
/* Log any net taps. */
static void
fec_set_mac_address(struct net_device *dev)
{
- int i;
volatile fec_t *fecp;
- fecp = fec_hwp;
-
- /* Set our copy of the Ethernet address */
- for (i = 0; i < (ETH_ALEN / 2); i++)
- my_enet_addr[i] = (dev->dev_addr[i*2] << 8) | dev->dev_addr[i*2 + 1];
+ fecp = ((struct fec_enet_private *)netdev_priv(dev))->hwp;
/* Set station address. */
- fecp->fec_addr_low = (my_enet_addr[0] << 16) | my_enet_addr[1];
- fecp->fec_addr_high = my_enet_addr[2] << 16;
+ fecp->fec_addr_low = dev->dev_addr[3] | (dev->dev_addr[2] << 8) |
+ (dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24);
+ fecp->fec_addr_high = (dev->dev_addr[5] << 16) |
+ (dev->dev_addr[4] << 24);
+
}
/* Initialize the FEC Ethernet on 860T (or ColdFire 5272).
cbd_t *cbd_base;
volatile fec_t *fecp;
int i, j;
+ static int index = 0;
/* Only allow us to be probed once. */
- if (found)
- return(-ENXIO);
+ if (index >= FEC_MAX_PORTS)
+ return -ENXIO;
+
+ /* Allocate memory for buffer descriptors.
+ */
+ mem_addr = __get_free_page(GFP_KERNEL);
+ if (mem_addr == 0) {
+ printk("FEC: allocate descriptor memory failed?\n");
+ return -ENOMEM;
+ }
/* Create an Ethernet device instance.
*/
- fecp = fec_hwp;
+ fecp = (volatile fec_t *) fec_hw[index];
+
+ fep->index = index;
+ fep->hwp = fecp;
/* Whack a reset. We should wait for this.
*/
fecp->fec_ecntrl = 1;
udelay(10);
- /* Clear and enable interrupts */
- fecp->fec_ievent = 0xffc0;
- fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
- FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
- fecp->fec_hash_table_high = 0;
- fecp->fec_hash_table_low = 0;
- fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
- fecp->fec_ecntrl = 2;
- fecp->fec_r_des_active = 0x01000000;
-
/* Set the Ethernet address. If using multiple Enets on the 8xx,
* this needs some work to get unique addresses.
*
* This is our default MAC address unless the user changes
* it via eth_mac_addr (our dev->set_mac_addr handler).
*/
- fec_get_mac(dev, fep);
+ fec_get_mac(dev);
- /* Allocate memory for buffer descriptors.
- */
- if (((RX_RING_SIZE + TX_RING_SIZE) * sizeof(cbd_t)) > PAGE_SIZE) {
- printk("FEC init error. Need more space.\n");
- printk("FEC initialization failed.\n");
- return 1;
- }
- mem_addr = __get_free_page(GFP_KERNEL);
cbd_base = (cbd_t *)mem_addr;
/* XXX: missing check for allocation failure */
/* ...and the same for transmmit.
*/
bdp = fep->tx_bd_base;
- for (i=0; i<TX_RING_SIZE; i++) {
+ for (i=0, j=FEC_ENET_TX_FRPPG; i<TX_RING_SIZE; i++) {
+ if (j >= FEC_ENET_TX_FRPPG) {
+ mem_addr = __get_free_page(GFP_KERNEL);
+ j = 1;
+ } else {
+ mem_addr += FEC_ENET_TX_FRSIZE;
+ j++;
+ }
+ fep->tx_bounce[i] = (unsigned char *) mem_addr;
/* Initialize the BD for every fragment in the page.
*/
/* Install our interrupt handlers. This varies depending on
* the architecture.
*/
- fec_request_intrs(dev, fecp);
+ fec_request_intrs(dev);
+
+ /* Clear and enable interrupts */
+ fecp->fec_ievent = 0xffc00000;
+ fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
+ FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
+ fecp->fec_hash_table_high = 0;
+ fecp->fec_hash_table_low = 0;
+ fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
+ fecp->fec_ecntrl = 2;
+ fecp->fec_r_des_active = 0x01000000;
dev->base_addr = (unsigned long)fecp;
/* setup MII interface */
fec_set_mii(dev, fep);
- printk("%s: FEC ENET Version 0.2, ", dev->name);
- for (i=0; i<5; i++)
- printk("%02x:", dev->dev_addr[i]);
- printk("%02x\n", dev->dev_addr[5]);
-
/* Queue up command to detect the PHY and initialize the
* remainder of the interface.
*/
fep->phy_addr = 0;
mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
- found++;
+ index++;
return 0;
}
fec_restart(struct net_device *dev, int duplex)
{
struct fec_enet_private *fep;
- int i;
- unsigned char *eap;
volatile cbd_t *bdp;
volatile fec_t *fecp;
-
- fecp = fec_hwp;
+ int i;
fep = netdev_priv(dev);
+ fecp = fep->hwp;
/* Whack a reset. We should wait for this.
*/
/* Clear any outstanding interrupt.
*/
- fecp->fec_ievent = 0xffc0;
+ fecp->fec_ievent = 0xffc00000;
fec_enable_phy_intr();
/* Set station address.
*/
- fecp->fec_addr_low = (my_enet_addr[0] << 16) | my_enet_addr[1];
- fecp->fec_addr_high = (my_enet_addr[2] << 16);
-
- eap = (unsigned char *)&my_enet_addr[0];
- for (i=0; i<6; i++)
- dev->dev_addr[i] = *eap++;
+ fec_set_mac_address(dev);
/* Reset all multicast.
*/
/* Enable MII mode.
*/
if (duplex) {
- fecp->fec_r_cntrl = 0x04; /* MII enable */
- fecp->fec_x_cntrl = 0x04; /* FD enable */
+ fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;/* MII enable */
+ fecp->fec_x_cntrl = 0x04; /* FD enable */
}
else {
- fecp->fec_r_cntrl = 0x06; /* MII enable|No Rcv on Xmit */
+ /* MII enable|No Rcv on Xmit */
+ fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x06;
fecp->fec_x_cntrl = 0x00;
}
fep->full_duplex = duplex;
volatile fec_t *fecp;
struct fec_enet_private *fep;
- fecp = fec_hwp;
fep = netdev_priv(dev);
+ fecp = fep->hwp;
fecp->fec_x_cntrl = 0x01; /* Graceful transmit stop */
- while(!(fecp->fec_ievent & 0x10000000));
+ while(!(fecp->fec_ievent & FEC_ENET_GRA));
/* Whack a reset. We should wait for this.
*/
fecp->fec_mii_speed = fep->phy_speed;
}
-static struct net_device *fec_dev;
-
static int __init fec_enet_module_init(void)
{
struct net_device *dev;
- int err;
-
- dev = alloc_etherdev(sizeof(struct fec_enet_private));
- if (!dev)
- return -ENOMEM;
- err = fec_enet_init(dev);
- if (err) {
- free_netdev(dev);
- return err;
- }
+ int i, j, err;
+
+ printk("FEC ENET Version 0.2\n");
+
+ for (i = 0; (i < FEC_MAX_PORTS); i++) {
+ dev = alloc_etherdev(sizeof(struct fec_enet_private));
+ if (!dev)
+ return -ENOMEM;
+ err = fec_enet_init(dev);
+ if (err) {
+ free_netdev(dev);
+ continue;
+ }
+ if (register_netdev(dev) != 0) {
+ /* XXX: missing cleanup here */
+ free_netdev(dev);
+ return -EIO;
+ }
- if (register_netdev(dev) != 0) {
- /* XXX: missing cleanup here */
- free_netdev(dev);
- return -EIO;
+ printk("%s: ethernet ", dev->name);
+ for (j = 0; (j < 5); j++)
+ printk("%02x:", dev->dev_addr[j]);
+ printk("%02x\n", dev->dev_addr[5]);
}
- fec_dev = dev;
- return(0);
+ return 0;
}
module_init(fec_enet_module_init);