--- linux.ori/drivers/net/ibm_ocp/ibm_ocp_enet.c Wed Dec 3 09:01:26 2003 +++ linux/drivers/net/ibm_ocp/ibm_ocp_enet.c Wed Dec 17 15:38:30 2003 @@ -178,6 +178,9 @@ * any BD, ppc405_rx_fill() gets called anyway with i == fep->rx_slot. It will * corrupt the current BD (fep->rx_slot) and NUM_RX_BUFF packets will be lost * + * Version: 4.8 12/17/03 - Pavel Bartusek + * Added support for PHY interrupts + * */ #include #include @@ -210,6 +213,10 @@ #include "ibm_ocp_enet.h" #include "ibm_ocp_mal.h" +#if defined(PHY0_INTERRUPT) || defined(PHY1_INTERRUPT) +# define PHY_INTERRUPT +#endif + /* Forward declarations of some structures to support different PHYs */ static int ppc405_enet_open(struct net_device *); @@ -227,10 +234,15 @@ static void ppc405_eth_mac(int, void *, struct pt_regs *); static void ppc405_rx_fill(struct net_device *, int); static void ppc405_rx_clean(struct net_device *, int); +#ifdef PHY_INTERRUPT +static void ppc405_phy(int irq, void * dev_id, struct pt_regs * regs); +#endif int ocp_enet_mdio_read(struct net_device *dev, int reg, uint * value); int ocp_enet_mdio_write(struct net_device *dev, int reg); int ocp_enet_ioctl(struct net_device *, struct ifreq *rq, int cmd); +void process_mii_queue(struct net_device *dev); + static struct net_device *emac_dev[EMAC_NUMS]; @@ -301,18 +313,53 @@ static int ppc405_enet_open(struct net_device *dev) { +#ifdef PHY_INTERRUPT + int ret; +#else unsigned long mode_reg; +#endif struct ocp_enet_private *fep = dev->priv; volatile emac_t *emacp = fep->emacp; unsigned long emac_ier; - if (!fep->link) { + + if (!fep->phy) { printk(KERN_NOTICE "%s: Cannot open interface without phy\n", dev->name); return -ENODEV; } + disable_mal_chan(fep); set_mal_chan_addr(fep); + +#ifdef PHY_INTERRUPT + /* + * we must disable irq before the startup PHY sequence, because it + * will assert interrupt line which can be shared + */ + fep->phy_irq = -1; + switch (fep->emac_num) { + case 0: +#ifdef PHY0_INTERRUPT + fep->phy_irq = PHY0_INTERRUPT; +#endif + break; + case 1: +#ifdef PHY1_INTERRUPT + fep->phy_irq = PHY1_INTERRUPT; +#endif + break; + } + if (fep->phy_irq != -1) { + snprintf(fep->phy_irqname, sizeof(fep->phy_irqname), "%s PHY %s", dev->name, fep->phy->name); + ret = request_irq(fep->phy_irq, ppc405_phy, SA_SHIRQ, fep->phy_irqname, dev); + if (ret) { + printk("Error allocating irq %d for %s",fep->phy_irq ,fep->phy_irqname); + return ret; + } + disable_irq(fep->phy_irq); + } +#endif /* set the high address */ out_be32(&emacp->em0iahr, (dev->dev_addr[0] << 8) | dev->dev_addr[1]); @@ -321,7 +368,8 @@ out_be32(&emacp->em0ialr, (dev->dev_addr[2] << 24) | (dev->dev_addr[3] << 16) | (dev->dev_addr[4] << 8) | dev->dev_addr[5]); - + + fep->sequence_done = 0; mii_do_cmd(dev, fep->phy->startup); mii_do_cmd(dev, fep->phy->ack_int); mii_do_cmd(dev, fep->phy->config); @@ -329,8 +377,10 @@ while (!fep->sequence_done) schedule(); - mii_display_status(dev); - +#ifdef PHY_INTERRUPT + if (fep->phy_irq != -1) + enable_irq(fep->phy_irq); +#else /* set receive fifo to 4k and tx fifo to 2k */ mode_reg = EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K | EMAC_M1_APP | EMAC_M1_TR0_MULTI; @@ -350,7 +400,7 @@ mode_reg = mode_reg & ~(EMAC_M1_FDE | EMAC_M1_EIFC | EMAC_M1_ILE); /* half duplex */ out_be32(&emacp->em0mr1, mode_reg); - +#endif /* enable broadcast and individual address */ out_be32(&emacp->em0rmr, EMAC_RMR_IAE | EMAC_RMR_BAE); @@ -386,7 +436,6 @@ request_irq(BL_MAL_TXEOB,ppc405_eth_txeob,0,"OCP EMAC TX EOB",dev); request_irq(BL_MAL_RXEOB,ppc405_eth_rxeob,0,"OCP EMAC RX EOB",dev); } - /* init buffer descriptors rings */ init_rings(dev); @@ -480,7 +529,18 @@ free_irq(BL_MAL_RXEOB,dev); } - +#ifdef PHY_INTERRUPT + if (fep->phy_irq != -1) + free_irq(fep->phy_irq, dev); +#endif + fep->sequence_done = 0; + mii_do_cmd(dev, fep->phy->shutdown); + mii_queue_schedule(dev); + while (!fep->sequence_done) + schedule(); + + fep->old_phy_status = 0; + fep->link = 0; free_phy(dev); return 0; } @@ -613,7 +673,7 @@ ocp_remove_one(emac_driver); return -1; } - ep->link = 1; + ep->link = 0; ep->txchan = 0x80000000 >> curr_emac*2 ; ep->rxchan = 0x80000000 >> curr_emac; dev->irq = ocp_get_irq(EMAC,curr_emac); @@ -1200,6 +1260,60 @@ get_mal_dcrn(fep, DCRN_MALRXCASR) | reenable_rxchans); } +#ifdef PHY_INTERRUPT +LIST_HEAD(phy_check_head); + +static void +proc_mii_queue_do_tasklet(unsigned long unused) +{ + struct ocp_enet_private *fep; + int flags; + struct list_head *pos; + struct net_device *dev; + static spinlock_t phy_check_list_lock; + + spin_lock_irqsave(&phy_check_list_lock, flags); + list_for_each(pos, &phy_check_head) { + fep = list_entry(pos, struct ocp_enet_private, emac_list); + spin_unlock_irqrestore(&phy_check_list_lock, flags); + dev = emac_dev[fep->emac_num]; + fep->sequence_done = 0; + mii_do_cmd(dev, fep->phy->ack_int); + process_mii_queue(dev); + + spin_lock_irqsave(&phy_check_list_lock, flags); + } + /* reenable PHY interrupt(s) */ + list_for_each(pos, &phy_check_head) { + fep = list_entry(pos, struct ocp_enet_private, emac_list); + if (fep->phy_irq != -1) + enable_irq(fep->phy_irq); + } + /* clear the list */ + INIT_LIST_HEAD(&phy_check_head); + + spin_unlock_irqrestore(&phy_check_list_lock, flags); +} + +DECLARE_TASKLET(proc_mii_queue_tasklet, proc_mii_queue_do_tasklet, 0); + +/* + * This interrupt occurs when the PHY detects a link change + */ +static void +ppc405_phy(int irq, void * dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + struct ocp_enet_private *fep = dev->priv; + + list_add_tail(&fep->emac_list, &phy_check_head); + tasklet_schedule(&proc_mii_queue_tasklet); + + disable_irq(irq); + return; +} +#endif + static void ppc405_eth_mac(int irq, void *dev_instance, struct pt_regs *regs) { --- linux.ori/drivers/net/ibm_ocp/ibm_ocp_enet.h Mon Mar 24 18:09:56 2003 +++ linux/drivers/net/ibm_ocp/ibm_ocp_enet.h Wed Dec 17 15:04:00 2003 @@ -164,6 +164,10 @@ int mal; volatile emac_t *emacp; struct ocp_dev ocpdev; + char phy_irqname[32]; + int phy_irq; + uint old_phy_status; + struct list_head emac_list; }; --- linux.ori/drivers/net/ibm_ocp/ibm_ocp_phy.c Wed Dec 3 09:01:26 2003 +++ linux/drivers/net/ibm_ocp/ibm_ocp_phy.c Wed Dec 17 15:36:36 2003 @@ -75,6 +75,10 @@ * using zmii_phyid_adj() to adjust phy addrs on those cpus * that use a zmii bridge * fixed find_phy for zmii bridge support + * + * Version: 2.2 12/17/03 - Pavel Bartusek + * Added support for PHY interrupts + */ #include @@ -92,6 +96,8 @@ #include #include "ibm_ocp_enet.h" +#include "ocp_zmii.h" + static int next_phy_available = MIN_PHY_ADDR; /* Forward declarations of some structures to support different PHYs */ @@ -135,6 +141,8 @@ static int mii_queue(struct net_device *dev, int request, void (*func) (uint, struct net_device *)); +static void check_phy_configuration(uint mii_reg, struct net_device *dev); + /* Register definitions for the PHY. */ #define MII_REG_CR 0 /* Control Register */ @@ -791,8 +798,8 @@ }, (const phy_cmd_t[]) { /* startup - enable interrupts */ {mk_mii_write(MII_REG_CR, PHY_BMCR_AUTON), NULL}, /* Auto neg. on */ -// { mk_mii_write(MII_AM79C875_MFR, 0x4000), NULL}, /* int 1 to signle interrupt */ -// { mk_mii_write(MII_AM79C875_ICR, 0x00ff), NULL }, /* enable interrupts */ + { mk_mii_write(MII_AM79C875_MFR, 0x0000), NULL}, /* int 0 to signle interrupt */ + { mk_mii_write(MII_AM79C875_ICR, 0x00ff), NULL }, /* enable interrupts */ {mk_mii_write(MII_REG_CR, PHY_BMCR_RST_NEG), NULL}, /* autonegotiate */ {mk_mii_read(MII_REG_ANLPAR), mii_parse_Am79C875_pcr}, @@ -798,8 +808,8 @@ {mk_mii_read(MII_AM79C875_ICR), NULL}, {mk_mii_read(MII_REG_SR), mii_parse_sr}, {mk_mii_read(MII_REG_ANAR), mii_parse_anar}, - {mk_mii_read(MII_REG_ANLPAR), - mii_parse_Am79C875_pcr}, + {mk_mii_read(MII_REG_ANLPAR), mii_parse_Am79C875_pcr}, + {mk_mii_read(MII_REG_PHYIR1), check_phy_configuration}, {mk_mii_end,} }, (const phy_cmd_t[]) { /* shutdown - nothing */ @@ -1172,3 +1182,48 @@ return(next_phy_available); } + +static void +check_phy_configuration(uint mii_reg, struct net_device *dev) +{ + struct ocp_enet_private *fep = dev->priv; + volatile emac_t *emacp = fep->emacp; + unsigned long mode_reg; + + if (fep->old_phy_status != fep->phy_status) { + fep->old_phy_status = fep->phy_status; + fep->old_link = fep->link; + if ((fep->phy_status & PHY_STAT_FAULT) || !(fep->phy_status & PHY_STAT_LINK)) { + /* the link is down */ + fep->link = 0; + } else { + /* the link is up */ + fep->link = 1; + } + if (fep->old_link != fep->link) { + /* display only the link configuration change */ + mii_display_status(dev); + } + + /* set receive fifo to 4k and tx fifo to 2k */ + mode_reg = EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K | EMAC_M1_APP | + EMAC_M1_TR0_MULTI; + + /* set speed */ + if (fep->phy_speed == _100BASET) { + mode_reg = mode_reg | EMAC_M1_MF_100MBPS; /* 100 MBPS */ + zmii_port_speed(100, dev); + } else { + mode_reg = mode_reg & ~EMAC_M1_MF_100MBPS; /* 10 MBPS */ + zmii_port_speed(10, dev); + } + + /* set duplex */ + if (fep->phy_duplex == FULL) + mode_reg = mode_reg | EMAC_M1_FDE | EMAC_M1_EIFC | EMAC_M1_IST; + else + mode_reg = mode_reg & ~(EMAC_M1_FDE | EMAC_M1_EIFC | EMAC_M1_ILE); /* half duplex */ + + out_be32(&emacp->em0mr1, mode_reg); + } +} --- linux.ori/drivers/net/ibm_ocp/ibm_ocp_zmii.c Mon Mar 24 18:11:04 2003 +++ linux/drivers/net/ibm_ocp/ibm_ocp_zmii.c Tue Nov 4 13:08:03 2003 @@ -105,6 +105,9 @@ if (speed == 100) zmii_speed |= zmii_speed100[fep->emac_num]; + if (speed == 10) + zmii_speed &= ~zmii_speed100[fep->emac_num]; + out_be32(&zmiip->ssr, zmii_speed); return; }