--- ixp425_eth.c.orig 2004-06-10 09:52:35.000000000 +0200 +++ ixp425_eth.c 2004-06-10 09:52:55.000000000 +0200 @@ -6,7 +6,7 @@ * IXP425 Ethernet Driver for Linux * * -- Copyright Notice -- - * Copyright 2004, Intel Corporation. + * Copyright 2004, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -53,21 +53,20 @@ */ #include #include -#include +#include #include +#include +#include #include +#include #include #include -#include +#include #include -#include -#include #include #include #include #include -#include -#include /* * Intel IXP400 Software specific header files @@ -97,18 +96,19 @@ /* * Module version information */ +#define DRV_VERSION "1.1A" +#define DRV_NAME "ixp425_eth" + MODULE_DESCRIPTION("IXP425 NPE Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Intel Corporation"); -#define MODULE_NAME "ixp425_eth" -#define MODULE_VERSION "1.1A" /* Module parameters */ static int npe_learning = 1; /* default : NPE learning & filtering enable */ static int log_level = 0; /* default : no log */ static int no_csr_init = 0; /* default : init CSR */ static int no_phy_scan = 1; /* default : no phy discovery */ -static int phy_reset = 0; /* default : mo phy reset */ +static int phy_reset = 0; /* default : no phy reset */ /* netdev_max_backlog: ideally /proc/sys/net/core/netdev_max_backlog, but any * value > 46 looks to work. This is used to control the maximum number of * skbuf to push into the linux stack, and avoid the performance degradations @@ -116,22 +116,19 @@ */ static int netdev_max_backlog = 290; -MODULE_PARM(npe_learning, "i"); +module_param(npe_learning, int, 4); MODULE_PARM_DESC(npe_learning, "If non-zero, NPE MAC Address Learning & Filtering feature will be enabled"); -MODULE_PARM(log_level, "i"); +module_param(log_level, int, 6); MODULE_PARM_DESC(log_level, "Set log level: 0 - None, 1 - Verbose, 2 - Debug"); -MODULE_PARM(no_csr_init, "i"); +module_param(no_csr_init, int, 0); MODULE_PARM_DESC(no_csr_init, "If non-zero, do not initialise Intel IXP400 Software Release core components"); -MODULE_PARM(no_phy_scan, "i"); +module_param(no_phy_scan, int, 0); MODULE_PARM_DESC(no_phy_scan, "If non-zero, use hard-coded phy addresses"); -MODULE_PARM(phy_reset, "i"); +module_param(phy_reset, int, 0); MODULE_PARM_DESC(phy_reset, "If non-zero, reset the phys"); -MODULE_PARM(netdev_max_backlog, "i"); +module_param(netdev_max_backlog, int, 4); MODULE_PARM_DESC(netdev_max_backlog, "Should be set to the value of /proc/sys/net/core/netdev_max_backlog (perf affecting)"); -/* devices will be called ixp0 and ixp1 */ -#define DEVICE_NAME "ixp" - /* boolean values for PHY link speed, duplex, and autonegotiation */ #define PHY_SPEED_10 0 #define PHY_SPEED_100 1 @@ -239,36 +236,35 @@ */ /* Print kernel error */ #define P_ERROR(args...) \ - printk(KERN_ERR MODULE_NAME ": " args) + printk(KERN_ERR DRV_NAME ": " args) /* Print kernel warning */ #define P_WARN(args...) \ - printk(KERN_WARNING MODULE_NAME ": " args) + printk(KERN_WARNING DRV_NAME ": " args) /* Print kernel notice */ #define P_NOTICE(args...) \ - printk(KERN_NOTICE MODULE_NAME ": " args) + printk(KERN_NOTICE DRV_NAME ": " args) /* Print kernel info */ #define P_INFO(args...) \ - printk(KERN_INFO MODULE_NAME ": " args) + printk(KERN_INFO DRV_NAME ": " args) /* Print verbose message. Enabled/disabled by 'log_level' param */ #define P_VERBOSE(args...) \ - if (log_level >= 1) printk(MODULE_NAME ": " args) + if (log_level >= 1) printk(DRV_NAME ": " args) /* Print debug message. Enabled/disabled by 'log_level' param */ #define P_DEBUG(args...) \ if (log_level >= 2) { \ - printk("%s: %s()\n", MODULE_NAME, __FUNCTION__); \ + printk("%s: %s()\n", DRV_NAME, __FUNCTION__); \ printk(args); } #ifdef DEBUG /* Print trace message */ #define TRACE \ - if (log_level >= 2) printk("%s: %s(): line %d\n", MODULE_NAME, __FUNCTION__, __LINE__) + if (log_level >= 2) printk("%s: %s(): line %d\n", \ + DRV_NAME, __FUNCTION__, __LINE__) #else /* no trace */ #define TRACE #endif -/* extern Linux kernel data */ -extern struct softnet_data softnet_data[]; /* used to get the current queue level */ extern unsigned long loops_per_jiffy; /* used to calculate CPU clock speed */ /* internal Ethernet Access layer polling entry points */ @@ -279,8 +275,6 @@ /* Private device data */ typedef struct { - spinlock_t lock; /* multicast management lock */ - unsigned int msdu_size; unsigned int replenish_size; unsigned int pkt_size; @@ -320,17 +314,11 @@ IX_MBUF tx_pool_buf[TX_QUEUE_MAX_LEN]; IX_MBUF_POOL *tx_pool; - /* id of thread for the link duplex monitoring */ - int maintenanceCheckThreadId; - - /* mutex locked by thread, until the thread exits */ - struct semaphore *maintenanceCheckThreadComplete; + /* monitor link duplex */ + struct work_struct mii_job; - /* Used to stop the kernel thread for link monitoring. */ - volatile BOOL maintenanceCheckStopped; - - /* used for tx timeout */ - struct tq_struct tq_timeout; + /* handle tx timeouts */ + struct work_struct tx_timeout_job; /* used to control the message output */ UINT32 devFlags; @@ -418,7 +406,8 @@ #endif }; -/* The default configuration of the phy on each Intel IXP400 Software EthAcc port. +/* The default configuration of the phy on each Intel IXP400 Software EthAcc + * port. * * This configuration is loaded when the phy's are discovered. * More PHYs can optionally be configured here by adding more @@ -455,13 +444,18 @@ {{0x00, 0x02, 0xB3, 0x02, 0x02, 0x02}} /* Port 2 */ }; +/* + * Shared workqueue thread for device maintenance tasks. + */ +static struct workqueue_struct *npe_eth_workqueue; + /* Mutex lock used to coordinate access to IxEthAcc functions * which manipulate the MII registers on the PHYs */ -static struct semaphore *miiAccessMutex; +static DECLARE_MUTEX(miiAccessMutex); /* mutex locked when maintenance is being performed */ -static struct semaphore *maintenance_mutex; +static DECLARE_MUTEX(maintenance_mutex); /* Flags which is set when corresponding NPE is running, * cleared when NPE is stopped @@ -470,16 +464,16 @@ /* Flags which is set when the corresponding IRQ is running, */ -static int irq_pmu_used = 0; -static int irq_qm1_used = 0; +static int irq_pmu_used; +static int irq_qm1_used; /* * ERROR COUNTERS */ -static UINT32 skbAllocFailErrorCount = 0; -static UINT32 replenishErrorCount = 0; -static UINT32 chainedRxErrorCount = 0; +static UINT32 skbAllocFailErrorCount; +static UINT32 replenishErrorCount; +static UINT32 chainedRxErrorCount; /* * ERROR NUMBER FUNCTIONS @@ -909,150 +903,112 @@ /* - * KERNEL THREADS + * WORKQUEUE JOBS */ -/* This timer will check the PHY for the link duplex and +/* This workqueue job will check the PHY for the link duplex and * update the MAC accordingly. It also executes some buffer * maintenance to release mbuf in excess or replenish after * a severe starvation * * This function loops and wake up every 3 seconds. */ -static int dev_media_check_thread (void* arg) +static void dev_media_check_work(void* arg) { struct net_device *dev = (struct net_device *) arg; priv_data_t *priv = dev->priv; - int linkUp; - int speed100; - int fullDuplex = -1; /* unknown duplex mode */ - int newDuplex; - int autonegotiate; - unsigned phyNum = phyAddresses[priv->port_id]; - int res; TRACE; + + /* + * Determine the link status + */ - /* Lock the mutex for this thread. - This mutex can be used to wait until the thread exits - */ - down (priv->maintenanceCheckThreadComplete); - - daemonize(); - reparent_to_init(); - spin_lock_irq(¤t->sigmask_lock); - sigemptyset(¤t->blocked); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); - - snprintf(current->comm, sizeof(current->comm), "ixp425 %s", dev->name); - - TRACE; - - while (1) + if (default_phy_cfg[priv->port_id].linkMonitor) { - /* We may have been woken up by a signal. If so, we need to flush it out */ - if (signal_pending (current)) { - spin_lock_irq(¤t->sigmask_lock); - flush_signals(current); - spin_unlock_irq(¤t->sigmask_lock); - } - - /* If the interface is down, we need to exit this thread */ - if (priv->maintenanceCheckStopped == TRUE) - { - break; - } - - /* - * Determine the link status - */ + int linkUp; + int speed100; + int fullDuplex = -1; /* unknown duplex mode */ + int newDuplex; + int autonegotiate; + unsigned phyNum = phyAddresses[priv->port_id]; + int res; - TRACE; + TRACE; - if (default_phy_cfg[priv->port_id].linkMonitor) - { - /* lock the MII register access mutex */ - down(miiAccessMutex); - - res = ixEthMiiLinkStatus(phyNum, - &linkUp, - &speed100, - &newDuplex, - &autonegotiate); - /* release the MII register access mutex */ - up(miiAccessMutex); + /* lock the MII register access mutex */ + down(&miiAccessMutex); - if (priv->maintenanceCheckStopped == TRUE) - { - break; - } + res = ixEthMiiLinkStatus(phyNum, + &linkUp, + &speed100, + &newDuplex, + &autonegotiate); + /* release the MII register access mutex */ + up(&miiAccessMutex); - if (res != IX_ETH_ACC_SUCCESS) - { - P_WARN("ixEthMiiLinkStatus failed on PHY%d.\n" - "\tCan't determine\nthe auto negotiated parameters. " - "Using default values.\n", - phyNum); + if (res != IX_ETH_ACC_SUCCESS) + { + P_WARN("ixEthMiiLinkStatus failed on PHY%d.\n" + "\tCan't determine\nthe auto negotiated parameters. " + "Using default values.\n", + phyNum); - /* this shouldn't happen. exit the thread if it does */ - break; - } + /* this shouldn't happen. exit the thread if it does */ + goto out; + } - if (linkUp) - { - if (! netif_carrier_ok(dev)) - { - /* inform the kernel of a change in link state */ - netif_carrier_on(dev); - } - - /* - * Update the MAC mode to match the PHY mode if - * there is a phy mode change. - */ - if (newDuplex != fullDuplex) - { - fullDuplex = newDuplex; - if (fullDuplex) - { - ixEthAccPortDuplexModeSet (priv->port_id, IX_ETH_ACC_FULL_DUPLEX); - } - else - { - ixEthAccPortDuplexModeSet (priv->port_id, IX_ETH_ACC_HALF_DUPLEX); - } - } - } - else - { - fullDuplex = -1; - if (netif_carrier_ok(dev)) - { - /* inform the kernel of a change in link state */ - netif_carrier_off(dev); - } - } - } + if (linkUp) + { + if (! netif_carrier_ok(dev)) + { + /* inform the kernel of a change in link state */ + netif_carrier_on(dev); + } + + /* + * Update the MAC mode to match the PHY mode if + * there is a phy mode change. + */ + if (newDuplex != fullDuplex) + { + fullDuplex = newDuplex; + if (fullDuplex) + { + ixEthAccPortDuplexModeSet(priv->port_id, + IX_ETH_ACC_FULL_DUPLEX); + } + else + { + ixEthAccPortDuplexModeSet(priv->port_id, + IX_ETH_ACC_HALF_DUPLEX); + } + } + } + else + { + fullDuplex = -1; + if (netif_carrier_ok(dev)) + { + /* inform the kernel of a change in link state */ + netif_carrier_off(dev); + } + } + } - TRACE; + TRACE; - /* this is to prevent the rx pool from emptying when - * there's not enough memory for a long time - * It prevents also from holding the memory for too - * long - */ - dev_buff_maintenance(dev); + /* this is to prevent the rx pool from emptying when + * there's not enough memory for a long time + * It prevents also from holding the memory for too + * long + */ + dev_buff_maintenance(dev); - /* Now sleep for 3 seconds */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(MEDIA_CHECK_INTERVAL); - } /* while (1) ... */ - - /* free the mutex for this thread. */ - up (priv->maintenanceCheckThreadComplete); - - return 0; + /* reschedule to run in 3 seconds */ + queue_delayed_work(npe_eth_workqueue, &priv->mii_job, 3*HZ); + out: + return; } /* @@ -1084,9 +1040,9 @@ /* Internal ISR : run a few thousand times per second and calls * the queue manager dispatcher entry point. */ -static void dev_qmgr_os_isr(int irg, void *dev_id, struct pt_regs *regs) +static irqreturn_t dev_qmgr_os_isr(int irg, void *dev_id, struct pt_regs *regs) { - int qlevel = softnet_data[0].input_pkt_queue.qlen; + int qlevel = __get_cpu_var(softnet_data).input_pkt_queue.qlen; /* at the interrupt entry, the queue contains already a few entries * so it is safe to decrease the number of entries @@ -1121,14 +1077,15 @@ /* call the queue manager entry point */ dispatcherFunc(IX_QMGR_QUELOW_GROUP); + return IRQ_HANDLED; } /* Internal ISR : run a few thousand times per second and calls * the ethernet entry point. */ -static void dev_poll_os_isr(int irg, void *dev_id, struct pt_regs *regs) +static irqreturn_t dev_poll_os_isr(int irg, void *dev_id, struct pt_regs *regs) { - int qlevel = softnet_data[0].input_pkt_queue.qlen; + int qlevel = __get_cpu_var(softnet_data).input_pkt_queue.qlen; dev_pmu_timer_restart(); /* set up the timer for the next interrupt */ @@ -1165,6 +1122,7 @@ ixEthRxFrameQMCallback(0,0); ixEthTxFrameDoneQMCallback(0,0); + return IRQ_HANDLED; } /* initialize the PMU timer */ @@ -1215,46 +1173,34 @@ ixOsServIntUnlock(key); } -/* This timer will call ixEthDBDatabaseMaintenance every - * IX_ETH_DB_MAINTENANCE_TIME jiffies +/* + * Ethernet learning database maintenance. */ -static void maintenance_timer_cb(unsigned long data); -static struct timer_list maintenance_timer = { - function:&maintenance_timer_cb -}; - -static void maintenance_timer_task(void *data); - -/* task spawned by timer interrupt for EthDB maintenance */ -static struct tq_struct tq_maintenance = { - routine:maintenance_timer_task -}; +static void db_maintenance_code(void *data); +static DECLARE_WORK(db_maintenance_job, db_maintenance_code, NULL); -static void maintenance_timer_set(void) +static inline +void schedule_db_maintenance(void) { - maintenance_timer.expires = jiffies + DB_MAINTENANCE_TIME; - add_timer(&maintenance_timer); + queue_delayed_work(npe_eth_workqueue, &db_maintenance_job, + DB_MAINTENANCE_TIME); } -static void maintenance_timer_clear(void) +static inline +void cancel_db_maintenance(void) { - del_timer_sync(&maintenance_timer); + cancel_delayed_work(&db_maintenance_job); } -static void maintenance_timer_task(void *data) +static void db_maintenance_code(void *data) { - down(maintenance_mutex); + down(&maintenance_mutex); ixEthDBDatabaseMaintenance(); - up(maintenance_mutex); + up(&maintenance_mutex); + schedule_db_maintenance(); } -static void maintenance_timer_cb(unsigned long data) -{ - schedule_task(&tq_maintenance); - - maintenance_timer_set(); -} /* * DATAPLANE @@ -1331,9 +1277,9 @@ * the flags returned by the NPE, so a payload lookup is not needed * in most of the cases. */ -static inline void dev_eth_type_trans(int mflags, - struct sk_buff *skb, - struct net_device *dev) +static inline +void +dev_eth_type_trans(int mflags, struct sk_buff *skb, struct net_device *dev) { unsigned header_len = dev->hard_header_len; skb->mac.raw=skb->data; @@ -1382,58 +1328,59 @@ } } - /* set the packet type - * check mcast and bcast bits as filled by the NPE - */ - if (mflags & (BIT(4) | BIT(5))) - { - if (mflags & BIT(4)) - { - /* the packet is a broadcast one */ - skb->pkt_type=PACKET_BROADCAST; - } - else - { - /* the packet is a multicast one */ - skb->pkt_type=PACKET_MULTICAST; - ((priv_data_t *)(dev->priv))->stats.multicast++; - } - } - else - { - if (dev->flags & IFF_PROMISC) - { - /* check dest mac address only if promiscuous - * mode is set This costs - * a lookup inside the packet payload. - */ - struct ethhdr *eth = skb->mac.ethernet; - unsigned char *hdest = eth->h_dest; - - if (memcmp(hdest, dev->dev_addr, ETH_ALEN)!=0) - { - skb->pkt_type = PACKET_OTHERHOST; - } - } - else - { - /* promiscuous mode is not set, All packets are filtered - * by the NPE and the destination MAC address matches - * the driver setup. There is no need for a lookup in the - * payload. - */ - } - } + /* set the packet type + * check mcast and bcast bits as filled by the NPE + */ + if (mflags & (BIT(4) | BIT(5))) + { + if (mflags & BIT(4)) + { + /* the packet is a broadcast one */ + skb->pkt_type=PACKET_BROADCAST; + } + else + { + /* the packet is a multicast one */ + skb->pkt_type=PACKET_MULTICAST; + ((priv_data_t *)(dev->priv))->stats.multicast++; + } + } + else + { + if (dev->flags & IFF_PROMISC) + { + /* check dest mac address only if promiscuous + * mode is set This costs + * a lookup inside the packet payload. + */ + struct ethhdr *eth = skb->mac.ethernet; + unsigned char *hdest = eth->h_dest; + + if (memcmp(hdest, dev->dev_addr, ETH_ALEN)!=0) + { + skb->pkt_type = PACKET_OTHERHOST; + } + } + else + { + /* promiscuous mode is not set, All packets are filtered + * by the NPE and the destination MAC address matches + * the driver setup. There is no need for a lookup in the + * payload. + */ + } + } - return; + return; } /* This callback is called when new packet received from MAC * and not ready to be transfered up-stack. (portDisable * is in progress or device is down) - * */ -static void rx_disable_cb(UINT32 callbackTag, IX_MBUF *mbuf, IxEthAccPortId portId) +static +void +rx_disable_cb(UINT32 callbackTag, IX_MBUF *mbuf, IxEthAccPortId portId) { TRACE; @@ -1452,7 +1399,6 @@ * * If this is a valid packet, then new skb is allocated and switched * with the one in mbuf, which is pushed upstack. - * */ static void rx_cb(UINT32 callbackTag, IX_MBUF *mbuf, IxEthAccPortId portId) { @@ -1468,7 +1414,7 @@ dev = (struct net_device *)callbackTag; priv = dev->priv; - qlevel = softnet_data[0].input_pkt_queue.qlen; + qlevel = __get_cpu_var(softnet_data).input_pkt_queue.qlen; /* check if the system accepts more traffic and * against chained mbufs @@ -1574,10 +1520,6 @@ TRACE; - /* if called from irq handler, lock already acquired */ - if (!in_irq()) - spin_lock_irq(&priv->lock); - /* clear multicast addresses that were set the last time (if exist) */ ixEthAccPortMulticastAddressLeaveAll (priv->port_id); @@ -1663,8 +1605,7 @@ Exit: - if (!in_irq()) - spin_unlock_irq(&priv->lock); + return; } /* start the NPEs */ @@ -1736,7 +1677,7 @@ if (request_irq(IXP425_INT_LVL_XSCALE_PMU, dev_poll_os_isr, SA_SHIRQ, - MODULE_NAME, + DRV_NAME, (void *)IRQ_ANY_PARAMETER)) { P_ERROR("Failed to reassign irq to PMU timer interrupt!\n"); @@ -1750,7 +1691,7 @@ if (request_irq(IXP425_INT_LVL_QM1, dev_qmgr_os_isr, SA_SHIRQ, - MODULE_NAME, + DRV_NAME, (void *)IRQ_ANY_PARAMETER)) { P_ERROR("Failed to reassign irq to QM1 timer interrupt!\n"); @@ -1833,8 +1774,8 @@ if ((res = ixEthDBFilteringPortMaximumFrameSizeSet(priv->port_id, priv->msdu_size))) { - P_ERROR("%s: ixEthDBFilteringPortMaximumFrameSizeSet failed for port %d, res = %d\n", - dev->name, priv->port_id, res); + P_ERROR("%s: ixEthDBFilteringPortMaximumFrameSizeSet failed" + " for port %d, res = %d\n", dev->name, priv->port_id, res); return -1; } @@ -1845,7 +1786,8 @@ return convert_error_ethAcc(res); } - /* Do not enable aging unless learning is enabled (nothing to age otherwise) */ + /* Do not enable aging unless learning is enabled (nothing to age + otherwise) */ if (npe_learning) { if ((res = ixEthDBPortAgingEnable(priv->port_id))) @@ -1860,8 +1802,6 @@ netif_start_queue(dev); - TRACE; - /* restore the scheduling discipline (it may have changed) */ dev->qdisc_sleeping = priv->qdisc; @@ -1869,31 +1809,6 @@ TRACE; -#if 0 - /* if no link monitoring is set, assume the link is up */ - if (!default_phy_cfg[priv->port_id].linkMonitor) - { - netif_carrier_on(dev); - } -#endif - - /* restart the link-monitoring thread if necessary */ - if (priv->maintenanceCheckStopped == TRUE) - { - /* Starts the driver monitoring thread, if configured */ - priv->maintenanceCheckStopped = FALSE; - - priv->maintenanceCheckThreadId = - kernel_thread(dev_media_check_thread, - (void *) dev, - CLONE_FS | CLONE_FILES); - if (priv->maintenanceCheckThreadId < 0) - { - P_ERROR("%s: Failed to start thread for media checks\n", dev->name); - priv->maintenanceCheckStopped = TRUE; - } - } - return 0; } @@ -1903,38 +1818,12 @@ static void port_disable(struct net_device *dev) { priv_data_t *priv = dev->priv; - int res; IX_STATUS status; TRACE; if (!netif_queue_stopped(dev)) - { - dev->trans_start = jiffies; netif_stop_queue(dev); - } - - if (priv->maintenanceCheckStopped == TRUE) - { - /* thread is not running */ - } - else - { - /* thread is running */ - priv->maintenanceCheckStopped = TRUE; - /* Wake up the media-check thread with a signal. - It will check the 'running' flag and exit */ - if ((res = kill_proc (priv->maintenanceCheckThreadId, SIGKILL, 1))) - { - P_ERROR("%s: unable to signal thread\n", dev->name); - } - else - { - /* wait for the thread to exit. */ - down (priv->maintenanceCheckThreadComplete); - up (priv->maintenanceCheckThreadComplete); - } - } /* Set callbacks when port is disabled */ ixEthAccPortTxDoneCallbackRegister(priv->port_id, @@ -1984,7 +1873,6 @@ ixEthAccPortTxDoneCallbackRegister(priv->port_id, tx_done_queue_stopped_cb, (UINT32)dev); - dev->trans_start = jiffies; netif_stop_queue (dev); } return 0; @@ -2044,13 +1932,16 @@ } /* prevent the maintenace task from running while bringing up port */ - down(maintenance_mutex); + down(&maintenance_mutex); if ((res = port_enable(dev))) { - up(maintenance_mutex); + up(&maintenance_mutex); return res; } - up(maintenance_mutex); + up(&maintenance_mutex); + + /* schedule mii job to run in 3 seconds */ + queue_delayed_work(npe_eth_workqueue, &priv->mii_job, 3*HZ); return res; } @@ -2060,32 +1951,38 @@ */ static int do_dev_stop(struct net_device *dev) { + priv_data_t *priv = dev->priv; + TRACE; - down(maintenance_mutex); + cancel_delayed_work(&priv->mii_job); + cancel_delayed_work(&priv->tx_timeout_job); + netif_stop_queue(dev); + netif_carrier_off(dev); + down(&maintenance_mutex); port_disable(dev); - up(maintenance_mutex); + up(&maintenance_mutex); return 0; } static void -dev_tx_timeout_task(void *dev_id) +dev_tx_timeout_work(void* arg) { - struct net_device *dev = (struct net_device *)dev_id; + struct net_device *dev = (struct net_device *)arg; priv_data_t *priv = dev->priv; P_ERROR("%s: Tx Timeout for port %d\n", dev->name, priv->port_id); - down(maintenance_mutex); + down(&maintenance_mutex); port_disable(dev); - /* Note to user: Consider performing other reset operations here (such as PHY reset), - * if it is known to help the Tx Flow to become "unstuck" + /* Note to user: Consider performing other reset operations here (such as + * PHY reset), if it is known to help the Tx Flow to become "unstuck" */ port_enable(dev); - up(maintenance_mutex); + up(&maintenance_mutex); } @@ -2095,8 +1992,7 @@ priv_data_t *priv = dev->priv; TRACE; - schedule_task(&priv->tq_timeout); - + queue_work(npe_eth_workqueue, &priv->tx_timeout_job); } /* update the maximum msdu value for this device */ @@ -2153,7 +2049,7 @@ } /* safer to stop maintenance task while bringing port down and up */ - down(maintenance_mutex); + down(&maintenance_mutex); if (npeRunning[priv->port_id]) { @@ -2164,9 +2060,9 @@ if (ixEthDBFilteringPortMaximumFrameSizeSet(priv->port_id, new_msdu_size)) { - P_ERROR("%s: ixEthDBFilteringPortMaximumFrameSizeSet failed for port %d\n", - dev->name, priv->port_id); - up(maintenance_mutex); + P_ERROR("%s: ixEthDBFilteringPortMaximumFrameSizeSet failed" + " for port %d\n", dev->name, priv->port_id); + up(&maintenance_mutex); return -1; } @@ -2182,7 +2078,7 @@ port_enable(dev); } - up(maintenance_mutex); + up(&maintenance_mutex); return 0; } @@ -2211,27 +2107,29 @@ /* Read MII PHY register */ case SIOCGMIIREG: case SIOCDEVPRIVATE+1: - down (miiAccessMutex); /* lock the MII register access mutex */ - if ((res = ixEthAccMiiReadRtn (data->phy_id, data->reg_num, &data->val_out))) + down (&miiAccessMutex); + if ((res = ixEthAccMiiReadRtn(data->phy_id, data->reg_num, + &data->val_out))) { P_ERROR("Error reading MII reg %d on phy %d\n", data->reg_num, data->phy_id); res = -1; } - up (miiAccessMutex); /* release the MII register access mutex */ + up (&miiAccessMutex); return res; /* Write MII PHY register */ case SIOCSMIIREG: case SIOCDEVPRIVATE+2: - down (miiAccessMutex); /* lock the MII register access mutex */ - if ((res = ixEthAccMiiWriteRtn (data->phy_id, data->reg_num, data->val_in))) + down (&miiAccessMutex); + if ((res = ixEthAccMiiWriteRtn(data->phy_id, data->reg_num, + data->val_in))) { P_ERROR("Error reading MII reg %d on phy %d\n", data->reg_num, data->phy_id); res = -1; } - up (miiAccessMutex); /* release the MII register access mutex */ + up (&miiAccessMutex); return res; /* set the MTU size */ @@ -2268,7 +2166,7 @@ TRACE; - invalidate_dcache_range((unsigned int)ðStats, sizeof(ethStats)); + IX_ACC_DATA_CACHE_INVALIDATE(ðStats, sizeof(ethStats)); if ((res = ixEthAccMibIIStatsGetClear(priv->port_id, ðStats))) { P_ERROR("%s: ixEthAccMibIIStatsGet failed for port %d, res = %d\n", @@ -2341,7 +2239,7 @@ if (request_irq(IXP425_INT_LVL_QM1, dev_qmgr_os_isr, SA_SHIRQ, - MODULE_NAME, + DRV_NAME, (void *)IRQ_ANY_PARAMETER)) { P_ERROR("Failed to request_irq to Queue Manager interrupt!\n"); @@ -2384,15 +2282,6 @@ BOOL physcan[IXP425_ETH_ACC_MII_MAX_ADDR]; int i, phy_found, num_phys_to_set; - /* initialise the MII register access mutex */ - miiAccessMutex = (struct semaphore *) kmalloc(sizeof(struct semaphore), GFP_KERNEL); - if (!miiAccessMutex) - return -ENOMEM; - - init_MUTEX(miiAccessMutex); - - TRACE; - /* detect the PHYs (ethMii requires the PHYs to be detected) * and provides a maximum number of PHYs to search for. */ @@ -2450,8 +2339,7 @@ for (i=0; i < IX_ETH_ACC_NUMBER_OF_PORTS && i < num_phys_to_set; i++) { - P_INFO("%s%d is using the PHY at address %d\n", - DEVICE_NAME, i, phyAddresses[i]); + P_INFO("npe%d is using the PHY at address %d\n", i, phyAddresses[i]); /* Set the MAC to the same duplex mode as the phy */ ixEthAccPortDuplexModeSet(i, @@ -2515,7 +2403,7 @@ static struct Qdisc_ops dev_qdisc_ops = { - NULL, NULL, "ixp425_eth", 0, + NULL, NULL, DRV_NAME, 0, dev_qdisc_no_enqueue, dev_qdisc_no_dequeue, dev_qdisc_no_enqueue, @@ -2526,30 +2414,16 @@ /* Initialize device structs. * Resource allocation is deffered until do_dev_open */ -static int __devinit dev_eth_probe(struct net_device *dev) +static int __devinit dev_eth_probe(struct device *dev) { - static int found_devices = 0; - priv_data_t *priv; + int res = -ENOMEM; + struct platform_device *pdev = to_platform_device(dev); + struct net_device *ndev = dev_get_drvdata(dev); + priv_data_t *priv = (priv_data_t*)ndev->priv; TRACE; - /* there are only 2 available ports */ - if (found_devices >= IX_ETH_ACC_NUMBER_OF_PORTS) - return -ENODEV; - - SET_MODULE_OWNER(dev); - - /* set device name */ - strcpy(dev->name, found_devices ? DEVICE_NAME "1" : DEVICE_NAME "0"); - - /* allocate and initialize priv struct */ - priv = dev->priv = kmalloc(sizeof(priv_data_t), GFP_KERNEL); - if (dev->priv == NULL) - return -ENOMEM; - - memset(dev->priv, 0, sizeof(priv_data_t)); - - priv->port_id = found_devices ? IX_ETH_PORT_2 : IX_ETH_PORT_1; + priv->port_id = pdev->id; /* initialize RX pool */ if (IX_MBUF_POOL_INIT_NO_ALLOC(&priv->rx_pool, @@ -2559,9 +2433,8 @@ "IXP425 Ethernet driver Rx Pool")) { P_ERROR("%s: Buffer Pool init failed on port %d\n", - dev->name, priv->port_id); - kfree(dev->priv); - return -ENOMEM; + ndev->name, priv->port_id); + goto out; } /* initialize TX pool */ @@ -2573,100 +2446,139 @@ "IXP425 Ethernet driver Tx Pool")) { P_ERROR("%s: Buffer Pool init failed on port %d\n", - dev->name, priv->port_id); - kfree(dev->priv); - return -ENOMEM; - } - - /* initialise the MII register access mutex */ - priv->maintenanceCheckThreadComplete = (struct semaphore *) - kmalloc(sizeof(struct semaphore), GFP_KERNEL); - if (!priv->maintenanceCheckThreadComplete) - { - kfree(dev->priv); - return -ENOMEM; + ndev->name, priv->port_id); + goto out; } - priv->lock = SPIN_LOCK_UNLOCKED; - init_MUTEX(priv->maintenanceCheckThreadComplete); - priv->maintenanceCheckStopped = TRUE; - /* initialize ethernet device (default handlers) */ - ether_setup(dev); + /* initialise the MII and tx timeout jobs */ + INIT_WORK(&priv->mii_job, dev_media_check_work, ndev); + INIT_WORK(&priv->tx_timeout_job, dev_tx_timeout_work, ndev); /* fill in dev struct callbacks with customized handlers */ - dev->open = do_dev_open; - dev->stop = do_dev_stop; - - dev->hard_start_xmit = dev_hard_start_xmit; + ndev->open = do_dev_open; + ndev->stop = do_dev_stop; + ndev->hard_start_xmit = dev_hard_start_xmit; + ndev->watchdog_timeo = DEV_WATCHDOG_TIMEO; + ndev->tx_timeout = dev_tx_timeout; + ndev->change_mtu = dev_change_mtu; + ndev->do_ioctl = do_dev_ioctl; + ndev->get_stats = dev_get_stats; + ndev->set_multicast_list = dev_set_multicast_list; + ndev->flags |= IFF_MULTICAST; - dev->watchdog_timeo = DEV_WATCHDOG_TIMEO; - dev->tx_timeout = dev_tx_timeout; - dev->change_mtu = dev_change_mtu; - dev->do_ioctl = do_dev_ioctl; - dev->get_stats = dev_get_stats; - dev->set_multicast_list = dev_set_multicast_list; - dev->flags |= IFF_MULTICAST; + ndev->set_mac_address = dev_set_mac_address; - dev->set_mac_address = dev_set_mac_address; - - memcpy(dev->dev_addr, &default_mac_addr[priv->port_id].macAddress, IX_IEEE803_MAC_ADDRESS_SIZE); + memcpy(ndev->dev_addr, &default_mac_addr[priv->port_id].macAddress, + IX_IEEE803_MAC_ADDRESS_SIZE); /* update the internal packet size */ - dev_change_msdu(dev, dev->mtu + dev->hard_header_len); - - priv->tq_timeout.routine = dev_tx_timeout_task; - priv->tq_timeout.data = (void *)dev; + dev_change_msdu(ndev, ndev->mtu + ndev->hard_header_len); /* set the scheduling discipline */ - priv->qdisc = qdisc_create_dflt(dev, &dev_qdisc_ops); - dev->qdisc_sleeping = priv->qdisc; - dev->qdisc = priv->qdisc; + priv->qdisc = qdisc_create_dflt(ndev, &dev_qdisc_ops); + ndev->qdisc_sleeping = priv->qdisc; + ndev->qdisc = priv->qdisc; - if (!dev->qdisc_sleeping) + if (!ndev->qdisc_sleeping) { P_ERROR("%s: qdisc_create_dflt failed on port %d\n", - dev->name, priv->port_id); - kfree(dev->priv); - return -ENOMEM; + ndev->name, priv->port_id); + goto out; } /* set the internal maximum queueing capabilities */ - dev->tx_queue_len = TX_QUEUE_MAX_LEN; + ndev->tx_queue_len = TX_QUEUE_MAX_LEN; - if (!netif_queue_stopped(dev)) - { - dev->trans_start = jiffies; - netif_stop_queue(dev); - } + if ((res = register_netdev(ndev))) + P_ERROR("Failed to register netdev. res = %d\n", res); + out: + return res; +} - found_devices++; +static int __devinit npe_eth_init_device(struct device *dev) +{ + int res = -ENOMEM; + struct platform_device *pdev = to_platform_device(dev); + struct net_device *ndev = alloc_etherdev(sizeof(priv_data_t)); + if (ndev == NULL) { + P_ERROR("could not allocate device.\n"); + goto out; + } + SET_MODULE_OWNER(ndev); + SET_NETDEV_DEV(ndev, dev); + ixEthAccTxSchedulingDisciplineSet(pdev->id, FIFO_NO_PRIORITY); + dev_set_drvdata(dev, ndev); + res = dev_eth_probe(dev); + if (res == 0) { + /* This was added in v0.1.8 of the driver. It seems that we need to + * enable the port before the user can set a mac address for the port + * using 'ifconfig hw ether ...'. To enable the port we must first + * register Q callbacks, so we register the portDisable callbacks to + * ensure that no buffers are passed up to the kernel until the port is + * brought up properly (ifconfig up) + */ + ixEthAccPortTxDoneCallbackRegister(pdev->id, + tx_done_disable_cb, + (UINT32)ndev); + ixEthAccPortRxCallbackRegister(pdev->id, + rx_disable_cb, + (UINT32)ndev); + port_enable(ndev); + } else { + dev_set_drvdata(dev, NULL); + kfree(ndev); + } +out: + return res; +} +static int __devexit npe_eth_fini_device(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + dev_set_drvdata(dev, NULL); + unregister_netdev(ndev); + kfree(ndev); return 0; } + /* Module initialization and cleanup */ -#ifdef MODULE +static struct device_driver npe_eth_driver = { + .name = DRV_NAME, + .bus = &platform_bus_type, + .probe = npe_eth_init_device, + .remove = npe_eth_fini_device, +}; -static struct net_device ixp425_devices[IX_ETH_ACC_NUMBER_OF_PORTS]; +static struct platform_device npe_eth_devs[] = { + { + .name = DRV_NAME, + .id = IX_ETH_PORT_1, + }, + { + .name = DRV_NAME, + .id = IX_ETH_PORT_2, + } +}; -int init_module(void) +static int __init ixp425_eth_init(void) { int res, i; - struct net_device *dev; TRACE; - P_INFO("\nInitializing IXP425 NPE Ethernet driver software v. " MODULE_VERSION " \n"); + P_INFO("\nInitializing IXP425 NPE Ethernet driver software v. " + DRV_VERSION " \n"); TRACE; - /* Enable/disable the EthDB MAC Learning & Filtering feature. - * This is a half-bridge feature, and should be disabled if this interface - * is used on a bridge with other non-NPE ethernet interfaces. - * This is because the NPE's are not aware of the other interfaces and thus - * may incorrectly filter (drop) incoming traffic correctly bound for another - * interface on the bridge. + /* Enable/disable the EthDB MAC Learning & Filtering feature. This is a + * half-bridge feature, and should be disabled if this interface is used on + * a bridge with other non-NPE ethernet interfaces. This is because the + * NPE's are not aware of the other interfaces and thus may incorrectly + * filter (drop) incoming traffic correctly bound for another interface on + * the bridge. */ if (npe_learning) { @@ -2716,47 +2628,56 @@ TRACE; - /* Set scheduling discipline */ - ixEthAccTxSchedulingDisciplineSet(IX_ETH_PORT_1, FIFO_NO_PRIORITY); - ixEthAccTxSchedulingDisciplineSet(IX_ETH_PORT_2, FIFO_NO_PRIORITY); + npe_eth_workqueue = create_workqueue(DRV_NAME); + if (npe_eth_workqueue == NULL) + return -ENOMEM; - for (i = 0; i < IX_ETH_ACC_NUMBER_OF_PORTS; i++) - { - dev = &ixp425_devices[i]; + TRACE; - dev->init = dev_eth_probe; + /* Do not start the EthDB maintenance thread if learning & filtering feature + is disabled */ + if (npe_learning) + { + schedule_db_maintenance(); + } - if ((res = register_netdev(dev))) - P_ERROR("Failed to register netdev. res = %d\n", res); + TRACE; - TRACE; + /* set the softirq rx queue thresholds (These numbers are based on tuning + * experiments) maxbacklog = (netdev_max_backlog * 10) / 63; + */ + if (netdev_max_backlog == 0) + { + netdev_max_backlog = 290; /* system default */ + } + netdev_max_backlog /= BACKLOG_TUNE; - /* This was added in v0.1.8 of the driver. - * It seems that we need to enable the port before the user can set - * a mac address for the port using 'ifconfig hw ether ...'. - * To enable the port we must first register Q callbacks, so we register - * the portDisable callbacks to ensure that no buffers are passed up to the - * kernel until the port is brought up properly (ifconfig up) - */ - ixEthAccPortTxDoneCallbackRegister(i, - tx_done_disable_cb, - (UINT32)dev); - ixEthAccPortRxCallbackRegister(i, - rx_disable_cb, - (UINT32)dev); + res = driver_register(&npe_eth_driver); + if (res != 0) { + P_ERROR("Failed to register NPE EThernet driver (res = %d)\n", res); + return res; + } - port_enable(dev); + TRACE; + + res = platform_device_register(&npe_eth_devs[0]); + if (res != 0) { + P_ERROR("Failed to register NPE platform device 0 (res = %d)\n", res); + return res; + } + res = platform_device_register(&npe_eth_devs[1]); + if (res != 0) { + P_ERROR("Failed to register NPE platform device 1 (res = %d)\n", res); + return res; } TRACE; if (no_csr_init == 0) /* module parameter */ { - /* The QMgr dispatch entry point is called from the - * IXP425_INT_LVL_QM1 irq (which will trigger - * an interrupt for every packet) - * This function setup the datapath in polling mode - * for better performances. + /* The QMgr dispatch entry point is called from the IXP425_INT_LVL_QM1 irq + * (which will trigger an interrupt for every packet) This function setup + * the datapath in polling mode for better performances. */ if ((res = ethAcc_datapath_poll_setup())) @@ -2767,42 +2688,11 @@ TRACE; - /* initialise the DB Maintenance task mutex */ - maintenance_mutex = (struct semaphore *) kmalloc(sizeof(struct semaphore), GFP_KERNEL); - if (!maintenance_mutex) - return -ENOMEM; - - init_MUTEX(maintenance_mutex); - - TRACE; - - /* Do not start the EthDB maintenance thread if learning & filtering feature is disabled */ - if (npe_learning) - { - maintenance_timer_set(); - } - - TRACE; - - /* set the softirq rx queue thresholds - * (These numbers are based on tuning experiments) - * maxbacklog = (netdev_max_backlog * 10) / 63; - */ - if (netdev_max_backlog == 0) - { - netdev_max_backlog = 290; /* system default */ - } - netdev_max_backlog /= BACKLOG_TUNE; - - TRACE; - return 0; } -void cleanup_module(void) +static void __exit ixp425_eth_exit(void) { - int i; - TRACE; /* We can only get here when the module use count is 0, @@ -2821,25 +2711,27 @@ irq_qm1_used = 0; } - /* stop the maintenance timer */ - maintenance_timer_clear(); - /* wait for maintenance task to complete */ - down(maintenance_mutex); - up(maintenance_mutex); - - for (i = 0; i < IX_ETH_ACC_NUMBER_OF_PORTS; i++) - { - struct net_device *dev = &ixp425_devices[i]; - if (dev->priv != NULL) - { - unregister_netdev(dev); - kfree(dev->priv); - dev->priv = NULL; - } - } + /* stop the maintenance timer and destroy the driver's work queue */ + cancel_db_maintenance(); + flush_workqueue(npe_eth_workqueue); + destroy_workqueue(npe_eth_workqueue); TRACE; + + driver_unregister(&npe_eth_driver); + platform_device_unregister(&npe_eth_devs[1]); + platform_device_unregister(&npe_eth_devs[0]); } -#endif /* MODULE */ +module_init(ixp425_eth_init); +module_exit(ixp425_eth_exit); +/* + * Local Variables: + * c-basic-offset: 4 + * c-file-offsets: ((knr-argdecl-intro . 0) (substatement-open . 0)) + * End: + * + * This file's unique identifier, don't change it. + * arch-tag: 15d00e77-3443-42b6-8d08-51b9a70f1987 + */