aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/net/ethernet/qlogic/qla3xxx.c
blob: b4b8ba00ee0151083ca33a14ebf860c7fb7e103b (plain) (tree)
1
2
3
4
5
6
7
8
9






                                                           

                                           
                         














                              
                     







                              

                        
                           


                    

                                                       
                                     



                                                        


                                                                            
















                                                                     
                                                      
                                                             
                                                             






                                         

                                            
                      



                          












                                                                    



  




                                                       

                                                         









                                                                   
                            




                                                                 

                                                         





                                                                            

                                                         













                                                                           
            





                                                                               

                                 

                           


                                                                         



                                                                    

                                                         






                                                                 
                                                                           










                                                         
                                                                         











                                                                          
                                              








                                                                        
                                              



                                                           
                                                            



                                                    
                           

                                                         


                                                         
                                                            
 
                           
                   

 





                                                            

 
                                                        
                                                           

                                    
                                              
                           
                   





                                                        
                                                           

                                    
                                              
                           
                   





                                                        
                                                           

                                    
                                              
                           
                   



                                                           

                                                         







                                                                               

                                                         








                                                                               

                       









                                                                               

                                                                         
                                                 











                                                                           
                                                                     
                                  


                                                                                 






                                                               



                                                        

                                                                     










                                                                             
                                                                   
 


                                                           



















                                                                     
                                                
                                                                          

                                                   
                                                                               











                                                                           
                                                
                                                                          

                                                    








                                                                          




                                                         


                                             
                                             



                                                                              

                                              





                                                                            






                                                         

                                                                  




                                                                          


                                                                              

                                              





                                                                            









                                                         
                                                
                                                                          
 
                                                   
                                                                               










                                                                            
                                                
                                                                          



                                                             







                                                                           

                                             
                           













                                                                  





                                                               










                                                        
                                               
                                  
                                                    

                                                                         
                                                                   











                                                                 

                                                                            



                                                                 










                                                          
                                                















                                                                            
                                                


























                                                                              
                                                                

























                                                                          
                                                                   

                                                         
                                                




                                                        
                                                                  



                                                               
                                              




                                                                       
                                                                  









                                                                    
                                                      

                                                         
                                                





                                                        
                                                                  



                                                               
                                              








                                                                            
                                                                  














                                                                             
                                                



                                          
                                                                  









                                                                       
                                                                  











                                                                             
                                                



                                          
                                                                  













                                                                            
                                                                  

































                                                                         
                                                       

                                                                           
                                                      

 
                                                           



                                           
                                                      
                                                     
                                    
                                                      
                                                      

                                                  
                                                               
                                                      



                                                                           
                                                      







                                                   
                                                      
 

                                    











                                                               

                                                                       
                                                                 


















                                                         

                                                                               
          








                                             

                                                                    
 
                                                       
                  
                    
              
 
                                
                              
 
                                
                              






                                                                      
                                                 

                                                           

                                                            

                                                          





                      



                                                     

                                 






                                                          

                                                                        
 
                                                
         
 

                      
                                  
               
                                 
               
                                
                
                          
         





                                                   

                                 

                                                      
 


                                                               
                  




                                                                        











                                                             









                                                                 
                       
                                                                                 
                           


                                                         
                       
                                                                                 
                           




                                                   
                                                                 
                                                         
                                         
                                                   
                    
                                                   
 

                                                                             

                                                                                                  
                                   


                                                                             
                               
                                                                                                              
                                   
                 
 
                                                                      
                                             







                                                                            
                                                    
                                                       
                                                           





                            





                                                               
                                                


















                                                                           
                                                


















                                                                           
                                                


















                                                                           
                                                


















                                                                           
                                                




















                                                                           
                                                




























                                                               
                                                













                                                               
                                                                                
                         
         

                                                                          















                                                                
                                                




































                                                               
                                                






















                                                                              
                                                

























                                                                            
                                                                  

                                                         
                                                


                           
                                  











                                                               

                                                                
                         
         


                                                                          

 
                                                     
 
                                                                   
                                                      

 
                                                         

                

                              
                                               
                                                     
                                                                        
 


                                                                        
            

                                                                        


                                                                 
                                   


                                                        
                                                       


                                                     

                                                                        
                                                 
                    
                                                 

         
                                                       






                                                               
                                                              

                                                             

                                                                  
                                                
 
                                                               


                                               

                                                                  
                                                
 
                                                               


                                               
                                                         
                         
 
                                                     
                                                      
 
                                                                              

                                              

                                                                           

 
                                                    
 


                                  







                                                         
                                                










                                             
 
                                                               
                              
                                  
            
                                    
 




                                                  
                                                      
                                                                 
                                    
                                                                          
                          
         




                                    
                                     








                                                       
                                                      




                                                                 
                                                             
                                               

                                                                        













                                                               

                                                                        


                                               


                                              



                                                                        


                                                   
                                                             

                                                                                         




                                                                   
                                                                          
                                          
                                 





                                                 
                                                                
 


                                                                             






                                                    
                                                      

                                                                              
 
                                                                 

                                                         
                                                                  
 




                                        
                                                           
                                            



                                                
                                               
                                                                           





                                                                
                                                      







                                                                          
                                                 
                                                                             

                                                        

                                                        


                                                         


                                                          






                                                      
                                                
                                                      
            
                                                        








                                                       
                                                      

                                                        
                

                                                        



         



                                                                    




                                                         
                                                
 
                                                      



                                                                         
                                                
                                        

                                                                     









                                                                         









                                                                 
                                                                 
 

                                                       

                                                    
 
                                  






                                                           


                                                            
                                                                 
                         
         










                                                         


                                                            
                                                                 
                         
         










                                                         


                                                            
                                                                 
                         
         





                                                         

                                                                    

                                                     
                                   
 
                                             
 
                                                      
                                            
                

                                                      
         









                                                                            






                                                           


                                                                              

                                                        













                                                               



                                                               

                                                         

                
                                 








                                                                         
                                                      
                                      


                                        
                                            
                                                    




                                                                   

                       


                                       


                                                                       
                                                         

                                                                             











                                                                              
 
                                                                             
                                          


                                                                                         





                                                                       
                                                              
                                                                    
                                                               
                                                                    

                                                                             














                                                                        

                                                                     


                                                         









                                                                     

                                                                             





                        




                                                                   

                                                         
 

                                                






                                                          

                                                           














                                                                        

                                                             


                                                                   
                                                            


                                                                          
                      
                                                        

                                                                      






                                                                   
              
 
                                                 

                                                                            
         
 
                                                       

                                                               
                                                 

                                                                            
 
                                              


                                    
                                    

                                                                     
 
                                              


                                       
                                    

                                                                 




                                                        
                                                                     
                                                               
                                                                             


                                                         

                                                      

               
                                      
                          

                  


                                    
                                                 





                                                         
                                                                  





                                                             
                          

 

                                                                




                                                                   
                                                       
                                                                 


                                                                


                                                                          

                                                 





                                                         
                          
 

                                                

                                    
                                        

                               

                                             


                                    

                                                              

                                             
                                      

                                                        
                                           

                                

                                                                   





                                                                          

                                                 
                                           







                                                        
                          
 

                                                   
                                                
                                        



                                                         

                                    
                                        
                                
 

                                                                         

                                                              


                                             
                                       




                                                                    

                                                                             

                                                                    


                                                   




                                                                                          


                                                                         
                                                               
                 
         

                                                          
                                            

                                       

                                

                                                                   


                                                           
                                                               
 

                                             
                          

                                                              
                                                            
                                                                    

                                            
                      
                  

                                                                            


                                                        





                                                                               


                                        
                                             

                                                                               
                                    


                                       
                                            

                                                                                
                                    
                              











                                                                           









                                                                    
 

         
                         

 
                                                        
 
                                                                                

                                                         


                                                 
 

                                                                        
 
                                                         



                                                                      
                                                              
 
                                           
         
                         

 
                                                    



                                                     

                                                         



                        

                                                                              






                                                        
                                                        







                                                                              


                                                                                           
                                                               



                                                
                                                                  


                                                                                                   
                 
                                                                          

                                                      
                                            
                                                            
                                                     
              
                                



                                   
  




                                                                             
   
                                                                           
 


                                                









                                 


                  
                                                       

                                                                  
                                             
 

                                                           
 

                                                                
                                               

                                                                
                                               
         
 


  

                                                                    
   



                                                                     
 

                                    
                                   


                              

                                                      
 
                                   


                                    
                                                                           
 
                                                     
                  

                                                                             


                                      
 



                                                                     

                                                           

              
                           
                                                 
                                                              



















                                                                           
 
                                                                     
                                  
                                                      
                                                                                                
                                                


                                               

                                                                        

                                                                         

                                                                           



                                                              
                 
 
                                                                                      
                                                      
 
                                                               








                                                                               
                                                                  
                                                                   
                                                                                 


                                                      
                            


                                                                
                                                                           

                                                                           
 


                                                                     
                                                     

                            








                                                       
                                                    

                                                                          





                                                   

                                                                         



                                                 

                                                                 



                                           





                                                                   


                                                                    
                                                                      
                                                                  

                         

                                                       
 
                                                     

                                                         



                                             
                                                       
                                      
 



                                                                        
                                                                           

                                    
 
                                          
                                                                        





                                                                

                                                  
                                                    
 
                                                                          
                                                                                

                                      
 
              




                                                          
                                                                     

                                                      


                                                         
 
                                    

                            
 




                                                                       






                                                                           






                                                                       
                                                        


                               






                                                                       
                                                                   





                                                                          
                                                       





                                                                
                                                               
                                                          














                                                                         
                                                         





                                                           
                                                                         




                                                                      



                                                                   
                               
 
                                         


                                                                      

                                                      
                                                         






                                                                    
                                                                   





                                                                          


                                                                        

                                                        
                                                                                 







                                                                           
                                                   




                                                           
                                                           
                                                          

                       
                             













                                                              
                                                     








                                                                                

                                                                 

                                   


                                                                

                                                
                                                                              







                                                                            









                                                                                
                                                       




                                                           
                                                               
                                                          
















                                                               
                                                       



                                                       

                                                                             













                                                                            
                                                       













                                                                        

                       
 
                                                       

                                                             

                                                    


                                                                                        

















                                                                            
 
                                                                     
                                  


                                                                                 
                                                       



                                                            

                                                                     










                                                                






                                                            

                                  




                                                             


                                   
                                                                   


                                                  
 



                                                

                                                      
                                       
         
                 



                                                           

                                                            
                                                       
                                                       



                                                                  

                                                      

                                                                                               

                               

                                                                     

                                                                              
                                                                             






                                                                           

                                                                            

                                                 
                                                                       
                                                        
                                                             
                                                       
                                                             

                                           
                                                                            
                                                        
                                                               
                                                       
                                                                  
                
                                                                   



                                                     
                                                                               



                                                
                                                                          



                                                
                                                                          



                                                
                                                                          




                                                

                                           



                                                  

                                     














                                                           
                                     














                                                               

                                                              
 
                                                      






















































                                                                                

                                                         
                                                                          
                                                                
                                          


                       
                               


                                        
                                       

                                                             

                                                     



                                        
                                       



                                                              

                                                       

















                                                                           
                                                      





























                                                                           


                                                      




















                                                                                
                                                                     
                                      
                                                            






                                       
                                                   


                                                              
                                                   











                                                                              
                                                   



                                    




                                                                          
                                                            












                                                                               







                                                                             
 
                                                      





                                                                         
                        















































                                                                            
                                                
                            
                                              


                          
                                                                      




                                      


                                                                       

                                                                          









                                                                          









                                                     

                                                         









                                               
                                                                            
                                 
                                                                    


                                                                       

                                                                      



















                                                                                

                                                           
                                         
                                                           






                                                                               
                                                           








                                                                           



                                                                          
                                      












                                                     

                                                         











                                                                                


                                                     
                                                               
                    
                                                                 




                                                                      


                                                     
                                                               
                    
                                                                 




                                  


                                                                                   

                      
                                                                    



                                                        
                                                     

                                          


                                                                            
                                                                                


                                                                                



                                    


                                                                    
 


                                                                   
 
                                                                           









                                                                  

                                                




                                        
                                                                  
                                                                       
                                                        




                                             
                                  






                                                            

                                                            

                                                                                  
                         

                                                                             
                        

                                                                                   











                                                                 
                                              


                                           
                                                                  




                                                 

                                                                                                                   

                                      
                                                              
                                                              
                                                  


                 


                                                       
                                
                                                                               
                                            




                                                    



                                                  
                                                                           

                                      
                                                            

                                                      
                                                                    




                                                         
                                             


                                                          
                                 





                                              
                                                         

                                        
                                                                  
                                                                 
                                                        






                                                                
                                                                  

                                                                            
                            
                                      
                              












                                                     
                                                      

                           
                                           





                                                     
                                   

 

                                                                   
                                                     
                                                         
                                                































                                                                             
                                                     
 
                                           







                                                    
                                                                       

 
                                                   
 

                                                                        



                                             

                                                         

                               

                                                                           




                                                                   
                              

                                                 

                                                                    
                                                            

                                                                       
                                                                               
                                                           
                                                                        
                                                                  



                                                                             

                                                                 




                                                          
                                                               














                                                                               

                                                                   



                                                     

                                                                            
                                                         
                                                                





                                                                             
                                                                         
                                  
                                                                    








                                                                         


                                                                                


                                                                   


                               



                                                           


         
                                                        
 



                                                                             



                                                       

                                                         















                                                                      
                                              
 
                                                                      
                                                                       

 



                                                        




                                                         
                                             
                                                              


                                        
                               
                                                  


                                      
                                                                        




                                                  
                                                                           




                                          
                                                        
                                  
                                                                          
                                                                       
                                  
                                                                          


                  
                                                                           



                                                          
                    
                              
                                          
         
 

                                         





                                    
                                            





                                                              


                                                  
                                                               
 
                                                           
                                       
                                                                            
                           






                                            
                                              
                                                

                                      
                                                       




                                          

                                                                       
                           






                                         
                                                                        
                                                                               
                
                                                                        
                                                                               
         


                                               






                                                                             
                        
                                                                     


                                    
                                                                           








                                                                     





                                        

                                                                      
                                                                              
 
                                                           
                                                                              
 



                                                          













                                         



                   
                                               




                                                        









                                                            
                                         
                                  







                                          
                                

  
                                 
