1/* drivers/atm/uPD98402.c - NEC uPD98402 (PHY) declarations */ 2 3/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 4 5 6#include <linux/module.h> 7#include <linux/mm.h> 8#include <linux/errno.h> 9#include <linux/atmdev.h> 10#include <linux/sonet.h> 11#include <linux/init.h> 12#include <asm/uaccess.h> 13#include <asm/atomic.h> 14 15#include "uPD98402.h" 16 17 18#define DPRINTK(format,args...) 19 20 21struct uPD98402_priv { 22 struct k_sonet_stats sonet_stats;/* link diagnostics */ 23 unsigned char framing; /* SONET/SDH framing */ 24 int loop_mode; /* loopback mode */ 25 spinlock_t lock; 26}; 27 28 29#define PRIV(dev) ((struct uPD98402_priv *) dev->phy_data) 30 31#define PUT(val,reg) dev->ops->phy_put(dev,val,uPD98402_##reg) 32#define GET(reg) dev->ops->phy_get(dev,uPD98402_##reg) 33 34 35static int fetch_stats(struct atm_dev *dev,struct sonet_stats __user *arg,int zero) 36{ 37 struct sonet_stats tmp; 38 int error = 0; 39 40 atomic_add(GET(HECCT),&PRIV(dev)->sonet_stats.uncorr_hcs); 41 sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp); 42 if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp)); 43 if (zero && !error) { 44 /* unused fields are reported as -1, but we must not "adjust" 45 them */ 46 tmp.corr_hcs = tmp.tx_cells = tmp.rx_cells = 0; 47 sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp); 48 } 49 return error ? -EFAULT : 0; 50} 51 52 53static int set_framing(struct atm_dev *dev,unsigned char framing) 54{ 55 static const unsigned char sonet[] = { 1,2,3,0 }; 56 static const unsigned char sdh[] = { 1,0,0,2 }; 57 const char *set; 58 unsigned long flags; 59 60 switch (framing) { 61 case SONET_FRAME_SONET: 62 set = sonet; 63 break; 64 case SONET_FRAME_SDH: 65 set = sdh; 66 break; 67 default: 68 return -EINVAL; 69 } 70 spin_lock_irqsave(&PRIV(dev)->lock, flags); 71 PUT(set[0],C11T); 72 PUT(set[1],C12T); 73 PUT(set[2],C13T); 74 PUT((GET(MDR) & ~uPD98402_MDR_SS_MASK) | (set[3] << 75 uPD98402_MDR_SS_SHIFT),MDR); 76 spin_unlock_irqrestore(&PRIV(dev)->lock, flags); 77 return 0; 78} 79 80 81static int get_sense(struct atm_dev *dev,u8 __user *arg) 82{ 83 unsigned long flags; 84 unsigned char s[3]; 85 86 spin_lock_irqsave(&PRIV(dev)->lock, flags); 87 s[0] = GET(C11R); 88 s[1] = GET(C12R); 89 s[2] = GET(C13R); 90 spin_unlock_irqrestore(&PRIV(dev)->lock, flags); 91 return (put_user(s[0], arg) || put_user(s[1], arg+1) || 92 put_user(s[2], arg+2) || put_user(0xff, arg+3) || 93 put_user(0xff, arg+4) || put_user(0xff, arg+5)) ? -EFAULT : 0; 94} 95 96 97static int set_loopback(struct atm_dev *dev,int mode) 98{ 99 unsigned char mode_reg; 100 101 mode_reg = GET(MDR) & ~(uPD98402_MDR_TPLP | uPD98402_MDR_ALP | 102 uPD98402_MDR_RPLP); 103 switch (__ATM_LM_XTLOC(mode)) { 104 case __ATM_LM_NONE: 105 break; 106 case __ATM_LM_PHY: 107 mode_reg |= uPD98402_MDR_TPLP; 108 break; 109 case __ATM_LM_ATM: 110 mode_reg |= uPD98402_MDR_ALP; 111 break; 112 default: 113 return -EINVAL; 114 } 115 switch (__ATM_LM_XTRMT(mode)) { 116 case __ATM_LM_NONE: 117 break; 118 case __ATM_LM_PHY: 119 mode_reg |= uPD98402_MDR_RPLP; 120 break; 121 default: 122 return -EINVAL; 123 } 124 PUT(mode_reg,MDR); 125 PRIV(dev)->loop_mode = mode; 126 return 0; 127} 128 129 130static int uPD98402_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) 131{ 132 switch (cmd) { 133 134 case SONET_GETSTATZ: 135 case SONET_GETSTAT: 136 return fetch_stats(dev,arg, cmd == SONET_GETSTATZ); 137 case SONET_SETFRAMING: 138 return set_framing(dev, (int)(unsigned long)arg); 139 case SONET_GETFRAMING: 140 return put_user(PRIV(dev)->framing,(int __user *)arg) ? 141 -EFAULT : 0; 142 case SONET_GETFRSENSE: 143 return get_sense(dev,arg); 144 case ATM_SETLOOP: 145 return set_loopback(dev, (int)(unsigned long)arg); 146 case ATM_GETLOOP: 147 return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ? 148 -EFAULT : 0; 149 case ATM_QUERYLOOP: 150 return put_user(ATM_LM_LOC_PHY | ATM_LM_LOC_ATM | 151 ATM_LM_RMT_PHY,(int __user *)arg) ? -EFAULT : 0; 152 default: 153 return -ENOIOCTLCMD; 154 } 155} 156 157 158#define ADD_LIMITED(s,v) \ 159 { atomic_add(GET(v),&PRIV(dev)->sonet_stats.s); \ 160 if (atomic_read(&PRIV(dev)->sonet_stats.s) < 0) \ 161 atomic_set(&PRIV(dev)->sonet_stats.s,INT_MAX); } 162 163 164static void stat_event(struct atm_dev *dev) 165{ 166 unsigned char events; 167 168 events = GET(PCR); 169 if (events & uPD98402_PFM_PFEB) ADD_LIMITED(path_febe,PFECB); 170 if (events & uPD98402_PFM_LFEB) ADD_LIMITED(line_febe,LECCT); 171 if (events & uPD98402_PFM_B3E) ADD_LIMITED(path_bip,B3ECT); 172 if (events & uPD98402_PFM_B2E) ADD_LIMITED(line_bip,B2ECT); 173 if (events & uPD98402_PFM_B1E) ADD_LIMITED(section_bip,B1ECT); 174} 175 176 177#undef ADD_LIMITED 178 179 180static void uPD98402_int(struct atm_dev *dev) 181{ 182 static unsigned long silence = 0; 183 unsigned char reason; 184 185 while ((reason = GET(PICR))) { 186 if (reason & uPD98402_INT_LOS) 187 printk(KERN_NOTICE "%s(itf %d): signal lost\n", 188 dev->type,dev->number); 189 if (reason & uPD98402_INT_PFM) stat_event(dev); 190 if (reason & uPD98402_INT_PCO) { 191 (void) GET(PCOCR); /* clear interrupt cause */ 192 atomic_add(GET(HECCT), 193 &PRIV(dev)->sonet_stats.uncorr_hcs); 194 } 195 if ((reason & uPD98402_INT_RFO) && 196 (time_after(jiffies, silence) || silence == 0)) { 197 printk(KERN_WARNING "%s(itf %d): uPD98402 receive " 198 "FIFO overflow\n",dev->type,dev->number); 199 silence = (jiffies+HZ/2)|1; 200 } 201 } 202} 203 204 205static int uPD98402_start(struct atm_dev *dev) 206{ 207 DPRINTK("phy_start\n"); 208 if (!(dev->dev_data = kmalloc(sizeof(struct uPD98402_priv),GFP_KERNEL))) 209 return -ENOMEM; 210 spin_lock_init(&PRIV(dev)->lock); 211 memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats)); 212 (void) GET(PCR); /* clear performance events */ 213 PUT(uPD98402_PFM_FJ,PCMR); /* ignore frequency adj */ 214 (void) GET(PCOCR); /* clear overflows */ 215 PUT(~uPD98402_PCO_HECC,PCOMR); 216 (void) GET(PICR); /* clear interrupts */ 217 PUT(~(uPD98402_INT_PFM | uPD98402_INT_ALM | uPD98402_INT_RFO | 218 uPD98402_INT_LOS),PIMR); /* enable them */ 219 (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ 220 atomic_set(&PRIV(dev)->sonet_stats.corr_hcs,-1); 221 atomic_set(&PRIV(dev)->sonet_stats.tx_cells,-1); 222 atomic_set(&PRIV(dev)->sonet_stats.rx_cells,-1); 223 return 0; 224} 225 226 227static int uPD98402_stop(struct atm_dev *dev) 228{ 229 /* let SAR driver worry about stopping interrupts */ 230 kfree(PRIV(dev)); 231 return 0; 232} 233 234 235static const struct atmphy_ops uPD98402_ops = { 236 .start = uPD98402_start, 237 .ioctl = uPD98402_ioctl, 238 .interrupt = uPD98402_int, 239 .stop = uPD98402_stop, 240}; 241 242 243int uPD98402_init(struct atm_dev *dev) 244{ 245DPRINTK("phy_init\n"); 246 dev->phy = &uPD98402_ops; 247 return 0; 248} 249 250 251MODULE_LICENSE("GPL"); 252 253EXPORT_SYMBOL(uPD98402_init); 254 255static __init int uPD98402_module_init(void) 256{ 257 return 0; 258} 259module_init(uPD98402_module_init); 260/* module_exit not defined so not unloadable */ 261