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