/*
 * QLogic QLA3xxx NIC HBA Driver
 * Copyright (c)  2003-2006 QLogic Corporation
 *
 * See LICENSE.qla3xxx for copyright and licensing details.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/dmapool.h>
#include <linux/mempool.h>
#include <linux/spinlock.h>
#include <linux/kthread.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
#include <linux/if_vlan.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/prefetch.h>

#include "qla3xxx.h"

#define DRV_NAME	"qla3xxx"
#define DRV_STRING	"QLogic ISP3XXX Network Driver"
#define DRV_VERSION	"v2.03.00-k5"

static const char ql3xxx_driver_name[] = DRV_NAME;
static const char ql3xxx_driver_version[] = DRV_VERSION;

#define TIMED_OUT_MSG							\
"Timed out waiting for management port to get free before issuing command\n"

MODULE_AUTHOR("QLogic Corporation");
MODULE_DESCRIPTION("QLogic ISP3XXX Network Driver " DRV_VERSION " ");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);

static const u32 default_msg
    = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK
    | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN;

static int debug = -1;		/* defaults above */
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");

static int msi;
module_param(msi, int, 0);
MODULE_PARM_DESC(msi, "Turn on Message Signaled Interrupts.");

static const struct pci_device_id ql3xxx_pci_tbl[] = {
	{PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QL3022_DEVICE_ID)},
	{PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QL3032_DEVICE_ID)},
	/* required last entry */
	{0,}
};

MODULE_DEVICE_TABLE(pci, ql3xxx_pci_tbl);

/*
 *  These are the known PHY's which are used
 */
enum PHY_DEVICE_TYPE {
   PHY_TYPE_UNKNOWN   = 0,
   PHY_VITESSE_VSC8211,
   PHY_AGERE_ET1011C,
   MAX_PHY_DEV_TYPES
};

struct PHY_DEVICE_INFO {
	const enum PHY_DEVICE_TYPE	phyDevice;
	const u32		phyIdOUI;
	const u16		phyIdModel;
	const char		*name;
};

static const struct PHY_DEVICE_INFO PHY_DEVICES[] = {
	{PHY_TYPE_UNKNOWN,    0x000000, 0x0, "PHY_TYPE_UNKNOWN"},
	{PHY_VITESSE_VSC8211, 0x0003f1, 0xb, "PHY_VITESSE_VSC8211"},
	{PHY_AGERE_ET1011C,   0x00a0bc, 0x1, "PHY_AGERE_ET1011C"},
};


/*
 * Caller must take hw_lock.
 */
static int ql_sem_spinlock(struct ql3_adapter *qdev,
			    u32 sem_mask, u32 sem_bits)
{
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;
	u32 value;
	unsigned int seconds = 3;

	do {
		writel((sem_mask | sem_bits),
		       &port_regs->CommonRegs.semaphoreReg);
		value = readl(&port_regs->CommonRegs.semaphoreReg);
		if ((value & (sem_mask >> 16)) == sem_bits)
			return 0;
		ssleep(1);
	} while (--seconds);
	return -1;
}

static void ql_sem_unlock(struct ql3_adapter *qdev, u32 sem_mask)
{
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;
	writel(sem_mask, &port_regs->CommonRegs.semaphoreReg);
	readl(&port_regs->CommonRegs.semaphoreReg);
}

static int ql_sem_lock(struct ql3_adapter *qdev, u32 sem_mask, u32 sem_bits)
{
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;
	u32 value;

	writel((sem_mask | sem_bits), &port_regs->CommonRegs.semaphoreReg);
	value = readl(&port_regs->CommonRegs.semaphoreReg);
	return ((value & (sem_mask >> 16)) == sem_bits);
}

/*
 * Caller holds hw_lock.
 */
static int ql_wait_for_drvr_lock(struct ql3_adapter *qdev)
{
	int i = 0;

	do {
		if (ql_sem_lock(qdev,
				QL_DRVR_SEM_MASK,
				(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index)
				 * 2) << 1)) {
			netdev_printk(KERN_DEBUG, qdev->ndev,
				      "driver lock acquired\n");
			return 1;
		}
		ssleep(1);
	} while (++i < 10);

	netdev_err(qdev->ndev, "Timed out waiting for driver lock...\n");
	return 0;
}

static void ql_set_register_page(struct ql3_adapter *qdev, u32 page)
{
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;

	writel(((ISP_CONTROL_NP_MASK << 16) | page),
			&port_regs->CommonRegs.ispControlStatus);
	readl(&port_regs->CommonRegs.ispControlStatus);
	qdev->current_page = page;
}

static u32 ql_read_common_reg_l(struct ql3_adapter *qdev, u32 __iomem *reg)
{
	u32 value;
	unsigned long hw_flags;

	spin_lock_irqsave(&qdev->hw_lock, hw_flags);
	value = readl(reg);
	spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);

	return value;
}

static u32 ql_read_common_reg(struct ql3_adapter *qdev, u32 __iomem *reg)
{
	return readl(reg);
}

static u32 ql_read_page0_reg_l(struct ql3_adapter *qdev, u32 __iomem *reg)
{
	u32 value;
	unsigned long hw_flags;

	spin_lock_irqsave(&qdev->hw_lock, hw_flags);

	if (qdev->current_page != 0)
		ql_set_register_page(qdev, 0);
	value = readl(reg);

	spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
	return value;
}

static u32 ql_read_page0_reg(struct ql3_adapter *qdev, u32 __iomem *reg)
{
	if (qdev->current_page != 0)
		ql_set_register_page(qdev, 0);
	return readl(reg);
}

static void ql_write_common_reg_l(struct ql3_adapter *qdev,
				u32 __iomem *reg, u32 value)
{
	unsigned long hw_flags;

	spin_lock_irqsave(&qdev->hw_lock, hw_flags);
	writel(value, reg);
	readl(reg);
	spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
}

static void ql_write_common_reg(struct ql3_adapter *qdev,
				u32 __iomem *reg, u32 value)
{
	writel(value, reg);
	readl(reg);
}

static void ql_write_nvram_reg(struct ql3_adapter *qdev,
				u32 __iomem *reg, u32 value)
{
	writel(value, reg);
	readl(reg);
	udelay(1);
}

static void ql_write_page0_reg(struct ql3_adapter *qdev,
			       u32 __iomem *reg, u32 value)
{
	if (qdev->current_page != 0)
		ql_set_register_page(qdev, 0);
	writel(value, reg);
	readl(reg);
}

/*
 * Caller holds hw_lock. Only called during init.
 */
static void ql_write_page1_reg(struct ql3_adapter *qdev,
			       u32 __iomem *reg, u32 value)
{
	if (qdev->current_page != 1)
		ql_set_register_page(qdev, 1);
	writel(value, reg);
	readl(reg);
}

/*
 * Caller holds hw_lock. Only called during init.
 */
