linux/drivers/atm/suni.c
<<
>>
Prefs
   1/*
   2 * drivers/atm/suni.c - S/UNI PHY driver
   3 *
   4 * Supports the following:
   5 *      PMC PM5346 S/UNI LITE
   6 *      PMC PM5350 S/UNI 155 ULTRA
   7 *      PMC PM5355 S/UNI 622
   8 */
   9 
  10/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
  11
  12#include <linux/module.h>
  13#include <linux/jiffies.h>
  14#include <linux/kernel.h>
  15#include <linux/mm.h>
  16#include <linux/errno.h>
  17#include <linux/atmdev.h>
  18#include <linux/sonet.h>
  19#include <linux/delay.h>
  20#include <linux/timer.h>
  21#include <linux/init.h>
  22#include <linux/capability.h>
  23#include <linux/atm_suni.h>
  24#include <asm/system.h>
  25#include <asm/param.h>
  26#include <asm/uaccess.h>
  27#include <asm/atomic.h>
  28
  29#include "suni.h"
  30
  31
  32#if 0
  33#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
  34#else
  35#define DPRINTK(format,args...)
  36#endif
  37
  38#define PRIV(dev) ((struct suni_priv *) dev->phy_data)
  39
  40#define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg)
  41#define GET(reg) dev->ops->phy_get(dev,SUNI_##reg)
  42#define REG_CHANGE(mask,shift,value,reg) \
  43  PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg)
  44
  45
  46static struct timer_list poll_timer;
  47static struct suni_priv *sunis = NULL;
  48static DEFINE_SPINLOCK(sunis_lock);
  49
  50
  51#define ADD_LIMITED(s,v) \
  52    atomic_add((v),&stats->s); \
  53    if (atomic_read(&stats->s) < 0) atomic_set(&stats->s,INT_MAX);
  54
  55
  56static void suni_hz(unsigned long from_timer)
  57{
  58        struct suni_priv *walk;
  59        struct atm_dev *dev;
  60        struct k_sonet_stats *stats;
  61
  62        for (walk = sunis; walk; walk = walk->next) {
  63                dev = walk->dev;
  64                stats = &walk->sonet_stats;
  65                PUT(0,MRI); /* latch counters */
  66                udelay(1);
  67                ADD_LIMITED(section_bip,(GET(RSOP_SBL) & 0xff) |
  68                    ((GET(RSOP_SBM) & 0xff) << 8));
  69                ADD_LIMITED(line_bip,(GET(RLOP_LBL) & 0xff) |
  70                    ((GET(RLOP_LB) & 0xff) << 8) |
  71                    ((GET(RLOP_LBM) & 0xf) << 16));
  72                ADD_LIMITED(path_bip,(GET(RPOP_PBL) & 0xff) |
  73                    ((GET(RPOP_PBM) & 0xff) << 8));
  74                ADD_LIMITED(line_febe,(GET(RLOP_LFL) & 0xff) |
  75                    ((GET(RLOP_LF) & 0xff) << 8) |
  76                    ((GET(RLOP_LFM) & 0xf) << 16));
  77                ADD_LIMITED(path_febe,(GET(RPOP_PFL) & 0xff) |
  78                    ((GET(RPOP_PFM) & 0xff) << 8));
  79                ADD_LIMITED(corr_hcs,GET(RACP_CHEC) & 0xff);
  80                ADD_LIMITED(uncorr_hcs,GET(RACP_UHEC) & 0xff);
  81                ADD_LIMITED(rx_cells,(GET(RACP_RCCL) & 0xff) |
  82                    ((GET(RACP_RCC) & 0xff) << 8) |
  83                    ((GET(RACP_RCCM) & 7) << 16));
  84                ADD_LIMITED(tx_cells,(GET(TACP_TCCL) & 0xff) |
  85                    ((GET(TACP_TCC) & 0xff) << 8) |
  86                    ((GET(TACP_TCCM) & 7) << 16));
  87        }
  88        if (from_timer) mod_timer(&poll_timer,jiffies+HZ);
  89}
  90
  91
  92#undef ADD_LIMITED
  93
  94
  95static int fetch_stats(struct atm_dev *dev,struct sonet_stats __user *arg,int zero)
  96{
  97        struct sonet_stats tmp;
  98        int error = 0;
  99
 100        sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp);
 101        if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp));
 102        if (zero && !error) sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp);
 103        return error ? -EFAULT : 0;
 104}
 105
 106
 107#define HANDLE_FLAG(flag,reg,bit) \
 108  if (todo & flag) { \
 109    if (set) PUT(GET(reg) | bit,reg); \
 110    else PUT(GET(reg) & ~bit,reg); \
 111    todo &= ~flag; \
 112  }
 113
 114
 115static int change_diag(struct atm_dev *dev,void __user *arg,int set)
 116{
 117        int todo;
 118
 119        if (get_user(todo,(int __user *)arg)) return -EFAULT;
 120        HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8);
 121        HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP);
 122        HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3);
 123        HANDLE_FLAG(SONET_INS_FRAME,RSOP_CIE,SUNI_RSOP_CIE_FOOF);
 124        HANDLE_FLAG(SONET_INS_LAIS,TSOP_CTRL,SUNI_TSOP_CTRL_LAIS);
 125        HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS);
 126        HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS);
 127        HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS);
 128        return put_user(todo,(int __user *)arg) ? -EFAULT : 0;
 129}
 130
 131
 132#undef HANDLE_FLAG
 133
 134
 135static int get_diag(struct atm_dev *dev,void __user *arg)
 136{
 137        int set;
 138
 139        set = 0;
 140        if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DBIP8) set |= SONET_INS_SBIP;
 141        if (GET(TLOP_DIAG) & SUNI_TLOP_DIAG_DBIP) set |= SONET_INS_LBIP;
 142        if (GET(TPOP_CD) & SUNI_TPOP_DIAG_DB3) set |= SONET_INS_PBIP;
 143        /* SONET_INS_FRAME is one-shot only */
 144        if (GET(TSOP_CTRL) & SUNI_TSOP_CTRL_LAIS) set |= SONET_INS_LAIS;
 145        if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS;
 146        if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS;
 147        if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS;
 148        return put_user(set,(int __user *)arg) ? -EFAULT : 0;
 149}
 150
 151
 152static int set_loopback(struct atm_dev *dev,int mode)
 153{
 154        unsigned char control;
 155        int reg, dle, lle;
 156
 157        if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
 158                reg = SUNI_MCM;
 159                dle = SUNI_MCM_DLE;
 160                lle = SUNI_MCM_LLE;
 161        } else {
 162                reg = SUNI_MCT;
 163                dle = SUNI_MCT_DLE;
 164                lle = SUNI_MCT_LLE;
 165        }
 166
 167        control = dev->ops->phy_get(dev, reg) & ~(dle | lle);
 168        switch (mode) {
 169                case ATM_LM_NONE:
 170                        break;
 171                case ATM_LM_LOC_PHY:
 172                        control |= dle;
 173                        break;
 174                case ATM_LM_RMT_PHY:
 175                        control |= lle;
 176                        break;
 177                default:
 178                        return -EINVAL;
 179        }
 180         dev->ops->phy_put(dev, control, reg);
 181        PRIV(dev)->loop_mode = mode;
 182        return 0;
 183}
 184
 185/*
 186 * SONET vs. SDH Configuration
 187 *
 188 * Z0INS (register 0x06): 0 for SONET, 1 for SDH
 189 * ENSS (register 0x3D): 0 for SONET, 1 for SDH
 190 * LEN16 (register 0x28): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD)
 191 * LEN16 (register 0x50): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD)
 192 * S[1:0] (register 0x46): 00 for SONET, 10 for SDH
 193 */
 194
 195static int set_sonet(struct atm_dev *dev)
 196{
 197        if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
 198                PUT(GET(RPOP_RC) & ~SUNI_RPOP_RC_ENSS, RPOP_RC);
 199                PUT(GET(SSTB_CTRL) & ~SUNI_SSTB_CTRL_LEN16, SSTB_CTRL);
 200                PUT(GET(SPTB_CTRL) & ~SUNI_SPTB_CTRL_LEN16, SPTB_CTRL);
 201        }
 202
 203        REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT,
 204                   SUNI_TPOP_S_SONET, TPOP_APM);
 205
 206        return 0;
 207}
 208
 209static int set_sdh(struct atm_dev *dev)
 210{
 211        if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
 212                PUT(GET(RPOP_RC) | SUNI_RPOP_RC_ENSS, RPOP_RC);
 213                PUT(GET(SSTB_CTRL) | SUNI_SSTB_CTRL_LEN16, SSTB_CTRL);
 214                PUT(GET(SPTB_CTRL) | SUNI_SPTB_CTRL_LEN16, SPTB_CTRL);
 215        }
 216
 217        REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT,
 218                   SUNI_TPOP_S_SDH, TPOP_APM);
 219
 220        return 0;
 221}
 222
 223
 224static int get_framing(struct atm_dev *dev, void __user *arg)
 225{
 226        int framing;
 227        unsigned char s;
 228
 229
 230        s = (GET(TPOP_APM) & SUNI_TPOP_APM_S) >> SUNI_TPOP_APM_S_SHIFT;
 231        if (s == SUNI_TPOP_S_SONET)
 232                framing = SONET_FRAME_SONET;
 233        else
 234                framing = SONET_FRAME_SDH;
 235
 236        return put_user(framing, (int __user *) arg) ? -EFAULT : 0;
 237}
 238
 239static int set_framing(struct atm_dev *dev, void __user *arg)
 240{
 241        int mode;
 242
 243        if (get_user(mode, (int __user *) arg))
 244                return -EFAULT;
 245
 246        if (mode == SONET_FRAME_SONET)
 247                return set_sonet(dev);
 248        else if (mode == SONET_FRAME_SDH)
 249                return set_sdh(dev);
 250
 251        return -EINVAL;
 252}
 253
 254
 255static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
 256{
 257        switch (cmd) {
 258                case SONET_GETSTATZ:
 259                case SONET_GETSTAT:
 260                        return fetch_stats(dev, arg, cmd == SONET_GETSTATZ);
 261                case SONET_SETDIAG:
 262                        return change_diag(dev,arg,1);
 263                case SONET_CLRDIAG:
 264                        return change_diag(dev,arg,0);
 265                case SONET_GETDIAG:
 266                        return get_diag(dev,arg);
 267                case SONET_SETFRAMING:
 268                        if (!capable(CAP_NET_ADMIN))
 269                                return -EPERM;
 270                        return set_framing(dev, arg);
 271                case SONET_GETFRAMING:
 272                        return get_framing(dev, arg);
 273                case SONET_GETFRSENSE:
 274                        return -EINVAL;
 275                case ATM_SETLOOP:
 276                        if (!capable(CAP_NET_ADMIN))
 277                                return -EPERM;
 278                        return set_loopback(dev,(int)(unsigned long)arg);
 279                case ATM_GETLOOP:
 280                        return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ?
 281                            -EFAULT : 0;
 282                case ATM_QUERYLOOP:
 283                        return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY,
 284                            (int __user *) arg) ? -EFAULT : 0;
 285                default:
 286                        return -ENOIOCTLCMD;
 287        }
 288}
 289
 290
 291static void poll_los(struct atm_dev *dev)
 292{
 293        dev->signal = GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ? ATM_PHY_SIG_LOST :
 294          ATM_PHY_SIG_FOUND;
 295}
 296
 297
 298static void suni_int(struct atm_dev *dev)
 299{
 300        poll_los(dev);
 301        printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number,
 302            dev->signal == ATM_PHY_SIG_LOST ?  "lost" : "detected again");
 303}
 304
 305
 306static int suni_start(struct atm_dev *dev)
 307{
 308        unsigned long flags;
 309        int first;
 310
 311        spin_lock_irqsave(&sunis_lock,flags);
 312        first = !sunis;
 313        PRIV(dev)->next = sunis;
 314        sunis = PRIV(dev);
 315        spin_unlock_irqrestore(&sunis_lock,flags);
 316        memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats));
 317        PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE);
 318                /* interrupt on loss of signal */
 319        poll_los(dev); /* ... and clear SUNI interrupts */
 320        if (dev->signal == ATM_PHY_SIG_LOST)
 321                printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type,
 322                    dev->number);
 323        PRIV(dev)->loop_mode = ATM_LM_NONE;
 324        suni_hz(0); /* clear SUNI counters */
 325        (void) fetch_stats(dev,NULL,1); /* clear kernel counters */
 326        if (first) {
 327                init_timer(&poll_timer);
 328                poll_timer.expires = jiffies+HZ;
 329                poll_timer.function = suni_hz;
 330                poll_timer.data = 1;
 331#if 0
 332printk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.list.prev,
 333    (unsigned long) poll_timer.list.next);
 334#endif
 335                add_timer(&poll_timer);
 336        }
 337        return 0;
 338}
 339
 340
 341static int suni_stop(struct atm_dev *dev)
 342{
 343        struct suni_priv **walk;
 344        unsigned long flags;
 345
 346        /* let SAR driver worry about stopping interrupts */
 347        spin_lock_irqsave(&sunis_lock,flags);
 348        for (walk = &sunis; *walk != PRIV(dev);
 349            walk = &PRIV((*walk)->dev)->next);
 350        *walk = PRIV((*walk)->dev)->next;
 351        if (!sunis) del_timer_sync(&poll_timer);
 352        spin_unlock_irqrestore(&sunis_lock,flags);
 353        kfree(PRIV(dev));
 354
 355        return 0;
 356}
 357
 358
 359static const struct atmphy_ops suni_ops = {
 360        .start          = suni_start,
 361        .ioctl          = suni_ioctl,
 362        .interrupt      = suni_int,
 363        .stop           = suni_stop,
 364};
 365
 366
 367int suni_init(struct atm_dev *dev)
 368{
 369        unsigned char mri;
 370
 371        if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
 372                return -ENOMEM;
 373        PRIV(dev)->dev = dev;
 374
 375        mri = GET(MRI); /* reset SUNI */
 376        PRIV(dev)->type = (mri & SUNI_MRI_TYPE) >> SUNI_MRI_TYPE_SHIFT;
 377        PUT(mri | SUNI_MRI_RESET,MRI);
 378        PUT(mri,MRI);
 379        PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */
 380        set_sonet(dev);
 381        REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP,
 382            TACP_IUCHP); /* idle cells */
 383        PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP);
 384        dev->phy = &suni_ops;
 385
 386        return 0;
 387}
 388
 389EXPORT_SYMBOL(suni_init);
 390
 391MODULE_LICENSE("GPL");
 392