18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/atm/suni.c - S/UNI PHY driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Supports the following: 68c2ecf20Sopenharmony_ci * PMC PM5346 S/UNI LITE 78c2ecf20Sopenharmony_ci * PMC PM5350 S/UNI 155 ULTRA 88c2ecf20Sopenharmony_ci * PMC PM5355 S/UNI 622 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/mm.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 198c2ecf20Sopenharmony_ci#include <linux/sonet.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/timer.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/capability.h> 248c2ecf20Sopenharmony_ci#include <linux/atm_suni.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <asm/param.h> 278c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 288c2ecf20Sopenharmony_ci#include <linux/atomic.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "suni.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#if 0 348c2ecf20Sopenharmony_ci#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) 358c2ecf20Sopenharmony_ci#else 368c2ecf20Sopenharmony_ci#define DPRINTK(format,args...) 378c2ecf20Sopenharmony_ci#endif 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define PRIV(dev) ((struct suni_priv *) dev->phy_data) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg) 428c2ecf20Sopenharmony_ci#define GET(reg) dev->ops->phy_get(dev,SUNI_##reg) 438c2ecf20Sopenharmony_ci#define REG_CHANGE(mask,shift,value,reg) \ 448c2ecf20Sopenharmony_ci PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct timer_list poll_timer; 488c2ecf20Sopenharmony_cistatic struct suni_priv *sunis = NULL; 498c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sunis_lock); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define ADD_LIMITED(s,v) \ 538c2ecf20Sopenharmony_ci atomic_add((v),&stats->s); \ 548c2ecf20Sopenharmony_ci if (atomic_read(&stats->s) < 0) atomic_set(&stats->s,INT_MAX); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void suni_hz(struct timer_list *timer) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct suni_priv *walk; 608c2ecf20Sopenharmony_ci struct atm_dev *dev; 618c2ecf20Sopenharmony_ci struct k_sonet_stats *stats; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci for (walk = sunis; walk; walk = walk->next) { 648c2ecf20Sopenharmony_ci dev = walk->dev; 658c2ecf20Sopenharmony_ci stats = &walk->sonet_stats; 668c2ecf20Sopenharmony_ci PUT(0,MRI); /* latch counters */ 678c2ecf20Sopenharmony_ci udelay(1); 688c2ecf20Sopenharmony_ci ADD_LIMITED(section_bip,(GET(RSOP_SBL) & 0xff) | 698c2ecf20Sopenharmony_ci ((GET(RSOP_SBM) & 0xff) << 8)); 708c2ecf20Sopenharmony_ci ADD_LIMITED(line_bip,(GET(RLOP_LBL) & 0xff) | 718c2ecf20Sopenharmony_ci ((GET(RLOP_LB) & 0xff) << 8) | 728c2ecf20Sopenharmony_ci ((GET(RLOP_LBM) & 0xf) << 16)); 738c2ecf20Sopenharmony_ci ADD_LIMITED(path_bip,(GET(RPOP_PBL) & 0xff) | 748c2ecf20Sopenharmony_ci ((GET(RPOP_PBM) & 0xff) << 8)); 758c2ecf20Sopenharmony_ci ADD_LIMITED(line_febe,(GET(RLOP_LFL) & 0xff) | 768c2ecf20Sopenharmony_ci ((GET(RLOP_LF) & 0xff) << 8) | 778c2ecf20Sopenharmony_ci ((GET(RLOP_LFM) & 0xf) << 16)); 788c2ecf20Sopenharmony_ci ADD_LIMITED(path_febe,(GET(RPOP_PFL) & 0xff) | 798c2ecf20Sopenharmony_ci ((GET(RPOP_PFM) & 0xff) << 8)); 808c2ecf20Sopenharmony_ci ADD_LIMITED(corr_hcs,GET(RACP_CHEC) & 0xff); 818c2ecf20Sopenharmony_ci ADD_LIMITED(uncorr_hcs,GET(RACP_UHEC) & 0xff); 828c2ecf20Sopenharmony_ci ADD_LIMITED(rx_cells,(GET(RACP_RCCL) & 0xff) | 838c2ecf20Sopenharmony_ci ((GET(RACP_RCC) & 0xff) << 8) | 848c2ecf20Sopenharmony_ci ((GET(RACP_RCCM) & 7) << 16)); 858c2ecf20Sopenharmony_ci ADD_LIMITED(tx_cells,(GET(TACP_TCCL) & 0xff) | 868c2ecf20Sopenharmony_ci ((GET(TACP_TCC) & 0xff) << 8) | 878c2ecf20Sopenharmony_ci ((GET(TACP_TCCM) & 7) << 16)); 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci if (timer) mod_timer(&poll_timer,jiffies+HZ); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#undef ADD_LIMITED 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int fetch_stats(struct atm_dev *dev,struct sonet_stats __user *arg,int zero) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct sonet_stats tmp; 998c2ecf20Sopenharmony_ci int error = 0; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp); 1028c2ecf20Sopenharmony_ci if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp)); 1038c2ecf20Sopenharmony_ci if (zero && !error) sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp); 1048c2ecf20Sopenharmony_ci return error ? -EFAULT : 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define HANDLE_FLAG(flag,reg,bit) \ 1098c2ecf20Sopenharmony_ci if (todo & flag) { \ 1108c2ecf20Sopenharmony_ci if (set) PUT(GET(reg) | bit,reg); \ 1118c2ecf20Sopenharmony_ci else PUT(GET(reg) & ~bit,reg); \ 1128c2ecf20Sopenharmony_ci todo &= ~flag; \ 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int change_diag(struct atm_dev *dev,void __user *arg,int set) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int todo; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (get_user(todo,(int __user *)arg)) return -EFAULT; 1218c2ecf20Sopenharmony_ci HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8); 1228c2ecf20Sopenharmony_ci HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP); 1238c2ecf20Sopenharmony_ci HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3); 1248c2ecf20Sopenharmony_ci HANDLE_FLAG(SONET_INS_FRAME,RSOP_CIE,SUNI_RSOP_CIE_FOOF); 1258c2ecf20Sopenharmony_ci HANDLE_FLAG(SONET_INS_LAIS,TSOP_CTRL,SUNI_TSOP_CTRL_LAIS); 1268c2ecf20Sopenharmony_ci HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS); 1278c2ecf20Sopenharmony_ci HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS); 1288c2ecf20Sopenharmony_ci HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS); 1298c2ecf20Sopenharmony_ci return put_user(todo,(int __user *)arg) ? -EFAULT : 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#undef HANDLE_FLAG 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int get_diag(struct atm_dev *dev,void __user *arg) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci int set; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci set = 0; 1418c2ecf20Sopenharmony_ci if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DBIP8) set |= SONET_INS_SBIP; 1428c2ecf20Sopenharmony_ci if (GET(TLOP_DIAG) & SUNI_TLOP_DIAG_DBIP) set |= SONET_INS_LBIP; 1438c2ecf20Sopenharmony_ci if (GET(TPOP_CD) & SUNI_TPOP_DIAG_DB3) set |= SONET_INS_PBIP; 1448c2ecf20Sopenharmony_ci /* SONET_INS_FRAME is one-shot only */ 1458c2ecf20Sopenharmony_ci if (GET(TSOP_CTRL) & SUNI_TSOP_CTRL_LAIS) set |= SONET_INS_LAIS; 1468c2ecf20Sopenharmony_ci if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS; 1478c2ecf20Sopenharmony_ci if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS; 1488c2ecf20Sopenharmony_ci if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS; 1498c2ecf20Sopenharmony_ci return put_user(set,(int __user *)arg) ? -EFAULT : 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int set_loopback(struct atm_dev *dev,int mode) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci unsigned char control; 1568c2ecf20Sopenharmony_ci int reg, dle, lle; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { 1598c2ecf20Sopenharmony_ci reg = SUNI_MCM; 1608c2ecf20Sopenharmony_ci dle = SUNI_MCM_DLE; 1618c2ecf20Sopenharmony_ci lle = SUNI_MCM_LLE; 1628c2ecf20Sopenharmony_ci } else { 1638c2ecf20Sopenharmony_ci reg = SUNI_MCT; 1648c2ecf20Sopenharmony_ci dle = SUNI_MCT_DLE; 1658c2ecf20Sopenharmony_ci lle = SUNI_MCT_LLE; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci control = dev->ops->phy_get(dev, reg) & ~(dle | lle); 1698c2ecf20Sopenharmony_ci switch (mode) { 1708c2ecf20Sopenharmony_ci case ATM_LM_NONE: 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci case ATM_LM_LOC_PHY: 1738c2ecf20Sopenharmony_ci control |= dle; 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci case ATM_LM_RMT_PHY: 1768c2ecf20Sopenharmony_ci control |= lle; 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci default: 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci dev->ops->phy_put(dev, control, reg); 1828c2ecf20Sopenharmony_ci PRIV(dev)->loop_mode = mode; 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* 1878c2ecf20Sopenharmony_ci * SONET vs. SDH Configuration 1888c2ecf20Sopenharmony_ci * 1898c2ecf20Sopenharmony_ci * Z0INS (register 0x06): 0 for SONET, 1 for SDH 1908c2ecf20Sopenharmony_ci * ENSS (register 0x3D): 0 for SONET, 1 for SDH 1918c2ecf20Sopenharmony_ci * LEN16 (register 0x28): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD) 1928c2ecf20Sopenharmony_ci * LEN16 (register 0x50): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD) 1938c2ecf20Sopenharmony_ci * S[1:0] (register 0x46): 00 for SONET, 10 for SDH 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int set_sonet(struct atm_dev *dev) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { 1998c2ecf20Sopenharmony_ci PUT(GET(RPOP_RC) & ~SUNI_RPOP_RC_ENSS, RPOP_RC); 2008c2ecf20Sopenharmony_ci PUT(GET(SSTB_CTRL) & ~SUNI_SSTB_CTRL_LEN16, SSTB_CTRL); 2018c2ecf20Sopenharmony_ci PUT(GET(SPTB_CTRL) & ~SUNI_SPTB_CTRL_LEN16, SPTB_CTRL); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT, 2058c2ecf20Sopenharmony_ci SUNI_TPOP_S_SONET, TPOP_APM); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int set_sdh(struct atm_dev *dev) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { 2138c2ecf20Sopenharmony_ci PUT(GET(RPOP_RC) | SUNI_RPOP_RC_ENSS, RPOP_RC); 2148c2ecf20Sopenharmony_ci PUT(GET(SSTB_CTRL) | SUNI_SSTB_CTRL_LEN16, SSTB_CTRL); 2158c2ecf20Sopenharmony_ci PUT(GET(SPTB_CTRL) | SUNI_SPTB_CTRL_LEN16, SPTB_CTRL); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT, 2198c2ecf20Sopenharmony_ci SUNI_TPOP_S_SDH, TPOP_APM); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int get_framing(struct atm_dev *dev, void __user *arg) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci int framing; 2288c2ecf20Sopenharmony_ci unsigned char s; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci s = (GET(TPOP_APM) & SUNI_TPOP_APM_S) >> SUNI_TPOP_APM_S_SHIFT; 2328c2ecf20Sopenharmony_ci if (s == SUNI_TPOP_S_SONET) 2338c2ecf20Sopenharmony_ci framing = SONET_FRAME_SONET; 2348c2ecf20Sopenharmony_ci else 2358c2ecf20Sopenharmony_ci framing = SONET_FRAME_SDH; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return put_user(framing, (int __user *) arg) ? -EFAULT : 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int set_framing(struct atm_dev *dev, void __user *arg) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci int mode; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (get_user(mode, (int __user *) arg)) 2458c2ecf20Sopenharmony_ci return -EFAULT; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (mode == SONET_FRAME_SONET) 2488c2ecf20Sopenharmony_ci return set_sonet(dev); 2498c2ecf20Sopenharmony_ci else if (mode == SONET_FRAME_SDH) 2508c2ecf20Sopenharmony_ci return set_sdh(dev); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci switch (cmd) { 2598c2ecf20Sopenharmony_ci case SONET_GETSTATZ: 2608c2ecf20Sopenharmony_ci case SONET_GETSTAT: 2618c2ecf20Sopenharmony_ci return fetch_stats(dev, arg, cmd == SONET_GETSTATZ); 2628c2ecf20Sopenharmony_ci case SONET_SETDIAG: 2638c2ecf20Sopenharmony_ci return change_diag(dev,arg,1); 2648c2ecf20Sopenharmony_ci case SONET_CLRDIAG: 2658c2ecf20Sopenharmony_ci return change_diag(dev,arg,0); 2668c2ecf20Sopenharmony_ci case SONET_GETDIAG: 2678c2ecf20Sopenharmony_ci return get_diag(dev,arg); 2688c2ecf20Sopenharmony_ci case SONET_SETFRAMING: 2698c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 2708c2ecf20Sopenharmony_ci return -EPERM; 2718c2ecf20Sopenharmony_ci return set_framing(dev, arg); 2728c2ecf20Sopenharmony_ci case SONET_GETFRAMING: 2738c2ecf20Sopenharmony_ci return get_framing(dev, arg); 2748c2ecf20Sopenharmony_ci case SONET_GETFRSENSE: 2758c2ecf20Sopenharmony_ci return -EINVAL; 2768c2ecf20Sopenharmony_ci case ATM_SETLOOP: 2778c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 2788c2ecf20Sopenharmony_ci return -EPERM; 2798c2ecf20Sopenharmony_ci return set_loopback(dev,(int)(unsigned long)arg); 2808c2ecf20Sopenharmony_ci case ATM_GETLOOP: 2818c2ecf20Sopenharmony_ci return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ? 2828c2ecf20Sopenharmony_ci -EFAULT : 0; 2838c2ecf20Sopenharmony_ci case ATM_QUERYLOOP: 2848c2ecf20Sopenharmony_ci return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY, 2858c2ecf20Sopenharmony_ci (int __user *) arg) ? -EFAULT : 0; 2868c2ecf20Sopenharmony_ci default: 2878c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic void poll_los(struct atm_dev *dev) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci atm_dev_signal_change(dev, 2958c2ecf20Sopenharmony_ci GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ? 2968c2ecf20Sopenharmony_ci ATM_PHY_SIG_LOST : ATM_PHY_SIG_FOUND); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic void suni_int(struct atm_dev *dev) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci poll_los(dev); 3038c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number, 3048c2ecf20Sopenharmony_ci dev->signal == ATM_PHY_SIG_LOST ? "lost" : "detected again"); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int suni_start(struct atm_dev *dev) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci unsigned long flags; 3118c2ecf20Sopenharmony_ci int first; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci spin_lock_irqsave(&sunis_lock,flags); 3148c2ecf20Sopenharmony_ci first = !sunis; 3158c2ecf20Sopenharmony_ci PRIV(dev)->next = sunis; 3168c2ecf20Sopenharmony_ci sunis = PRIV(dev); 3178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sunis_lock,flags); 3188c2ecf20Sopenharmony_ci memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats)); 3198c2ecf20Sopenharmony_ci PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE); 3208c2ecf20Sopenharmony_ci /* interrupt on loss of signal */ 3218c2ecf20Sopenharmony_ci poll_los(dev); /* ... and clear SUNI interrupts */ 3228c2ecf20Sopenharmony_ci if (dev->signal == ATM_PHY_SIG_LOST) 3238c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type, 3248c2ecf20Sopenharmony_ci dev->number); 3258c2ecf20Sopenharmony_ci PRIV(dev)->loop_mode = ATM_LM_NONE; 3268c2ecf20Sopenharmony_ci suni_hz(NULL); /* clear SUNI counters */ 3278c2ecf20Sopenharmony_ci (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ 3288c2ecf20Sopenharmony_ci if (first) { 3298c2ecf20Sopenharmony_ci timer_setup(&poll_timer, suni_hz, 0); 3308c2ecf20Sopenharmony_ci poll_timer.expires = jiffies+HZ; 3318c2ecf20Sopenharmony_ci#if 0 3328c2ecf20Sopenharmony_ciprintk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.list.prev, 3338c2ecf20Sopenharmony_ci (unsigned long) poll_timer.list.next); 3348c2ecf20Sopenharmony_ci#endif 3358c2ecf20Sopenharmony_ci add_timer(&poll_timer); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int suni_stop(struct atm_dev *dev) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct suni_priv **walk; 3448c2ecf20Sopenharmony_ci unsigned long flags; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* let SAR driver worry about stopping interrupts */ 3478c2ecf20Sopenharmony_ci spin_lock_irqsave(&sunis_lock,flags); 3488c2ecf20Sopenharmony_ci for (walk = &sunis; *walk != PRIV(dev); 3498c2ecf20Sopenharmony_ci walk = &PRIV((*walk)->dev)->next); 3508c2ecf20Sopenharmony_ci *walk = PRIV((*walk)->dev)->next; 3518c2ecf20Sopenharmony_ci if (!sunis) del_timer_sync(&poll_timer); 3528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sunis_lock,flags); 3538c2ecf20Sopenharmony_ci kfree(PRIV(dev)); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic const struct atmphy_ops suni_ops = { 3608c2ecf20Sopenharmony_ci .start = suni_start, 3618c2ecf20Sopenharmony_ci .ioctl = suni_ioctl, 3628c2ecf20Sopenharmony_ci .interrupt = suni_int, 3638c2ecf20Sopenharmony_ci .stop = suni_stop, 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ciint suni_init(struct atm_dev *dev) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci unsigned char mri; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL))) 3728c2ecf20Sopenharmony_ci return -ENOMEM; 3738c2ecf20Sopenharmony_ci PRIV(dev)->dev = dev; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci mri = GET(MRI); /* reset SUNI */ 3768c2ecf20Sopenharmony_ci PRIV(dev)->type = (mri & SUNI_MRI_TYPE) >> SUNI_MRI_TYPE_SHIFT; 3778c2ecf20Sopenharmony_ci PUT(mri | SUNI_MRI_RESET,MRI); 3788c2ecf20Sopenharmony_ci PUT(mri,MRI); 3798c2ecf20Sopenharmony_ci PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */ 3808c2ecf20Sopenharmony_ci set_sonet(dev); 3818c2ecf20Sopenharmony_ci REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP, 3828c2ecf20Sopenharmony_ci TACP_IUCHP); /* idle cells */ 3838c2ecf20Sopenharmony_ci PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP); 3848c2ecf20Sopenharmony_ci dev->phy = &suni_ops; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(suni_init); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 392