Import 2.3.18pre1
[davej-history.git] / drivers / atm / suni.c
blob43904624fd839e9e97e4dc663e8bbad16256dc51
1 /* drivers/atm/suni.c - PMC SUNI (PHY) driver */
3 /* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
6 #include <linux/module.h>
7 #include <linux/sched.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>
22 #include "suni.h"
25 #if 0
26 #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
27 #else
28 #define DPRINTK(format,args...)
29 #endif
32 struct suni_priv {
33 struct sonet_stats sonet_stats; /* link diagnostics */
34 unsigned char loop_mode; /* loopback mode */
35 struct atm_dev *dev; /* device back-pointer */
36 struct suni_priv *next; /* next SUNI */
40 #define PRIV(dev) ((struct suni_priv *) dev->phy_data)
42 #define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg)
43 #define GET(reg) dev->ops->phy_get(dev,SUNI_##reg)
44 #define REG_CHANGE(mask,shift,value,reg) \
45 PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg)
48 static struct timer_list poll_timer;
49 static int start_timer = 1;
50 static struct suni_priv *sunis = NULL;
53 static void suni_hz(unsigned long dummy)
55 struct suni_priv *walk;
56 struct atm_dev *dev;
57 struct sonet_stats *stats;
59 for (walk = sunis; walk; walk = walk->next) {
60 dev = walk->dev;
61 stats = &walk->sonet_stats;
62 PUT(0,MRI); /* latch counters */
63 udelay(1);
64 stats->section_bip += (GET(RSOP_SBL) & 0xff) |
65 ((GET(RSOP_SBM) & 0xff) << 8);
66 if (stats->section_bip < 0) stats->section_bip = LONG_MAX;
67 stats->line_bip += (GET(RLOP_LBL) & 0xff) |
68 ((GET(RLOP_LB) & 0xff) << 8) |
69 ((GET(RLOP_LBM) & 0xf) << 16);
70 if (stats->line_bip < 0) stats->line_bip = LONG_MAX;
71 stats->path_bip += (GET(RPOP_PBL) & 0xff) |
72 ((GET(RPOP_PBM) & 0xff) << 8);
73 if (stats->path_bip < 0) stats->path_bip = LONG_MAX;
74 stats->line_febe += (GET(RLOP_LFL) & 0xff) |
75 ((GET(RLOP_LF) & 0xff) << 8) |
76 ((GET(RLOP_LFM) & 0xf) << 16);
77 if (stats->line_febe < 0) stats->line_febe = LONG_MAX;
78 stats->path_febe += (GET(RPOP_PFL) & 0xff) |
79 ((GET(RPOP_PFM) & 0xff) << 8);
80 if (stats->path_febe < 0) stats->path_febe = LONG_MAX;
81 stats->corr_hcs += GET(RACP_CHEC) & 0xff;
82 if (stats->corr_hcs < 0) stats->corr_hcs = LONG_MAX;
83 stats->uncorr_hcs += GET(RACP_UHEC) & 0xff;
84 if (stats->uncorr_hcs < 0) stats->uncorr_hcs = LONG_MAX;
85 stats->rx_cells += (GET(RACP_RCCL) & 0xff) |
86 ((GET(RACP_RCC) & 0xff) << 8) |
87 ((GET(RACP_RCCM) & 7) << 16);
88 if (stats->rx_cells < 0) stats->rx_cells = LONG_MAX;
89 stats->tx_cells += (GET(TACP_TCCL) & 0xff) |
90 ((GET(TACP_TCC) & 0xff) << 8) |
91 ((GET(TACP_TCCM) & 7) << 16);
92 if (stats->tx_cells < 0) stats->tx_cells = LONG_MAX;
94 if (!start_timer) mod_timer(&poll_timer,jiffies+HZ);
98 static int fetch_stats(struct atm_dev *dev,struct sonet_stats *arg,int zero)
100 unsigned long flags;
101 int error;
103 error = 0;
104 save_flags(flags);
105 cli();
106 if (arg)
107 error = copy_to_user(arg,&PRIV(dev)->sonet_stats,
108 sizeof(struct sonet_stats));
109 if (zero && !error)
110 memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
111 restore_flags(flags);
112 return error ? -EFAULT : 0;
116 #define HANDLE_FLAG(flag,reg,bit) \
117 if (todo & flag) { \
118 if (set) PUT(GET(reg) | bit,reg); \
119 else PUT(GET(reg) & ~bit,reg); \
120 todo &= ~flag; \
124 static int change_diag(struct atm_dev *dev,void *arg,int set)
126 int todo;
128 if (get_user(todo,(int *) arg)) return -EFAULT;
129 HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8);
130 HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP);
131 HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3);
132 HANDLE_FLAG(SONET_INS_FRAME,RSOP_CIE,SUNI_RSOP_CIE_FOOF);
133 HANDLE_FLAG(SONET_INS_LAIS,TSOP_CTRL,SUNI_TSOP_CTRL_LAIS);
134 HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS);
135 HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS);
136 HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS);
137 return put_user(todo,(int *) arg) ? -EFAULT : 0;
141 #undef HANDLE_FLAG
144 static int get_diag(struct atm_dev *dev,void *arg)
146 int set;
148 set = 0;
149 if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DBIP8) set |= SONET_INS_SBIP;
150 if (GET(TLOP_DIAG) & SUNI_TLOP_DIAG_DBIP) set |= SONET_INS_LBIP;
151 if (GET(TPOP_CD) & SUNI_TPOP_DIAG_DB3) set |= SONET_INS_PBIP;
152 /* SONET_INS_FRAME is one-shot only */
153 if (GET(TSOP_CTRL) & SUNI_TSOP_CTRL_LAIS) set |= SONET_INS_LAIS;
154 if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS;
155 if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS;
156 if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS;
157 return put_user(set,(int *) arg) ? -EFAULT : 0;
161 static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg)
163 switch (cmd) {
164 case SONET_GETSTATZ:
165 case SONET_GETSTAT:
166 return fetch_stats(dev,(struct sonet_stats *) arg,
167 cmd == SONET_GETSTATZ);
168 case SONET_SETDIAG:
169 return change_diag(dev,arg,1);
170 case SONET_CLRDIAG:
171 return change_diag(dev,arg,0);
172 case SONET_GETDIAG:
173 return get_diag(dev,arg);
174 case SONET_SETFRAMING:
175 if (!capable(CAP_NET_ADMIN)) return -EPERM;
176 if (arg != SONET_FRAME_SONET) return -EINVAL;
177 return 0;
178 case SONET_GETFRAMING:
179 return put_user(SONET_FRAME_SONET,(int *) arg) ?
180 -EFAULT : 0;
181 case SONET_GETFRSENSE:
182 return -EINVAL;
183 case SUNI_SETLOOP:
184 if (!capable(CAP_NET_ADMIN)) return -EPERM;
185 if ((int) arg < 0 || (int) arg > SUNI_LM_LOOP)
186 return -EINVAL;
187 PUT((GET(MCT) & ~(SUNI_MCT_DLE | SUNI_MCT_LLE)) |
188 ((int) arg == SUNI_LM_DIAG ? SUNI_MCT_DLE : 0) |
189 ((int) arg == SUNI_LM_LOOP ? SUNI_MCT_LLE : 0),MCT);
190 PRIV(dev)->loop_mode = (int) arg;
191 return 0;
192 case SUNI_GETLOOP:
193 return put_user(PRIV(dev)->loop_mode,(int *) arg) ?
194 -EFAULT : 0;
195 default:
196 return -EINVAL;
201 static void poll_los(struct atm_dev *dev)
203 dev->signal = GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ? ATM_PHY_SIG_LOST :
204 ATM_PHY_SIG_FOUND;
208 static void suni_int(struct atm_dev *dev)
210 poll_los(dev);
211 printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number,
212 dev->signal == ATM_PHY_SIG_LOST ? "lost" : "detected again");
216 static int suni_start(struct atm_dev *dev)
218 unsigned long flags;
220 if (!(PRIV(dev) = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
221 return -ENOMEM;
222 PRIV(dev)->dev = dev;
223 save_flags(flags);
224 cli();
225 PRIV(dev)->next = sunis;
226 sunis = PRIV(dev);
227 restore_flags(flags);
228 memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
229 PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE);
230 /* interrupt on loss of signal */
231 poll_los(dev); /* ... and clear SUNI interrupts */
232 if (dev->signal == ATM_PHY_SIG_LOST)
233 printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type,
234 dev->number);
235 PRIV(dev)->loop_mode = SUNI_LM_NONE;
236 suni_hz(0); /* clear SUNI counters */
237 (void) fetch_stats(dev,NULL,1); /* clear kernel counters */
238 cli();
239 if (!start_timer) restore_flags(flags);
240 else {
241 start_timer = 0;
242 restore_flags(flags);
243 init_timer(&poll_timer);
244 poll_timer.expires = jiffies+HZ;
245 poll_timer.function = suni_hz;
246 #if 0
247 printk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.prev,
248 (unsigned long) poll_timer.next);
249 #endif
250 add_timer(&poll_timer);
252 return 0;
256 static const struct atmphy_ops suni_ops = {
257 suni_start,
258 suni_ioctl,
259 suni_int
263 int __init suni_init(struct atm_dev *dev)
265 unsigned char mri;
267 mri = GET(MRI); /* reset SUNI */
268 PUT(mri | SUNI_MRI_RESET,MRI);
269 PUT(mri,MRI);
270 PUT(0,MT); /* disable all tests */
271 REG_CHANGE(SUNI_TPOP_APM_S,SUNI_TPOP_APM_S_SHIFT,SUNI_TPOP_S_SONET,
272 TPOP_APM); /* use SONET */
273 REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP,
274 TACP_IUCHP); /* idle cells */
275 PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP);
276 dev->phy = &suni_ops;
277 return 0;
281 EXPORT_SYMBOL(suni_init);
284 #ifdef MODULE
287 int init_module(void)
289 MOD_INC_USE_COUNT;
290 return 0;
294 void cleanup_module(void)
296 /* Nay */
299 #endif