static void ql_write_page2_reg(struct ql3_adapter *qdev,
			       u32 __iomem *reg, u32 value)
{
	if (qdev->current_page != 2)
		ql_set_register_page(qdev, 2);
	writel(value, reg);
	readl(reg);
}

static void ql_disable_interrupts(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;

	ql_write_common_reg_l(qdev, &port_regs->CommonRegs.ispInterruptMaskReg,
			    (ISP_IMR_ENABLE_INT << 16));

}

static void ql_enable_interrupts(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;

	ql_write_common_reg_l(qdev, &port_regs->CommonRegs.ispInterruptMaskReg,
			    ((0xff << 16) | ISP_IMR_ENABLE_INT));

}

static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev,
					    struct ql_rcv_buf_cb *lrg_buf_cb)
{
	dma_addr_t map;
	int err;
	lrg_buf_cb->next = NULL;

	if (qdev->lrg_buf_free_tail == NULL) {	/* The list is empty  */
		qdev->lrg_buf_free_head = qdev->lrg_buf_free_tail = lrg_buf_cb;
	} else {
		qdev->lrg_buf_free_tail->next = lrg_buf_cb;
		qdev->lrg_buf_free_tail = lrg_buf_cb;
	}

	if (!lrg_buf_cb->skb) {
		lrg_buf_cb->skb = netdev_alloc_skb(qdev->ndev,
						   qdev->lrg_buffer_len);
		if (unlikely(!lrg_buf_cb->skb)) {
			qdev->lrg_buf_skb_check++;
		} else {
			/*
			 * We save some space to copy the ethhdr from first
			 * buffer
			 */
			skb_reserve(lrg_buf_cb->skb, QL_HEADER_SPACE);
			map = pci_map_single(qdev->pdev,
					     lrg_buf_cb->skb->data,
					     qdev->lrg_buffer_len -
					     QL_HEADER_SPACE,
					     PCI_DMA_FROMDEVICE);
			err = pci_dma_mapping_error(qdev->pdev, map);
			if (err) {
				netdev_err(qdev->ndev,
					   "PCI mapping failed with error: %d\n",
					   err);
				dev_kfree_skb(lrg_buf_cb->skb);
				lrg_buf_cb->skb = NULL;

				qdev->lrg_buf_skb_check++;
				return;
			}

			lrg_buf_cb->buf_phy_addr_low =
			    cpu_to_le32(LS_64BITS(map));
			lrg_buf_cb->buf_phy_addr_high =
			    cpu_to_le32(MS_64BITS(map));
			dma_unmap_addr_set(lrg_buf_cb, mapaddr, map);
			dma_unmap_len_set(lrg_buf_cb, maplen,
					  qdev->lrg_buffer_len -
					  QL_HEADER_SPACE);
		}
	}

	qdev->lrg_buf_free_count++;
}

static struct ql_rcv_buf_cb *ql_get_from_lrg_buf_free_list(struct ql3_adapter
							   *qdev)
{
	struct ql_rcv_buf_cb *lrg_buf_cb = qdev->lrg_buf_free_head;

	if (lrg_buf_cb != NULL) {
		qdev->lrg_buf_free_head = lrg_buf_cb->next;
		if (qdev->lrg_buf_free_head == NULL)
			qdev->lrg_buf_free_tail = NULL;
		qdev->lrg_buf_free_count--;
	}

	return lrg_buf_cb;
}

static u32 addrBits = EEPROM_NO_ADDR_BITS;
static u32 dataBits = EEPROM_NO_DATA_BITS;

static void fm93c56a_deselect(struct ql3_adapter *qdev);
static void eeprom_readword(struct ql3_adapter *qdev, u32 eepromAddr,
			    unsigned short *value);

/*
 * Caller holds hw_lock.
 */
static void fm93c56a_select(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	__iomem u32 *spir = &port_regs->CommonRegs.serialPortInterfaceReg;

	qdev->eeprom_cmd_data = AUBURN_EEPROM_CS_1;
	ql_write_nvram_reg(qdev, spir, ISP_NVRAM_MASK | qdev->eeprom_cmd_data);
}

/*
 * Caller holds hw_lock.
 */
static void fm93c56a_cmd(struct ql3_adapter *qdev, u32 cmd, u32 eepromAddr)
{
	int i;
	u32 mask;
	u32 dataBit;
	u32 previousBit;
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	__iomem u32 *spir = &port_regs->CommonRegs.serialPortInterfaceReg;

	/* Clock in a zero, then do the start bit */
	ql_write_nvram_reg(qdev, spir,
			   (ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
			    AUBURN_EEPROM_DO_1));
	ql_write_nvram_reg(qdev, spir,
			   (ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
			    AUBURN_EEPROM_DO_1 | AUBURN_EEPROM_CLK_RISE));
	ql_write_nvram_reg(qdev, spir,
			   (ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
			    AUBURN_EEPROM_DO_1 | AUBURN_EEPROM_CLK_FALL));

	mask = 1 << (FM93C56A_CMD_BITS - 1);
	/* Force the previous data bit to be different */
	previousBit = 0xffff;
	for (i = 0; i < FM93C56A_CMD_BITS; i++) {
		dataBit = (cmd & mask)
			? AUBURN_EEPROM_DO_1
			: AUBURN_EEPROM_DO_0;
		if (previousBit != dataBit) {
			/* If the bit changed, change the DO state to match */
			ql_write_nvram_reg(qdev, spir,
					   (ISP_NVRAM_MASK |
					    qdev->eeprom_cmd_data | dataBit));
			previousBit = dataBit;
		}
		ql_write_nvram_reg(qdev, spir,
				   (ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
				    dataBit | AUBURN_EEPROM_CLK_RISE));
		ql_write_nvram_reg(qdev, spir,
				   (ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
				    dataBit | AUBURN_EEPROM_CLK_FALL));
		cmd = cmd << 1;
	}

	mask = 1 << (addrBits - 1);
	/* Force the previous data bit to be different */
	previousBit = 0xffff;
	for (i = 0; i < addrBits; i++) {
		dataBit = (eepromAddr & mask) ? AUBURN_EEPROM_DO_1
			: AUBURN_EEPROM_DO_0;
		if (previousBit != dataBit) {
			/*
			 * If the bit changed, then change the DO state to
			 * match
			 */
			ql_write_nvram_reg(qdev, spir,
					   (ISP_NVRAM_MASK |
					    qdev->eeprom_cmd_data | dataBit));
			previousBit = dataBit;
		}
		ql_write_nvram_reg(qdev, spir,
				   (ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
				    dataBit | AUBURN_EEPROM_CLK_RISE));
		ql_write_nvram_reg(qdev, spir,
				   (ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
				    dataBit | AUBURN_EEPROM_CLK_FALL));
		eepromAddr = eepromAddr << 1;
	}
}

/*
 * Caller holds hw_lock.
 */
static void fm93c56a_deselect(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	__iomem u32 *spir = &port_regs->CommonRegs.serialPortInterfaceReg;

	qdev->eeprom_cmd_data = AUBURN_EEPROM_CS_0;
	ql_write_nvram_reg(qdev, spir, ISP_NVRAM_MASK | qdev->eeprom_cmd_data);
}

/*
 * Caller holds hw_lock.
 */
static void fm93c56a_datain(struct ql3_adapter *qdev, unsigned short *value)
{
	int i;
	u32 data = 0;
	u32 dataBit;
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	__iomem u32 *spir = &port_regs->CommonRegs.serialPortInterfaceReg;

	/* Read the data bits */
	/* The first bit is a dummy.  Clock right over it. */
	for (i = 0; i < dataBits; i++) {
		ql_write_nvram_reg(qdev, spir,
				   ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
				   AUBURN_EEPROM_CLK_RISE);
		ql_write_nvram_reg(qdev, spir,
				   ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
				   AUBURN_EEPROM_CLK_FALL);
		dataBit = (ql_read_common_reg(qdev, spir) &
			   AUBURN_EEPROM_DI_1) ? 1 : 0;
		data = (data << 1) | dataBit;
	}
	*value = (u16)data;
}

/*
 * Caller holds hw_lock.
 */
static void eeprom_readword(struct ql3_adapter *qdev,
			    u32 eepromAddr, unsigned short *value)
{
	fm93c56a_select(qdev);
	fm93c56a_cmd(qdev, (int)FM93C56A_READ, eepromAddr);
	fm93c56a_datain(qdev, value);
	fm93c56a_deselect(qdev);
}

static void ql_set_mac_addr(struct net_device *ndev, u16 *addr)
{
	__le16 *p = (__le16 *)ndev->dev_addr;
	p[0] = cpu_to_le16(addr[0]);
	p[1] = cpu_to_le16(addr[1]);
	p[2] = cpu_to_le16(addr[2]);
}

static int ql_get_nvram_params(struct ql3_adapter *qdev)
{
	u16 *pEEPROMData;
	u16 checksum = 0;
	u32 index;
	unsigned long hw_flags;

	spin_lock_irqsave(&qdev->hw_lock, hw_flags);

	pEEPROMData = (u16 *)&qdev->nvram_data;
	qdev->eeprom_cmd_data = 0;
	if (ql_sem_spinlock(qdev, QL_NVRAM_SEM_MASK,
			(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
			 2) << 10)) {
		pr_err("%s: Failed ql_sem_spinlock()\n", __func__);
		spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
		return -1;
	}

	for (index = 0; index < EEPROM_SIZE; index++) {
		eeprom_readword(qdev, index, pEEPROMData);
		checksum += *pEEPROMData;
		pEEPROMData++;
	}
	ql_sem_unlock(qdev, QL_NVRAM_SEM_MASK);

	if (checksum != 0) {
		netdev_err(qdev->ndev, "checksum should be zero, is %x!!\n",
			   checksum);
		spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
		return -1;
	}

	spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
	return checksum;
}

static const u32 PHYAddr[2] = {
	PORT0_PHY_ADDRESS, PORT1_PHY_ADDRESS
};

static int ql_wait_for_mii_ready(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 temp;
	int count = 1000;

	while (count) {
		temp = ql_read_page0_reg(qdev, &port_regs->macMIIStatusReg);
		if (!(temp & MAC_MII_STATUS_BSY))
			return 0;
		udelay(10);
		count--;
	}
	return -1;
}

static void ql_mii_enable_scan_mode(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 scanControl;

	if (qdev->numPorts > 1) {
		/* Auto scan will cycle through multiple ports */
		scanControl = MAC_MII_CONTROL_AS | MAC_MII_CONTROL_SC;
	} else {
		scanControl = MAC_MII_CONTROL_SC;
	}

	/*
	 * Scan register 1 of PHY/PETBI,
	 * Set up to scan both devices
	 * The autoscan starts from the first register, completes
	 * the last one before rolling over to the first
	 */
	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
			   PHYAddr[0] | MII_SCAN_REGISTER);

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
			   (scanControl) |
			   ((MAC_MII_CONTROL_SC | MAC_MII_CONTROL_AS) << 16));
}

