1/* drivers/atm/idt77105.c - IDT77105 (PHY) driver */ 2 3/* Written 1999 by Greg Banks, NEC Australia <gnb@linuxfan.com>. Based on suni.c */ 4 5 6#include <linux/module.h> 7#include <linux/sched.h> 8#include <linux/kernel.h> 9#include <linux/mm.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_idt77105.h> 18#include <asm/system.h> 19#include <asm/param.h> 20#include <asm/uaccess.h> 21 22#include "idt77105.h" 23 24#undef GENERAL_DEBUG 25 26#ifdef GENERAL_DEBUG 27#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) 28#else 29#define DPRINTK(format,args...) 30#endif 31 32 33struct idt77105_priv { 34 struct idt77105_stats stats; /* link diagnostics */ 35 struct atm_dev *dev; /* device back-pointer */ 36 struct idt77105_priv *next; 37 int loop_mode; 38 unsigned char old_mcr; /* storage of MCR reg while signal lost */ 39}; 40 41 42#define PRIV(dev) ((struct idt77105_priv *) dev->phy_data) 43 44#define PUT(val,reg) dev->ops->phy_put(dev,val,IDT77105_##reg) 45#define GET(reg) dev->ops->phy_get(dev,IDT77105_##reg) 46 47static void idt77105_stats_timer_func(unsigned long); 48static void idt77105_restart_timer_func(unsigned long); 49 50 51static struct timer_list stats_timer = { 52 function: &idt77105_stats_timer_func 53}; 54static struct timer_list restart_timer = { 55 function: &idt77105_restart_timer_func 56}; 57static int start_timer = 1; 58static struct idt77105_priv *idt77105_all = NULL; 59 60/* 61 * Retrieve the value of one of the IDT77105's counters. 62 * `counter' is one of the IDT77105_CTRSEL_* constants. 63 */ 64static u16 get_counter(struct atm_dev *dev, int counter) 65{ 66 u16 val; 67 68 /* write the counter bit into PHY register 6 */ 69 PUT(counter, CTRSEL); 70 /* read the low 8 bits from register 4 */ 71 val = GET(CTRLO); 72 /* read the high 8 bits from register 5 */ 73 val |= GET(CTRHI)<<8; 74 75 return val; 76} 77 78/* 79 * Timer function called every second to gather statistics 80 * from the 77105. This is done because the h/w registers 81 * will overflow if not read at least once per second. The 82 * kernel's stats are much higher precision. Also, having 83 * a separate copy of the stats allows implementation of 84 * an ioctl which gathers the stats *without* zero'ing them. 85 */ 86static void idt77105_stats_timer_func(unsigned long dummy) 87{ 88 struct idt77105_priv *walk; 89 struct atm_dev *dev; 90 struct idt77105_stats *stats; 91 92 DPRINTK("IDT77105 gathering statistics\n"); 93 for (walk = idt77105_all; walk; walk = walk->next) { 94 dev = walk->dev; 95 96 stats = &walk->stats; 97 stats->symbol_errors += get_counter(dev, IDT77105_CTRSEL_SEC); 98 stats->tx_cells += get_counter(dev, IDT77105_CTRSEL_TCC); 99 stats->rx_cells += get_counter(dev, IDT77105_CTRSEL_RCC); 100 stats->rx_hec_errors += get_counter(dev, IDT77105_CTRSEL_RHEC); 101 } 102 if (!start_timer) mod_timer(&stats_timer,jiffies+IDT77105_STATS_TIMER_PERIOD); 103} 104 105 106/* 107 * A separate timer func which handles restarting PHY chips which 108 * have had the cable re-inserted after being pulled out. This is 109 * done by polling the Good Signal Bit in the Interrupt Status 110 * register every 5 seconds. The other technique (checking Good 111 * Signal Bit in the interrupt handler) cannot be used because PHY 112 * interrupts need to be disabled when the cable is pulled out 113 * to avoid lots of spurious cell error interrupts. 114 */ 115static void idt77105_restart_timer_func(unsigned long dummy) 116{ 117 struct idt77105_priv *walk; 118 struct atm_dev *dev; 119 unsigned char istat; 120 121 DPRINTK("IDT77105 checking for cable re-insertion\n"); 122 for (walk = idt77105_all; walk; walk = walk->next) { 123 dev = walk->dev; 124 125 if (dev->signal != ATM_PHY_SIG_LOST) 126 continue; 127 128 istat = GET(ISTAT); /* side effect: clears all interrupt status bits */ 129 if (istat & IDT77105_ISTAT_GOODSIG) { 130 /* Found signal again */ 131 dev->signal = ATM_PHY_SIG_FOUND; 132 printk(KERN_NOTICE "%s(itf %d): signal detected again\n", 133 dev->type,dev->number); 134 /* flush the receive FIFO */ 135 PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG); 136 /* re-enable interrupts */ 137 PUT( walk->old_mcr ,MCR); 138 } 139 } 140 if (!start_timer) mod_timer(&restart_timer,jiffies+IDT77105_RESTART_TIMER_PERIOD); 141} 142 143 144static int fetch_stats(struct atm_dev *dev,struct idt77105_stats *arg,int zero) 145{ 146 unsigned long flags; 147 struct idt77105_stats stats; 148 149 save_flags(flags); 150 cli(); 151 memcpy(&stats, &PRIV(dev)->stats, sizeof(struct idt77105_stats)); 152 if (zero) 153 memset(&PRIV(dev)->stats, 0, sizeof(struct idt77105_stats)); 154 restore_flags(flags); 155 if (arg == NULL) 156 return 0; 157 return copy_to_user(arg, &PRIV(dev)->stats, 158 sizeof(struct idt77105_stats)) ? -EFAULT : 0; 159} 160 161 162static int set_loopback(struct atm_dev *dev,int mode) 163{ 164 int diag; 165 166 diag = GET(DIAG) & ~IDT77105_DIAG_LCMASK; 167 switch (mode) { 168 case ATM_LM_NONE: 169 break; 170 case ATM_LM_LOC_ATM: 171 diag |= IDT77105_DIAG_LC_PHY_LOOPBACK; 172 break; 173 case ATM_LM_RMT_ATM: 174 diag |= IDT77105_DIAG_LC_LINE_LOOPBACK; 175 break; 176 default: 177 return -EINVAL; 178 } 179 PUT(diag,DIAG); 180 printk(KERN_NOTICE "%s(%d) Loopback mode is: %s\n", dev->type, 181 dev->number, 182 (mode == ATM_LM_NONE ? "NONE" : 183 (mode == ATM_LM_LOC_ATM ? "DIAG (local)" : 184 (mode == IDT77105_DIAG_LC_LINE_LOOPBACK ? "LOOP (remote)" : 185 "unknown"))) 186 ); 187 PRIV(dev)->loop_mode = mode; 188 return 0; 189} 190 191 192static int idt77105_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg) 193{ 194 printk(KERN_NOTICE "%s(%d) idt77105_ioctl() called\n",dev->type,dev->number); 195 switch (cmd) { 196 case IDT77105_GETSTATZ: 197 if (!capable(CAP_NET_ADMIN)) return -EPERM; 198 /* fall through */ 199 case IDT77105_GETSTAT: 200 return fetch_stats(dev,(struct idt77105_stats *) arg, 201 cmd == IDT77105_GETSTATZ); 202 case ATM_SETLOOP: 203 return set_loopback(dev,(int) (long) arg); 204 case ATM_GETLOOP: 205 return put_user(PRIV(dev)->loop_mode,(int *) arg) ? 206 -EFAULT : 0; 207 case ATM_QUERYLOOP: 208 return put_user(ATM_LM_LOC_ATM | ATM_LM_RMT_ATM, 209 (int *) arg) ? -EFAULT : 0; 210 default: 211 return -ENOIOCTLCMD; 212 } 213} 214 215 216 217static void idt77105_int(struct atm_dev *dev) 218{ 219 unsigned char istat; 220 221 istat = GET(ISTAT); /* side effect: clears all interrupt status bits */ 222 223 DPRINTK("IDT77105 generated an interrupt, istat=%02x\n", (unsigned)istat); 224 225 if (istat & IDT77105_ISTAT_RSCC) { 226 /* Rx Signal Condition Change - line went up or down */ 227 if (istat & IDT77105_ISTAT_GOODSIG) { /* signal detected again */ 228 /* This should not happen (restart timer does it) but JIC */ 229 dev->signal = ATM_PHY_SIG_FOUND; 230 } else { /* signal lost */ 231 /* 232 * Disable interrupts and stop all transmission and 233 * reception - the restart timer will restore these. 234 */ 235 PRIV(dev)->old_mcr = GET(MCR); 236 PUT( 237 (PRIV(dev)->old_mcr| 238 IDT77105_MCR_DREC| 239 IDT77105_MCR_DRIC| 240 IDT77105_MCR_HALTTX 241 ) & ~IDT77105_MCR_EIP, MCR); 242 dev->signal = ATM_PHY_SIG_LOST; 243 printk(KERN_NOTICE "%s(itf %d): signal lost\n", 244 dev->type,dev->number); 245 } 246 } 247 248 if (istat & IDT77105_ISTAT_RFO) { 249 /* Rx FIFO Overrun -- perform a FIFO flush */ 250 PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG); 251 printk(KERN_NOTICE "%s(itf %d): receive FIFO overrun\n", 252 dev->type,dev->number); 253 } 254#ifdef GENERAL_DEBUG 255 if (istat & (IDT77105_ISTAT_HECERR | IDT77105_ISTAT_SCR | 256 IDT77105_ISTAT_RSE)) { 257 /* normally don't care - just report in stats */ 258 printk(KERN_NOTICE "%s(itf %d): received cell with error\n", 259 dev->type,dev->number); 260 } 261#endif 262} 263 264 265static int idt77105_start(struct atm_dev *dev) 266{ 267 unsigned long flags; 268 269 if (!(PRIV(dev) = kmalloc(sizeof(struct idt77105_priv),GFP_KERNEL))) 270 return -ENOMEM; 271 PRIV(dev)->dev = dev; 272 save_flags(flags); 273 cli(); 274 PRIV(dev)->next = idt77105_all; 275 idt77105_all = PRIV(dev); 276 restore_flags(flags); 277 memset(&PRIV(dev)->stats,0,sizeof(struct idt77105_stats)); 278 279 /* initialise dev->signal from Good Signal Bit */ 280 dev->signal = GET(ISTAT) & IDT77105_ISTAT_GOODSIG ? ATM_PHY_SIG_FOUND : 281 ATM_PHY_SIG_LOST; 282 if (dev->signal == ATM_PHY_SIG_LOST) 283 printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type, 284 dev->number); 285 286 /* initialise loop mode from hardware */ 287 switch ( GET(DIAG) & IDT77105_DIAG_LCMASK ) { 288 case IDT77105_DIAG_LC_NORMAL: 289 PRIV(dev)->loop_mode = ATM_LM_NONE; 290 break; 291 case IDT77105_DIAG_LC_PHY_LOOPBACK: 292 PRIV(dev)->loop_mode = ATM_LM_LOC_ATM; 293 break; 294 case IDT77105_DIAG_LC_LINE_LOOPBACK: 295 PRIV(dev)->loop_mode = ATM_LM_RMT_ATM; 296 break; 297 } 298 299 /* enable interrupts, e.g. on loss of signal */ 300 PRIV(dev)->old_mcr = GET(MCR); 301 if (dev->signal == ATM_PHY_SIG_FOUND) { 302 PRIV(dev)->old_mcr |= IDT77105_MCR_EIP; 303 PUT(PRIV(dev)->old_mcr, MCR); 304 } 305 306 307 idt77105_stats_timer_func(0); /* clear 77105 counters */ 308 (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ 309 310 cli(); 311 if (!start_timer) restore_flags(flags); 312 else { 313 start_timer = 0; 314 restore_flags(flags); 315 316 init_timer(&stats_timer); 317 stats_timer.expires = jiffies+IDT77105_STATS_TIMER_PERIOD; 318 stats_timer.function = idt77105_stats_timer_func; 319 add_timer(&stats_timer); 320 321 init_timer(&restart_timer); 322 restart_timer.expires = jiffies+IDT77105_RESTART_TIMER_PERIOD; 323 restart_timer.function = idt77105_restart_timer_func; 324 add_timer(&restart_timer); 325 } 326 return 0; 327} 328 329 330static const struct atmphy_ops idt77105_ops = { 331 idt77105_start, 332 idt77105_ioctl, 333 idt77105_int 334}; 335 336 337int __init idt77105_init(struct atm_dev *dev) 338{ 339 MOD_INC_USE_COUNT; 340 341 dev->phy = &idt77105_ops; 342 return 0; 343} 344 345 346/* 347 * TODO: this function should be called through phy_ops 348 * but that will not be possible for some time as there is 349 * currently a freeze on modifying that structure 350 * -- Greg Banks, 13 Sep 1999 351 */ 352int idt77105_stop(struct atm_dev *dev) 353{ 354 struct idt77105_priv *walk, *prev; 355 356 DPRINTK("%s(itf %d): stopping IDT77105\n",dev->type,dev->number); 357 358 /* disable interrupts */ 359 PUT( GET(MCR) & ~IDT77105_MCR_EIP, MCR ); 360 361 /* detach private struct from atm_dev & free */ 362 for (prev = NULL, walk = idt77105_all ; 363 walk != NULL; 364 prev = walk, walk = walk->next) { 365 if (walk->dev == dev) { 366 if (prev != NULL) 367 prev->next = walk->next; 368 else 369 idt77105_all = walk->next; 370 dev->phy = NULL; 371 PRIV(dev) = NULL; 372 kfree(walk); 373 break; 374 } 375 } 376 377 MOD_DEC_USE_COUNT; 378 return 0; 379} 380 381 382 383EXPORT_SYMBOL(idt77105_init); 384EXPORT_SYMBOL(idt77105_stop); 385 386MODULE_LICENSE("GPL"); 387 388#ifdef MODULE 389 390int init_module(void) 391{ 392 return 0; 393} 394 395 396void cleanup_module(void) 397{ 398 /* turn off timers */ 399 del_timer(&stats_timer); 400 del_timer(&restart_timer); 401} 402 403#endif 404