162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* drivers/atm/idt77105.c - IDT77105 (PHY) driver */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* Written 1999 by Greg Banks, NEC Australia <gnb@linuxfan.com>. Based on suni.c */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/mm.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/atmdev.h> 1262306a36Sopenharmony_ci#include <linux/sonet.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/timer.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/capability.h> 1762306a36Sopenharmony_ci#include <linux/atm_idt77105.h> 1862306a36Sopenharmony_ci#include <linux/spinlock.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <asm/param.h> 2162306a36Sopenharmony_ci#include <linux/uaccess.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "idt77105.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#undef GENERAL_DEBUG 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#ifdef GENERAL_DEBUG 2862306a36Sopenharmony_ci#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) 2962306a36Sopenharmony_ci#else 3062306a36Sopenharmony_ci#define DPRINTK(format,args...) 3162306a36Sopenharmony_ci#endif 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct idt77105_priv { 3562306a36Sopenharmony_ci struct idt77105_stats stats; /* link diagnostics */ 3662306a36Sopenharmony_ci struct atm_dev *dev; /* device back-pointer */ 3762306a36Sopenharmony_ci struct idt77105_priv *next; 3862306a36Sopenharmony_ci int loop_mode; 3962306a36Sopenharmony_ci unsigned char old_mcr; /* storage of MCR reg while signal lost */ 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(idt77105_priv_lock); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define PRIV(dev) ((struct idt77105_priv *) dev->phy_data) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define PUT(val,reg) dev->ops->phy_put(dev,val,IDT77105_##reg) 4762306a36Sopenharmony_ci#define GET(reg) dev->ops->phy_get(dev,IDT77105_##reg) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void idt77105_stats_timer_func(struct timer_list *); 5062306a36Sopenharmony_cistatic void idt77105_restart_timer_func(struct timer_list *); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic DEFINE_TIMER(stats_timer, idt77105_stats_timer_func); 5462306a36Sopenharmony_cistatic DEFINE_TIMER(restart_timer, idt77105_restart_timer_func); 5562306a36Sopenharmony_cistatic int start_timer = 1; 5662306a36Sopenharmony_cistatic struct idt77105_priv *idt77105_all = NULL; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Retrieve the value of one of the IDT77105's counters. 6062306a36Sopenharmony_ci * `counter' is one of the IDT77105_CTRSEL_* constants. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistatic u16 get_counter(struct atm_dev *dev, int counter) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci u16 val; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* write the counter bit into PHY register 6 */ 6762306a36Sopenharmony_ci PUT(counter, CTRSEL); 6862306a36Sopenharmony_ci /* read the low 8 bits from register 4 */ 6962306a36Sopenharmony_ci val = GET(CTRLO); 7062306a36Sopenharmony_ci /* read the high 8 bits from register 5 */ 7162306a36Sopenharmony_ci val |= GET(CTRHI)<<8; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return val; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * Timer function called every second to gather statistics 7862306a36Sopenharmony_ci * from the 77105. This is done because the h/w registers 7962306a36Sopenharmony_ci * will overflow if not read at least once per second. The 8062306a36Sopenharmony_ci * kernel's stats are much higher precision. Also, having 8162306a36Sopenharmony_ci * a separate copy of the stats allows implementation of 8262306a36Sopenharmony_ci * an ioctl which gathers the stats *without* zero'ing them. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistatic void idt77105_stats_timer_func(struct timer_list *unused) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct idt77105_priv *walk; 8762306a36Sopenharmony_ci struct atm_dev *dev; 8862306a36Sopenharmony_ci struct idt77105_stats *stats; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci DPRINTK("IDT77105 gathering statistics\n"); 9162306a36Sopenharmony_ci for (walk = idt77105_all; walk; walk = walk->next) { 9262306a36Sopenharmony_ci dev = walk->dev; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci stats = &walk->stats; 9562306a36Sopenharmony_ci stats->symbol_errors += get_counter(dev, IDT77105_CTRSEL_SEC); 9662306a36Sopenharmony_ci stats->tx_cells += get_counter(dev, IDT77105_CTRSEL_TCC); 9762306a36Sopenharmony_ci stats->rx_cells += get_counter(dev, IDT77105_CTRSEL_RCC); 9862306a36Sopenharmony_ci stats->rx_hec_errors += get_counter(dev, IDT77105_CTRSEL_RHEC); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci if (!start_timer) mod_timer(&stats_timer,jiffies+IDT77105_STATS_TIMER_PERIOD); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * A separate timer func which handles restarting PHY chips which 10662306a36Sopenharmony_ci * have had the cable re-inserted after being pulled out. This is 10762306a36Sopenharmony_ci * done by polling the Good Signal Bit in the Interrupt Status 10862306a36Sopenharmony_ci * register every 5 seconds. The other technique (checking Good 10962306a36Sopenharmony_ci * Signal Bit in the interrupt handler) cannot be used because PHY 11062306a36Sopenharmony_ci * interrupts need to be disabled when the cable is pulled out 11162306a36Sopenharmony_ci * to avoid lots of spurious cell error interrupts. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_cistatic void idt77105_restart_timer_func(struct timer_list *unused) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct idt77105_priv *walk; 11662306a36Sopenharmony_ci struct atm_dev *dev; 11762306a36Sopenharmony_ci unsigned char istat; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci DPRINTK("IDT77105 checking for cable re-insertion\n"); 12062306a36Sopenharmony_ci for (walk = idt77105_all; walk; walk = walk->next) { 12162306a36Sopenharmony_ci dev = walk->dev; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (dev->signal != ATM_PHY_SIG_LOST) 12462306a36Sopenharmony_ci continue; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci istat = GET(ISTAT); /* side effect: clears all interrupt status bits */ 12762306a36Sopenharmony_ci if (istat & IDT77105_ISTAT_GOODSIG) { 12862306a36Sopenharmony_ci /* Found signal again */ 12962306a36Sopenharmony_ci atm_dev_signal_change(dev, ATM_PHY_SIG_FOUND); 13062306a36Sopenharmony_ci printk(KERN_NOTICE "%s(itf %d): signal detected again\n", 13162306a36Sopenharmony_ci dev->type,dev->number); 13262306a36Sopenharmony_ci /* flush the receive FIFO */ 13362306a36Sopenharmony_ci PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG); 13462306a36Sopenharmony_ci /* re-enable interrupts */ 13562306a36Sopenharmony_ci PUT( walk->old_mcr ,MCR); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci if (!start_timer) mod_timer(&restart_timer,jiffies+IDT77105_RESTART_TIMER_PERIOD); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int fetch_stats(struct atm_dev *dev,struct idt77105_stats __user *arg,int zero) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci unsigned long flags; 14562306a36Sopenharmony_ci struct idt77105_stats stats; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci spin_lock_irqsave(&idt77105_priv_lock, flags); 14862306a36Sopenharmony_ci memcpy(&stats, &PRIV(dev)->stats, sizeof(struct idt77105_stats)); 14962306a36Sopenharmony_ci if (zero) 15062306a36Sopenharmony_ci memset(&PRIV(dev)->stats, 0, sizeof(struct idt77105_stats)); 15162306a36Sopenharmony_ci spin_unlock_irqrestore(&idt77105_priv_lock, flags); 15262306a36Sopenharmony_ci if (arg == NULL) 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci return copy_to_user(arg, &stats, 15562306a36Sopenharmony_ci sizeof(struct idt77105_stats)) ? -EFAULT : 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int set_loopback(struct atm_dev *dev,int mode) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int diag; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci diag = GET(DIAG) & ~IDT77105_DIAG_LCMASK; 16462306a36Sopenharmony_ci switch (mode) { 16562306a36Sopenharmony_ci case ATM_LM_NONE: 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case ATM_LM_LOC_ATM: 16862306a36Sopenharmony_ci diag |= IDT77105_DIAG_LC_PHY_LOOPBACK; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci case ATM_LM_RMT_ATM: 17162306a36Sopenharmony_ci diag |= IDT77105_DIAG_LC_LINE_LOOPBACK; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci default: 17462306a36Sopenharmony_ci return -EINVAL; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci PUT(diag,DIAG); 17762306a36Sopenharmony_ci printk(KERN_NOTICE "%s(%d) Loopback mode is: %s\n", dev->type, 17862306a36Sopenharmony_ci dev->number, 17962306a36Sopenharmony_ci (mode == ATM_LM_NONE ? "NONE" : 18062306a36Sopenharmony_ci (mode == ATM_LM_LOC_ATM ? "DIAG (local)" : 18162306a36Sopenharmony_ci (mode == IDT77105_DIAG_LC_LINE_LOOPBACK ? "LOOP (remote)" : 18262306a36Sopenharmony_ci "unknown"))) 18362306a36Sopenharmony_ci ); 18462306a36Sopenharmony_ci PRIV(dev)->loop_mode = mode; 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int idt77105_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci printk(KERN_NOTICE "%s(%d) idt77105_ioctl() called\n",dev->type,dev->number); 19262306a36Sopenharmony_ci switch (cmd) { 19362306a36Sopenharmony_ci case IDT77105_GETSTATZ: 19462306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) return -EPERM; 19562306a36Sopenharmony_ci fallthrough; 19662306a36Sopenharmony_ci case IDT77105_GETSTAT: 19762306a36Sopenharmony_ci return fetch_stats(dev, arg, cmd == IDT77105_GETSTATZ); 19862306a36Sopenharmony_ci case ATM_SETLOOP: 19962306a36Sopenharmony_ci return set_loopback(dev,(int)(unsigned long) arg); 20062306a36Sopenharmony_ci case ATM_GETLOOP: 20162306a36Sopenharmony_ci return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ? 20262306a36Sopenharmony_ci -EFAULT : 0; 20362306a36Sopenharmony_ci case ATM_QUERYLOOP: 20462306a36Sopenharmony_ci return put_user(ATM_LM_LOC_ATM | ATM_LM_RMT_ATM, 20562306a36Sopenharmony_ci (int __user *) arg) ? -EFAULT : 0; 20662306a36Sopenharmony_ci default: 20762306a36Sopenharmony_ci return -ENOIOCTLCMD; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void idt77105_int(struct atm_dev *dev) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci unsigned char istat; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci istat = GET(ISTAT); /* side effect: clears all interrupt status bits */ 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci DPRINTK("IDT77105 generated an interrupt, istat=%02x\n", (unsigned)istat); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (istat & IDT77105_ISTAT_RSCC) { 22262306a36Sopenharmony_ci /* Rx Signal Condition Change - line went up or down */ 22362306a36Sopenharmony_ci if (istat & IDT77105_ISTAT_GOODSIG) { /* signal detected again */ 22462306a36Sopenharmony_ci /* This should not happen (restart timer does it) but JIC */ 22562306a36Sopenharmony_ci atm_dev_signal_change(dev, ATM_PHY_SIG_FOUND); 22662306a36Sopenharmony_ci } else { /* signal lost */ 22762306a36Sopenharmony_ci /* 22862306a36Sopenharmony_ci * Disable interrupts and stop all transmission and 22962306a36Sopenharmony_ci * reception - the restart timer will restore these. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci PRIV(dev)->old_mcr = GET(MCR); 23262306a36Sopenharmony_ci PUT( 23362306a36Sopenharmony_ci (PRIV(dev)->old_mcr| 23462306a36Sopenharmony_ci IDT77105_MCR_DREC| 23562306a36Sopenharmony_ci IDT77105_MCR_DRIC| 23662306a36Sopenharmony_ci IDT77105_MCR_HALTTX 23762306a36Sopenharmony_ci ) & ~IDT77105_MCR_EIP, MCR); 23862306a36Sopenharmony_ci atm_dev_signal_change(dev, ATM_PHY_SIG_LOST); 23962306a36Sopenharmony_ci printk(KERN_NOTICE "%s(itf %d): signal lost\n", 24062306a36Sopenharmony_ci dev->type,dev->number); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (istat & IDT77105_ISTAT_RFO) { 24562306a36Sopenharmony_ci /* Rx FIFO Overrun -- perform a FIFO flush */ 24662306a36Sopenharmony_ci PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG); 24762306a36Sopenharmony_ci printk(KERN_NOTICE "%s(itf %d): receive FIFO overrun\n", 24862306a36Sopenharmony_ci dev->type,dev->number); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci#ifdef GENERAL_DEBUG 25162306a36Sopenharmony_ci if (istat & (IDT77105_ISTAT_HECERR | IDT77105_ISTAT_SCR | 25262306a36Sopenharmony_ci IDT77105_ISTAT_RSE)) { 25362306a36Sopenharmony_ci /* normally don't care - just report in stats */ 25462306a36Sopenharmony_ci printk(KERN_NOTICE "%s(itf %d): received cell with error\n", 25562306a36Sopenharmony_ci dev->type,dev->number); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci#endif 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int idt77105_start(struct atm_dev *dev) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci unsigned long flags; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (!(dev->phy_data = kmalloc(sizeof(struct idt77105_priv),GFP_KERNEL))) 26662306a36Sopenharmony_ci return -ENOMEM; 26762306a36Sopenharmony_ci PRIV(dev)->dev = dev; 26862306a36Sopenharmony_ci spin_lock_irqsave(&idt77105_priv_lock, flags); 26962306a36Sopenharmony_ci PRIV(dev)->next = idt77105_all; 27062306a36Sopenharmony_ci idt77105_all = PRIV(dev); 27162306a36Sopenharmony_ci spin_unlock_irqrestore(&idt77105_priv_lock, flags); 27262306a36Sopenharmony_ci memset(&PRIV(dev)->stats,0,sizeof(struct idt77105_stats)); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* initialise dev->signal from Good Signal Bit */ 27562306a36Sopenharmony_ci atm_dev_signal_change(dev, 27662306a36Sopenharmony_ci GET(ISTAT) & IDT77105_ISTAT_GOODSIG ? 27762306a36Sopenharmony_ci ATM_PHY_SIG_FOUND : ATM_PHY_SIG_LOST); 27862306a36Sopenharmony_ci if (dev->signal == ATM_PHY_SIG_LOST) 27962306a36Sopenharmony_ci printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type, 28062306a36Sopenharmony_ci dev->number); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* initialise loop mode from hardware */ 28362306a36Sopenharmony_ci switch ( GET(DIAG) & IDT77105_DIAG_LCMASK ) { 28462306a36Sopenharmony_ci case IDT77105_DIAG_LC_NORMAL: 28562306a36Sopenharmony_ci PRIV(dev)->loop_mode = ATM_LM_NONE; 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci case IDT77105_DIAG_LC_PHY_LOOPBACK: 28862306a36Sopenharmony_ci PRIV(dev)->loop_mode = ATM_LM_LOC_ATM; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case IDT77105_DIAG_LC_LINE_LOOPBACK: 29162306a36Sopenharmony_ci PRIV(dev)->loop_mode = ATM_LM_RMT_ATM; 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* enable interrupts, e.g. on loss of signal */ 29662306a36Sopenharmony_ci PRIV(dev)->old_mcr = GET(MCR); 29762306a36Sopenharmony_ci if (dev->signal == ATM_PHY_SIG_FOUND) { 29862306a36Sopenharmony_ci PRIV(dev)->old_mcr |= IDT77105_MCR_EIP; 29962306a36Sopenharmony_ci PUT(PRIV(dev)->old_mcr, MCR); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci idt77105_stats_timer_func(0); /* clear 77105 counters */ 30462306a36Sopenharmony_ci (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci spin_lock_irqsave(&idt77105_priv_lock, flags); 30762306a36Sopenharmony_ci if (start_timer) { 30862306a36Sopenharmony_ci start_timer = 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci stats_timer.expires = jiffies+IDT77105_STATS_TIMER_PERIOD; 31162306a36Sopenharmony_ci add_timer(&stats_timer); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci restart_timer.expires = jiffies+IDT77105_RESTART_TIMER_PERIOD; 31462306a36Sopenharmony_ci add_timer(&restart_timer); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci spin_unlock_irqrestore(&idt77105_priv_lock, flags); 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int idt77105_stop(struct atm_dev *dev) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct idt77105_priv *walk, *prev; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci DPRINTK("%s(itf %d): stopping IDT77105\n",dev->type,dev->number); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* disable interrupts */ 32862306a36Sopenharmony_ci PUT( GET(MCR) & ~IDT77105_MCR_EIP, MCR ); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* detach private struct from atm_dev & free */ 33162306a36Sopenharmony_ci for (prev = NULL, walk = idt77105_all ; 33262306a36Sopenharmony_ci walk != NULL; 33362306a36Sopenharmony_ci prev = walk, walk = walk->next) { 33462306a36Sopenharmony_ci if (walk->dev == dev) { 33562306a36Sopenharmony_ci if (prev != NULL) 33662306a36Sopenharmony_ci prev->next = walk->next; 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci idt77105_all = walk->next; 33962306a36Sopenharmony_ci dev->phy = NULL; 34062306a36Sopenharmony_ci dev->phy_data = NULL; 34162306a36Sopenharmony_ci kfree(walk); 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic const struct atmphy_ops idt77105_ops = { 35162306a36Sopenharmony_ci .start = idt77105_start, 35262306a36Sopenharmony_ci .ioctl = idt77105_ioctl, 35362306a36Sopenharmony_ci .interrupt = idt77105_int, 35462306a36Sopenharmony_ci .stop = idt77105_stop, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ciint idt77105_init(struct atm_dev *dev) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci dev->phy = &idt77105_ops; 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ciEXPORT_SYMBOL(idt77105_init); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void __exit idt77105_exit(void) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci /* turn off timers */ 36962306a36Sopenharmony_ci del_timer_sync(&stats_timer); 37062306a36Sopenharmony_ci del_timer_sync(&restart_timer); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cimodule_exit(idt77105_exit); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 376