static u8 ql_mii_disable_scan_mode(struct ql3_adapter *qdev)
{
	u8 ret;
	struct ql3xxx_port_registers __iomem *port_regs =
					qdev->mem_map_registers;

	/* See if scan mode is enabled before we turn it off */
	if (ql_read_page0_reg(qdev, &port_regs->macMIIMgmtControlReg) &
	    (MAC_MII_CONTROL_AS | MAC_MII_CONTROL_SC)) {
		/* Scan is enabled */
		ret = 1;
	} else {
		/* Scan is disabled */
		ret = 0;
	}

	/*
	 * When disabling scan mode you must first change the MII register
	 * address
	 */
	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
			   PHYAddr[0] | MII_SCAN_REGISTER);

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
			   ((MAC_MII_CONTROL_SC | MAC_MII_CONTROL_AS |
			     MAC_MII_CONTROL_RC) << 16));

	return ret;
}

static int ql_mii_write_reg_ex(struct ql3_adapter *qdev,
			       u16 regAddr, u16 value, u32 phyAddr)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u8 scanWasEnabled;

	scanWasEnabled = ql_mii_disable_scan_mode(qdev);

	if (ql_wait_for_mii_ready(qdev)) {
		netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
		return -1;
	}

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
			   phyAddr | regAddr);

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtDataReg, value);

	/* Wait for write to complete 9/10/04 SJP */
	if (ql_wait_for_mii_ready(qdev)) {
		netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
		return -1;
	}

	if (scanWasEnabled)
		ql_mii_enable_scan_mode(qdev);

	return 0;
}

static int ql_mii_read_reg_ex(struct ql3_adapter *qdev, u16 regAddr,
			      u16 *value, u32 phyAddr)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u8 scanWasEnabled;
	u32 temp;

	scanWasEnabled = ql_mii_disable_scan_mode(qdev);

	if (ql_wait_for_mii_ready(qdev)) {
		netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
		return -1;
	}

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
			   phyAddr | regAddr);

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
			   (MAC_MII_CONTROL_RC << 16));

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
			   (MAC_MII_CONTROL_RC << 16) | MAC_MII_CONTROL_RC);

	/* Wait for the read to complete */
	if (ql_wait_for_mii_ready(qdev)) {
		netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
		return -1;
	}

	temp = ql_read_page0_reg(qdev, &port_regs->macMIIMgmtDataReg);
	*value = (u16) temp;

	if (scanWasEnabled)
		ql_mii_enable_scan_mode(qdev);

	return 0;
}

static int ql_mii_write_reg(struct ql3_adapter *qdev, u16 regAddr, u16 value)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;

	ql_mii_disable_scan_mode(qdev);

	if (ql_wait_for_mii_ready(qdev)) {
		netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
		return -1;
	}

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
			   qdev->PHYAddr | regAddr);

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtDataReg, value);

	/* Wait for write to complete. */
	if (ql_wait_for_mii_ready(qdev)) {
		netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
		return -1;
	}

	ql_mii_enable_scan_mode(qdev);

	return 0;
}

static int ql_mii_read_reg(struct ql3_adapter *qdev, u16 regAddr, u16 *value)
{
	u32 temp;
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;

	ql_mii_disable_scan_mode(qdev);

	if (ql_wait_for_mii_ready(qdev)) {
		netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
		return -1;
	}

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
			   qdev->PHYAddr | regAddr);

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
			   (MAC_MII_CONTROL_RC << 16));

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
			   (MAC_MII_CONTROL_RC << 16) | MAC_MII_CONTROL_RC);

	/* Wait for the read to complete */
	if (ql_wait_for_mii_ready(qdev)) {
		netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
		return -1;
	}

	temp = ql_read_page0_reg(qdev, &port_regs->macMIIMgmtDataReg);
	*value = (u16) temp;

	ql_mii_enable_scan_mode(qdev);

	return 0;
}

static void ql_petbi_reset(struct ql3_adapter *qdev)
{
	ql_mii_write_reg(qdev, PETBI_CONTROL_REG, PETBI_CTRL_SOFT_RESET);
}

static void ql_petbi_start_neg(struct ql3_adapter *qdev)
{
	u16 reg;

	/* Enable Auto-negotiation sense */
	ql_mii_read_reg(qdev, PETBI_TBI_CTRL, &reg);
	reg |= PETBI_TBI_AUTO_SENSE;
	ql_mii_write_reg(qdev, PETBI_TBI_CTRL, reg);

	ql_mii_write_reg(qdev, PETBI_NEG_ADVER,
			 PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX);

	ql_mii_write_reg(qdev, PETBI_CONTROL_REG,
			 PETBI_CTRL_AUTO_NEG | PETBI_CTRL_RESTART_NEG |
			 PETBI_CTRL_FULL_DUPLEX | PETBI_CTRL_SPEED_1000);

}

static void ql_petbi_reset_ex(struct ql3_adapter *qdev)
{
	ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG, PETBI_CTRL_SOFT_RESET,
			    PHYAddr[qdev->mac_index]);
}

static void ql_petbi_start_neg_ex(struct ql3_adapter *qdev)
{
	u16 reg;

	/* Enable Auto-negotiation sense */
	ql_mii_read_reg_ex(qdev, PETBI_TBI_CTRL, &reg,
			   PHYAddr[qdev->mac_index]);
	reg |= PETBI_TBI_AUTO_SENSE;
	ql_mii_write_reg_ex(qdev, PETBI_TBI_CTRL, reg,
			    PHYAddr[qdev->mac_index]);

	ql_mii_write_reg_ex(qdev, PETBI_NEG_ADVER,
			    PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX,
			    PHYAddr[qdev->mac_index]);

	ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG,
			    PETBI_CTRL_AUTO_NEG | PETBI_CTRL_RESTART_NEG |
			    PETBI_CTRL_FULL_DUPLEX | PETBI_CTRL_SPEED_1000,
			    PHYAddr[qdev->mac_index]);
}

static void ql_petbi_init(struct ql3_adapter *qdev)
{
	ql_petbi_reset(qdev);
	ql_petbi_start_neg(qdev);
}

static void ql_petbi_init_ex(struct ql3_adapter *qdev)
{
	ql_petbi_reset_ex(qdev);
	ql_petbi_start_neg_ex(qdev);
}

static int ql_is_petbi_neg_pause(struct ql3_adapter *qdev)
{
	u16 reg;

	if (ql_mii_read_reg(qdev, PETBI_NEG_PARTNER, &reg) < 0)
		return 0;

	return (reg & PETBI_NEG_PAUSE_MASK) == PETBI_NEG_PAUSE;
}

static void phyAgereSpecificInit(struct ql3_adapter *qdev, u32 miiAddr)
{
	netdev_info(qdev->ndev, "enabling Agere specific PHY\n");
	/* power down device bit 11 = 1 */
	ql_mii_write_reg_ex(qdev, 0x00, 0x1940, miiAddr);
	/* enable diagnostic mode bit 2 = 1 */
	ql_mii_write_reg_ex(qdev, 0x12, 0x840e, miiAddr);
	/* 1000MB amplitude adjust (see Agere errata) */
	ql_mii_write_reg_ex(qdev, 0x10, 0x8805, miiAddr);
	/* 1000MB amplitude adjust (see Agere errata) */
	ql_mii_write_reg_ex(qdev, 0x11, 0xf03e, miiAddr);
	/* 100MB amplitude adjust (see Agere errata) */
	ql_mii_write_reg_ex(qdev, 0x10, 0x8806, miiAddr);
	/* 100MB amplitude adjust (see Agere errata) */
	ql_mii_write_reg_ex(qdev, 0x11, 0x003e, miiAddr);
	/* 10MB amplitude adjust (see Agere errata) */
	ql_mii_write_reg_ex(qdev, 0x10, 0x8807, miiAddr);
	/* 10MB amplitude adjust (see Agere errata) */
	ql_mii_write_reg_ex(qdev, 0x11, 0x1f00, miiAddr);
	/* point to hidden reg 0x2806 */
	ql_mii_write_reg_ex(qdev, 0x10, 0x2806, miiAddr);
	/* Write new PHYAD w/bit 5 set */
	ql_mii_write_reg_ex(qdev, 0x11,
			    0x0020 | (PHYAddr[qdev->mac_index] >> 8), miiAddr);
	/*
	 * Disable diagnostic mode bit 2 = 0
	 * Power up device bit 11 = 0
	 * Link up (on) and activity (blink)
	 */
	ql_mii_write_reg(qdev, 0x12, 0x840a);
	ql_mii_write_reg(qdev, 0x00, 0x1140);
	ql_mii_write_reg(qdev, 0x1c, 0xfaf0);
}

static enum PHY_DEVICE_TYPE getPhyType(struct ql3_adapter *qdev,
				       u16 phyIdReg0, u16 phyIdReg1)
{
	enum PHY_DEVICE_TYPE result = PHY_TYPE_UNKNOWN;
	u32   oui;
	u16   model;
	int i;

	if (phyIdReg0 == 0xffff)
		return result;

	if (phyIdReg1 == 0xffff)
		return result;

	/* oui is split between two registers */
	oui = (phyIdReg0 << 6) | ((phyIdReg1 & PHY_OUI_1_MASK) >> 10);

	model = (phyIdReg1 & PHY_MODEL_MASK) >> 4;

	/* Scan table for this PHY */
	for (i = 0; i < MAX_PHY_DEV_TYPES; i++) {
		if ((oui == PHY_DEVICES[i].phyIdOUI) &&
		    (model == PHY_DEVICES[i].phyIdModel)) {
			netdev_info(qdev->ndev, "Phy: %s\n",
				    PHY_DEVICES[i].name);
			result = PHY_DEVICES[i].phyDevice;
			break;
		}
	}

	return result;
}

static int ql_phy_get_speed(struct ql3_adapter *qdev)
{
	u16 reg;

	switch (qdev->phyType) {
	case PHY_AGERE_ET1011C: {
		if (ql_mii_read_reg(qdev, 0x1A, &reg) < 0)
			return 0;

		reg = (reg >> 8) & 3;
		break;
	}
	default:
		if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, &reg) < 0)
			return 0;

		reg = (((reg & 0x18) >> 3) & 3);
	}

	switch (reg) {
	case 2:
		return SPEED_1000;
	case 1:
		return SPEED_100;
	case 0:
		return SPEED_10;
	default:
		return -1;
	}
}

