Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* drivers/atm/idt77105.c - IDT77105 (PHY) driver */
0003  
0004 /* Written 1999 by Greg Banks, NEC Australia <gnb@linuxfan.com>. Based on suni.c */
0005 
0006 
0007 #include <linux/module.h>
0008 #include <linux/kernel.h>
0009 #include <linux/mm.h>
0010 #include <linux/errno.h>
0011 #include <linux/atmdev.h>
0012 #include <linux/sonet.h>
0013 #include <linux/delay.h>
0014 #include <linux/timer.h>
0015 #include <linux/init.h>
0016 #include <linux/capability.h>
0017 #include <linux/atm_idt77105.h>
0018 #include <linux/spinlock.h>
0019 #include <linux/slab.h>
0020 #include <asm/param.h>
0021 #include <linux/uaccess.h>
0022 
0023 #include "idt77105.h"
0024 
0025 #undef GENERAL_DEBUG
0026 
0027 #ifdef GENERAL_DEBUG
0028 #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
0029 #else
0030 #define DPRINTK(format,args...)
0031 #endif
0032 
0033 
0034 struct idt77105_priv {
0035     struct idt77105_stats stats;    /* link diagnostics */
0036     struct atm_dev *dev;        /* device back-pointer */
0037     struct idt77105_priv *next;
0038         int loop_mode;
0039         unsigned char old_mcr;          /* storage of MCR reg while signal lost */
0040 };
0041 
0042 static DEFINE_SPINLOCK(idt77105_priv_lock);
0043 
0044 #define PRIV(dev) ((struct idt77105_priv *) dev->phy_data)
0045 
0046 #define PUT(val,reg) dev->ops->phy_put(dev,val,IDT77105_##reg)
0047 #define GET(reg) dev->ops->phy_get(dev,IDT77105_##reg)
0048 
0049 static void idt77105_stats_timer_func(struct timer_list *);
0050 static void idt77105_restart_timer_func(struct timer_list *);
0051 
0052 
0053 static DEFINE_TIMER(stats_timer, idt77105_stats_timer_func);
0054 static DEFINE_TIMER(restart_timer, idt77105_restart_timer_func);
0055 static int start_timer = 1;
0056 static struct idt77105_priv *idt77105_all = NULL;
0057 
0058 /*
0059  * Retrieve the value of one of the IDT77105's counters.
0060  * `counter' is one of the IDT77105_CTRSEL_* constants.
0061  */
0062 static u16 get_counter(struct atm_dev *dev, int counter)
0063 {
0064         u16 val;
0065         
0066         /* write the counter bit into PHY register 6 */
0067         PUT(counter, CTRSEL);
0068         /* read the low 8 bits from register 4 */
0069         val = GET(CTRLO);
0070         /* read the high 8 bits from register 5 */
0071         val |= GET(CTRHI)<<8;
0072         
0073         return val;
0074 }
0075 
0076 /*
0077  * Timer function called every second to gather statistics
0078  * from the 77105. This is done because the h/w registers
0079  * will overflow if not read at least once per second. The
0080  * kernel's stats are much higher precision. Also, having
0081  * a separate copy of the stats allows implementation of
0082  * an ioctl which gathers the stats *without* zero'ing them.
0083  */
0084 static void idt77105_stats_timer_func(struct timer_list *unused)
0085 {
0086     struct idt77105_priv *walk;
0087     struct atm_dev *dev;
0088     struct idt77105_stats *stats;
0089 
0090         DPRINTK("IDT77105 gathering statistics\n");
0091     for (walk = idt77105_all; walk; walk = walk->next) {
0092         dev = walk->dev;
0093                 
0094         stats = &walk->stats;
0095                 stats->symbol_errors += get_counter(dev, IDT77105_CTRSEL_SEC);
0096                 stats->tx_cells += get_counter(dev, IDT77105_CTRSEL_TCC);
0097                 stats->rx_cells += get_counter(dev, IDT77105_CTRSEL_RCC);
0098                 stats->rx_hec_errors += get_counter(dev, IDT77105_CTRSEL_RHEC);
0099     }
0100         if (!start_timer) mod_timer(&stats_timer,jiffies+IDT77105_STATS_TIMER_PERIOD);
0101 }
0102 
0103 
0104 /*
0105  * A separate timer func which handles restarting PHY chips which
0106  * have had the cable re-inserted after being pulled out. This is
0107  * done by polling the Good Signal Bit in the Interrupt Status
0108  * register every 5 seconds. The other technique (checking Good
0109  * Signal Bit in the interrupt handler) cannot be used because PHY
0110  * interrupts need to be disabled when the cable is pulled out
0111  * to avoid lots of spurious cell error interrupts.
0112  */
0113 static void idt77105_restart_timer_func(struct timer_list *unused)
0114 {
0115     struct idt77105_priv *walk;
0116     struct atm_dev *dev;
0117         unsigned char istat;
0118 
0119         DPRINTK("IDT77105 checking for cable re-insertion\n");
0120     for (walk = idt77105_all; walk; walk = walk->next) {
0121         dev = walk->dev;
0122                 
0123                 if (dev->signal != ATM_PHY_SIG_LOST)
0124                     continue;
0125                     
0126                 istat = GET(ISTAT); /* side effect: clears all interrupt status bits */
0127                 if (istat & IDT77105_ISTAT_GOODSIG) {
0128                     /* Found signal again */
0129                     atm_dev_signal_change(dev, ATM_PHY_SIG_FOUND);
0130                 printk(KERN_NOTICE "%s(itf %d): signal detected again\n",
0131                         dev->type,dev->number);
0132                     /* flush the receive FIFO */
0133                     PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG);
0134                     /* re-enable interrupts */
0135                 PUT( walk->old_mcr ,MCR);
0136                 }
0137     }
0138         if (!start_timer) mod_timer(&restart_timer,jiffies+IDT77105_RESTART_TIMER_PERIOD);
0139 }
0140 
0141 
0142 static int fetch_stats(struct atm_dev *dev,struct idt77105_stats __user *arg,int zero)
0143 {
0144     unsigned long flags;
0145     struct idt77105_stats stats;
0146 
0147     spin_lock_irqsave(&idt77105_priv_lock, flags);
0148     memcpy(&stats, &PRIV(dev)->stats, sizeof(struct idt77105_stats));
0149     if (zero)
0150         memset(&PRIV(dev)->stats, 0, sizeof(struct idt77105_stats));
0151     spin_unlock_irqrestore(&idt77105_priv_lock, flags);
0152     if (arg == NULL)
0153         return 0;
0154     return copy_to_user(arg, &stats,
0155             sizeof(struct idt77105_stats)) ? -EFAULT : 0;
0156 }
0157 
0158 
0159 static int set_loopback(struct atm_dev *dev,int mode)
0160 {
0161     int diag;
0162 
0163     diag = GET(DIAG) & ~IDT77105_DIAG_LCMASK;
0164     switch (mode) {
0165         case ATM_LM_NONE:
0166             break;
0167         case ATM_LM_LOC_ATM:
0168             diag |= IDT77105_DIAG_LC_PHY_LOOPBACK;
0169             break;
0170         case ATM_LM_RMT_ATM:
0171             diag |= IDT77105_DIAG_LC_LINE_LOOPBACK;
0172             break;
0173         default:
0174             return -EINVAL;
0175     }
0176     PUT(diag,DIAG);
0177     printk(KERN_NOTICE "%s(%d) Loopback mode is: %s\n", dev->type,
0178         dev->number,
0179         (mode == ATM_LM_NONE ? "NONE" : 
0180           (mode == ATM_LM_LOC_ATM ? "DIAG (local)" :
0181         (mode == IDT77105_DIAG_LC_LINE_LOOPBACK ? "LOOP (remote)" :
0182           "unknown")))
0183             );
0184     PRIV(dev)->loop_mode = mode;
0185     return 0;
0186 }
0187 
0188 
0189 static int idt77105_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
0190 {
0191         printk(KERN_NOTICE "%s(%d) idt77105_ioctl() called\n",dev->type,dev->number);
0192     switch (cmd) {
0193         case IDT77105_GETSTATZ:
0194             if (!capable(CAP_NET_ADMIN)) return -EPERM;
0195             fallthrough;
0196         case IDT77105_GETSTAT:
0197             return fetch_stats(dev, arg, cmd == IDT77105_GETSTATZ);
0198         case ATM_SETLOOP:
0199             return set_loopback(dev,(int)(unsigned long) arg);
0200         case ATM_GETLOOP:
0201             return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ?
0202                 -EFAULT : 0;
0203         case ATM_QUERYLOOP:
0204             return put_user(ATM_LM_LOC_ATM | ATM_LM_RMT_ATM,
0205                 (int __user *) arg) ? -EFAULT : 0;
0206         default:
0207             return -ENOIOCTLCMD;
0208     }
0209 }
0210 
0211 
0212 
0213 static void idt77105_int(struct atm_dev *dev)
0214 {
0215         unsigned char istat;
0216         
0217         istat = GET(ISTAT); /* side effect: clears all interrupt status bits */
0218      
0219         DPRINTK("IDT77105 generated an interrupt, istat=%02x\n", (unsigned)istat);
0220                 
0221         if (istat & IDT77105_ISTAT_RSCC) {
0222             /* Rx Signal Condition Change - line went up or down */
0223             if (istat & IDT77105_ISTAT_GOODSIG) {   /* signal detected again */
0224                 /* This should not happen (restart timer does it) but JIC */
0225         atm_dev_signal_change(dev, ATM_PHY_SIG_FOUND);
0226             } else {    /* signal lost */
0227                 /*
0228                  * Disable interrupts and stop all transmission and
0229                  * reception - the restart timer will restore these.
0230                  */
0231                 PRIV(dev)->old_mcr = GET(MCR);
0232             PUT(
0233                     (PRIV(dev)->old_mcr|
0234                     IDT77105_MCR_DREC|
0235                     IDT77105_MCR_DRIC|
0236                     IDT77105_MCR_HALTTX
0237                     ) & ~IDT77105_MCR_EIP, MCR);
0238         atm_dev_signal_change(dev, ATM_PHY_SIG_LOST);
0239             printk(KERN_NOTICE "%s(itf %d): signal lost\n",
0240                     dev->type,dev->number);
0241             }
0242         }
0243         
0244         if (istat & IDT77105_ISTAT_RFO) {
0245             /* Rx FIFO Overrun -- perform a FIFO flush */
0246             PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG);
0247         printk(KERN_NOTICE "%s(itf %d): receive FIFO overrun\n",
0248                 dev->type,dev->number);
0249         }
0250 #ifdef GENERAL_DEBUG
0251         if (istat & (IDT77105_ISTAT_HECERR | IDT77105_ISTAT_SCR |
0252                      IDT77105_ISTAT_RSE)) {
0253             /* normally don't care - just report in stats */
0254         printk(KERN_NOTICE "%s(itf %d): received cell with error\n",
0255                 dev->type,dev->number);
0256         }
0257 #endif
0258 }
0259 
0260 
0261 static int idt77105_start(struct atm_dev *dev)
0262 {
0263     unsigned long flags;
0264 
0265     if (!(dev->phy_data = kmalloc(sizeof(struct idt77105_priv),GFP_KERNEL)))
0266         return -ENOMEM;
0267     PRIV(dev)->dev = dev;
0268     spin_lock_irqsave(&idt77105_priv_lock, flags);
0269     PRIV(dev)->next = idt77105_all;
0270     idt77105_all = PRIV(dev);
0271     spin_unlock_irqrestore(&idt77105_priv_lock, flags);
0272     memset(&PRIV(dev)->stats,0,sizeof(struct idt77105_stats));
0273         
0274         /* initialise dev->signal from Good Signal Bit */
0275     atm_dev_signal_change(dev,
0276         GET(ISTAT) & IDT77105_ISTAT_GOODSIG ?
0277         ATM_PHY_SIG_FOUND : ATM_PHY_SIG_LOST);
0278     if (dev->signal == ATM_PHY_SIG_LOST)
0279         printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type,
0280             dev->number);
0281 
0282         /* initialise loop mode from hardware */
0283         switch ( GET(DIAG) & IDT77105_DIAG_LCMASK ) {
0284         case IDT77105_DIAG_LC_NORMAL:
0285             PRIV(dev)->loop_mode = ATM_LM_NONE;
0286             break;
0287         case IDT77105_DIAG_LC_PHY_LOOPBACK:
0288             PRIV(dev)->loop_mode = ATM_LM_LOC_ATM;
0289             break;
0290         case IDT77105_DIAG_LC_LINE_LOOPBACK:
0291             PRIV(dev)->loop_mode = ATM_LM_RMT_ATM;
0292             break;
0293         }
0294         
0295         /* enable interrupts, e.g. on loss of signal */
0296         PRIV(dev)->old_mcr = GET(MCR);
0297         if (dev->signal == ATM_PHY_SIG_FOUND) {
0298             PRIV(dev)->old_mcr |= IDT77105_MCR_EIP;
0299         PUT(PRIV(dev)->old_mcr, MCR);
0300         }
0301 
0302                     
0303     idt77105_stats_timer_func(0); /* clear 77105 counters */
0304     (void) fetch_stats(dev,NULL,1); /* clear kernel counters */
0305         
0306     spin_lock_irqsave(&idt77105_priv_lock, flags);
0307     if (start_timer) {
0308         start_timer = 0;
0309                 
0310         stats_timer.expires = jiffies+IDT77105_STATS_TIMER_PERIOD;
0311         add_timer(&stats_timer);
0312                 
0313         restart_timer.expires = jiffies+IDT77105_RESTART_TIMER_PERIOD;
0314         add_timer(&restart_timer);
0315     }
0316     spin_unlock_irqrestore(&idt77105_priv_lock, flags);
0317     return 0;
0318 }
0319 
0320 
0321 static int idt77105_stop(struct atm_dev *dev)
0322 {
0323     struct idt77105_priv *walk, *prev;
0324 
0325         DPRINTK("%s(itf %d): stopping IDT77105\n",dev->type,dev->number);
0326         
0327         /* disable interrupts */
0328     PUT( GET(MCR) & ~IDT77105_MCR_EIP, MCR );
0329         
0330         /* detach private struct from atm_dev & free */
0331     for (prev = NULL, walk = idt77105_all ;
0332              walk != NULL;
0333              prev = walk, walk = walk->next) {
0334             if (walk->dev == dev) {
0335                 if (prev != NULL)
0336                     prev->next = walk->next;
0337                 else
0338                     idt77105_all = walk->next;
0339             dev->phy = NULL;
0340                 dev->phy_data = NULL;
0341                 kfree(walk);
0342                 break;
0343             }
0344         }
0345 
0346     return 0;
0347 }
0348 
0349 
0350 static const struct atmphy_ops idt77105_ops = {
0351     .start =    idt77105_start,
0352     .ioctl =    idt77105_ioctl,
0353     .interrupt =    idt77105_int,
0354     .stop =     idt77105_stop,
0355 };
0356 
0357 
0358 int idt77105_init(struct atm_dev *dev)
0359 {
0360     dev->phy = &idt77105_ops;
0361     return 0;
0362 }
0363 
0364 EXPORT_SYMBOL(idt77105_init);
0365 
0366 static void __exit idt77105_exit(void)
0367 {
0368     /* turn off timers */
0369     del_timer_sync(&stats_timer);
0370     del_timer_sync(&restart_timer);
0371 }
0372 
0373 module_exit(idt77105_exit);
0374 
0375 MODULE_LICENSE("GPL");