18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci drivers/net/ethernet/dec/tulip/pnic.c 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Copyright 2000,2001 The Linux Kernel Team 58c2ecf20Sopenharmony_ci Written/copyright 1994-2001 by Donald Becker. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci This software may be used and distributed according to the terms 88c2ecf20Sopenharmony_ci of the GNU General Public License, incorporated herein by reference. 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci Please submit bugs to http://bugzilla.kernel.org/ . 118c2ecf20Sopenharmony_ci*/ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 168c2ecf20Sopenharmony_ci#include "tulip.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_civoid pnic_do_nway(struct net_device *dev) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci struct tulip_private *tp = netdev_priv(dev); 228c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->base_addr; 238c2ecf20Sopenharmony_ci u32 phy_reg = ioread32(ioaddr + 0xB8); 248c2ecf20Sopenharmony_ci u32 new_csr6 = tp->csr6 & ~0x40C40200; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (phy_reg & 0x78000000) { /* Ignore baseT4 */ 278c2ecf20Sopenharmony_ci if (phy_reg & 0x20000000) dev->if_port = 5; 288c2ecf20Sopenharmony_ci else if (phy_reg & 0x40000000) dev->if_port = 3; 298c2ecf20Sopenharmony_ci else if (phy_reg & 0x10000000) dev->if_port = 4; 308c2ecf20Sopenharmony_ci else if (phy_reg & 0x08000000) dev->if_port = 0; 318c2ecf20Sopenharmony_ci tp->nwayset = 1; 328c2ecf20Sopenharmony_ci new_csr6 = (dev->if_port & 1) ? 0x01860000 : 0x00420000; 338c2ecf20Sopenharmony_ci iowrite32(0x32 | (dev->if_port & 1), ioaddr + CSR12); 348c2ecf20Sopenharmony_ci if (dev->if_port & 1) 358c2ecf20Sopenharmony_ci iowrite32(0x1F868, ioaddr + 0xB8); 368c2ecf20Sopenharmony_ci if (phy_reg & 0x30000000) { 378c2ecf20Sopenharmony_ci tp->full_duplex = 1; 388c2ecf20Sopenharmony_ci new_csr6 |= 0x00000200; 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci if (tulip_debug > 1) 418c2ecf20Sopenharmony_ci netdev_dbg(dev, "PNIC autonegotiated status %08x, %s\n", 428c2ecf20Sopenharmony_ci phy_reg, medianame[dev->if_port]); 438c2ecf20Sopenharmony_ci if (tp->csr6 != new_csr6) { 448c2ecf20Sopenharmony_ci tp->csr6 = new_csr6; 458c2ecf20Sopenharmony_ci /* Restart Tx */ 468c2ecf20Sopenharmony_ci tulip_restart_rxtx(tp); 478c2ecf20Sopenharmony_ci netif_trans_update(dev); 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_civoid pnic_lnk_change(struct net_device *dev, int csr5) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct tulip_private *tp = netdev_priv(dev); 558c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->base_addr; 568c2ecf20Sopenharmony_ci int phy_reg = ioread32(ioaddr + 0xB8); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (tulip_debug > 1) 598c2ecf20Sopenharmony_ci netdev_dbg(dev, "PNIC link changed state %08x, CSR5 %08x\n", 608c2ecf20Sopenharmony_ci phy_reg, csr5); 618c2ecf20Sopenharmony_ci if (ioread32(ioaddr + CSR5) & TPLnkFail) { 628c2ecf20Sopenharmony_ci iowrite32((ioread32(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7); 638c2ecf20Sopenharmony_ci /* If we use an external MII, then we mustn't use the 648c2ecf20Sopenharmony_ci * internal negotiation. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci if (tulip_media_cap[dev->if_port] & MediaIsMII) 678c2ecf20Sopenharmony_ci return; 688c2ecf20Sopenharmony_ci if (! tp->nwayset || time_after(jiffies, dev_trans_start(dev) + 1*HZ)) { 698c2ecf20Sopenharmony_ci tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff); 708c2ecf20Sopenharmony_ci iowrite32(tp->csr6, ioaddr + CSR6); 718c2ecf20Sopenharmony_ci iowrite32(0x30, ioaddr + CSR12); 728c2ecf20Sopenharmony_ci iowrite32(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ 738c2ecf20Sopenharmony_ci netif_trans_update(dev); 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci } else if (ioread32(ioaddr + CSR5) & TPLnkPass) { 768c2ecf20Sopenharmony_ci if (tulip_media_cap[dev->if_port] & MediaIsMII) { 778c2ecf20Sopenharmony_ci spin_lock(&tp->lock); 788c2ecf20Sopenharmony_ci tulip_check_duplex(dev); 798c2ecf20Sopenharmony_ci spin_unlock(&tp->lock); 808c2ecf20Sopenharmony_ci } else { 818c2ecf20Sopenharmony_ci pnic_do_nway(dev); 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci iowrite32((ioread32(ioaddr + CSR7) & ~TPLnkPass) | TPLnkFail, ioaddr + CSR7); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_civoid pnic_timer(struct timer_list *t) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct tulip_private *tp = from_timer(tp, t, timer); 908c2ecf20Sopenharmony_ci struct net_device *dev = tp->dev; 918c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->base_addr; 928c2ecf20Sopenharmony_ci int next_tick = 60*HZ; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if(!ioread32(ioaddr + CSR7)) { 958c2ecf20Sopenharmony_ci /* the timer was called due to a work overflow 968c2ecf20Sopenharmony_ci * in the interrupt handler. Skip the connection 978c2ecf20Sopenharmony_ci * checks, the nic is definitively speaking with 988c2ecf20Sopenharmony_ci * his link partner. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci goto too_good_connection; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (tulip_media_cap[dev->if_port] & MediaIsMII) { 1048c2ecf20Sopenharmony_ci spin_lock_irq(&tp->lock); 1058c2ecf20Sopenharmony_ci if (tulip_check_duplex(dev) > 0) 1068c2ecf20Sopenharmony_ci next_tick = 3*HZ; 1078c2ecf20Sopenharmony_ci spin_unlock_irq(&tp->lock); 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci int csr12 = ioread32(ioaddr + CSR12); 1108c2ecf20Sopenharmony_ci int new_csr6 = tp->csr6 & ~0x40C40200; 1118c2ecf20Sopenharmony_ci int phy_reg = ioread32(ioaddr + 0xB8); 1128c2ecf20Sopenharmony_ci int csr5 = ioread32(ioaddr + CSR5); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (tulip_debug > 1) 1158c2ecf20Sopenharmony_ci netdev_dbg(dev, "PNIC timer PHY status %08x, %s CSR5 %08x\n", 1168c2ecf20Sopenharmony_ci phy_reg, medianame[dev->if_port], csr5); 1178c2ecf20Sopenharmony_ci if (phy_reg & 0x04000000) { /* Remote link fault */ 1188c2ecf20Sopenharmony_ci iowrite32(0x0201F078, ioaddr + 0xB8); 1198c2ecf20Sopenharmony_ci next_tick = 1*HZ; 1208c2ecf20Sopenharmony_ci tp->nwayset = 0; 1218c2ecf20Sopenharmony_ci } else if (phy_reg & 0x78000000) { /* Ignore baseT4 */ 1228c2ecf20Sopenharmony_ci pnic_do_nway(dev); 1238c2ecf20Sopenharmony_ci next_tick = 60*HZ; 1248c2ecf20Sopenharmony_ci } else if (csr5 & TPLnkFail) { /* 100baseTx link beat */ 1258c2ecf20Sopenharmony_ci if (tulip_debug > 1) 1268c2ecf20Sopenharmony_ci netdev_dbg(dev, "%s link beat failed, CSR12 %04x, CSR5 %08x, PHY %03x\n", 1278c2ecf20Sopenharmony_ci medianame[dev->if_port], 1288c2ecf20Sopenharmony_ci csr12, 1298c2ecf20Sopenharmony_ci ioread32(ioaddr + CSR5), 1308c2ecf20Sopenharmony_ci ioread32(ioaddr + 0xB8)); 1318c2ecf20Sopenharmony_ci next_tick = 3*HZ; 1328c2ecf20Sopenharmony_ci if (tp->medialock) { 1338c2ecf20Sopenharmony_ci } else if (tp->nwayset && (dev->if_port & 1)) { 1348c2ecf20Sopenharmony_ci next_tick = 1*HZ; 1358c2ecf20Sopenharmony_ci } else if (dev->if_port == 0) { 1368c2ecf20Sopenharmony_ci dev->if_port = 3; 1378c2ecf20Sopenharmony_ci iowrite32(0x33, ioaddr + CSR12); 1388c2ecf20Sopenharmony_ci new_csr6 = 0x01860000; 1398c2ecf20Sopenharmony_ci iowrite32(0x1F868, ioaddr + 0xB8); 1408c2ecf20Sopenharmony_ci } else { 1418c2ecf20Sopenharmony_ci dev->if_port = 0; 1428c2ecf20Sopenharmony_ci iowrite32(0x32, ioaddr + CSR12); 1438c2ecf20Sopenharmony_ci new_csr6 = 0x00420000; 1448c2ecf20Sopenharmony_ci iowrite32(0x1F078, ioaddr + 0xB8); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci if (tp->csr6 != new_csr6) { 1478c2ecf20Sopenharmony_ci tp->csr6 = new_csr6; 1488c2ecf20Sopenharmony_ci /* Restart Tx */ 1498c2ecf20Sopenharmony_ci tulip_restart_rxtx(tp); 1508c2ecf20Sopenharmony_ci netif_trans_update(dev); 1518c2ecf20Sopenharmony_ci if (tulip_debug > 1) 1528c2ecf20Sopenharmony_ci dev_info(&dev->dev, 1538c2ecf20Sopenharmony_ci "Changing PNIC configuration to %s %s-duplex, CSR6 %08x\n", 1548c2ecf20Sopenharmony_ci medianame[dev->if_port], 1558c2ecf20Sopenharmony_ci tp->full_duplex ? "full" : "half", 1568c2ecf20Sopenharmony_ci new_csr6); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_citoo_good_connection: 1618c2ecf20Sopenharmony_ci mod_timer(&tp->timer, RUN_AT(next_tick)); 1628c2ecf20Sopenharmony_ci if(!ioread32(ioaddr + CSR7)) { 1638c2ecf20Sopenharmony_ci if (tulip_debug > 1) 1648c2ecf20Sopenharmony_ci dev_info(&dev->dev, "sw timer wakeup\n"); 1658c2ecf20Sopenharmony_ci disable_irq(dev->irq); 1668c2ecf20Sopenharmony_ci tulip_refill_rx(dev); 1678c2ecf20Sopenharmony_ci enable_irq(dev->irq); 1688c2ecf20Sopenharmony_ci iowrite32(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 171