[PATCH] Fixed a number of bugs in the PHY Layer
[linux-2.6] / drivers / net / netxen / netxen_nic_isr.c
1 /*
2  * Copyright (C) 2003 - 2006 NetXen, Inc.
3  * All rights reserved.
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *                            
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *                                   
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
18  * MA  02111-1307, USA.
19  * 
20  * The full GNU General Public License is included in this distribution
21  * in the file called LICENSE.
22  * 
23  * Contact Information:
24  *    info@netxen.com
25  * NetXen,
26  * 3965 Freedom Circle, Fourth floor,
27  * Santa Clara, CA 95054
28  */
29
30 #include <linux/netdevice.h>
31 #include <linux/delay.h>
32
33 #include "netxen_nic.h"
34 #include "netxen_nic_hw.h"
35 #include "netxen_nic_phan_reg.h"
36
37 /*
38  * netxen_nic_get_stats - Get System Network Statistics
39  * @netdev: network interface device structure
40  */
41 struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev)
42 {
43         struct netxen_port *port = netdev_priv(netdev);
44         struct net_device_stats *stats = &port->net_stats;
45
46         memset(stats, 0, sizeof(*stats));
47
48         /* total packets received   */
49         stats->rx_packets = port->stats.no_rcv;
50         /* total packets transmitted    */
51         stats->tx_packets = port->stats.xmitedframes + port->stats.xmitfinished;
52         /* total bytes received     */
53         stats->rx_bytes = port->stats.rxbytes;
54         /* total bytes transmitted  */
55         stats->tx_bytes = port->stats.txbytes;
56         /* bad packets received     */
57         stats->rx_errors = port->stats.rcvdbadskb;
58         /* packet transmit problems */
59         stats->tx_errors = port->stats.nocmddescriptor;
60         /* no space in linux buffers    */
61         stats->rx_dropped = port->stats.updropped;
62         /* no space available in linux  */
63         stats->tx_dropped = port->stats.txdropped;
64
65         return stats;
66 }
67
68 void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 portno,
69                                  u32 link)
70 {
71         struct netxen_port *pport = adapter->port[portno];
72         struct net_device *netdev = pport->netdev;
73
74         if (link)
75                 netif_carrier_on(netdev);
76         else
77                 netif_carrier_off(netdev);
78 }
79
80 void netxen_handle_port_int(struct netxen_adapter *adapter, u32 portno,
81                             u32 enable)
82 {
83         __le32 int_src;
84         struct netxen_port *port;
85
86         /*  This should clear the interrupt source */
87         if (adapter->ops->phy_read)
88                 adapter->ops->phy_read(adapter, portno,
89                                        NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS,
90                                        &int_src);
91         if (int_src == 0) {
92                 DPRINTK(INFO, "No phy interrupts for port #%d\n", portno);
93                 return;
94         }
95         if (adapter->ops->disable_phy_interrupts)
96                 adapter->ops->disable_phy_interrupts(adapter, portno);
97
98         port = adapter->port[portno];
99
100         if (netxen_get_phy_int_jabber(int_src))
101                 DPRINTK(INFO, "NetXen: %s Jabber interrupt \n",
102                         port->netdev->name);
103
104         if (netxen_get_phy_int_polarity_changed(int_src))
105                 DPRINTK(INFO, "NetXen: %s POLARITY CHANGED int \n",
106                         port->netdev->name);
107
108         if (netxen_get_phy_int_energy_detect(int_src))
109                 DPRINTK(INFO, "NetXen: %s ENERGY DETECT INT \n",
110                         port->netdev->name);
111
112         if (netxen_get_phy_int_downshift(int_src))
113                 DPRINTK(INFO, "NetXen: %s DOWNSHIFT INT \n",
114                         port->netdev->name);
115         /* write it down later.. */
116         if ((netxen_get_phy_int_speed_changed(int_src))
117             || (netxen_get_phy_int_link_status_changed(int_src))) {
118                 __le32 status;
119
120                 DPRINTK(INFO, "NetXen: %s SPEED CHANGED OR"
121                         " LINK STATUS CHANGED \n", port->netdev->name);
122
123                 if (adapter->ops->phy_read
124                     && adapter->ops->phy_read(adapter, portno,
125                                               NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
126                                               &status) == 0) {
127                         if (netxen_get_phy_int_link_status_changed(int_src)) {
128                                 if (netxen_get_phy_link(status)) {
129                                         netxen_niu_gbe_init_port(adapter,
130                                                                  portno);
131                                         printk("%s: %s Link UP\n",
132                                                netxen_nic_driver_name,
133                                                port->netdev->name);
134
135                                 } else {
136                                         printk("%s: %s Link DOWN\n",
137                                                netxen_nic_driver_name,
138                                                port->netdev->name);
139                                 }
140                                 netxen_indicate_link_status(adapter, portno,
141                                                             netxen_get_phy_link
142                                                             (status));
143                         }
144                 }
145         }
146         if (adapter->ops->enable_phy_interrupts)
147                 adapter->ops->enable_phy_interrupts(adapter, portno);
148 }
149
150 void netxen_nic_isr_other(struct netxen_adapter *adapter)
151 {
152         u32 enable, portno;
153         u32 i2qhi;
154
155         /*
156          * bit 3 is for i2qInt, if high its enabled
157          * check for phy interrupts
158          * read vector and check for bit 45 for phy
159          * clear int by writing the same value into ISR_INT_VECTOR REG
160          */
161
162         DPRINTK(INFO, "I2Q is the source of INT \n");
163
164         /* verify the offset */
165         i2qhi = readl(NETXEN_CRB_NORMALIZE(adapter, NETXEN_I2Q_CLR_PCI_HI));
166
167         DPRINTK(INFO, "isr NETXEN_I2Q_CLR_PCI_HI = 0x%x \n", i2qhi);
168
169         if (i2qhi & 0x4000) {
170                 for (portno = 0; portno < NETXEN_NIU_MAX_GBE_PORTS; portno++) {
171                         DPRINTK(INFO, "External PHY interrupt ON PORT %d\n",
172                                 portno);
173
174                         enable = 1;
175                         netxen_handle_port_int(adapter, portno, enable);
176                 }
177
178                 /* Clear the interrupt on I2Q */
179                 writel((u32) i2qhi,
180                        NETXEN_CRB_NORMALIZE(adapter, NETXEN_I2Q_CLR_PCI_HI));
181
182         }
183 }
184
185 void netxen_nic_gbe_handle_phy_intr(struct netxen_adapter *adapter)
186 {
187         u32 val;
188         val = readl(NETXEN_CRB_NORMALIZE(adapter, ISR_INT_VECTOR));
189         if (val & 0x4) {
190                 adapter->stats.otherints++;
191                 netxen_nic_isr_other(adapter);
192         }
193 }
194
195 void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter)
196 {
197         struct net_device *netdev = adapter->port[0]->netdev;
198         u32 val;
199
200         /* WINDOW = 1 */
201         val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE));
202
203         if (adapter->ahw.xg_linkup == 1 && val != XG_LINK_UP) {
204                 printk(KERN_INFO "%s: %s NIC Link is down\n",
205                        netxen_nic_driver_name, netdev->name);
206                 adapter->ahw.xg_linkup = 0;
207                 /* read twice to clear sticky bits */
208                 /* WINDOW = 0 */
209                 netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val);
210                 netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val);
211
212                 if ((val & 0xffb) != 0xffb) {
213                         printk(KERN_INFO "%s ISR: Sync/Align BAD: 0x%08x\n",
214                                netxen_nic_driver_name, val);
215                 }
216         } else if (adapter->ahw.xg_linkup == 0 && val == XG_LINK_UP) {
217                 printk(KERN_INFO "%s: %s NIC Link is up\n",
218                        netxen_nic_driver_name, netdev->name);
219                 adapter->ahw.xg_linkup = 1;
220         }
221 }