162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/atm/suni.c - S/UNI PHY driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Supports the following: 662306a36Sopenharmony_ci * PMC PM5346 S/UNI LITE 762306a36Sopenharmony_ci * PMC PM5350 S/UNI 155 ULTRA 862306a36Sopenharmony_ci * PMC PM5355 S/UNI 622 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/jiffies.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/errno.h> 1862306a36Sopenharmony_ci#include <linux/atmdev.h> 1962306a36Sopenharmony_ci#include <linux/sonet.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/timer.h> 2262306a36Sopenharmony_ci#include <linux/init.h> 2362306a36Sopenharmony_ci#include <linux/capability.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <asm/param.h> 2662306a36Sopenharmony_ci#include <linux/uaccess.h> 2762306a36Sopenharmony_ci#include <linux/atomic.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "suni.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#if 0 3362306a36Sopenharmony_ci#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) 3462306a36Sopenharmony_ci#else 3562306a36Sopenharmony_ci#define DPRINTK(format,args...) 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define PRIV(dev) ((struct suni_priv *) dev->phy_data) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg) 4162306a36Sopenharmony_ci#define GET(reg) dev->ops->phy_get(dev,SUNI_##reg) 4262306a36Sopenharmony_ci#define REG_CHANGE(mask,shift,value,reg) \ 4362306a36Sopenharmony_ci PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct timer_list poll_timer; 4762306a36Sopenharmony_cistatic struct suni_priv *sunis = NULL; 4862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(sunis_lock); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define ADD_LIMITED(s,v) \ 5262306a36Sopenharmony_ci atomic_add((v),&stats->s); \ 5362306a36Sopenharmony_ci if (atomic_read(&stats->s) < 0) atomic_set(&stats->s,INT_MAX); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void suni_hz(struct timer_list *timer) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct suni_priv *walk; 5962306a36Sopenharmony_ci struct atm_dev *dev; 6062306a36Sopenharmony_ci struct k_sonet_stats *stats; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci for (walk = sunis; walk; walk = walk->next) { 6362306a36Sopenharmony_ci dev = walk->dev; 6462306a36Sopenharmony_ci stats = &walk->sonet_stats; 6562306a36Sopenharmony_ci PUT(0,MRI); /* latch counters */ 6662306a36Sopenharmony_ci udelay(1); 6762306a36Sopenharmony_ci ADD_LIMITED(section_bip,(GET(RSOP_SBL) & 0xff) | 6862306a36Sopenharmony_ci ((GET(RSOP_SBM) & 0xff) << 8)); 6962306a36Sopenharmony_ci ADD_LIMITED(line_bip,(GET(RLOP_LBL) & 0xff) | 7062306a36Sopenharmony_ci ((GET(RLOP_LB) & 0xff) << 8) | 7162306a36Sopenharmony_ci ((GET(RLOP_LBM) & 0xf) << 16)); 7262306a36Sopenharmony_ci ADD_LIMITED(path_bip,(GET(RPOP_PBL) & 0xff) | 7362306a36Sopenharmony_ci ((GET(RPOP_PBM) & 0xff) << 8)); 7462306a36Sopenharmony_ci ADD_LIMITED(line_febe,(GET(RLOP_LFL) & 0xff) | 7562306a36Sopenharmony_ci ((GET(RLOP_LF) & 0xff) << 8) | 7662306a36Sopenharmony_ci ((GET(RLOP_LFM) & 0xf) << 16)); 7762306a36Sopenharmony_ci ADD_LIMITED(path_febe,(GET(RPOP_PFL) & 0xff) | 7862306a36Sopenharmony_ci ((GET(RPOP_PFM) & 0xff) << 8)); 7962306a36Sopenharmony_ci ADD_LIMITED(corr_hcs,GET(RACP_CHEC) & 0xff); 8062306a36Sopenharmony_ci ADD_LIMITED(uncorr_hcs,GET(RACP_UHEC) & 0xff); 8162306a36Sopenharmony_ci ADD_LIMITED(rx_cells,(GET(RACP_RCCL) & 0xff) | 8262306a36Sopenharmony_ci ((GET(RACP_RCC) & 0xff) << 8) | 8362306a36Sopenharmony_ci ((GET(RACP_RCCM) & 7) << 16)); 8462306a36Sopenharmony_ci ADD_LIMITED(tx_cells,(GET(TACP_TCCL) & 0xff) | 8562306a36Sopenharmony_ci ((GET(TACP_TCC) & 0xff) << 8) | 8662306a36Sopenharmony_ci ((GET(TACP_TCCM) & 7) << 16)); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci if (timer) mod_timer(&poll_timer,jiffies+HZ); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#undef ADD_LIMITED 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int fetch_stats(struct atm_dev *dev,struct sonet_stats __user *arg,int zero) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct sonet_stats tmp; 9862306a36Sopenharmony_ci int error = 0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp); 10162306a36Sopenharmony_ci if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp)); 10262306a36Sopenharmony_ci if (zero && !error) sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp); 10362306a36Sopenharmony_ci return error ? -EFAULT : 0; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#define HANDLE_FLAG(flag,reg,bit) \ 10862306a36Sopenharmony_ci if (todo & flag) { \ 10962306a36Sopenharmony_ci if (set) PUT(GET(reg) | bit,reg); \ 11062306a36Sopenharmony_ci else PUT(GET(reg) & ~bit,reg); \ 11162306a36Sopenharmony_ci todo &= ~flag; \ 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int change_diag(struct atm_dev *dev,void __user *arg,int set) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int todo; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (get_user(todo,(int __user *)arg)) return -EFAULT; 12062306a36Sopenharmony_ci HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8); 12162306a36Sopenharmony_ci HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP); 12262306a36Sopenharmony_ci HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3); 12362306a36Sopenharmony_ci HANDLE_FLAG(SONET_INS_FRAME,RSOP_CIE,SUNI_RSOP_CIE_FOOF); 12462306a36Sopenharmony_ci HANDLE_FLAG(SONET_INS_LAIS,TSOP_CTRL,SUNI_TSOP_CTRL_LAIS); 12562306a36Sopenharmony_ci HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS); 12662306a36Sopenharmony_ci HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS); 12762306a36Sopenharmony_ci HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS); 12862306a36Sopenharmony_ci return put_user(todo,(int __user *)arg) ? -EFAULT : 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#undef HANDLE_FLAG 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int get_diag(struct atm_dev *dev,void __user *arg) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int set; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci set = 0; 14062306a36Sopenharmony_ci if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DBIP8) set |= SONET_INS_SBIP; 14162306a36Sopenharmony_ci if (GET(TLOP_DIAG) & SUNI_TLOP_DIAG_DBIP) set |= SONET_INS_LBIP; 14262306a36Sopenharmony_ci if (GET(TPOP_CD) & SUNI_TPOP_DIAG_DB3) set |= SONET_INS_PBIP; 14362306a36Sopenharmony_ci /* SONET_INS_FRAME is one-shot only */ 14462306a36Sopenharmony_ci if (GET(TSOP_CTRL) & SUNI_TSOP_CTRL_LAIS) set |= SONET_INS_LAIS; 14562306a36Sopenharmony_ci if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS; 14662306a36Sopenharmony_ci if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS; 14762306a36Sopenharmony_ci if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS; 14862306a36Sopenharmony_ci return put_user(set,(int __user *)arg) ? -EFAULT : 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int set_loopback(struct atm_dev *dev,int mode) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci unsigned char control; 15562306a36Sopenharmony_ci int reg, dle, lle; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { 15862306a36Sopenharmony_ci reg = SUNI_MCM; 15962306a36Sopenharmony_ci dle = SUNI_MCM_DLE; 16062306a36Sopenharmony_ci lle = SUNI_MCM_LLE; 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci reg = SUNI_MCT; 16362306a36Sopenharmony_ci dle = SUNI_MCT_DLE; 16462306a36Sopenharmony_ci lle = SUNI_MCT_LLE; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci control = dev->ops->phy_get(dev, reg) & ~(dle | lle); 16862306a36Sopenharmony_ci switch (mode) { 16962306a36Sopenharmony_ci case ATM_LM_NONE: 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci case ATM_LM_LOC_PHY: 17262306a36Sopenharmony_ci control |= dle; 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci case ATM_LM_RMT_PHY: 17562306a36Sopenharmony_ci control |= lle; 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci default: 17862306a36Sopenharmony_ci return -EINVAL; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci dev->ops->phy_put(dev, control, reg); 18162306a36Sopenharmony_ci PRIV(dev)->loop_mode = mode; 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * SONET vs. SDH Configuration 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci * Z0INS (register 0x06): 0 for SONET, 1 for SDH 18962306a36Sopenharmony_ci * ENSS (register 0x3D): 0 for SONET, 1 for SDH 19062306a36Sopenharmony_ci * LEN16 (register 0x28): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD) 19162306a36Sopenharmony_ci * LEN16 (register 0x50): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD) 19262306a36Sopenharmony_ci * S[1:0] (register 0x46): 00 for SONET, 10 for SDH 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int set_sonet(struct atm_dev *dev) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { 19862306a36Sopenharmony_ci PUT(GET(RPOP_RC) & ~SUNI_RPOP_RC_ENSS, RPOP_RC); 19962306a36Sopenharmony_ci PUT(GET(SSTB_CTRL) & ~SUNI_SSTB_CTRL_LEN16, SSTB_CTRL); 20062306a36Sopenharmony_ci PUT(GET(SPTB_CTRL) & ~SUNI_SPTB_CTRL_LEN16, SPTB_CTRL); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT, 20462306a36Sopenharmony_ci SUNI_TPOP_S_SONET, TPOP_APM); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int set_sdh(struct atm_dev *dev) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { 21262306a36Sopenharmony_ci PUT(GET(RPOP_RC) | SUNI_RPOP_RC_ENSS, RPOP_RC); 21362306a36Sopenharmony_ci PUT(GET(SSTB_CTRL) | SUNI_SSTB_CTRL_LEN16, SSTB_CTRL); 21462306a36Sopenharmony_ci PUT(GET(SPTB_CTRL) | SUNI_SPTB_CTRL_LEN16, SPTB_CTRL); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT, 21862306a36Sopenharmony_ci SUNI_TPOP_S_SDH, TPOP_APM); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int get_framing(struct atm_dev *dev, void __user *arg) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci int framing; 22762306a36Sopenharmony_ci unsigned char s; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci s = (GET(TPOP_APM) & SUNI_TPOP_APM_S) >> SUNI_TPOP_APM_S_SHIFT; 23162306a36Sopenharmony_ci if (s == SUNI_TPOP_S_SONET) 23262306a36Sopenharmony_ci framing = SONET_FRAME_SONET; 23362306a36Sopenharmony_ci else 23462306a36Sopenharmony_ci framing = SONET_FRAME_SDH; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return put_user(framing, (int __user *) arg) ? -EFAULT : 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int set_framing(struct atm_dev *dev, void __user *arg) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci int mode; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (get_user(mode, (int __user *) arg)) 24462306a36Sopenharmony_ci return -EFAULT; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (mode == SONET_FRAME_SONET) 24762306a36Sopenharmony_ci return set_sonet(dev); 24862306a36Sopenharmony_ci else if (mode == SONET_FRAME_SDH) 24962306a36Sopenharmony_ci return set_sdh(dev); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return -EINVAL; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci switch (cmd) { 25862306a36Sopenharmony_ci case SONET_GETSTATZ: 25962306a36Sopenharmony_ci case SONET_GETSTAT: 26062306a36Sopenharmony_ci return fetch_stats(dev, arg, cmd == SONET_GETSTATZ); 26162306a36Sopenharmony_ci case SONET_SETDIAG: 26262306a36Sopenharmony_ci return change_diag(dev,arg,1); 26362306a36Sopenharmony_ci case SONET_CLRDIAG: 26462306a36Sopenharmony_ci return change_diag(dev,arg,0); 26562306a36Sopenharmony_ci case SONET_GETDIAG: 26662306a36Sopenharmony_ci return get_diag(dev,arg); 26762306a36Sopenharmony_ci case SONET_SETFRAMING: 26862306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 26962306a36Sopenharmony_ci return -EPERM; 27062306a36Sopenharmony_ci return set_framing(dev, arg); 27162306a36Sopenharmony_ci case SONET_GETFRAMING: 27262306a36Sopenharmony_ci return get_framing(dev, arg); 27362306a36Sopenharmony_ci case SONET_GETFRSENSE: 27462306a36Sopenharmony_ci return -EINVAL; 27562306a36Sopenharmony_ci case ATM_SETLOOP: 27662306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 27762306a36Sopenharmony_ci return -EPERM; 27862306a36Sopenharmony_ci return set_loopback(dev,(int)(unsigned long)arg); 27962306a36Sopenharmony_ci case ATM_GETLOOP: 28062306a36Sopenharmony_ci return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ? 28162306a36Sopenharmony_ci -EFAULT : 0; 28262306a36Sopenharmony_ci case ATM_QUERYLOOP: 28362306a36Sopenharmony_ci return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY, 28462306a36Sopenharmony_ci (int __user *) arg) ? -EFAULT : 0; 28562306a36Sopenharmony_ci default: 28662306a36Sopenharmony_ci return -ENOIOCTLCMD; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void poll_los(struct atm_dev *dev) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci atm_dev_signal_change(dev, 29462306a36Sopenharmony_ci GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ? 29562306a36Sopenharmony_ci ATM_PHY_SIG_LOST : ATM_PHY_SIG_FOUND); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic void suni_int(struct atm_dev *dev) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci poll_los(dev); 30262306a36Sopenharmony_ci printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number, 30362306a36Sopenharmony_ci dev->signal == ATM_PHY_SIG_LOST ? "lost" : "detected again"); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int suni_start(struct atm_dev *dev) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci unsigned long flags; 31062306a36Sopenharmony_ci int first; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci spin_lock_irqsave(&sunis_lock,flags); 31362306a36Sopenharmony_ci first = !sunis; 31462306a36Sopenharmony_ci PRIV(dev)->next = sunis; 31562306a36Sopenharmony_ci sunis = PRIV(dev); 31662306a36Sopenharmony_ci spin_unlock_irqrestore(&sunis_lock,flags); 31762306a36Sopenharmony_ci memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats)); 31862306a36Sopenharmony_ci PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE); 31962306a36Sopenharmony_ci /* interrupt on loss of signal */ 32062306a36Sopenharmony_ci poll_los(dev); /* ... and clear SUNI interrupts */ 32162306a36Sopenharmony_ci if (dev->signal == ATM_PHY_SIG_LOST) 32262306a36Sopenharmony_ci printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type, 32362306a36Sopenharmony_ci dev->number); 32462306a36Sopenharmony_ci PRIV(dev)->loop_mode = ATM_LM_NONE; 32562306a36Sopenharmony_ci suni_hz(NULL); /* clear SUNI counters */ 32662306a36Sopenharmony_ci (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ 32762306a36Sopenharmony_ci if (first) { 32862306a36Sopenharmony_ci timer_setup(&poll_timer, suni_hz, 0); 32962306a36Sopenharmony_ci poll_timer.expires = jiffies+HZ; 33062306a36Sopenharmony_ci#if 0 33162306a36Sopenharmony_ciprintk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.list.prev, 33262306a36Sopenharmony_ci (unsigned long) poll_timer.list.next); 33362306a36Sopenharmony_ci#endif 33462306a36Sopenharmony_ci add_timer(&poll_timer); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int suni_stop(struct atm_dev *dev) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct suni_priv **walk; 34362306a36Sopenharmony_ci unsigned long flags; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* let SAR driver worry about stopping interrupts */ 34662306a36Sopenharmony_ci spin_lock_irqsave(&sunis_lock,flags); 34762306a36Sopenharmony_ci for (walk = &sunis; *walk != PRIV(dev); 34862306a36Sopenharmony_ci walk = &PRIV((*walk)->dev)->next); 34962306a36Sopenharmony_ci *walk = PRIV((*walk)->dev)->next; 35062306a36Sopenharmony_ci if (!sunis) del_timer_sync(&poll_timer); 35162306a36Sopenharmony_ci spin_unlock_irqrestore(&sunis_lock,flags); 35262306a36Sopenharmony_ci kfree(PRIV(dev)); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic const struct atmphy_ops suni_ops = { 35962306a36Sopenharmony_ci .start = suni_start, 36062306a36Sopenharmony_ci .ioctl = suni_ioctl, 36162306a36Sopenharmony_ci .interrupt = suni_int, 36262306a36Sopenharmony_ci .stop = suni_stop, 36362306a36Sopenharmony_ci}; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ciint suni_init(struct atm_dev *dev) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci unsigned char mri; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL))) 37162306a36Sopenharmony_ci return -ENOMEM; 37262306a36Sopenharmony_ci PRIV(dev)->dev = dev; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci mri = GET(MRI); /* reset SUNI */ 37562306a36Sopenharmony_ci PRIV(dev)->type = (mri & SUNI_MRI_TYPE) >> SUNI_MRI_TYPE_SHIFT; 37662306a36Sopenharmony_ci PUT(mri | SUNI_MRI_RESET,MRI); 37762306a36Sopenharmony_ci PUT(mri,MRI); 37862306a36Sopenharmony_ci PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */ 37962306a36Sopenharmony_ci set_sonet(dev); 38062306a36Sopenharmony_ci REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP, 38162306a36Sopenharmony_ci TACP_IUCHP); /* idle cells */ 38262306a36Sopenharmony_ci PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP); 38362306a36Sopenharmony_ci dev->phy = &suni_ops; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ciEXPORT_SYMBOL(suni_init); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 391