static int ql_is_full_dup(struct ql3_adapter *qdev)
{
	u16 reg;

	switch (qdev->phyType) {
	case PHY_AGERE_ET1011C: {
		if (ql_mii_read_reg(qdev, 0x1A, &reg))
			return 0;

		return ((reg & 0x0080) && (reg & 0x1000)) != 0;
	}
	case PHY_VITESSE_VSC8211:
	default: {
		if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, &reg) < 0)
			return 0;
		return (reg & PHY_AUX_DUPLEX_STAT) != 0;
	}
	}
}

static int ql_is_phy_neg_pause(struct ql3_adapter *qdev)
{
	u16 reg;

	if (ql_mii_read_reg(qdev, PHY_NEG_PARTNER, &reg) < 0)
		return 0;

	return (reg & PHY_NEG_PAUSE) != 0;
}

static int PHY_Setup(struct ql3_adapter *qdev)
{
	u16   reg1;
	u16   reg2;
	bool  agereAddrChangeNeeded = false;
	u32 miiAddr = 0;
	int err;

	/*  Determine the PHY we are using by reading the ID's */
	err = ql_mii_read_reg(qdev, PHY_ID_0_REG, &reg1);
	if (err != 0) {
		netdev_err(qdev->ndev, "Could not read from reg PHY_ID_0_REG\n");
		return err;
	}

	err = ql_mii_read_reg(qdev, PHY_ID_1_REG, &reg2);
	if (err != 0) {
		netdev_err(qdev->ndev, "Could not read from reg PHY_ID_1_REG\n");
		return err;
	}

	/*  Check if we have a Agere PHY */
	if ((reg1 == 0xffff) || (reg2 == 0xffff)) {

		/* Determine which MII address we should be using
		   determined by the index of the card */
		if (qdev->mac_index == 0)
			miiAddr = MII_AGERE_ADDR_1;
		else
			miiAddr = MII_AGERE_ADDR_2;

		err = ql_mii_read_reg_ex(qdev, PHY_ID_0_REG, &reg1, miiAddr);
		if (err != 0) {
			netdev_err(qdev->ndev,
				   "Could not read from reg PHY_ID_0_REG after Agere detected\n");
			return err;
		}

		err = ql_mii_read_reg_ex(qdev, PHY_ID_1_REG, &reg2, miiAddr);
		if (err != 0) {
			netdev_err(qdev->ndev, "Could not read from reg PHY_ID_1_REG after Agere detected\n");
			return err;
		}

		/*  We need to remember to initialize the Agere PHY */
		agereAddrChangeNeeded = true;
	}

	/*  Determine the particular PHY we have on board to apply
	    PHY specific initializations */
	qdev->phyType = getPhyType(qdev, reg1, reg2);

	if ((qdev->phyType == PHY_AGERE_ET1011C) && agereAddrChangeNeeded) {
		/* need this here so address gets changed */
		phyAgereSpecificInit(qdev, miiAddr);
	} else if (qdev->phyType == PHY_TYPE_UNKNOWN) {
		netdev_err(qdev->ndev, "PHY is unknown\n");
		return -EIO;
	}

	return 0;
}

/*
 * Caller holds hw_lock.
 */
static void ql_mac_enable(struct ql3_adapter *qdev, u32 enable)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 value;

	if (enable)
		value = (MAC_CONFIG_REG_PE | (MAC_CONFIG_REG_PE << 16));
	else
		value = (MAC_CONFIG_REG_PE << 16);

	if (qdev->mac_index)
		ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value);
	else
		ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value);
}

/*
 * Caller holds hw_lock.
 */
static void ql_mac_cfg_soft_reset(struct ql3_adapter *qdev, u32 enable)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 value;

	if (enable)
		value = (MAC_CONFIG_REG_SR | (MAC_CONFIG_REG_SR << 16));
	else
		value = (MAC_CONFIG_REG_SR << 16);

	if (qdev->mac_index)
		ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value);
	else
		ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value);
}

/*
 * Caller holds hw_lock.
 */
static void ql_mac_cfg_gig(struct ql3_adapter *qdev, u32 enable)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 value;

	if (enable)
		value = (MAC_CONFIG_REG_GM | (MAC_CONFIG_REG_GM << 16));
	else
		value = (MAC_CONFIG_REG_GM << 16);

	if (qdev->mac_index)
		ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value);
	else
		ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value);
}

/*
 * Caller holds hw_lock.
 */
static void ql_mac_cfg_full_dup(struct ql3_adapter *qdev, u32 enable)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 value;

	if (enable)
		value = (MAC_CONFIG_REG_FD | (MAC_CONFIG_REG_FD << 16));
	else
		value = (MAC_CONFIG_REG_FD << 16);

	if (qdev->mac_index)
		ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value);
	else
		ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value);
}

/*
 * Caller holds hw_lock.
 */
static void ql_mac_cfg_pause(struct ql3_adapter *qdev, u32 enable)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 value;

	if (enable)
		value =
		    ((MAC_CONFIG_REG_TF | MAC_CONFIG_REG_RF) |
		     ((MAC_CONFIG_REG_TF | MAC_CONFIG_REG_RF) << 16));
	else
		value = ((MAC_CONFIG_REG_TF | MAC_CONFIG_REG_RF) << 16);

	if (qdev->mac_index)
		ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value);
	else
		ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value);
}

/*
 * Caller holds hw_lock.
 */
static int ql_is_fiber(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 bitToCheck = 0;
	u32 temp;

	switch (qdev->mac_index) {
	case 0:
		bitToCheck = PORT_STATUS_SM0;
		break;
	case 1:
		bitToCheck = PORT_STATUS_SM1;
		break;
	}

	temp = ql_read_page0_reg(qdev, &port_regs->portStatus);
	return (temp & bitToCheck) != 0;
}

static int ql_is_auto_cfg(struct ql3_adapter *qdev)
{
	u16 reg;
	ql_mii_read_reg(qdev, 0x00, &reg);
	return (reg & 0x1000) != 0;
}

/*
 * Caller holds hw_lock.
 */
static int ql_is_auto_neg_complete(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 bitToCheck = 0;
	u32 temp;

	switch (qdev->mac_index) {
	case 0:
		bitToCheck = PORT_STATUS_AC0;
		break;
	case 1:
		bitToCheck = PORT_STATUS_AC1;
		break;
	}

	temp = ql_read_page0_reg(qdev, &port_regs->portStatus);
	if (temp & bitToCheck) {
		netif_info(qdev, link, qdev->ndev, "Auto-Negotiate complete\n");
		return 1;
	}
	netif_info(qdev, link, qdev->ndev, "Auto-Negotiate incomplete\n");
	return 0;
}

/*
 *  ql_is_neg_pause() returns 1 if pause was negotiated to be on
 */
static int ql_is_neg_pause(struct ql3_adapter *qdev)
{
	if (ql_is_fiber(qdev))
		return ql_is_petbi_neg_pause(qdev);
	else
		return ql_is_phy_neg_pause(qdev);
}

static int ql_auto_neg_error(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 bitToCheck = 0;
	u32 temp;

	switch (qdev->mac_index) {
	case 0:
		bitToCheck = PORT_STATUS_AE0;
		break;
	case 1:
		bitToCheck = PORT_STATUS_AE1;
		break;
	}
	temp = ql_read_page0_reg(qdev, &port_regs->portStatus);
	return (temp & bitToCheck) != 0;
}

static u32 ql_get_link_speed(struct ql3_adapter *qdev)
{
	if (ql_is_fiber(qdev))
		return SPEED_1000;
	else
		return ql_phy_get_speed(qdev);
}

static int ql_is_link_full_dup(struct ql3_adapter *qdev)
{
	if (ql_is_fiber(qdev))
		return 1;
	else
		return ql_is_full_dup(qdev);
}

/*
 * Caller holds hw_lock.
 */
static int ql_link_down_detect(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 bitToCheck = 0;
	u32 temp;

	switch (qdev->mac_index) {
	case 0:
		bitToCheck = ISP_CONTROL_LINK_DN_0;
		break;
	case 1:
		bitToCheck = ISP_CONTROL_LINK_DN_1;
		break;
	}

	temp =
	    ql_read_common_reg(qdev, &port_regs->CommonRegs.ispControlStatus);
	return (temp & bitToCheck) != 0;
}

/*
 * Caller holds hw_lock.
 */
static int ql_link_down_detect_clear(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;

	switch (qdev->mac_index) {
	case 0:
		ql_write_common_reg(qdev,
				    &port_regs->CommonRegs.ispControlStatus,
				    (ISP_CONTROL_LINK_DN_0) |
				    (ISP_CONTROL_LINK_DN_0 << 16));
		break;

	case 1:
		ql_write_common_reg(qdev,
				    &port_regs->CommonRegs.ispControlStatus,
				    (ISP_CONTROL_LINK_DN_1) |
				    (ISP_CONTROL_LINK_DN_1 << 16));
		break;

	default:
		return 1;
	}

	return 0;
}

/*
 * Caller holds hw_lock.
 */
static int ql_this_adapter_controls_port(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 bitToCheck = 0;
	u32 temp;

	switch (qdev->mac_index) {
	case 0:
		bitToCheck = PORT_STATUS_F1_ENABLED;
		break;
	case 1:
		bitToCheck = PORT_STATUS_F3_ENABLED;
		break;
	default:
		break;
	}

	temp = ql_read_page0_reg(qdev, &port_regs->portStatus);
	if (temp & bitToCheck) {
		netif_printk(qdev, link, KERN_DEBUG, qdev->ndev,
			     "not link master\n");
		return 0;
	}

	netif_printk(qdev, link, KERN_DEBUG, qdev->ndev, "link master\n");
	return 1;
}

static void ql_phy_reset_ex(struct ql3_adapter *qdev)
{
	ql_mii_write_reg_ex(qdev, CONTROL_REG, PHY_CTRL_SOFT_RESET,
			    PHYAddr[qdev->mac_index]);
}

