Skip to content
Snippets Groups Projects
fec.c 62.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • }
    
    static phy_cmd_t const phy_cmd_qs6612_config[] = {
    
    		/* The PHY powers up isolated on the RPX,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		 * so send a command to allow operation.
    		 */
    		{ mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL },
    
    		/* parse cr and anar to get some info */
    		{ 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_qs6612_startup[] = {  /* enable interrupts */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{ mk_mii_write(MII_QS6612_IMR, 0x003a), NULL },
    		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
    		{ mk_mii_end, }
    
    	};
    static phy_cmd_t const phy_cmd_qs6612_ack_int[] = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		/* 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 },
    		{ mk_mii_read(MII_REG_ANER), NULL },
    
    		/* read pcr to get info */
    		{ mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr },
    		{ mk_mii_end, }
    
    	};
    static phy_cmd_t const phy_cmd_qs6612_shutdown[] = { /* disable interrupts */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{ 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
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    };
    
    /* ------------------------------------------------------------------------- */
    /* AMD AM79C874 phy                                                          */
    
    /* register definitions for the 874 */
    
    #define MII_AM79C874_MFR       16  /* Miscellaneous Feature Register */
    #define MII_AM79C874_ICSR      17  /* Interrupt/Status Register      */
    #define MII_AM79C874_DR        18  /* Diagnostic Register            */
    #define MII_AM79C874_PMLR      19  /* Power and Loopback Register    */
    #define MII_AM79C874_MCR       21  /* ModeControl Register           */
    #define MII_AM79C874_DC        23  /* Disconnect Counter             */
    #define MII_AM79C874_REC       24  /* Recieve Error Counter          */
    
    static void mii_parse_am79c874_dr(uint mii_reg, struct net_device *dev)
    {
    	struct fec_enet_private *fep = netdev_priv(dev);
    	volatile uint *s = &(fep->phy_status);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_ANC);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	if (mii_reg & 0x0080)
    
    		status |= PHY_STAT_ANC;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (mii_reg & 0x0400)
    
    		status |= ((mii_reg & 0x0800) ? PHY_STAT_100FDX : PHY_STAT_100HDX);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	else
    
    		status |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX);
    
    	*s = status;
    
    static phy_cmd_t const phy_cmd_am79c874_config[] = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{ 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, }
    
    	};
    static phy_cmd_t const phy_cmd_am79c874_startup[] = {  /* enable interrupts */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{ 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 },
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{ mk_mii_end, }
    
    	};
    static phy_cmd_t const phy_cmd_am79c874_ack_int[] = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		/* 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, }
    
    	};
    static phy_cmd_t const phy_cmd_am79c874_shutdown[] = { /* disable interrupts */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{ 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
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /* ------------------------------------------------------------------------- */
    /* 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[] = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{ 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 */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{ 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 },
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{ mk_mii_end, }
    
    	};
    static phy_cmd_t const phy_cmd_ks8721bl_ack_int[] = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		/* 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 */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{ 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 = netdev_priv(dev);
    
    	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, }
    	},
    };
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /* ------------------------------------------------------------------------- */
    
    
    static phy_info_t const * const phy_info[] = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	&phy_info_lxt970,
    	&phy_info_lxt971,
    	&phy_info_qs6612,
    	&phy_info_am79c874,
    	&phy_info_ks8721bl,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	NULL
    };
    
    /* ------------------------------------------------------------------------- */
    
    #ifdef HAVE_mii_link_interrupt
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static irqreturn_t
    
    mii_link_interrupt(int irq, void * dev_id);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #endif
    
    #if defined(CONFIG_M5272)
    /*
     *	Code specific to Coldfire 5272 setup.
     */
    static void __inline__ fec_request_intrs(struct net_device *dev)
    {
    	volatile unsigned long *icrp;
    
    	static const struct idesc {
    		char *name;
    		unsigned short irq;
    
    	} *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 },
    	};
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* Setup interrupt handlers. */
    
    	for (idp = id; idp->name; idp++) {
    
    		if (request_irq(idp->irq, idp->handler, IRQF_DISABLED, idp->name, dev) != 0)
    
    			printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, idp->irq);
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* Unmask interrupt at ColdFire 5272 SIM */
    	icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR3);
    	*icrp = 0x00000ddd;
    	icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);
    
    	*icrp = 0x0d000000;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    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 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 = netdev_priv(dev);
    	volatile fec_t *fecp;
    
    	unsigned char *iap, tmpaddr[ETH_ALEN];
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	fecp = fep->hwp;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		/*
    		 * Get MAC address from FLASH.
    		 * If it is all 1's or 0's, use the default.
    		 */
    
    		iap = (unsigned char *)FEC_FLASHMAC;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		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);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* 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;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static void __inline__ fec_disable_phy_intr(void)
    {
    	volatile unsigned long *icrp;
    	icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);
    
    	*icrp = 0x08000000;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static void __inline__ fec_phy_ack_intr(void)
    {
    	volatile unsigned long *icrp;
    	/* Acknowledge the interrupt */
    	icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);
    
    	*icrp = 0x0d000000;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /* ------------------------------------------------------------------------- */
    
    
    #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.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    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(RXF)", 27 },
    		{ "fec(MII)", 29 },
    		{ NULL },
    	};
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	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, IRQF_DISABLED, idp->name, dev) != 0)
    
    			printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, b+idp->irq);
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* Unmask interrupts at ColdFire 5280/5282 interrupt controller */
    	{
    		volatile unsigned char  *icrp;
    		volatile unsigned long  *imrp;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    		b = (fep->index) ? MCFICM_INTC1 : MCFICM_INTC0;
    		icrp = (volatile unsigned char *) (MCF_IPSBAR + b +
    			MCFINTC_ICR0);
    
    		for (i = 23, ilip = 0x28; (i < 36); i++)
    			icrp[i] = ilip--;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    		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;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    #endif
    
    
    #if defined(CONFIG_M527x)
    	/* Set up gpio outputs for MII lines */
    	{
    		volatile u8 *gpio_par_fec;
    		volatile u16 *gpio_par_feci2c;
    
    		gpio_par_feci2c = (volatile u16 *)(MCF_IPSBAR + 0x100082);
    		/* Set up gpio outputs for FEC0 MII lines */
    		gpio_par_fec = (volatile u8 *)(MCF_IPSBAR + 0x100078);
    
    		*gpio_par_feci2c |= 0x0f00;
    		*gpio_par_fec |= 0xc0;
    
    #if defined(CONFIG_FEC2)
    		/* Set up gpio outputs for FEC1 MII lines */
    		gpio_par_fec = (volatile u8 *)(MCF_IPSBAR + 0x100079);
    
    		*gpio_par_feci2c |= 0x00a0;
    		*gpio_par_fec |= 0xc0;
    #endif /* CONFIG_FEC2 */
    	}
    #endif /* CONFIG_M527x */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    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];
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	fecp = fep->hwp;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		/*
    		 * Get MAC address from FLASH.
    		 * If it is all 1's or 0's, use the default.
    		 */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		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);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* 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;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static void __inline__ fec_disable_phy_intr(void)
    {
    }
    
    static void __inline__ fec_phy_ack_intr(void)
    {
    }
    
    /* ------------------------------------------------------------------------- */
    
    
    #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(RXF)", 27 },
    		{ "fec(MII)", 29 },
    		{ 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, IRQF_DISABLED, 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_disable_phy_intr(void)
    {
    }
    
    static void __inline__ fec_phy_ack_intr(void)
    {
    }
    
    /* ------------------------------------------------------------------------- */
    
    
    #elif defined(CONFIG_M532x)
    /*
     * Code specific for M532x
     */
    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)", 36 },
    	    { "fec(RXF)", 40 },
    	    { "fec(MII)", 42 },
    	    { 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, IRQF_DISABLED, idp->name,dev) != 0)
    
    			printk("FEC: Could not allocate %s IRQ(%d)!\n",
    
    				idp->name, b+idp->irq);
    	}
    
    	/* Unmask interrupts */
    	MCF_INTC0_ICR36 = 0x2;
    	MCF_INTC0_ICR37 = 0x2;
    	MCF_INTC0_ICR38 = 0x2;
    	MCF_INTC0_ICR39 = 0x2;
    	MCF_INTC0_ICR40 = 0x2;
    	MCF_INTC0_ICR41 = 0x2;
    	MCF_INTC0_ICR42 = 0x2;
    	MCF_INTC0_ICR43 = 0x2;
    	MCF_INTC0_ICR44 = 0x2;
    	MCF_INTC0_ICR45 = 0x2;
    	MCF_INTC0_ICR46 = 0x2;
    	MCF_INTC0_ICR47 = 0x2;
    	MCF_INTC0_ICR48 = 0x2;
    
    	MCF_INTC0_IMRH &= ~(
    		MCF_INTC_IMRH_INT_MASK36 |
    		MCF_INTC_IMRH_INT_MASK37 |
    		MCF_INTC_IMRH_INT_MASK38 |
    		MCF_INTC_IMRH_INT_MASK39 |
    		MCF_INTC_IMRH_INT_MASK40 |
    		MCF_INTC_IMRH_INT_MASK41 |
    		MCF_INTC_IMRH_INT_MASK42 |
    		MCF_INTC_IMRH_INT_MASK43 |
    		MCF_INTC_IMRH_INT_MASK44 |
    		MCF_INTC_IMRH_INT_MASK45 |
    		MCF_INTC_IMRH_INT_MASK46 |
    		MCF_INTC_IMRH_INT_MASK47 |
    		MCF_INTC_IMRH_INT_MASK48 );
    
    	/* Set up gpio outputs for MII lines */
    	MCF_GPIO_PAR_FECI2C |= (0 |
    		MCF_GPIO_PAR_FECI2C_PAR_MDC_EMDC |
    		MCF_GPIO_PAR_FECI2C_PAR_MDIO_EMDIO);
    	MCF_GPIO_PAR_FEC = (0 |
    		MCF_GPIO_PAR_FEC_PAR_FEC_7W_FEC |
    		MCF_GPIO_PAR_FEC_PAR_FEC_MII_FEC);
    }
    
    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
    	 */
    	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_disable_phy_intr(void)
    {
    }
    
    static void __inline__ fec_phy_ack_intr(void)
    {
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #endif
    
    /* ------------------------------------------------------------------------- */
    
    static void mii_display_status(struct net_device *dev)
    {
    	struct fec_enet_private *fep = netdev_priv(dev);
    	volatile uint *s = &(fep->phy_status);
    
    	if (!fep->link && !fep->old_link) {
    		/* Link is still down - don't print anything */
    		return;
    	}
    
    	printk("%s: status: ", dev->name);
    
    	if (!fep->link) {
    		printk("link down");
    	} else {
    		printk("link up");
    
    		switch(*s & PHY_STAT_SPMASK) {
    		case PHY_STAT_100FDX: printk(", 100MBit Full Duplex"); break;
    		case PHY_STAT_100HDX: printk(", 100MBit Half Duplex"); break;
    		case PHY_STAT_10FDX: printk(", 10MBit Full Duplex"); break;
    		case PHY_STAT_10HDX: printk(", 10MBit Half Duplex"); break;
    		default:
    			printk(", Unknown speed/duplex");
    		}
    
    		if (*s & PHY_STAT_ANC)
    			printk(", auto-negotiation complete");
    	}
    
    	if (*s & PHY_STAT_FAULT)
    		printk(", remote fault");
    
    	printk(".\n");
    }
    
    
    static void mii_display_config(struct work_struct *work)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
    	struct net_device *dev = fep->netdev;
    
    	uint status = fep->phy_status;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	** 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 (status & PHY_CONF_ANE)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk("on");
    	else
    		printk("off");
    
    
    	if (status & PHY_CONF_100FDX)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk(", 100FDX");
    
    	if (status & PHY_CONF_100HDX)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk(", 100HDX");
    
    	if (status & PHY_CONF_10FDX)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk(", 10FDX");
    
    	if (status & PHY_CONF_10HDX)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk(", 10HDX");
    
    	if (!(status & PHY_CONF_SPMASK))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk(", No speed/duplex selected?");
    
    
    	if (status & PHY_CONF_LOOP)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk(", loopback enabled");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	printk(".\n");
    
    	fep->sequence_done = 1;
    }
    
    
    static void mii_relink(struct work_struct *work)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
    	struct net_device *dev = fep->netdev;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	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;
    
    	if (fep->link) {
    		duplex = 0;
    
    		if (fep->phy_status
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		    & (PHY_STAT_100FDX | PHY_STAT_10FDX))
    			duplex = 1;
    		fec_restart(dev, duplex);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		fec_stop(dev);
    
    #if 0
    	enable_irq(fep->mii_irq);
    #endif
    
    }
    
    /* 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, mii_relink);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	schedule_work(&fep->phy_task);
    }
    
    
    /* mii_queue_config is called in interrupt context from fec_enet_mii */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    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, mii_display_config);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	schedule_work(&fep->phy_task);
    }
    
    
    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, }
    	};
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /* Read remainder of PHY ID.
    */
    static void
    mii_discover_phy3(uint mii_reg, struct net_device *dev)
    {
    	struct fec_enet_private *fep;
    	int i;
    
    	fep = netdev_priv(dev);
    	fep->phy_id |= (mii_reg & 0xffff);
    	printk("fec: PHY @ 0x%x, ID 0x%08x", fep->phy_addr, fep->phy_id);
    
    	for(i = 0; phy_info[i]; i++) {
    		if(phy_info[i]->id == (fep->phy_id >> 4))
    			break;
    	}
    
    	if (phy_info[i])
    		printk(" -- %s\n", phy_info[i]->name);
    	else
    		printk(" -- unknown PHY!\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	fep->phy = phy_info[i];
    	fep->phy_id_done = 1;
    }
    
    /* Scan all of the MII PHY addresses looking for someone to respond
     * with a valid ID.  This usually happens quickly.
     */
    static void
    mii_discover_phy(uint mii_reg, struct net_device *dev)
    {
    	struct fec_enet_private *fep;
    	volatile fec_t *fecp;
    	uint phytype;
    
    	fep = netdev_priv(dev);
    	fecp = fep->hwp;
    
    	if (fep->phy_addr < 32) {
    		if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			/* Got first part of ID, now get remainder.
    			*/
    			fep->phy_id = phytype << 16;
    			mii_queue(dev, mk_mii_read(MII_REG_PHYIR2),
    							mii_discover_phy3);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			fep->phy_addr++;
    			mii_queue(dev, mk_mii_read(MII_REG_PHYIR1),
    							mii_discover_phy);
    		}
    	} else {
    		printk("FEC: No PHY device found.\n");
    		/* Disable external MII interface */
    		fecp->fec_mii_speed = fep->phy_speed = 0;
    		fec_disable_phy_intr();
    	}
    }
    
    /* This interrupt occurs when the PHY detects a link change.
    */
    
    #ifdef HAVE_mii_link_interrupt
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static irqreturn_t
    
    mii_link_interrupt(int irq, void * dev_id)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct	net_device *dev = dev_id;
    	struct fec_enet_private *fep = netdev_priv(dev);
    
    	fec_phy_ack_intr();
    
    #if 0
    	disable_irq(fep->mii_irq);  /* disable now, enable later */
    #endif
    
    	mii_do_cmd(dev, fep->phy->ack_int);
    	mii_do_cmd(dev, phy_cmd_relink);  /* restart and display status */
    
    	return IRQ_HANDLED;
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    static int
    fec_enet_open(struct net_device *dev)
    {
    	struct fec_enet_private *fep = netdev_priv(dev);
    
    	/* 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;
    	fep->link = 0;
    
    	if (fep->phy) {
    		mii_do_cmd(dev, fep->phy->ack_int);
    		mii_do_cmd(dev, fep->phy->config);
    		mii_do_cmd(dev, phy_cmd_config);  /* display configuration */
    
    
    		/* Poll until the PHY tells us its configuration
    		 * (not link state).
    		 * Request is initiated by mii_do_cmd above, but answer
    		 * comes by interrupt.
    		 * This should take about 25 usec per register at 2.5 MHz,
    		 * and we read approximately 5 registers.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		 */
    		while(!fep->sequence_done)
    			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 */
    		fec_restart(dev, 1);
    	}
    
    	netif_start_queue(dev);
    	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.
    	*/
    	fep->opened = 0;
    	netif_stop_queue(dev);
    	fec_stop(dev);
    
    	return 0;
    }
    
    /* Set or clear the multicast filter for this adaptor.
     * Skeleton taken from sunlance driver.
     * The CPM Ethernet implementation allows Multicast as well as individual
     * MAC address filtering.  Some of the drivers check to make sure it is
     * a group multicast address, and discard those that are not.  I guess I
     * will do the same for now, but just remove the test if you want
     * individual filtering as well (do the upper net layers want or support
     * this kind of feature?).