PPC4xx PHY interrupts patch
PATCH 1/1 PPC4xx PHY interrupts patch
Hi all,
On PPC4xx are not currently supported interrupts from external PHY. It's OK on all CPUs instead of IBM PPC440. The CPU has internal ZMII bridge where must be set the current link speed (Speed Selection Register - ZMII0_SSR).
The current interruptless implementation fails in the following cases:
- Linux starts without ethernet connection with default 10MB speed. When you will try connect 100MB link it fails
- reconection between 100 and 10 MB connection
The attached patch uses the interrpts from PHY to detect link change and sets apporiate speed in the Speed Selection Register and reports link change to console. The patch was tested with the IBM evaluation Ebony board.
regards
--
Pavel Bartusek <[email protected]>
Software Engineering
SYSGO Real-Time Solutions AG | Embedded and Real-Time Software Lise-Meitner-Str.15
89081 Ulm, Germany
Voice: +49-731-9533-1295 FAX: +49-731-94683-10
www.sysgo.de | www.elinos.com | www.osek.de
drivers/net/ibm_ocp/ibm_ocp_enet.c +122 -8
@@ -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 <linux/module.h> #include <linux/kernel.h>
@@ -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) {
drivers/net/ibm_ocp/ibm_ocp_enet.h +4 -0
@@ -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; };
drivers/net/ibm_ocp/ibm_ocp_phy.c +57 -4
94 lines not on this page · raw message
drivers/net/ibm_ocp/ibm_ocp_zmii.c +3 -0
@@ -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 ; }