static void ql_phy_start_neg_ex(struct ql3_adapter *qdev)
{
	u16 reg;
	u16 portConfiguration;

	if (qdev->phyType == PHY_AGERE_ET1011C)
		ql_mii_write_reg(qdev, 0x13, 0x0000);
					/* turn off external loopback */

	if (qdev->mac_index == 0)
		portConfiguration =
			qdev->nvram_data.macCfg_port0.portConfiguration;
	else
		portConfiguration =
			qdev->nvram_data.macCfg_port1.portConfiguration;

	/*  Some HBA's in the field are set to 0 and they need to
	    be reinterpreted with a default value */
	if (portConfiguration == 0)
		portConfiguration = PORT_CONFIG_DEFAULT;

	/* Set the 1000 advertisements */
	ql_mii_read_reg_ex(qdev, PHY_GIG_CONTROL, &reg,
			   PHYAddr[qdev->mac_index]);
	reg &= ~PHY_GIG_ALL_PARAMS;

	if (portConfiguration & PORT_CONFIG_1000MB_SPEED) {
		if (portConfiguration & PORT_CONFIG_FULL_DUPLEX_ENABLED)
			reg |= PHY_GIG_ADV_1000F;
		else
			reg |= PHY_GIG_ADV_1000H;
	}

	ql_mii_write_reg_ex(qdev, PHY_GIG_CONTROL, reg,
			    PHYAddr[qdev->mac_index]);

	/* Set the 10/100 & pause negotiation advertisements */
	ql_mii_read_reg_ex(qdev, PHY_NEG_ADVER, &reg,
			   PHYAddr[qdev->mac_index]);
	reg &= ~PHY_NEG_ALL_PARAMS;

	if (portConfiguration & PORT_CONFIG_SYM_PAUSE_ENABLED)
		reg |= PHY_NEG_ASY_PAUSE | PHY_NEG_SYM_PAUSE;

	if (portConfiguration & PORT_CONFIG_FULL_DUPLEX_ENABLED) {
		if (portConfiguration & PORT_CONFIG_100MB_SPEED)
			reg |= PHY_NEG_ADV_100F;

		if (portConfiguration & PORT_CONFIG_10MB_SPEED)
			reg |= PHY_NEG_ADV_10F;
	}

	if (portConfiguration & PORT_CONFIG_HALF_DUPLEX_ENABLED) {
		if (portConfiguration & PORT_CONFIG_100MB_SPEED)
			reg |= PHY_NEG_ADV_100H;

		if (portConfiguration & PORT_CONFIG_10MB_SPEED)
			reg |= PHY_NEG_ADV_10H;
	}

	if (portConfiguration & PORT_CONFIG_1000MB_SPEED)
		reg |= 1;

	ql_mii_write_reg_ex(qdev, PHY_NEG_ADVER, reg,
			    PHYAddr[qdev->mac_index]);

	ql_mii_read_reg_ex(qdev, CONTROL_REG, &reg, PHYAddr[qdev->mac_index]);

	ql_mii_write_reg_ex(qdev, CONTROL_REG,
			    reg | PHY_CTRL_RESTART_NEG | PHY_CTRL_AUTO_NEG,
			    PHYAddr[qdev->mac_index]);
}

static void ql_phy_init_ex(struct ql3_adapter *qdev)
{
	ql_phy_reset_ex(qdev);
	PHY_Setup(qdev);
	ql_phy_start_neg_ex(qdev);
}

/*
 * Caller holds hw_lock.
 */
static u32 ql_get_link_state(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;
	u32 bitToCheck = 0;
	u32 temp, linkState;

	switch (qdev->mac_index) {
	case 0:
		bitToCheck = PORT_STATUS_UP0;
		break;
	case 1:
		bitToCheck = PORT_STATUS_UP1;
		break;
	}

	temp = ql_read_page0_reg(qdev, &port_regs->portStatus);
	if (temp & bitToCheck)
		linkState = LS_UP;
	else
		linkState = LS_DOWN;

	return linkState;
}

static int ql_port_start(struct ql3_adapter *qdev)
{
	if (ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
		(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
			 2) << 7)) {
		netdev_err(qdev->ndev, "Could not get hw lock for GIO\n");
		return -1;
	}

	if (ql_is_fiber(qdev)) {
		ql_petbi_init(qdev);
	} else {
		/* Copper port */
		ql_phy_init_ex(qdev);
	}

	ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
	return 0;
}

static int ql_finish_auto_neg(struct ql3_adapter *qdev)
{

	if (ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
		(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
			 2) << 7))
		return -1;

	if (!ql_auto_neg_error(qdev)) {
		if (test_bit(QL_LINK_MASTER, &qdev->flags)) {
			/* configure the MAC */
			netif_printk(qdev, link, KERN_DEBUG, qdev->ndev,
				     "Configuring link\n");
			ql_mac_cfg_soft_reset(qdev, 1);
			ql_mac_cfg_gig(qdev,
				       (ql_get_link_speed
					(qdev) ==
					SPEED_1000));
			ql_mac_cfg_full_dup(qdev,
					    ql_is_link_full_dup
					    (qdev));
			ql_mac_cfg_pause(qdev,
					 ql_is_neg_pause
					 (qdev));
			ql_mac_cfg_soft_reset(qdev, 0);

			/* enable the MAC */
			netif_printk(qdev, link, KERN_DEBUG, qdev->ndev,
				     "Enabling mac\n");
			ql_mac_enable(qdev, 1);
		}

		qdev->port_link_state = LS_UP;
		netif_start_queue(qdev->ndev);
		netif_carrier_on(qdev->ndev);
		netif_info(qdev, link, qdev->ndev,
			   "Link is up at %d Mbps, %s duplex\n",
			   ql_get_link_speed(qdev),
			   ql_is_link_full_dup(qdev) ? "full" : "half");

	} else {	/* Remote error detected */

		if (test_bit(QL_LINK_MASTER, &qdev->flags)) {
			netif_printk(qdev, link, KERN_DEBUG, qdev->ndev,
				     "Remote error detected. Calling ql_port_start()\n");
			/*
			 * ql_port_start() is shared code and needs
			 * to lock the PHY on it's own.
			 */
			ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
			if (ql_port_start(qdev))	/* Restart port */
				return -1;
			return 0;
		}
	}
	ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
	return 0;
}

static void ql_link_state_machine_work(struct work_struct *work)
{
	struct ql3_adapter *qdev =
		container_of(work, struct ql3_adapter, link_state_work.work);

	u32 curr_link_state;
	unsigned long hw_flags;

	spin_lock_irqsave(&qdev->hw_lock, hw_flags);

	curr_link_state = ql_get_link_state(qdev);

	if (test_bit(QL_RESET_ACTIVE, &qdev->flags)) {
		netif_info(qdev, link, qdev->ndev,
			   "Reset in progress, skip processing link state\n");

		spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);

		/* Restart timer on 2 second interval. */
		mod_timer(&qdev->adapter_timer, jiffies + HZ * 1);

		return;
	}

	switch (qdev->port_link_state) {
	default:
		if (test_bit(QL_LINK_MASTER, &qdev->flags))
			ql_port_start(qdev);
		qdev->port_link_state = LS_DOWN;
		/* Fall Through */

	case LS_DOWN:
		if (curr_link_state == LS_UP) {
			netif_info(qdev, link, qdev->ndev, "Link is up\n");
			if (ql_is_auto_neg_complete(qdev))
				ql_finish_auto_neg(qdev);

			if (qdev->port_link_state == LS_UP)
				ql_link_down_detect_clear(qdev);

			qdev->port_link_state = LS_UP;
		}
		break;

	case LS_UP:
		/*
		 * See if the link is currently down or went down and came
		 * back up
		 */
		if (curr_link_state == LS_DOWN) {
			netif_info(qdev, link, qdev->ndev, "Link is down\n");
			qdev->port_link_state = LS_DOWN;
		}
		if (ql_link_down_detect(qdev))
			qdev->port_link_state = LS_DOWN;
		break;
	}
	spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);

	/* Restart timer on 2 second interval. */
	mod_timer(&qdev->adapter_timer, jiffies + HZ * 1);
}

/*
 * Caller must take hw_lock and QL_PHY_GIO_SEM.
 */
static void ql_get_phy_owner(struct ql3_adapter *qdev)
{
	if (ql_this_adapter_controls_port(qdev))
		set_bit(QL_LINK_MASTER, &qdev->flags);
	else
		clear_bit(QL_LINK_MASTER, &qdev->flags);
}

/*
 * Caller must take hw_lock and QL_PHY_GIO_SEM.
 */
static void ql_init_scan_mode(struct ql3_adapter *qdev)
{
	ql_mii_enable_scan_mode(qdev);

	if (test_bit(QL_LINK_OPTICAL, &qdev->flags)) {
		if (ql_this_adapter_controls_port(qdev))
			ql_petbi_init_ex(qdev);
	} else {
		if (ql_this_adapter_controls_port(qdev))
			ql_phy_init_ex(qdev);
	}
}

/*
 * MII_Setup needs to be called before taking the PHY out of reset
 * so that the management interface clock speed can be set properly.
 * It would be better if we had a way to disable MDC until after the
 * PHY is out of reset, but we don't have that capability.
 */
static int ql_mii_setup(struct ql3_adapter *qdev)
{
	u32 reg;
	struct ql3xxx_port_registers __iomem *port_regs =
			qdev->mem_map_registers;

	if (ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
			(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
			 2) << 7))
		return -1;

	if (qdev->device_id == QL3032_DEVICE_ID)
		ql_write_page0_reg(qdev,
			&port_regs->macMIIMgmtControlReg, 0x0f00000);

	/* Divide 125MHz clock by 28 to meet PHY timing requirements */
	reg = MAC_MII_CONTROL_CLK_SEL_DIV28;

	ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
			   reg | ((MAC_MII_CONTROL_CLK_SEL_MASK) << 16));

	ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
	return 0;
}

#define SUPPORTED_OPTICAL_MODES	(SUPPORTED_1000baseT_Full |	\
				 SUPPORTED_FIBRE |		\
				 SUPPORTED_Autoneg)
#define SUPPORTED_TP_MODES	(SUPPORTED_10baseT_Half |	\
				 SUPPORTED_10baseT_Full |	\
				 SUPPORTED_100baseT_Half |	\
				 SUPPORTED_100baseT_Full |	\
				 SUPPORTED_1000baseT_Half |	\
				 SUPPORTED_1000baseT_Full |	\
				 SUPPORTED_Autoneg |		\
				 SUPPORTED_TP)			\

static u32 ql_supported_modes(struct ql3_adapter *qdev)
{
	if (test_bit(QL_LINK_OPTICAL, &qdev->flags))
		return SUPPORTED_OPTICAL_MODES;

	return SUPPORTED_TP_MODES;
}

static int ql_get_auto_cfg_status(struct ql3_adapter *qdev)
{
	int status;
	unsigned long hw_flags;
	spin_lock_irqsave(&qdev->hw_lock, hw_flags);
	if (ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
			    (QL_RESOURCE_BITS_BASE_CODE |
			     (qdev->mac_index) * 2) << 7)) {
		spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
		return 0;
	}
	status = ql_is_auto_cfg(qdev);
	ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
	spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
	return status;
}

