summary refs log tree commit diff
path: root/drivers/atm/suni.c
diff options
context:
space:
mode:
authorChas Williams <chas@cmf.nrl.navy.mil>2008-06-17 16:19:24 -0700
committerDavid S. Miller <davem@davemloft.net>2008-06-17 16:19:24 -0700
commitdf3bc8bd8f8fd17e9b22859d82af38fa702e75b7 (patch)
treeff3172a18511fb244e8f8216dd6b344150b5c4f3 /drivers/atm/suni.c
parent2be63b878f2a1e6d939b05f4f5cb733cb39bcd22 (diff)
downloadlinux-df3bc8bd8f8fd17e9b22859d82af38fa702e75b7.tar.gz
atm: [suni] add support for setting loopback and framing modes
Signed-off-by: Chas Williams <chas@cmf.nrl.navy.mil>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/atm/suni.c')
-rw-r--r--drivers/atm/suni.c121
1 files changed, 105 insertions, 16 deletions
diff --git a/drivers/atm/suni.c b/drivers/atm/suni.c
index 6097f6877a44..6dd3f5919968 100644
--- a/drivers/atm/suni.c
+++ b/drivers/atm/suni.c
@@ -1,8 +1,14 @@
-/* drivers/atm/suni.c - PMC PM5346 SUNI (PHY) driver */
+/*
+ * drivers/atm/suni.c - S/UNI PHY driver
+ *
+ * Supports the following:
+ * 	PMC PM5346 S/UNI LITE
+ * 	PMC PM5350 S/UNI 155 ULTRA
+ * 	PMC PM5355 S/UNI 622
+ */
  
 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
-
 #include <linux/module.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
@@ -146,25 +152,105 @@ static int get_diag(struct atm_dev *dev,void __user *arg)
 static int set_loopback(struct atm_dev *dev,int mode)
 {
 	unsigned char control;
+	int reg, dle, lle;
+
+	if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
+		reg = SUNI_MCM;
+		dle = SUNI_MCM_DLE;
+		lle = SUNI_MCM_LLE;
+	} else {
+		reg = SUNI_MCT;
+		dle = SUNI_MCT_DLE;
+		lle = SUNI_MCT_LLE;
+	}
 
-	control = GET(MCT) & ~(SUNI_MCT_DLE | SUNI_MCT_LLE);
+	control = dev->ops->phy_get(dev, reg) & ~(dle | lle);
 	switch (mode) {
 		case ATM_LM_NONE:
 			break;
 		case ATM_LM_LOC_PHY:
-			control |= SUNI_MCT_DLE;
+			control |= dle;
 			break;
 		case ATM_LM_RMT_PHY:
-			control |= SUNI_MCT_LLE;
+			control |= lle;
 			break;
 		default:
 			return -EINVAL;
 	}
-	PUT(control,MCT);
+	 dev->ops->phy_put(dev, control, reg);
 	PRIV(dev)->loop_mode = mode;
 	return 0;
 }
 
+/*
+ * SONET vs. SDH Configuration
+ *
+ * Z0INS (register 0x06): 0 for SONET, 1 for SDH
+ * ENSS (register 0x3D): 0 for SONET, 1 for SDH
+ * LEN16 (register 0x28): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD)
+ * LEN16 (register 0x50): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD)
+ * S[1:0] (register 0x46): 00 for SONET, 10 for SDH
+ */
+
+static int set_sonet(struct atm_dev *dev)
+{
+	if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
+		PUT(GET(RPOP_RC) & ~SUNI_RPOP_RC_ENSS, RPOP_RC);
+		PUT(GET(SSTB_CTRL) & ~SUNI_SSTB_CTRL_LEN16, SSTB_CTRL);
+		PUT(GET(SPTB_CTRL) & ~SUNI_SPTB_CTRL_LEN16, SPTB_CTRL);
+	}
+
+	REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT,
+		   SUNI_TPOP_S_SONET, TPOP_APM);
+
+	return 0;
+}
+
+static int set_sdh(struct atm_dev *dev)
+{
+	if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
+		PUT(GET(RPOP_RC) | SUNI_RPOP_RC_ENSS, RPOP_RC);
+		PUT(GET(SSTB_CTRL) | SUNI_SSTB_CTRL_LEN16, SSTB_CTRL);
+		PUT(GET(SPTB_CTRL) | SUNI_SPTB_CTRL_LEN16, SPTB_CTRL);
+	}
+
+	REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT,
+		   SUNI_TPOP_S_SDH, TPOP_APM);
+
+	return 0;
+}
+
+
+static int get_framing(struct atm_dev *dev, void __user *arg)
+{
+	int framing;
+	unsigned char s;
+
+
+	s = (GET(TPOP_APM) & SUNI_TPOP_APM_S) >> SUNI_TPOP_APM_S_SHIFT;
+	if (s == SUNI_TPOP_S_SONET)
+		framing = SONET_FRAME_SONET;
+	else
+		framing = SONET_FRAME_SDH;
+
+	return put_user(framing, (int __user *) arg) ? -EFAULT : 0;
+}
+
+static int set_framing(struct atm_dev *dev, void __user *arg)
+{
+	int mode;
+
+	if (get_user(mode, (int __user *) arg))
+		return -EFAULT;
+
+	if (mode == SONET_FRAME_SONET)
+		return set_sonet(dev);
+	else if (mode == SONET_FRAME_SDH)
+		return set_sdh(dev);
+
+	return -EINVAL;
+}
+
 
 static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
 {
@@ -179,14 +265,16 @@ static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
 		case SONET_GETDIAG:
 			return get_diag(dev,arg);
 		case SONET_SETFRAMING:
-			if ((int)(unsigned long)arg != SONET_FRAME_SONET) return -EINVAL;
-			return 0;
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
+			return set_framing(dev, arg);
 		case SONET_GETFRAMING:
-			return put_user(SONET_FRAME_SONET,(int __user *)arg) ?
-			    -EFAULT : 0;
+			return get_framing(dev, arg);
 		case SONET_GETFRSENSE:
 			return -EINVAL;
 		case ATM_SETLOOP:
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
 			return set_loopback(dev,(int)(unsigned long)arg);
 		case ATM_GETLOOP:
 			return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ?
@@ -220,10 +308,6 @@ static int suni_start(struct atm_dev *dev)
 	unsigned long flags;
 	int first;
 
-	if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
-		return -ENOMEM;
-
-	PRIV(dev)->dev = dev;
 	spin_lock_irqsave(&sunis_lock,flags);
 	first = !sunis;
 	PRIV(dev)->next = sunis;
@@ -284,16 +368,21 @@ int suni_init(struct atm_dev *dev)
 {
 	unsigned char mri;
 
+	if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
+		return -ENOMEM;
+	PRIV(dev)->dev = dev;
+
 	mri = GET(MRI); /* reset SUNI */
+	PRIV(dev)->type = (mri & SUNI_MRI_TYPE) >> SUNI_MRI_TYPE_SHIFT;
 	PUT(mri | SUNI_MRI_RESET,MRI);
 	PUT(mri,MRI);
 	PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */
-	REG_CHANGE(SUNI_TPOP_APM_S,SUNI_TPOP_APM_S_SHIFT,SUNI_TPOP_S_SONET,
-	    TPOP_APM); /* use SONET */
+        set_sonet(dev);
 	REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP,
 	    TACP_IUCHP); /* idle cells */
 	PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP);
 	dev->phy = &suni_ops;
+
 	return 0;
 }