18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* drivers/atm/idt77105.c - IDT77105 (PHY) driver */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* Written 1999 by Greg Banks, NEC Australia <gnb@linuxfan.com>. Based on suni.c */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/mm.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 128c2ecf20Sopenharmony_ci#include <linux/sonet.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/timer.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/capability.h> 178c2ecf20Sopenharmony_ci#include <linux/atm_idt77105.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <asm/param.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "idt77105.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#undef GENERAL_DEBUG 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#ifdef GENERAL_DEBUG 288c2ecf20Sopenharmony_ci#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) 298c2ecf20Sopenharmony_ci#else 308c2ecf20Sopenharmony_ci#define DPRINTK(format,args...) 318c2ecf20Sopenharmony_ci#endif 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct idt77105_priv { 358c2ecf20Sopenharmony_ci struct idt77105_stats stats; /* link diagnostics */ 368c2ecf20Sopenharmony_ci struct atm_dev *dev; /* device back-pointer */ 378c2ecf20Sopenharmony_ci struct idt77105_priv *next; 388c2ecf20Sopenharmony_ci int loop_mode; 398c2ecf20Sopenharmony_ci unsigned char old_mcr; /* storage of MCR reg while signal lost */ 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(idt77105_priv_lock); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define PRIV(dev) ((struct idt77105_priv *) dev->phy_data) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define PUT(val,reg) dev->ops->phy_put(dev,val,IDT77105_##reg) 478c2ecf20Sopenharmony_ci#define GET(reg) dev->ops->phy_get(dev,IDT77105_##reg) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void idt77105_stats_timer_func(struct timer_list *); 508c2ecf20Sopenharmony_cistatic void idt77105_restart_timer_func(struct timer_list *); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic DEFINE_TIMER(stats_timer, idt77105_stats_timer_func); 548c2ecf20Sopenharmony_cistatic DEFINE_TIMER(restart_timer, idt77105_restart_timer_func); 558c2ecf20Sopenharmony_cistatic int start_timer = 1; 568c2ecf20Sopenharmony_cistatic struct idt77105_priv *idt77105_all = NULL; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Retrieve the value of one of the IDT77105's counters. 608c2ecf20Sopenharmony_ci * `counter' is one of the IDT77105_CTRSEL_* constants. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistatic u16 get_counter(struct atm_dev *dev, int counter) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci u16 val; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* write the counter bit into PHY register 6 */ 678c2ecf20Sopenharmony_ci PUT(counter, CTRSEL); 688c2ecf20Sopenharmony_ci /* read the low 8 bits from register 4 */ 698c2ecf20Sopenharmony_ci val = GET(CTRLO); 708c2ecf20Sopenharmony_ci /* read the high 8 bits from register 5 */ 718c2ecf20Sopenharmony_ci val |= GET(CTRHI)<<8; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return val; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * Timer function called every second to gather statistics 788c2ecf20Sopenharmony_ci * from the 77105. This is done because the h/w registers 798c2ecf20Sopenharmony_ci * will overflow if not read at least once per second. The 808c2ecf20Sopenharmony_ci * kernel's stats are much higher precision. Also, having 818c2ecf20Sopenharmony_ci * a separate copy of the stats allows implementation of 828c2ecf20Sopenharmony_ci * an ioctl which gathers the stats *without* zero'ing them. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic void idt77105_stats_timer_func(struct timer_list *unused) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct idt77105_priv *walk; 878c2ecf20Sopenharmony_ci struct atm_dev *dev; 888c2ecf20Sopenharmony_ci struct idt77105_stats *stats; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci DPRINTK("IDT77105 gathering statistics\n"); 918c2ecf20Sopenharmony_ci for (walk = idt77105_all; walk; walk = walk->next) { 928c2ecf20Sopenharmony_ci dev = walk->dev; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci stats = &walk->stats; 958c2ecf20Sopenharmony_ci stats->symbol_errors += get_counter(dev, IDT77105_CTRSEL_SEC); 968c2ecf20Sopenharmony_ci stats->tx_cells += get_counter(dev, IDT77105_CTRSEL_TCC); 978c2ecf20Sopenharmony_ci stats->rx_cells += get_counter(dev, IDT77105_CTRSEL_RCC); 988c2ecf20Sopenharmony_ci stats->rx_hec_errors += get_counter(dev, IDT77105_CTRSEL_RHEC); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci if (!start_timer) mod_timer(&stats_timer,jiffies+IDT77105_STATS_TIMER_PERIOD); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * A separate timer func which handles restarting PHY chips which 1068c2ecf20Sopenharmony_ci * have had the cable re-inserted after being pulled out. This is 1078c2ecf20Sopenharmony_ci * done by polling the Good Signal Bit in the Interrupt Status 1088c2ecf20Sopenharmony_ci * register every 5 seconds. The other technique (checking Good 1098c2ecf20Sopenharmony_ci * Signal Bit in the interrupt handler) cannot be used because PHY 1108c2ecf20Sopenharmony_ci * interrupts need to be disabled when the cable is pulled out 1118c2ecf20Sopenharmony_ci * to avoid lots of spurious cell error interrupts. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistatic void idt77105_restart_timer_func(struct timer_list *unused) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct idt77105_priv *walk; 1168c2ecf20Sopenharmony_ci struct atm_dev *dev; 1178c2ecf20Sopenharmony_ci unsigned char istat; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci DPRINTK("IDT77105 checking for cable re-insertion\n"); 1208c2ecf20Sopenharmony_ci for (walk = idt77105_all; walk; walk = walk->next) { 1218c2ecf20Sopenharmony_ci dev = walk->dev; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (dev->signal != ATM_PHY_SIG_LOST) 1248c2ecf20Sopenharmony_ci continue; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci istat = GET(ISTAT); /* side effect: clears all interrupt status bits */ 1278c2ecf20Sopenharmony_ci if (istat & IDT77105_ISTAT_GOODSIG) { 1288c2ecf20Sopenharmony_ci /* Found signal again */ 1298c2ecf20Sopenharmony_ci atm_dev_signal_change(dev, ATM_PHY_SIG_FOUND); 1308c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s(itf %d): signal detected again\n", 1318c2ecf20Sopenharmony_ci dev->type,dev->number); 1328c2ecf20Sopenharmony_ci /* flush the receive FIFO */ 1338c2ecf20Sopenharmony_ci PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG); 1348c2ecf20Sopenharmony_ci /* re-enable interrupts */ 1358c2ecf20Sopenharmony_ci PUT( walk->old_mcr ,MCR); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci if (!start_timer) mod_timer(&restart_timer,jiffies+IDT77105_RESTART_TIMER_PERIOD); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int fetch_stats(struct atm_dev *dev,struct idt77105_stats __user *arg,int zero) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci unsigned long flags; 1458c2ecf20Sopenharmony_ci struct idt77105_stats stats; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci spin_lock_irqsave(&idt77105_priv_lock, flags); 1488c2ecf20Sopenharmony_ci memcpy(&stats, &PRIV(dev)->stats, sizeof(struct idt77105_stats)); 1498c2ecf20Sopenharmony_ci if (zero) 1508c2ecf20Sopenharmony_ci memset(&PRIV(dev)->stats, 0, sizeof(struct idt77105_stats)); 1518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&idt77105_priv_lock, flags); 1528c2ecf20Sopenharmony_ci if (arg == NULL) 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci return copy_to_user(arg, &stats, 1558c2ecf20Sopenharmony_ci sizeof(struct idt77105_stats)) ? -EFAULT : 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int set_loopback(struct atm_dev *dev,int mode) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int diag; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci diag = GET(DIAG) & ~IDT77105_DIAG_LCMASK; 1648c2ecf20Sopenharmony_ci switch (mode) { 1658c2ecf20Sopenharmony_ci case ATM_LM_NONE: 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci case ATM_LM_LOC_ATM: 1688c2ecf20Sopenharmony_ci diag |= IDT77105_DIAG_LC_PHY_LOOPBACK; 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci case ATM_LM_RMT_ATM: 1718c2ecf20Sopenharmony_ci diag |= IDT77105_DIAG_LC_LINE_LOOPBACK; 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci default: 1748c2ecf20Sopenharmony_ci return -EINVAL; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci PUT(diag,DIAG); 1778c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s(%d) Loopback mode is: %s\n", dev->type, 1788c2ecf20Sopenharmony_ci dev->number, 1798c2ecf20Sopenharmony_ci (mode == ATM_LM_NONE ? "NONE" : 1808c2ecf20Sopenharmony_ci (mode == ATM_LM_LOC_ATM ? "DIAG (local)" : 1818c2ecf20Sopenharmony_ci (mode == IDT77105_DIAG_LC_LINE_LOOPBACK ? "LOOP (remote)" : 1828c2ecf20Sopenharmony_ci "unknown"))) 1838c2ecf20Sopenharmony_ci ); 1848c2ecf20Sopenharmony_ci PRIV(dev)->loop_mode = mode; 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int idt77105_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s(%d) idt77105_ioctl() called\n",dev->type,dev->number); 1928c2ecf20Sopenharmony_ci switch (cmd) { 1938c2ecf20Sopenharmony_ci case IDT77105_GETSTATZ: 1948c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) return -EPERM; 1958c2ecf20Sopenharmony_ci fallthrough; 1968c2ecf20Sopenharmony_ci case IDT77105_GETSTAT: 1978c2ecf20Sopenharmony_ci return fetch_stats(dev, arg, cmd == IDT77105_GETSTATZ); 1988c2ecf20Sopenharmony_ci case ATM_SETLOOP: 1998c2ecf20Sopenharmony_ci return set_loopback(dev,(int)(unsigned long) arg); 2008c2ecf20Sopenharmony_ci case ATM_GETLOOP: 2018c2ecf20Sopenharmony_ci return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ? 2028c2ecf20Sopenharmony_ci -EFAULT : 0; 2038c2ecf20Sopenharmony_ci case ATM_QUERYLOOP: 2048c2ecf20Sopenharmony_ci return put_user(ATM_LM_LOC_ATM | ATM_LM_RMT_ATM, 2058c2ecf20Sopenharmony_ci (int __user *) arg) ? -EFAULT : 0; 2068c2ecf20Sopenharmony_ci default: 2078c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void idt77105_int(struct atm_dev *dev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci unsigned char istat; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci istat = GET(ISTAT); /* side effect: clears all interrupt status bits */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci DPRINTK("IDT77105 generated an interrupt, istat=%02x\n", (unsigned)istat); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (istat & IDT77105_ISTAT_RSCC) { 2228c2ecf20Sopenharmony_ci /* Rx Signal Condition Change - line went up or down */ 2238c2ecf20Sopenharmony_ci if (istat & IDT77105_ISTAT_GOODSIG) { /* signal detected again */ 2248c2ecf20Sopenharmony_ci /* This should not happen (restart timer does it) but JIC */ 2258c2ecf20Sopenharmony_ci atm_dev_signal_change(dev, ATM_PHY_SIG_FOUND); 2268c2ecf20Sopenharmony_ci } else { /* signal lost */ 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * Disable interrupts and stop all transmission and 2298c2ecf20Sopenharmony_ci * reception - the restart timer will restore these. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci PRIV(dev)->old_mcr = GET(MCR); 2328c2ecf20Sopenharmony_ci PUT( 2338c2ecf20Sopenharmony_ci (PRIV(dev)->old_mcr| 2348c2ecf20Sopenharmony_ci IDT77105_MCR_DREC| 2358c2ecf20Sopenharmony_ci IDT77105_MCR_DRIC| 2368c2ecf20Sopenharmony_ci IDT77105_MCR_HALTTX 2378c2ecf20Sopenharmony_ci ) & ~IDT77105_MCR_EIP, MCR); 2388c2ecf20Sopenharmony_ci atm_dev_signal_change(dev, ATM_PHY_SIG_LOST); 2398c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s(itf %d): signal lost\n", 2408c2ecf20Sopenharmony_ci dev->type,dev->number); 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (istat & IDT77105_ISTAT_RFO) { 2458c2ecf20Sopenharmony_ci /* Rx FIFO Overrun -- perform a FIFO flush */ 2468c2ecf20Sopenharmony_ci PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG); 2478c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s(itf %d): receive FIFO overrun\n", 2488c2ecf20Sopenharmony_ci dev->type,dev->number); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci#ifdef GENERAL_DEBUG 2518c2ecf20Sopenharmony_ci if (istat & (IDT77105_ISTAT_HECERR | IDT77105_ISTAT_SCR | 2528c2ecf20Sopenharmony_ci IDT77105_ISTAT_RSE)) { 2538c2ecf20Sopenharmony_ci /* normally don't care - just report in stats */ 2548c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s(itf %d): received cell with error\n", 2558c2ecf20Sopenharmony_ci dev->type,dev->number); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci#endif 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int idt77105_start(struct atm_dev *dev) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci unsigned long flags; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (!(dev->phy_data = kmalloc(sizeof(struct idt77105_priv),GFP_KERNEL))) 2668c2ecf20Sopenharmony_ci return -ENOMEM; 2678c2ecf20Sopenharmony_ci PRIV(dev)->dev = dev; 2688c2ecf20Sopenharmony_ci spin_lock_irqsave(&idt77105_priv_lock, flags); 2698c2ecf20Sopenharmony_ci PRIV(dev)->next = idt77105_all; 2708c2ecf20Sopenharmony_ci idt77105_all = PRIV(dev); 2718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&idt77105_priv_lock, flags); 2728c2ecf20Sopenharmony_ci memset(&PRIV(dev)->stats,0,sizeof(struct idt77105_stats)); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* initialise dev->signal from Good Signal Bit */ 2758c2ecf20Sopenharmony_ci atm_dev_signal_change(dev, 2768c2ecf20Sopenharmony_ci GET(ISTAT) & IDT77105_ISTAT_GOODSIG ? 2778c2ecf20Sopenharmony_ci ATM_PHY_SIG_FOUND : ATM_PHY_SIG_LOST); 2788c2ecf20Sopenharmony_ci if (dev->signal == ATM_PHY_SIG_LOST) 2798c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type, 2808c2ecf20Sopenharmony_ci dev->number); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* initialise loop mode from hardware */ 2838c2ecf20Sopenharmony_ci switch ( GET(DIAG) & IDT77105_DIAG_LCMASK ) { 2848c2ecf20Sopenharmony_ci case IDT77105_DIAG_LC_NORMAL: 2858c2ecf20Sopenharmony_ci PRIV(dev)->loop_mode = ATM_LM_NONE; 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci case IDT77105_DIAG_LC_PHY_LOOPBACK: 2888c2ecf20Sopenharmony_ci PRIV(dev)->loop_mode = ATM_LM_LOC_ATM; 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case IDT77105_DIAG_LC_LINE_LOOPBACK: 2918c2ecf20Sopenharmony_ci PRIV(dev)->loop_mode = ATM_LM_RMT_ATM; 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* enable interrupts, e.g. on loss of signal */ 2968c2ecf20Sopenharmony_ci PRIV(dev)->old_mcr = GET(MCR); 2978c2ecf20Sopenharmony_ci if (dev->signal == ATM_PHY_SIG_FOUND) { 2988c2ecf20Sopenharmony_ci PRIV(dev)->old_mcr |= IDT77105_MCR_EIP; 2998c2ecf20Sopenharmony_ci PUT(PRIV(dev)->old_mcr, MCR); 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci idt77105_stats_timer_func(0); /* clear 77105 counters */ 3048c2ecf20Sopenharmony_ci (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci spin_lock_irqsave(&idt77105_priv_lock, flags); 3078c2ecf20Sopenharmony_ci if (start_timer) { 3088c2ecf20Sopenharmony_ci start_timer = 0; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci stats_timer.expires = jiffies+IDT77105_STATS_TIMER_PERIOD; 3118c2ecf20Sopenharmony_ci add_timer(&stats_timer); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci restart_timer.expires = jiffies+IDT77105_RESTART_TIMER_PERIOD; 3148c2ecf20Sopenharmony_ci add_timer(&restart_timer); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&idt77105_priv_lock, flags); 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int idt77105_stop(struct atm_dev *dev) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct idt77105_priv *walk, *prev; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci DPRINTK("%s(itf %d): stopping IDT77105\n",dev->type,dev->number); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* disable interrupts */ 3288c2ecf20Sopenharmony_ci PUT( GET(MCR) & ~IDT77105_MCR_EIP, MCR ); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* detach private struct from atm_dev & free */ 3318c2ecf20Sopenharmony_ci for (prev = NULL, walk = idt77105_all ; 3328c2ecf20Sopenharmony_ci walk != NULL; 3338c2ecf20Sopenharmony_ci prev = walk, walk = walk->next) { 3348c2ecf20Sopenharmony_ci if (walk->dev == dev) { 3358c2ecf20Sopenharmony_ci if (prev != NULL) 3368c2ecf20Sopenharmony_ci prev->next = walk->next; 3378c2ecf20Sopenharmony_ci else 3388c2ecf20Sopenharmony_ci idt77105_all = walk->next; 3398c2ecf20Sopenharmony_ci dev->phy = NULL; 3408c2ecf20Sopenharmony_ci dev->phy_data = NULL; 3418c2ecf20Sopenharmony_ci kfree(walk); 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic const struct atmphy_ops idt77105_ops = { 3518c2ecf20Sopenharmony_ci .start = idt77105_start, 3528c2ecf20Sopenharmony_ci .ioctl = idt77105_ioctl, 3538c2ecf20Sopenharmony_ci .interrupt = idt77105_int, 3548c2ecf20Sopenharmony_ci .stop = idt77105_stop, 3558c2ecf20Sopenharmony_ci}; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ciint idt77105_init(struct atm_dev *dev) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci dev->phy = &idt77105_ops; 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(idt77105_init); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic void __exit idt77105_exit(void) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci /* turn off timers */ 3698c2ecf20Sopenharmony_ci del_timer_sync(&stats_timer); 3708c2ecf20Sopenharmony_ci del_timer_sync(&restart_timer); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cimodule_exit(idt77105_exit); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 376