static u32 ql_get_speed(struct ql3_adapter *qdev)
{
	u32 status;
	unsigned long hw_flags;
	spin_lock_irqsave(&qdev->hw_lock, hw_flags);
	if (ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
			    (QL_RESOURCE_BITS_BASE_CODE |
			     (qdev->mac_index) * 2) << 7)) {
		spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
		return 0;
	}
	status = ql_get_link_speed(qdev);
	ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
	spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
	return status;
}

static int ql_get_full_dup(struct ql3_adapter *qdev)
{
	int status;
	unsigned long hw_flags;
	spin_lock_irqsave(&qdev->hw_lock, hw_flags);
	if (ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
			    (QL_RESOURCE_BITS_BASE_CODE |
			     (qdev->mac_index) * 2) << 7)) {
		spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
		return 0;
	}
	status = ql_is_link_full_dup(qdev);
	ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
	spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
	return status;
}

static int ql_get_link_ksettings(struct net_device *ndev,
				 struct ethtool_link_ksettings *cmd)
{
	struct ql3_adapter *qdev = netdev_priv(ndev);
	u32 supported, advertising;

	supported = ql_supported_modes(qdev);

	if (test_bit(QL_LINK_OPTICAL, &qdev->flags)) {
		cmd->base.port = PORT_FIBRE;
	} else {
		cmd->base.port = PORT_TP;
		cmd->base.phy_address = qdev->PHYAddr;
	}
	advertising = ql_supported_modes(qdev);
	cmd->base.autoneg = ql_get_auto_cfg_status(qdev);
	cmd->base.speed = ql_get_speed(qdev);
	cmd->base.duplex = ql_get_full_dup(qdev);

	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
						supported);
	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
						advertising);

	return 0;
}

static void ql_get_drvinfo(struct net_device *ndev,
			   struct ethtool_drvinfo *drvinfo)
{
	struct ql3_adapter *qdev = netdev_priv(ndev);
	strlcpy(drvinfo->driver, ql3xxx_driver_name, sizeof(drvinfo->driver));
	strlcpy(drvinfo->version, ql3xxx_driver_version,
		sizeof(drvinfo->version));
	strlcpy(drvinfo->bus_info, pci_name(qdev->pdev),
		sizeof(drvinfo->bus_info));
}

static u32 ql_get_msglevel(struct net_device *ndev)
{
	struct ql3_adapter *qdev = netdev_priv(ndev);
	return qdev->msg_enable;
}

static void ql_set_msglevel(struct net_device *ndev, u32 value)
{
	struct ql3_adapter *qdev = netdev_priv(ndev);
	qdev->msg_enable = value;
}

static void ql_get_pauseparam(struct net_device *ndev,
			      struct ethtool_pauseparam *pause)
{
	struct ql3_adapter *qdev = netdev_priv(ndev);
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;

	u32 reg;
	if (qdev->mac_index == 0)
		reg = ql_read_page0_reg(qdev, &port_regs->mac0ConfigReg);
	else
		reg = ql_read_page0_reg(qdev, &port_regs->mac1ConfigReg);

	pause->autoneg  = ql_get_auto_cfg_status(qdev);
	pause->rx_pause = (reg & MAC_CONFIG_REG_RF) >> 2;
	pause->tx_pause = (reg & MAC_CONFIG_REG_TF) >> 1;
}

static const struct ethtool_ops ql3xxx_ethtool_ops = {
	.get_drvinfo = ql_get_drvinfo,
	.get_link = ethtool_op_get_link,
	.get_msglevel = ql_get_msglevel,
	.set_msglevel = ql_set_msglevel,
	.get_pauseparam = ql_get_pauseparam,
	.get_link_ksettings = ql_get_link_ksettings,
};

static int ql_populate_free_queue(struct ql3_adapter *qdev)
{
	struct ql_rcv_buf_cb *lrg_buf_cb = qdev->lrg_buf_free_head;
	dma_addr_t map;
	int err;

	while (lrg_buf_cb) {
		if (!lrg_buf_cb->skb) {
			lrg_buf_cb->skb =
				netdev_alloc_skb(qdev->ndev,
						 qdev->lrg_buffer_len);
			if (unlikely(!lrg_buf_cb->skb)) {
				netdev_printk(KERN_DEBUG, qdev->ndev,
					      "Failed netdev_alloc_skb()\n");
				break;
			} else {
				/*
				 * We save some space to copy the ethhdr from
				 * first buffer
				 */
				skb_reserve(lrg_buf_cb->skb, QL_HEADER_SPACE);
				map = pci_map_single(qdev->pdev,
						     lrg_buf_cb->skb->data,
						     qdev->lrg_buffer_len -
						     QL_HEADER_SPACE,
						     PCI_DMA_FROMDEVICE);

				err = pci_dma_mapping_error(qdev->pdev, map);
				if (err) {
					netdev_err(qdev->ndev,
						   "PCI mapping failed with error: %d\n",
						   err);
					dev_kfree_skb(lrg_buf_cb->skb);
					lrg_buf_cb->skb = NULL;
					break;
				}


				lrg_buf_cb->buf_phy_addr_low =
					cpu_to_le32(LS_64BITS(map));
				lrg_buf_cb->buf_phy_addr_high =
					cpu_to_le32(MS_64BITS(map));
				dma_unmap_addr_set(lrg_buf_cb, mapaddr, map);
				dma_unmap_len_set(lrg_buf_cb, maplen,
						  qdev->lrg_buffer_len -
						  QL_HEADER_SPACE);
				--qdev->lrg_buf_skb_check;
				if (!qdev->lrg_buf_skb_check)
					return 1;
			}
		}
		lrg_buf_cb = lrg_buf_cb->next;
	}
	return 0;
}

/*
 * Caller holds hw_lock.
 */
static void ql_update_small_bufq_prod_index(struct ql3_adapter *qdev)
{
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;

	if (qdev->small_buf_release_cnt >= 16) {
		while (qdev->small_buf_release_cnt >= 16) {
			qdev->small_buf_q_producer_index++;

			if (qdev->small_buf_q_producer_index ==
			    NUM_SBUFQ_ENTRIES)
				qdev->small_buf_q_producer_index = 0;
			qdev->small_buf_release_cnt -= 8;
		}
		wmb();
		writel_relaxed(qdev->small_buf_q_producer_index,
			       &port_regs->CommonRegs.rxSmallQProducerIndex);
	}
}

/*
 * Caller holds hw_lock.
 */
static void ql_update_lrg_bufq_prod_index(struct ql3_adapter *qdev)
{
	struct bufq_addr_element *lrg_buf_q_ele;
	int i;
	struct ql_rcv_buf_cb *lrg_buf_cb;
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;

	if ((qdev->lrg_buf_free_count >= 8) &&
	    (qdev->lrg_buf_release_cnt >= 16)) {

		if (qdev->lrg_buf_skb_check)
			if (!ql_populate_free_queue(qdev))
				return;

		lrg_buf_q_ele = qdev->lrg_buf_next_free;

		while ((qdev->lrg_buf_release_cnt >= 16) &&
		       (qdev->lrg_buf_free_count >= 8)) {

			for (i = 0; i < 8; i++) {
				lrg_buf_cb =
				    ql_get_from_lrg_buf_free_list(qdev);
				lrg_buf_q_ele->addr_high =
				    lrg_buf_cb->buf_phy_addr_high;
				lrg_buf_q_ele->addr_low =
				    lrg_buf_cb->buf_phy_addr_low;
				lrg_buf_q_ele++;

				qdev->lrg_buf_release_cnt--;
			}

			qdev->lrg_buf_q_producer_index++;

			if (qdev->lrg_buf_q_producer_index ==
			    qdev->num_lbufq_entries)
				qdev->lrg_buf_q_producer_index = 0;

			if (qdev->lrg_buf_q_producer_index ==
			    (qdev->num_lbufq_entries - 1)) {
				lrg_buf_q_ele = qdev->lrg_buf_q_virt_addr;
			}
		}
		wmb();
		qdev->lrg_buf_next_free = lrg_buf_q_ele;
		writel(qdev->lrg_buf_q_producer_index,
			&port_regs->CommonRegs.rxLargeQProducerIndex);
	}
}

static void ql_process_mac_tx_intr(struct ql3_adapter *qdev,
				   struct ob_mac_iocb_rsp *mac_rsp)
{
	struct ql_tx_buf_cb *tx_cb;
	int i;

	if (mac_rsp->flags & OB_MAC_IOCB_RSP_S) {
		netdev_warn(qdev->ndev,
			    "Frame too short but it was padded and sent\n");
	}

	tx_cb = &qdev->tx_buf[mac_rsp->transaction_id];

	/*  Check the transmit response flags for any errors */
	if (mac_rsp->flags & OB_MAC_IOCB_RSP_S) {
		netdev_err(qdev->ndev,
			   "Frame too short to be legal, frame not sent\n");

		qdev->ndev->stats.tx_errors++;
		goto frame_not_sent;
	}

	if (tx_cb->seg_count == 0) {
		netdev_err(qdev->ndev, "tx_cb->seg_count == 0: %d\n",
			   mac_rsp->transaction_id);

		qdev->ndev->stats.tx_errors++;
		goto invalid_seg_count;
	}

	pci_unmap_single(qdev->pdev,
			 dma_unmap_addr(&tx_cb->map[0], mapaddr),
			 dma_unmap_len(&tx_cb->map[0], maplen),
			 PCI_DMA_TODEVICE);
	tx_cb->seg_count--;
	if (tx_cb->seg_count) {
		for (i = 1; i < tx_cb->seg_count; i++) {
			pci_unmap_page(qdev->pdev,
				       dma_unmap_addr(&tx_cb->map[i],
						      mapaddr),
				       dma_unmap_len(&tx_cb->map[i], maplen),
				       PCI_DMA_TODEVICE);
		}
	}
	qdev->ndev->stats.tx_packets++;
	qdev->ndev->stats.tx_bytes += tx_cb->skb->len;

frame_not_sent:
	dev_kfree_skb_irq(tx_cb->skb);
	tx_cb->skb = NULL;

invalid_seg_count:
	atomic_inc(&qdev->tx_count);
}

static void ql_get_sbuf(struct ql3_adapter *qdev)
{
	if (++qdev->small_buf_index == NUM_SMALL_BUFFERS)
		qdev->small_buf_index = 0;
	qdev->small_buf_release_cnt++;
}

static struct ql_rcv_buf_cb *ql_get_lbuf(struct ql3_adapter *qdev)
{
	struct ql_rcv_buf_cb *lrg_buf_cb = NULL;
	lrg_buf_cb = &qdev->lrg_buf[qdev->lrg_buf_index];
	qdev->lrg_buf_release_cnt++;
	if (++qdev->lrg_buf_index == qdev->num_large_buffers)
		qdev->lrg_buf_index = 0;
	return lrg_buf_cb;
}

