]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/atm/suni.c
atm: [iphase] move struct suni_priv to suni.h
[karo-tx-linux.git] / drivers / atm / suni.c
1 /* drivers/atm/suni.c - PMC PM5346 SUNI (PHY) driver */
2  
3 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
4
5
6 #include <linux/module.h>
7 #include <linux/jiffies.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_suni.h>
18 #include <asm/system.h>
19 #include <asm/param.h>
20 #include <asm/uaccess.h>
21 #include <asm/atomic.h>
22
23 #include "suni.h"
24
25
26 #if 0
27 #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
28 #else
29 #define DPRINTK(format,args...)
30 #endif
31
32 #define PRIV(dev) ((struct suni_priv *) dev->phy_data)
33
34 #define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg)
35 #define GET(reg) dev->ops->phy_get(dev,SUNI_##reg)
36 #define REG_CHANGE(mask,shift,value,reg) \
37   PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg)
38
39
40 static struct timer_list poll_timer;
41 static struct suni_priv *sunis = NULL;
42 static DEFINE_SPINLOCK(sunis_lock);
43
44
45 #define ADD_LIMITED(s,v) \
46     atomic_add((v),&stats->s); \
47     if (atomic_read(&stats->s) < 0) atomic_set(&stats->s,INT_MAX);
48
49
50 static void suni_hz(unsigned long from_timer)
51 {
52         struct suni_priv *walk;
53         struct atm_dev *dev;
54         struct k_sonet_stats *stats;
55
56         for (walk = sunis; walk; walk = walk->next) {
57                 dev = walk->dev;
58                 stats = &walk->sonet_stats;
59                 PUT(0,MRI); /* latch counters */
60                 udelay(1);
61                 ADD_LIMITED(section_bip,(GET(RSOP_SBL) & 0xff) |
62                     ((GET(RSOP_SBM) & 0xff) << 8));
63                 ADD_LIMITED(line_bip,(GET(RLOP_LBL) & 0xff) |
64                     ((GET(RLOP_LB) & 0xff) << 8) |
65                     ((GET(RLOP_LBM) & 0xf) << 16));
66                 ADD_LIMITED(path_bip,(GET(RPOP_PBL) & 0xff) |
67                     ((GET(RPOP_PBM) & 0xff) << 8));
68                 ADD_LIMITED(line_febe,(GET(RLOP_LFL) & 0xff) |
69                     ((GET(RLOP_LF) & 0xff) << 8) |
70                     ((GET(RLOP_LFM) & 0xf) << 16));
71                 ADD_LIMITED(path_febe,(GET(RPOP_PFL) & 0xff) |
72                     ((GET(RPOP_PFM) & 0xff) << 8));
73                 ADD_LIMITED(corr_hcs,GET(RACP_CHEC) & 0xff);
74                 ADD_LIMITED(uncorr_hcs,GET(RACP_UHEC) & 0xff);
75                 ADD_LIMITED(rx_cells,(GET(RACP_RCCL) & 0xff) |
76                     ((GET(RACP_RCC) & 0xff) << 8) |
77                     ((GET(RACP_RCCM) & 7) << 16));
78                 ADD_LIMITED(tx_cells,(GET(TACP_TCCL) & 0xff) |
79                     ((GET(TACP_TCC) & 0xff) << 8) |
80                     ((GET(TACP_TCCM) & 7) << 16));
81         }
82         if (from_timer) mod_timer(&poll_timer,jiffies+HZ);
83 }
84
85
86 #undef ADD_LIMITED
87
88
89 static int fetch_stats(struct atm_dev *dev,struct sonet_stats __user *arg,int zero)
90 {
91         struct sonet_stats tmp;
92         int error = 0;
93
94         sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp);
95         if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp));
96         if (zero && !error) sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp);
97         return error ? -EFAULT : 0;
98 }
99
100
101 #define HANDLE_FLAG(flag,reg,bit) \
102   if (todo & flag) { \
103     if (set) PUT(GET(reg) | bit,reg); \
104     else PUT(GET(reg) & ~bit,reg); \
105     todo &= ~flag; \
106   }
107
108
109 static int change_diag(struct atm_dev *dev,void __user *arg,int set)
110 {
111         int todo;
112
113         if (get_user(todo,(int __user *)arg)) return -EFAULT;
114         HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8);
115         HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP);
116         HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3);
117         HANDLE_FLAG(SONET_INS_FRAME,RSOP_CIE,SUNI_RSOP_CIE_FOOF);
118         HANDLE_FLAG(SONET_INS_LAIS,TSOP_CTRL,SUNI_TSOP_CTRL_LAIS);
119         HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS);
120         HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS);
121         HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS);
122         return put_user(todo,(int __user *)arg) ? -EFAULT : 0;
123 }
124
125
126 #undef HANDLE_FLAG
127
128
129 static int get_diag(struct atm_dev *dev,void __user *arg)
130 {
131         int set;
132
133         set = 0;
134         if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DBIP8) set |= SONET_INS_SBIP;
135         if (GET(TLOP_DIAG) & SUNI_TLOP_DIAG_DBIP) set |= SONET_INS_LBIP;
136         if (GET(TPOP_CD) & SUNI_TPOP_DIAG_DB3) set |= SONET_INS_PBIP;
137         /* SONET_INS_FRAME is one-shot only */
138         if (GET(TSOP_CTRL) & SUNI_TSOP_CTRL_LAIS) set |= SONET_INS_LAIS;
139         if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS;
140         if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS;
141         if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS;
142         return put_user(set,(int __user *)arg) ? -EFAULT : 0;
143 }
144
145
146 static int set_loopback(struct atm_dev *dev,int mode)
147 {
148         unsigned char control;
149
150         control = GET(MCT) & ~(SUNI_MCT_DLE | SUNI_MCT_LLE);
151         switch (mode) {
152                 case ATM_LM_NONE:
153                         break;
154                 case ATM_LM_LOC_PHY:
155                         control |= SUNI_MCT_DLE;
156                         break;
157                 case ATM_LM_RMT_PHY:
158                         control |= SUNI_MCT_LLE;
159                         break;
160                 default:
161                         return -EINVAL;
162         }
163         PUT(control,MCT);
164         PRIV(dev)->loop_mode = mode;
165         return 0;
166 }
167
168
169 static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
170 {
171         switch (cmd) {
172                 case SONET_GETSTATZ:
173                 case SONET_GETSTAT:
174                         return fetch_stats(dev, arg, cmd == SONET_GETSTATZ);
175                 case SONET_SETDIAG:
176                         return change_diag(dev,arg,1);
177                 case SONET_CLRDIAG:
178                         return change_diag(dev,arg,0);
179                 case SONET_GETDIAG:
180                         return get_diag(dev,arg);
181                 case SONET_SETFRAMING:
182                         if ((int)(unsigned long)arg != SONET_FRAME_SONET) return -EINVAL;
183                         return 0;
184                 case SONET_GETFRAMING:
185                         return put_user(SONET_FRAME_SONET,(int __user *)arg) ?
186                             -EFAULT : 0;
187                 case SONET_GETFRSENSE:
188                         return -EINVAL;
189                 case ATM_SETLOOP:
190                         return set_loopback(dev,(int)(unsigned long)arg);
191                 case ATM_GETLOOP:
192                         return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ?
193                             -EFAULT : 0;
194                 case ATM_QUERYLOOP:
195                         return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY,
196                             (int __user *) arg) ? -EFAULT : 0;
197                 default:
198                         return -ENOIOCTLCMD;
199         }
200 }
201
202
203 static void poll_los(struct atm_dev *dev)
204 {
205         dev->signal = GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ? ATM_PHY_SIG_LOST :
206           ATM_PHY_SIG_FOUND;
207 }
208
209
210 static void suni_int(struct atm_dev *dev)
211 {
212         poll_los(dev);
213         printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number,
214             dev->signal == ATM_PHY_SIG_LOST ?  "lost" : "detected again");
215 }
216
217
218 static int suni_start(struct atm_dev *dev)
219 {
220         unsigned long flags;
221         int first;
222
223         if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
224                 return -ENOMEM;
225
226         PRIV(dev)->dev = dev;
227         spin_lock_irqsave(&sunis_lock,flags);
228         first = !sunis;
229         PRIV(dev)->next = sunis;
230         sunis = PRIV(dev);
231         spin_unlock_irqrestore(&sunis_lock,flags);
232         memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats));
233         PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE);
234                 /* interrupt on loss of signal */
235         poll_los(dev); /* ... and clear SUNI interrupts */
236         if (dev->signal == ATM_PHY_SIG_LOST)
237                 printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type,
238                     dev->number);
239         PRIV(dev)->loop_mode = ATM_LM_NONE;
240         suni_hz(0); /* clear SUNI counters */
241         (void) fetch_stats(dev,NULL,1); /* clear kernel counters */
242         if (first) {
243                 init_timer(&poll_timer);
244                 poll_timer.expires = jiffies+HZ;
245                 poll_timer.function = suni_hz;
246                 poll_timer.data = 1;
247 #if 0
248 printk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.list.prev,
249     (unsigned long) poll_timer.list.next);
250 #endif
251                 add_timer(&poll_timer);
252         }
253         return 0;
254 }
255
256
257 static int suni_stop(struct atm_dev *dev)
258 {
259         struct suni_priv **walk;
260         unsigned long flags;
261
262         /* let SAR driver worry about stopping interrupts */
263         spin_lock_irqsave(&sunis_lock,flags);
264         for (walk = &sunis; *walk != PRIV(dev);
265             walk = &PRIV((*walk)->dev)->next);
266         *walk = PRIV((*walk)->dev)->next;
267         if (!sunis) del_timer_sync(&poll_timer);
268         spin_unlock_irqrestore(&sunis_lock,flags);
269         kfree(PRIV(dev));
270
271         return 0;
272 }
273
274
275 static const struct atmphy_ops suni_ops = {
276         .start          = suni_start,
277         .ioctl          = suni_ioctl,
278         .interrupt      = suni_int,
279         .stop           = suni_stop,
280 };
281
282
283 int suni_init(struct atm_dev *dev)
284 {
285         unsigned char mri;
286
287         mri = GET(MRI); /* reset SUNI */
288         PUT(mri | SUNI_MRI_RESET,MRI);
289         PUT(mri,MRI);
290         PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */
291         REG_CHANGE(SUNI_TPOP_APM_S,SUNI_TPOP_APM_S_SHIFT,SUNI_TPOP_S_SONET,
292             TPOP_APM); /* use SONET */
293         REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP,
294             TACP_IUCHP); /* idle cells */
295         PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP);
296         dev->phy = &suni_ops;
297         return 0;
298 }
299
300 EXPORT_SYMBOL(suni_init);
301
302 MODULE_LICENSE("GPL");