1 /* drivers/atm/suni.c - PMC PM5346 SUNI (PHY) driver */ 3 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 6 #include <linux/module.h> 7 #include <linux/sched.h> 8 #include <linux/kernel.h> 10 #include <linux/errno.h> 11 #include <linux/atmdev.h> 12 #include <linux/sonet.h> 13 #include <linux/delay.h> 14 #include <linux/timer.h> 15 #include <linux/init.h> 16 #include <linux/capability.h> 17 #include <linux/atm_suni.h> 18 #include <asm/system.h> 19 #include <asm/param.h> 20 #include <asm/uaccess.h> 21 #include <asm/atomic.h> 27 #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) 29 #define DPRINTK(format,args...) 34 struct k_sonet_stats sonet_stats
;/* link diagnostics */ 35 int loop_mode
;/* loopback mode */ 36 struct atm_dev
*dev
;/* device back-pointer */ 37 struct suni_priv
*next
;/* next SUNI */ 41 #define PRIV(dev) ((struct suni_priv *) dev->phy_data) 43 #define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg) 44 #define GET(reg) dev->ops->phy_get(dev,SUNI_##reg) 45 #define REG_CHANGE(mask,shift,value,reg) \ 46 PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg) 49 static struct timer_list poll_timer
; 50 static struct suni_priv
*sunis
= NULL
; 51 static spinlock_t sunis_lock
= SPIN_LOCK_UNLOCKED
; 54 #define ADD_LIMITED(s,v) \ 55 atomic_add((v),&stats->s); \ 56 if (atomic_read(&stats->s) < 0) atomic_set(&stats->s,INT_MAX); 59 static voidsuni_hz(unsigned long from_timer
) 61 struct suni_priv
*walk
; 63 struct k_sonet_stats
*stats
; 65 for(walk
= sunis
; walk
; walk
= walk
->next
) { 67 stats
= &walk
->sonet_stats
; 68 PUT(0,MRI
);/* latch counters */ 70 ADD_LIMITED(section_bip
,(GET(RSOP_SBL
) &0xff) | 71 ((GET(RSOP_SBM
) &0xff) <<8)); 72 ADD_LIMITED(line_bip
,(GET(RLOP_LBL
) &0xff) | 73 ((GET(RLOP_LB
) &0xff) <<8) | 74 ((GET(RLOP_LBM
) &0xf) <<16)); 75 ADD_LIMITED(path_bip
,(GET(RPOP_PBL
) &0xff) | 76 ((GET(RPOP_PBM
) &0xff) <<8)); 77 ADD_LIMITED(line_febe
,(GET(RLOP_LFL
) &0xff) | 78 ((GET(RLOP_LF
) &0xff) <<8) | 79 ((GET(RLOP_LFM
) &0xf) <<16)); 80 ADD_LIMITED(path_febe
,(GET(RPOP_PFL
) &0xff) | 81 ((GET(RPOP_PFM
) &0xff) <<8)); 82 ADD_LIMITED(corr_hcs
,GET(RACP_CHEC
) &0xff); 83 ADD_LIMITED(uncorr_hcs
,GET(RACP_UHEC
) &0xff); 84 ADD_LIMITED(rx_cells
,(GET(RACP_RCCL
) &0xff) | 85 ((GET(RACP_RCC
) &0xff) <<8) | 86 ((GET(RACP_RCCM
) &7) <<16)); 87 ADD_LIMITED(tx_cells
,(GET(TACP_TCCL
) &0xff) | 88 ((GET(TACP_TCC
) &0xff) <<8) | 89 ((GET(TACP_TCCM
) &7) <<16)); 91 if(from_timer
)mod_timer(&poll_timer
,jiffies
+HZ
); 98 static intfetch_stats(struct atm_dev
*dev
,struct sonet_stats
*arg
,int zero
) 100 struct sonet_stats tmp
; 103 sonet_copy_stats(&PRIV(dev
)->sonet_stats
,&tmp
); 104 if(arg
) error
=copy_to_user(arg
,&tmp
,sizeof(tmp
)); 105 if(zero
&& !error
)sonet_subtract_stats(&PRIV(dev
)->sonet_stats
,&tmp
); 106 return error
? -EFAULT
:0; 110 #define HANDLE_FLAG(flag,reg,bit) \ 112 if (set) PUT(GET(reg) | bit,reg); \ 113 else PUT(GET(reg) & ~bit,reg); \ 118 static intchange_diag(struct atm_dev
*dev
,void*arg
,int set
) 122 if(get_user(todo
,(int*) arg
))return-EFAULT
; 123 HANDLE_FLAG(SONET_INS_SBIP
,TSOP_DIAG
,SUNI_TSOP_DIAG_DBIP8
); 124 HANDLE_FLAG(SONET_INS_LBIP
,TLOP_DIAG
,SUNI_TLOP_DIAG_DBIP
); 125 HANDLE_FLAG(SONET_INS_PBIP
,TPOP_CD
,SUNI_TPOP_DIAG_DB3
); 126 HANDLE_FLAG(SONET_INS_FRAME
,RSOP_CIE
,SUNI_RSOP_CIE_FOOF
); 127 HANDLE_FLAG(SONET_INS_LAIS
,TSOP_CTRL
,SUNI_TSOP_CTRL_LAIS
); 128 HANDLE_FLAG(SONET_INS_PAIS
,TPOP_CD
,SUNI_TPOP_DIAG_PAIS
); 129 HANDLE_FLAG(SONET_INS_LOS
,TSOP_DIAG
,SUNI_TSOP_DIAG_DLOS
); 130 HANDLE_FLAG(SONET_INS_HCS
,TACP_CS
,SUNI_TACP_CS_DHCS
); 131 returnput_user(todo
,(int*) arg
) ? -EFAULT
:0; 138 static intget_diag(struct atm_dev
*dev
,void*arg
) 143 if(GET(TSOP_DIAG
) & SUNI_TSOP_DIAG_DBIP8
) set
|= SONET_INS_SBIP
; 144 if(GET(TLOP_DIAG
) & SUNI_TLOP_DIAG_DBIP
) set
|= SONET_INS_LBIP
; 145 if(GET(TPOP_CD
) & SUNI_TPOP_DIAG_DB3
) set
|= SONET_INS_PBIP
; 146 /* SONET_INS_FRAME is one-shot only */ 147 if(GET(TSOP_CTRL
) & SUNI_TSOP_CTRL_LAIS
) set
|= SONET_INS_LAIS
; 148 if(GET(TPOP_CD
) & SUNI_TPOP_DIAG_PAIS
) set
|= SONET_INS_PAIS
; 149 if(GET(TSOP_DIAG
) & SUNI_TSOP_DIAG_DLOS
) set
|= SONET_INS_LOS
; 150 if(GET(TACP_CS
) & SUNI_TACP_CS_DHCS
) set
|= SONET_INS_HCS
; 151 returnput_user(set
,(int*) arg
) ? -EFAULT
:0; 155 static intset_loopback(struct atm_dev
*dev
,int mode
) 157 unsigned char control
; 159 control
=GET(MCT
) & ~(SUNI_MCT_DLE
| SUNI_MCT_LLE
); 164 control
|= SUNI_MCT_DLE
; 167 control
|= SUNI_MCT_LLE
; 173 PRIV(dev
)->loop_mode
= mode
; 178 static intsuni_ioctl(struct atm_dev
*dev
,unsigned int cmd
,void*arg
) 183 returnfetch_stats(dev
,(struct sonet_stats
*) arg
, 184 cmd
== SONET_GETSTATZ
); 186 returnchange_diag(dev
,arg
,1); 188 returnchange_diag(dev
,arg
,0); 190 returnget_diag(dev
,arg
); 191 case SONET_SETFRAMING
: 192 if(arg
!= SONET_FRAME_SONET
)return-EINVAL
; 194 case SONET_GETFRAMING
: 195 returnput_user(SONET_FRAME_SONET
,(int*) arg
) ? 197 case SONET_GETFRSENSE
: 200 returnset_loopback(dev
,(int) (long) arg
); 202 returnput_user(PRIV(dev
)->loop_mode
,(int*) arg
) ? 205 returnput_user(ATM_LM_LOC_PHY
| ATM_LM_RMT_PHY
, 206 (int*) arg
) ? -EFAULT
:0; 213 static voidpoll_los(struct atm_dev
*dev
) 215 dev
->signal
=GET(RSOP_SIS
) & SUNI_RSOP_SIS_LOSV
? ATM_PHY_SIG_LOST
: 220 static voidsuni_int(struct atm_dev
*dev
) 223 printk(KERN_NOTICE
"%s(itf %d): signal %s\n",dev
->type
,dev
->number
, 224 dev
->signal
== ATM_PHY_SIG_LOST
?"lost":"detected again"); 228 static intsuni_start(struct atm_dev
*dev
) 233 if(!(PRIV(dev
) =kmalloc(sizeof(struct suni_priv
),GFP_KERNEL
))) 235 PRIV(dev
)->dev
= dev
; 236 spin_lock_irqsave(&sunis_lock
,flags
); 238 PRIV(dev
)->next
= sunis
; 240 spin_unlock_irqrestore(&sunis_lock
,flags
); 241 memset(&PRIV(dev
)->sonet_stats
,0,sizeof(struct k_sonet_stats
)); 242 PUT(GET(RSOP_CIE
) | SUNI_RSOP_CIE_LOSE
,RSOP_CIE
); 243 /* interrupt on loss of signal */ 244 poll_los(dev
);/* ... and clear SUNI interrupts */ 245 if(dev
->signal
== ATM_PHY_SIG_LOST
) 246 printk(KERN_WARNING
"%s(itf %d): no signal\n",dev
->type
, 248 PRIV(dev
)->loop_mode
= ATM_LM_NONE
; 249 suni_hz(0);/* clear SUNI counters */ 250 (void)fetch_stats(dev
,NULL
,1);/* clear kernel counters */ 252 init_timer(&poll_timer
); 253 poll_timer
.expires
= jiffies
+HZ
; 254 poll_timer
.function
= suni_hz
; 257 printk(KERN_DEBUG
"[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer
.list
.prev
, 258 (unsigned long) poll_timer
.list
.next
); 260 add_timer(&poll_timer
); 266 static intsuni_stop(struct atm_dev
*dev
) 268 struct suni_priv
**walk
; 271 /* let SAR driver worry about stopping interrupts */ 272 spin_lock_irqsave(&sunis_lock
,flags
); 273 for(walk
= &sunis
; *walk
!=PRIV(dev
); 274 walk
= &PRIV((*walk
)->dev
)->next
); 275 *walk
=PRIV((*walk
)->dev
)->next
; 276 if(!sunis
)del_timer_sync(&poll_timer
); 277 spin_unlock_irqrestore(&sunis_lock
,flags
); 283 static const struct atmphy_ops suni_ops
= { 291 int __init
suni_init(struct atm_dev
*dev
) 295 mri
=GET(MRI
);/* reset SUNI */ 296 PUT(mri
| SUNI_MRI_RESET
,MRI
); 298 PUT(0,MT
);/* disable all tests */ 299 REG_CHANGE(SUNI_TPOP_APM_S
,SUNI_TPOP_APM_S_SHIFT
,SUNI_TPOP_S_SONET
, 300 TPOP_APM
);/* use SONET */ 301 REG_CHANGE(SUNI_TACP_IUCHP_CLP
,0,SUNI_TACP_IUCHP_CLP
, 302 TACP_IUCHP
);/* idle cells */ 303 PUT(SUNI_IDLE_PATTERN
,TACP_IUCPOP
); 304 dev
->phy
= &suni_ops
; 309 EXPORT_SYMBOL(suni_init
); 322 voidcleanup_module(void)