xref: /openbmc/linux/drivers/atm/suni.c (revision 9cb24ea0)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2df3bc8bdSChas Williams /*
3df3bc8bdSChas Williams  * drivers/atm/suni.c - S/UNI PHY driver
4df3bc8bdSChas Williams  *
5df3bc8bdSChas Williams  * Supports the following:
6df3bc8bdSChas Williams  * 	PMC PM5346 S/UNI LITE
7df3bc8bdSChas Williams  * 	PMC PM5350 S/UNI 155 ULTRA
8df3bc8bdSChas Williams  * 	PMC PM5355 S/UNI 622
9df3bc8bdSChas Williams  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #include <linux/module.h>
141da177e4SLinus Torvalds #include <linux/jiffies.h>
151da177e4SLinus Torvalds #include <linux/kernel.h>
161da177e4SLinus Torvalds #include <linux/mm.h>
171da177e4SLinus Torvalds #include <linux/errno.h>
181da177e4SLinus Torvalds #include <linux/atmdev.h>
191da177e4SLinus Torvalds #include <linux/sonet.h>
201da177e4SLinus Torvalds #include <linux/delay.h>
211da177e4SLinus Torvalds #include <linux/timer.h>
221da177e4SLinus Torvalds #include <linux/init.h>
231da177e4SLinus Torvalds #include <linux/capability.h>
245a0e3ad6STejun Heo #include <linux/slab.h>
251da177e4SLinus Torvalds #include <asm/param.h>
267c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
2760063497SArun Sharma #include <linux/atomic.h>
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #include "suni.h"
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds #if 0
331da177e4SLinus Torvalds #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
341da177e4SLinus Torvalds #else
351da177e4SLinus Torvalds #define DPRINTK(format,args...)
361da177e4SLinus Torvalds #endif
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds #define PRIV(dev) ((struct suni_priv *) dev->phy_data)
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds #define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg)
411da177e4SLinus Torvalds #define GET(reg) dev->ops->phy_get(dev,SUNI_##reg)
421da177e4SLinus Torvalds #define REG_CHANGE(mask,shift,value,reg) \
431da177e4SLinus Torvalds   PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg)
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds static struct timer_list poll_timer;
471da177e4SLinus Torvalds static struct suni_priv *sunis = NULL;
481da177e4SLinus Torvalds static DEFINE_SPINLOCK(sunis_lock);
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds #define ADD_LIMITED(s,v) \
521da177e4SLinus Torvalds     atomic_add((v),&stats->s); \
531da177e4SLinus Torvalds     if (atomic_read(&stats->s) < 0) atomic_set(&stats->s,INT_MAX);
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 
suni_hz(struct timer_list * timer)56847f03eeSKees Cook static void suni_hz(struct timer_list *timer)
571da177e4SLinus Torvalds {
581da177e4SLinus Torvalds 	struct suni_priv *walk;
591da177e4SLinus Torvalds 	struct atm_dev *dev;
601da177e4SLinus Torvalds 	struct k_sonet_stats *stats;
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	for (walk = sunis; walk; walk = walk->next) {
631da177e4SLinus Torvalds 		dev = walk->dev;
641da177e4SLinus Torvalds 		stats = &walk->sonet_stats;
651da177e4SLinus Torvalds 		PUT(0,MRI); /* latch counters */
661da177e4SLinus Torvalds 		udelay(1);
671da177e4SLinus Torvalds 		ADD_LIMITED(section_bip,(GET(RSOP_SBL) & 0xff) |
681da177e4SLinus Torvalds 		    ((GET(RSOP_SBM) & 0xff) << 8));
691da177e4SLinus Torvalds 		ADD_LIMITED(line_bip,(GET(RLOP_LBL) & 0xff) |
701da177e4SLinus Torvalds 		    ((GET(RLOP_LB) & 0xff) << 8) |
711da177e4SLinus Torvalds 		    ((GET(RLOP_LBM) & 0xf) << 16));
721da177e4SLinus Torvalds 		ADD_LIMITED(path_bip,(GET(RPOP_PBL) & 0xff) |
731da177e4SLinus Torvalds 		    ((GET(RPOP_PBM) & 0xff) << 8));
741da177e4SLinus Torvalds 		ADD_LIMITED(line_febe,(GET(RLOP_LFL) & 0xff) |
751da177e4SLinus Torvalds 		    ((GET(RLOP_LF) & 0xff) << 8) |
761da177e4SLinus Torvalds 		    ((GET(RLOP_LFM) & 0xf) << 16));
771da177e4SLinus Torvalds 		ADD_LIMITED(path_febe,(GET(RPOP_PFL) & 0xff) |
781da177e4SLinus Torvalds 		    ((GET(RPOP_PFM) & 0xff) << 8));
791da177e4SLinus Torvalds 		ADD_LIMITED(corr_hcs,GET(RACP_CHEC) & 0xff);
801da177e4SLinus Torvalds 		ADD_LIMITED(uncorr_hcs,GET(RACP_UHEC) & 0xff);
811da177e4SLinus Torvalds 		ADD_LIMITED(rx_cells,(GET(RACP_RCCL) & 0xff) |
821da177e4SLinus Torvalds 		    ((GET(RACP_RCC) & 0xff) << 8) |
831da177e4SLinus Torvalds 		    ((GET(RACP_RCCM) & 7) << 16));
841da177e4SLinus Torvalds 		ADD_LIMITED(tx_cells,(GET(TACP_TCCL) & 0xff) |
851da177e4SLinus Torvalds 		    ((GET(TACP_TCC) & 0xff) << 8) |
861da177e4SLinus Torvalds 		    ((GET(TACP_TCCM) & 7) << 16));
871da177e4SLinus Torvalds 	}
88847f03eeSKees Cook 	if (timer) mod_timer(&poll_timer,jiffies+HZ);
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds #undef ADD_LIMITED
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 
fetch_stats(struct atm_dev * dev,struct sonet_stats __user * arg,int zero)951da177e4SLinus Torvalds static int fetch_stats(struct atm_dev *dev,struct sonet_stats __user *arg,int zero)
961da177e4SLinus Torvalds {
971da177e4SLinus Torvalds 	struct sonet_stats tmp;
981da177e4SLinus Torvalds 	int error = 0;
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds 	sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp);
1011da177e4SLinus Torvalds 	if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp));
1021da177e4SLinus Torvalds 	if (zero && !error) sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp);
1031da177e4SLinus Torvalds 	return error ? -EFAULT : 0;
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds #define HANDLE_FLAG(flag,reg,bit) \
1081da177e4SLinus Torvalds   if (todo & flag) { \
1091da177e4SLinus Torvalds     if (set) PUT(GET(reg) | bit,reg); \
1101da177e4SLinus Torvalds     else PUT(GET(reg) & ~bit,reg); \
1111da177e4SLinus Torvalds     todo &= ~flag; \
1121da177e4SLinus Torvalds   }
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 
change_diag(struct atm_dev * dev,void __user * arg,int set)1151da177e4SLinus Torvalds static int change_diag(struct atm_dev *dev,void __user *arg,int set)
1161da177e4SLinus Torvalds {
1171da177e4SLinus Torvalds 	int todo;
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	if (get_user(todo,(int __user *)arg)) return -EFAULT;
1201da177e4SLinus Torvalds 	HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8);
1211da177e4SLinus Torvalds 	HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP);
1221da177e4SLinus Torvalds 	HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3);
1231da177e4SLinus Torvalds 	HANDLE_FLAG(SONET_INS_FRAME,RSOP_CIE,SUNI_RSOP_CIE_FOOF);
1241da177e4SLinus Torvalds 	HANDLE_FLAG(SONET_INS_LAIS,TSOP_CTRL,SUNI_TSOP_CTRL_LAIS);
1251da177e4SLinus Torvalds 	HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS);
1261da177e4SLinus Torvalds 	HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS);
1271da177e4SLinus Torvalds 	HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS);
1281da177e4SLinus Torvalds 	return put_user(todo,(int __user *)arg) ? -EFAULT : 0;
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds #undef HANDLE_FLAG
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 
get_diag(struct atm_dev * dev,void __user * arg)1351da177e4SLinus Torvalds static int get_diag(struct atm_dev *dev,void __user *arg)
1361da177e4SLinus Torvalds {
1371da177e4SLinus Torvalds 	int set;
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds 	set = 0;
1401da177e4SLinus Torvalds 	if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DBIP8) set |= SONET_INS_SBIP;
1411da177e4SLinus Torvalds 	if (GET(TLOP_DIAG) & SUNI_TLOP_DIAG_DBIP) set |= SONET_INS_LBIP;
1421da177e4SLinus Torvalds 	if (GET(TPOP_CD) & SUNI_TPOP_DIAG_DB3) set |= SONET_INS_PBIP;
1431da177e4SLinus Torvalds 	/* SONET_INS_FRAME is one-shot only */
1441da177e4SLinus Torvalds 	if (GET(TSOP_CTRL) & SUNI_TSOP_CTRL_LAIS) set |= SONET_INS_LAIS;
1451da177e4SLinus Torvalds 	if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS;
1461da177e4SLinus Torvalds 	if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS;
1471da177e4SLinus Torvalds 	if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS;
1481da177e4SLinus Torvalds 	return put_user(set,(int __user *)arg) ? -EFAULT : 0;
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 
set_loopback(struct atm_dev * dev,int mode)1521da177e4SLinus Torvalds static int set_loopback(struct atm_dev *dev,int mode)
1531da177e4SLinus Torvalds {
1541da177e4SLinus Torvalds 	unsigned char control;
155df3bc8bdSChas Williams 	int reg, dle, lle;
1561da177e4SLinus Torvalds 
157df3bc8bdSChas Williams 	if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
158df3bc8bdSChas Williams 		reg = SUNI_MCM;
159df3bc8bdSChas Williams 		dle = SUNI_MCM_DLE;
160df3bc8bdSChas Williams 		lle = SUNI_MCM_LLE;
161df3bc8bdSChas Williams 	} else {
162df3bc8bdSChas Williams 		reg = SUNI_MCT;
163df3bc8bdSChas Williams 		dle = SUNI_MCT_DLE;
164df3bc8bdSChas Williams 		lle = SUNI_MCT_LLE;
165df3bc8bdSChas Williams 	}
166df3bc8bdSChas Williams 
167df3bc8bdSChas Williams 	control = dev->ops->phy_get(dev, reg) & ~(dle | lle);
1681da177e4SLinus Torvalds 	switch (mode) {
1691da177e4SLinus Torvalds 		case ATM_LM_NONE:
1701da177e4SLinus Torvalds 			break;
1711da177e4SLinus Torvalds 		case ATM_LM_LOC_PHY:
172df3bc8bdSChas Williams 			control |= dle;
1731da177e4SLinus Torvalds 			break;
1741da177e4SLinus Torvalds 		case ATM_LM_RMT_PHY:
175df3bc8bdSChas Williams 			control |= lle;
1761da177e4SLinus Torvalds 			break;
1771da177e4SLinus Torvalds 		default:
1781da177e4SLinus Torvalds 			return -EINVAL;
1791da177e4SLinus Torvalds 	}
180df3bc8bdSChas Williams 	dev->ops->phy_put(dev, control, reg);
1811da177e4SLinus Torvalds 	PRIV(dev)->loop_mode = mode;
1821da177e4SLinus Torvalds 	return 0;
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds 
185df3bc8bdSChas Williams /*
186df3bc8bdSChas Williams  * SONET vs. SDH Configuration
187df3bc8bdSChas Williams  *
188df3bc8bdSChas Williams  * Z0INS (register 0x06): 0 for SONET, 1 for SDH
189df3bc8bdSChas Williams  * ENSS (register 0x3D): 0 for SONET, 1 for SDH
190df3bc8bdSChas Williams  * LEN16 (register 0x28): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD)
191df3bc8bdSChas Williams  * LEN16 (register 0x50): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD)
192df3bc8bdSChas Williams  * S[1:0] (register 0x46): 00 for SONET, 10 for SDH
193df3bc8bdSChas Williams  */
194df3bc8bdSChas Williams 
set_sonet(struct atm_dev * dev)195df3bc8bdSChas Williams static int set_sonet(struct atm_dev *dev)
196df3bc8bdSChas Williams {
197df3bc8bdSChas Williams 	if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
198df3bc8bdSChas Williams 		PUT(GET(RPOP_RC) & ~SUNI_RPOP_RC_ENSS, RPOP_RC);
199df3bc8bdSChas Williams 		PUT(GET(SSTB_CTRL) & ~SUNI_SSTB_CTRL_LEN16, SSTB_CTRL);
200df3bc8bdSChas Williams 		PUT(GET(SPTB_CTRL) & ~SUNI_SPTB_CTRL_LEN16, SPTB_CTRL);
201df3bc8bdSChas Williams 	}
202df3bc8bdSChas Williams 
203df3bc8bdSChas Williams 	REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT,
204df3bc8bdSChas Williams 		   SUNI_TPOP_S_SONET, TPOP_APM);
205df3bc8bdSChas Williams 
206df3bc8bdSChas Williams 	return 0;
207df3bc8bdSChas Williams }
208df3bc8bdSChas Williams 
set_sdh(struct atm_dev * dev)209df3bc8bdSChas Williams static int set_sdh(struct atm_dev *dev)
210df3bc8bdSChas Williams {
211df3bc8bdSChas Williams 	if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
212df3bc8bdSChas Williams 		PUT(GET(RPOP_RC) | SUNI_RPOP_RC_ENSS, RPOP_RC);
213df3bc8bdSChas Williams 		PUT(GET(SSTB_CTRL) | SUNI_SSTB_CTRL_LEN16, SSTB_CTRL);
214df3bc8bdSChas Williams 		PUT(GET(SPTB_CTRL) | SUNI_SPTB_CTRL_LEN16, SPTB_CTRL);
215df3bc8bdSChas Williams 	}
216df3bc8bdSChas Williams 
217df3bc8bdSChas Williams 	REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT,
218df3bc8bdSChas Williams 		   SUNI_TPOP_S_SDH, TPOP_APM);
219df3bc8bdSChas Williams 
220df3bc8bdSChas Williams 	return 0;
221df3bc8bdSChas Williams }
222df3bc8bdSChas Williams 
223df3bc8bdSChas Williams 
get_framing(struct atm_dev * dev,void __user * arg)224df3bc8bdSChas Williams static int get_framing(struct atm_dev *dev, void __user *arg)
225df3bc8bdSChas Williams {
226df3bc8bdSChas Williams 	int framing;
227df3bc8bdSChas Williams 	unsigned char s;
228df3bc8bdSChas Williams 
229df3bc8bdSChas Williams 
230df3bc8bdSChas Williams 	s = (GET(TPOP_APM) & SUNI_TPOP_APM_S) >> SUNI_TPOP_APM_S_SHIFT;
231df3bc8bdSChas Williams 	if (s == SUNI_TPOP_S_SONET)
232df3bc8bdSChas Williams 		framing = SONET_FRAME_SONET;
233df3bc8bdSChas Williams 	else
234df3bc8bdSChas Williams 		framing = SONET_FRAME_SDH;
235df3bc8bdSChas Williams 
236df3bc8bdSChas Williams 	return put_user(framing, (int __user *) arg) ? -EFAULT : 0;
237df3bc8bdSChas Williams }
238df3bc8bdSChas Williams 
set_framing(struct atm_dev * dev,void __user * arg)239df3bc8bdSChas Williams static int set_framing(struct atm_dev *dev, void __user *arg)
240df3bc8bdSChas Williams {
241df3bc8bdSChas Williams 	int mode;
242df3bc8bdSChas Williams 
243df3bc8bdSChas Williams 	if (get_user(mode, (int __user *) arg))
244df3bc8bdSChas Williams 		return -EFAULT;
245df3bc8bdSChas Williams 
246df3bc8bdSChas Williams 	if (mode == SONET_FRAME_SONET)
247df3bc8bdSChas Williams 		return set_sonet(dev);
248df3bc8bdSChas Williams 	else if (mode == SONET_FRAME_SDH)
249df3bc8bdSChas Williams 		return set_sdh(dev);
250df3bc8bdSChas Williams 
251df3bc8bdSChas Williams 	return -EINVAL;
252df3bc8bdSChas Williams }
253df3bc8bdSChas Williams 
2541da177e4SLinus Torvalds 
suni_ioctl(struct atm_dev * dev,unsigned int cmd,void __user * arg)2551da177e4SLinus Torvalds static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
2561da177e4SLinus Torvalds {
2571da177e4SLinus Torvalds 	switch (cmd) {
2581da177e4SLinus Torvalds 		case SONET_GETSTATZ:
2591da177e4SLinus Torvalds 		case SONET_GETSTAT:
2601da177e4SLinus Torvalds 			return fetch_stats(dev, arg, cmd == SONET_GETSTATZ);
2611da177e4SLinus Torvalds 		case SONET_SETDIAG:
2621da177e4SLinus Torvalds 			return change_diag(dev,arg,1);
2631da177e4SLinus Torvalds 		case SONET_CLRDIAG:
2641da177e4SLinus Torvalds 			return change_diag(dev,arg,0);
2651da177e4SLinus Torvalds 		case SONET_GETDIAG:
2661da177e4SLinus Torvalds 			return get_diag(dev,arg);
2671da177e4SLinus Torvalds 		case SONET_SETFRAMING:
268df3bc8bdSChas Williams 			if (!capable(CAP_NET_ADMIN))
269df3bc8bdSChas Williams 				return -EPERM;
270df3bc8bdSChas Williams 			return set_framing(dev, arg);
2711da177e4SLinus Torvalds 		case SONET_GETFRAMING:
272df3bc8bdSChas Williams 			return get_framing(dev, arg);
2731da177e4SLinus Torvalds 		case SONET_GETFRSENSE:
2741da177e4SLinus Torvalds 			return -EINVAL;
2751da177e4SLinus Torvalds 		case ATM_SETLOOP:
276df3bc8bdSChas Williams 			if (!capable(CAP_NET_ADMIN))
277df3bc8bdSChas Williams 				return -EPERM;
2781da177e4SLinus Torvalds 			return set_loopback(dev,(int)(unsigned long)arg);
2791da177e4SLinus Torvalds 		case ATM_GETLOOP:
2801da177e4SLinus Torvalds 			return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ?
2811da177e4SLinus Torvalds 			    -EFAULT : 0;
2821da177e4SLinus Torvalds 		case ATM_QUERYLOOP:
2831da177e4SLinus Torvalds 			return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY,
2841da177e4SLinus Torvalds 			    (int __user *) arg) ? -EFAULT : 0;
2851da177e4SLinus Torvalds 		default:
2861da177e4SLinus Torvalds 			return -ENOIOCTLCMD;
2871da177e4SLinus Torvalds 	}
2881da177e4SLinus Torvalds }
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 
poll_los(struct atm_dev * dev)2911da177e4SLinus Torvalds static void poll_los(struct atm_dev *dev)
2921da177e4SLinus Torvalds {
293e0b901a9SKarl Hiramoto 	atm_dev_signal_change(dev,
294e0b901a9SKarl Hiramoto 		GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ?
295e0b901a9SKarl Hiramoto 		ATM_PHY_SIG_LOST : ATM_PHY_SIG_FOUND);
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 
suni_int(struct atm_dev * dev)2991da177e4SLinus Torvalds static void suni_int(struct atm_dev *dev)
3001da177e4SLinus Torvalds {
3011da177e4SLinus Torvalds 	poll_los(dev);
3021da177e4SLinus Torvalds 	printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number,
3031da177e4SLinus Torvalds 	    dev->signal == ATM_PHY_SIG_LOST ?  "lost" : "detected again");
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 
suni_start(struct atm_dev * dev)3071da177e4SLinus Torvalds static int suni_start(struct atm_dev *dev)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds 	unsigned long flags;
3101da177e4SLinus Torvalds 	int first;
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	spin_lock_irqsave(&sunis_lock,flags);
3131da177e4SLinus Torvalds 	first = !sunis;
3141da177e4SLinus Torvalds 	PRIV(dev)->next = sunis;
3151da177e4SLinus Torvalds 	sunis = PRIV(dev);
3161da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sunis_lock,flags);
3171da177e4SLinus Torvalds 	memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats));
3181da177e4SLinus Torvalds 	PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE);
3191da177e4SLinus Torvalds 		/* interrupt on loss of signal */
3201da177e4SLinus Torvalds 	poll_los(dev); /* ... and clear SUNI interrupts */
3211da177e4SLinus Torvalds 	if (dev->signal == ATM_PHY_SIG_LOST)
3221da177e4SLinus Torvalds 		printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type,
3231da177e4SLinus Torvalds 		    dev->number);
3241da177e4SLinus Torvalds 	PRIV(dev)->loop_mode = ATM_LM_NONE;
325847f03eeSKees Cook 	suni_hz(NULL); /* clear SUNI counters */
3261da177e4SLinus Torvalds 	(void) fetch_stats(dev,NULL,1); /* clear kernel counters */
3271da177e4SLinus Torvalds 	if (first) {
328847f03eeSKees Cook 		timer_setup(&poll_timer, suni_hz, 0);
3291da177e4SLinus Torvalds 		poll_timer.expires = jiffies+HZ;
3301da177e4SLinus Torvalds #if 0
3311da177e4SLinus Torvalds printk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.list.prev,
3321da177e4SLinus Torvalds     (unsigned long) poll_timer.list.next);
3331da177e4SLinus Torvalds #endif
3341da177e4SLinus Torvalds 		add_timer(&poll_timer);
3351da177e4SLinus Torvalds 	}
3361da177e4SLinus Torvalds 	return 0;
3371da177e4SLinus Torvalds }
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 
suni_stop(struct atm_dev * dev)3401da177e4SLinus Torvalds static int suni_stop(struct atm_dev *dev)
3411da177e4SLinus Torvalds {
3421da177e4SLinus Torvalds 	struct suni_priv **walk;
3431da177e4SLinus Torvalds 	unsigned long flags;
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds 	/* let SAR driver worry about stopping interrupts */
3461da177e4SLinus Torvalds 	spin_lock_irqsave(&sunis_lock,flags);
3471da177e4SLinus Torvalds 	for (walk = &sunis; *walk != PRIV(dev);
3481da177e4SLinus Torvalds 	    walk = &PRIV((*walk)->dev)->next);
3491da177e4SLinus Torvalds 	*walk = PRIV((*walk)->dev)->next;
3501da177e4SLinus Torvalds 	if (!sunis) del_timer_sync(&poll_timer);
3511da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sunis_lock,flags);
3521da177e4SLinus Torvalds 	kfree(PRIV(dev));
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds 	return 0;
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds static const struct atmphy_ops suni_ops = {
3591da177e4SLinus Torvalds 	.start		= suni_start,
3601da177e4SLinus Torvalds 	.ioctl		= suni_ioctl,
3611da177e4SLinus Torvalds 	.interrupt	= suni_int,
3621da177e4SLinus Torvalds 	.stop		= suni_stop,
3631da177e4SLinus Torvalds };
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 
suni_init(struct atm_dev * dev)366421c9914SAdrian Bunk int suni_init(struct atm_dev *dev)
3671da177e4SLinus Torvalds {
3681da177e4SLinus Torvalds 	unsigned char mri;
3691da177e4SLinus Torvalds 
370df3bc8bdSChas Williams 	if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
371df3bc8bdSChas Williams 		return -ENOMEM;
372df3bc8bdSChas Williams 	PRIV(dev)->dev = dev;
373df3bc8bdSChas Williams 
3741da177e4SLinus Torvalds 	mri = GET(MRI); /* reset SUNI */
375df3bc8bdSChas Williams 	PRIV(dev)->type = (mri & SUNI_MRI_TYPE) >> SUNI_MRI_TYPE_SHIFT;
3761da177e4SLinus Torvalds 	PUT(mri | SUNI_MRI_RESET,MRI);
3771da177e4SLinus Torvalds 	PUT(mri,MRI);
3781da177e4SLinus Torvalds 	PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */
379df3bc8bdSChas Williams         set_sonet(dev);
3801da177e4SLinus Torvalds 	REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP,
3811da177e4SLinus Torvalds 	    TACP_IUCHP); /* idle cells */
3821da177e4SLinus Torvalds 	PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP);
3831da177e4SLinus Torvalds 	dev->phy = &suni_ops;
384df3bc8bdSChas Williams 
3851da177e4SLinus Torvalds 	return 0;
3861da177e4SLinus Torvalds }
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds EXPORT_SYMBOL(suni_init);
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds MODULE_LICENSE("GPL");
391