cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

idt77105.c (11515B)


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