/*
 * The difference between 3022 and 3032 for inbound completions:
 * 3022 uses two buffers per completion.  The first buffer contains
 * (some) header info, the second the remainder of the headers plus
 * the data.  For this chip we reserve some space at the top of the
 * receive buffer so that the header info in buffer one can be
 * prepended to the buffer two.  Buffer two is the sent up while
 * buffer one is returned to the hardware to be reused.
 * 3032 receives all of it's data and headers in one buffer for a
 * simpler process.  3032 also supports checksum verification as
 * can be seen in ql_process_macip_rx_intr().
 */
static void ql_process_mac_rx_intr(struct ql3_adapter *qdev,
				   struct ib_mac_iocb_rsp *ib_mac_rsp_ptr)
{
	struct ql_rcv_buf_cb *lrg_buf_cb1 = NULL;
	struct ql_rcv_buf_cb *lrg_buf_cb2 = NULL;
	struct sk_buff *skb;
	u16 length = le16_to_cpu(ib_mac_rsp_ptr->length);

	/*
	 * Get the inbound address list (small buffer).
	 */
	ql_get_sbuf(qdev);

	if (qdev->device_id == QL3022_DEVICE_ID)
		lrg_buf_cb1 = ql_get_lbuf(qdev);

	/* start of second buffer */
	lrg_buf_cb2 = ql_get_lbuf(qdev);
	skb = lrg_buf_cb2->skb;

	qdev->ndev->stats.rx_packets++;
	qdev->ndev->stats.rx_bytes += length;

	skb_put(skb, length);
	pci_unmap_single(qdev->pdev,
			 dma_unmap_addr(lrg_buf_cb2, mapaddr),
			 dma_unmap_len(lrg_buf_cb2, maplen),
			 PCI_DMA_FROMDEVICE);
	prefetch(skb->data);
	skb_checksum_none_assert(skb);
	skb->protocol = eth_type_trans(skb, qdev->ndev);

	napi_gro_receive(&qdev->napi, skb);
	lrg_buf_cb2->skb = NULL;

	if (qdev->device_id == QL3022_DEVICE_ID)
		ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1);
	ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb2);
}

static void ql_process_macip_rx_intr(struct ql3_adapter *qdev,
				     struct ib_ip_iocb_rsp *ib_ip_rsp_ptr)
{
	struct ql_rcv_buf_cb *lrg_buf_cb1 = NULL;
	struct ql_rcv_buf_cb *lrg_buf_cb2 = NULL;
	struct sk_buff *skb1 = NULL, *skb2;
	struct net_device *ndev = qdev->ndev;
	u16 length = le16_to_cpu(ib_ip_rsp_ptr->length);
	u16 size = 0;

	/*
	 * Get the inbound address list (small buffer).
	 */

	ql_get_sbuf(qdev);

	if (qdev->device_id == QL3022_DEVICE_ID) {
		/* start of first buffer on 3022 */
		lrg_buf_cb1 = ql_get_lbuf(qdev);
		skb1 = lrg_buf_cb1->skb;
		size = ETH_HLEN;
		if (*((u16 *) skb1->data) != 0xFFFF)
			size += VLAN_ETH_HLEN - ETH_HLEN;
	}

	/* start of second buffer */
	lrg_buf_cb2 = ql_get_lbuf(qdev);
	skb2 = lrg_buf_cb2->skb;

	skb_put(skb2, length);	/* Just the second buffer length here. */
	pci_unmap_single(qdev->pdev,
			 dma_unmap_addr(lrg_buf_cb2, mapaddr),
			 dma_unmap_len(lrg_buf_cb2, maplen),
			 PCI_DMA_FROMDEVICE);
	prefetch(skb2->data);

	skb_checksum_none_assert(skb2);
	if (qdev->device_id == QL3022_DEVICE_ID) {
		/*
		 * Copy the ethhdr from first buffer to second. This
		 * is necessary for 3022 IP completions.
		 */
		skb_copy_from_linear_data_offset(skb1, VLAN_ID_LEN,
						 skb_push(skb2, size), size);
	} else {
		u16 checksum = le16_to_cpu(ib_ip_rsp_ptr->checksum);
		if (checksum &
			(IB_IP_IOCB_RSP_3032_ICE |
			 IB_IP_IOCB_RSP_3032_CE)) {
			netdev_err(ndev,
				   "%s: Bad checksum for this %s packet, checksum = %x\n",
				   __func__,
				   ((checksum & IB_IP_IOCB_RSP_3032_TCP) ?
				    "TCP" : "UDP"), checksum);
		} else if ((checksum & IB_IP_IOCB_RSP_3032_TCP) ||
				(checksum & IB_IP_IOCB_RSP_3032_UDP &&
				!(checksum & IB_IP_IOCB_RSP_3032_NUC))) {
			skb2->ip_summed = CHECKSUM_UNNECESSARY;
		}
	}
	skb2->protocol = eth_type_trans(skb2, qdev->ndev);

	napi_gro_receive(&qdev->napi, skb2);
	ndev->stats.rx_packets++;
	ndev->stats.rx_bytes += length;
	lrg_buf_cb2->skb = NULL;

	if (qdev->device_id == QL3022_DEVICE_ID)
		ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1);
	ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb2);
}

static int ql_tx_rx_clean(struct ql3_adapter *qdev, int budget)
{
	struct net_rsp_iocb *net_rsp;
	struct net_device *ndev = qdev->ndev;
	int work_done = 0;

	/* While there are entries in the completion queue. */
	while ((le32_to_cpu(*(qdev->prsp_producer_index)) !=
		qdev->rsp_consumer_index) && (work_done < budget)) {

		net_rsp = qdev->rsp_current;
		rmb();
		/*
		 * Fix 4032 chip's undocumented "feature" where bit-8 is set
		 * if the inbound completion is for a VLAN.
		 */
		if (qdev->device_id == QL3032_DEVICE_ID)
			net_rsp->opcode &= 0x7f;
		switch (net_rsp->opcode) {

		case OPCODE_OB_MAC_IOCB_FN0:
		case OPCODE_OB_MAC_IOCB_FN2:
			ql_process_mac_tx_intr(qdev, (struct ob_mac_iocb_rsp *)
					       net_rsp);
			break;

		case OPCODE_IB_MAC_IOCB:
		case OPCODE_IB_3032_MAC_IOCB:
			ql_process_mac_rx_intr(qdev, (struct ib_mac_iocb_rsp *)
					       net_rsp);
			work_done++;
			break;

		case OPCODE_IB_IP_IOCB:
		case OPCODE_IB_3032_IP_IOCB:
			ql_process_macip_rx_intr(qdev, (struct ib_ip_iocb_rsp *)
						 net_rsp);
			work_done++;
			break;
		default: {
			u32 *tmp = (u32 *)net_rsp;
			netdev_err(ndev,
				   "Hit default case, not handled!\n"
				   "	dropping the packet, opcode = %x\n"
				   "0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",
				   net_rsp->opcode,
				   (unsigned long int)tmp[0],
				   (unsigned long int)tmp[1],
				   (unsigned long int)tmp[2],
				   (unsigned long int)tmp[3]);
		}
		}

		qdev->rsp_consumer_index++;

		if (qdev->rsp_consumer_index == NUM_RSP_Q_ENTRIES) {
			qdev->rsp_consumer_index = 0;
			qdev->rsp_current = qdev->rsp_q_virt_addr;
		} else {
			qdev->rsp_current++;
		}

	}

	return work_done;
}

static int ql_poll(struct napi_struct *napi, int budget)
{
	struct ql3_adapter *qdev = container_of(napi, struct ql3_adapter, napi);
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;
	int work_done;

	work_done = ql_tx_rx_clean(qdev, budget);

	if (work_done < budget && napi_complete_done(napi, work_done)) {
		unsigned long flags;

		spin_lock_irqsave(&qdev->hw_lock, flags);
		ql_update_small_bufq_prod_index(qdev);
		ql_update_lrg_bufq_prod_index(qdev);
		writel(qdev->rsp_consumer_index,
			    &port_regs->CommonRegs.rspQConsumerIndex);
		spin_unlock_irqrestore(&qdev->hw_lock, flags);

		ql_enable_interrupts(qdev);
	}
	return work_done;
}

static irqreturn_t ql3xxx_isr(int irq, void *dev_id)
{

	struct net_device *ndev = dev_id;
	struct ql3_adapter *qdev = netdev_priv(ndev);
	struct ql3xxx_port_registers __iomem *port_regs =
		qdev->mem_map_registers;
	u32 value;
	int handled = 1;
	u32 var;

	value = ql_read_common_reg_l(qdev,
				     &port_regs->CommonRegs.ispControlStatus);

	if (value & (ISP_CONTROL_FE | ISP_CONTROL_RI)) {
		spin_lock(&qdev->adapter_lock);
		netif_stop_queue(qdev->ndev);
		netif_carrier_off(qdev->ndev);
		ql_disable_interrupts(qdev);
		qdev->port_link_state = LS_DOWN;
		set_bit(QL_RESET_ACTIVE, &qdev->flags) ;

		if (value & ISP_CONTROL_FE) {
			/*
			 * Chip Fatal Error.
			 */
			var =
			    ql_read_page0_reg_l(qdev,
					      &port_regs->PortFatalErrStatus);
			netdev_warn(ndev,
				    "Resetting chip. PortFatalErrStatus register = 0x%x\n",
				    var);
			set_bit(QL_RESET_START, &qdev->flags) ;
		} else {
			/*
			 * Soft Reset Requested.
			 */
			set_bit(QL_RESET_PER_SCSI, &qdev->flags) ;
			netdev_err(ndev,
				   "Another function issued a reset to the chip. ISR value = %x\n",
				   value);
		}
		queue_delayed_work(qdev->workqueue, &qdev->reset_work, 0);
		spin_unlock(&qdev->adapter_lock);
	} else if (value & ISP_IMR_DISABLE_CMPL_INT) {
		ql_disable_interrupts(qdev);
		if (likely(napi_schedule_prep(&qdev->napi)))
			__napi_schedule(&qdev->napi);
	} else
		return IRQ_NONE;

	return IRQ_RETVAL(handled);
}

/*
 * Get the total number of segments needed for the given number of fragments.
 * This is necessary because outbound address lists (OAL) will be used when
 * more than two frags are given.  Each address list has 5 addr/len pairs.
 * The 5th pair in each OAL is used to  point to the next OAL if more frags
 * are coming.  That is why the frags:segment count ratio is not linear.
 */
static int ql_get_seg_count