summaryrefslogblamecommitdiffstats
path: root/drivers/staging/cxt1e1/hwprobe.c
blob: 4fa27c8931b1ae41ed75830baebba680bd7286dc (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14













                                                                         

                                           
















                                



                        
                              

                                                                       




                                       
           
                                   
 



                              
                             


                                             
                              
                               
                                                        

                               
                                                        
























                                                                


 
           
                                     
 

                                                
 
                                                         
 
                                                           

                                                                  

     



                                                                                  

                                                                        
                                                                                  

                                                                            
                                                                                  

                                                                            
                                                                                  

                                                                            
                                                                                  

                                                                            
                                                                                  

                                                                            
         

      






                                                

      

                                                           





                                                                         


 
                   
                   
 


                       
                                                               






                                     

                                          
         


    
                     
 


                       
                                                               

                                         
                                         
                                                    
                                                                           
                                                  
                 
                                         
                                                    
                                                                           
                                                  

                 



    
                  
 


                       
                                                               


                                                      
                     
                                                               
      

                                                     
                             
                                                     
      
                                
         


 
                  
                                            
 





                                                           

                                    



                                                                     

                               

                                          
                                                                     








                                                                                
                                                               
                  

                                                                              




                                                                         


                                                       

                                                                         

                         
 


                                                  
                                                                             
 























                                                                                 

 
               
                     
 








                                                             
                                               


                                             

                     


                                                
 
                                                        

                                                                             





                                                                                    
                                                               

                                         

                                                                                        




                                                                                          
 
                                                                              
                                                  




                                                                                          
                    
                                                                               

                                                                        
      





                                                                                    
                                                               


                                                     
                                                     








                                                                            




                                                                       


                                                                            
     
                                       
      
                                                                              
                 
                                                                 

                 


                       
/* Copyright (C) 2007  One Stop Systems
 * Copyright (C) 2003-2005  SBE, Inc.
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/netdevice.h>
#include <linux/hdlc.h>
#include <linux/if_arp.h>
#include <asm/uaccess.h>
#include <linux/rtnetlink.h>
#include <linux/pci.h>
#include "pmcc4_sysdep.h"
#include "sbecom_inline_linux.h"
#include "libsbew.h"
#include "pmcc4_private.h"
#include "pmcc4.h"
#include "pmcc4_ioctls.h"
#include "pmc93x6_eeprom.h"
#ifdef CONFIG_PROC_FS
#include "sbeproc.h"
#endif

extern int  error_flag;
extern int  drvr_state;

/* forward references */
void        c4_stopwd(ci_t *);
struct net_device * __init c4_add_dev(hdw_info_t *, int, unsigned long,
				      unsigned long, int, int);


struct s_hdw_info hdw_info[MAX_BOARDS];


void __init
show_two(hdw_info_t *hi, int brdno)
{
	ci_t       *ci;
	struct pci_dev *pdev;
	char       *bid;
	char       banner[80];
	char	sn[6] = {0,};

	ci = (ci_t *)(netdev_priv(hi->ndev));
	bid = sbeid_get_bdname(ci);
	switch (hi->promfmt) {
	case PROM_FORMAT_TYPE1:
		memcpy(sn, hi->mfg_info.pft1.Serial, 6);
		break;
	case PROM_FORMAT_TYPE2:
		memcpy(sn, hi->mfg_info.pft2.Serial, 6);
		break;
	}

	sprintf(banner, "%s: %s  S/N %06X, MUSYCC Rev %02X",
		hi->devname, bid,
		((sn[3] << 16) & 0xff0000) |
		((sn[4] << 8) & 0x00ff00) |
		(sn[5] & 0x0000ff),
		(u_int8_t) hi->revid[0]);

	pr_info("%s\n", banner);

	pdev = hi->pdev[0];
	pr_info("%s: %s at v/p=%lx/%lx (%02x:%02x.%x) irq %d\n",
		hi->devname, "MUSYCC",
		(unsigned long) hi->addr_mapped[0], hi->addr[0],
		hi->pci_busno, (u_int8_t) PCI_SLOT(pdev->devfn),
		(u_int8_t) PCI_FUNC(pdev->devfn), pdev->irq);

	pdev = hi->pdev[1];
	pr_info("%s: %s at v/p=%lx/%lx (%02x:%02x.%x) irq %d\n",
		hi->devname, "EBUS  ",
		(unsigned long) hi->addr_mapped[1], hi->addr[1],
		hi->pci_busno, (u_int8_t) PCI_SLOT(pdev->devfn),
		(u_int8_t) PCI_FUNC(pdev->devfn), pdev->irq);
}


void __init
hdw_sn_get(hdw_info_t *hi, int brdno)
{
	/* obtain hardware EEPROM information */
	long        addr;

	addr = (long) hi->addr_mapped[1] + EEPROM_OFFSET;

	/* read EEPROM with largest known format size... */
	pmc_eeprom_read_buffer(addr, 0, (char *)hi->mfg_info.data,
			       sizeof(FLD_TYPE2));

#if 0
	{
		unsigned char *ucp = (unsigned char *) &hi->mfg_info.data;

		pr_info("eeprom[00]:  %02x %02x %02x %02x  %02x %02x %02x %02x\n",
			*(ucp + 0), *(ucp + 1), *(ucp + 2), *(ucp + 3),
			*(ucp + 4), *(ucp + 5), *(ucp + 6), *(ucp + 7));
		pr_info("eeprom[08]:  %02x %02x %02x %02x  %02x %02x %02x %02x\n",
			*(ucp + 8), *(ucp + 9), *(ucp + 10), *(ucp + 11),
			*(ucp + 12), *(ucp + 13), *(ucp + 14), *(ucp + 15));
		pr_info("eeprom[16]:  %02x %02x %02x %02x  %02x %02x %02x %02x\n",
			*(ucp + 16), *(ucp + 17), *(ucp + 18), *(ucp + 19),
			*(ucp + 20), *(ucp + 21), *(ucp + 22), *(ucp + 23));
		pr_info("eeprom[24]:  %02x %02x %02x %02x  %02x %02x %02x %02x\n",
			*(ucp + 24), *(ucp + 25), *(ucp + 26), *(ucp + 27),
			*(ucp + 28), *(ucp + 29), *(ucp + 30), *(ucp + 31));
		pr_info("eeprom[32]:  %02x %02x %02x %02x  %02x %02x %02x %02x\n",
			*(ucp + 32), *(ucp + 33), *(ucp + 34), *(ucp + 35),
			*(ucp + 36), *(ucp + 37), *(ucp + 38), *(ucp + 39));
		pr_info("eeprom[40]:  %02x %02x %02x %02x  %02x %02x %02x %02x\n",
			*(ucp + 40), *(ucp + 41), *(ucp + 42), *(ucp + 43),
			*(ucp + 44), *(ucp + 45), *(ucp + 46), *(ucp + 47));
	}
#endif
#if 0
	pr_info("sn: %x %x %x %x %x %x\n",
			hi->mfg_info.Serial[0],
			hi->mfg_info.Serial[1],
			hi->mfg_info.Serial[2],
			hi->mfg_info.Serial[3],
			hi->mfg_info.Serial[4],
			hi->mfg_info.Serial[5]);
#endif

	hi->promfmt = pmc_verify_cksum(&hi->mfg_info.data);
	if (hi->promfmt == PROM_FORMAT_Unk) {
		/* bad crc, data is suspect */
		if (cxt1e1_log_level >= LOG_WARN)
			pr_info("%s: EEPROM cksum error\n", hi->devname);
		hi->mfg_info_sts = EEPROM_CRCERR;
	} else
		hi->mfg_info_sts = EEPROM_OK;
}


	void __init
prep_hdw_info(void)
{
	hdw_info_t *hi;
	int         i;

	for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) {
		hi->pci_busno = 0xff;
		hi->pci_slot = 0xff;
		hi->pci_pin[0] = 0;
		hi->pci_pin[1] = 0;
		hi->ndev = NULL;
		hi->addr[0] = 0L;
		hi->addr[1] = 0L;
		hi->addr_mapped[0] = NULL;
		hi->addr_mapped[1] = NULL;
	}
}

void
cleanup_ioremap(void)
{
	hdw_info_t *hi;
	int         i;

	for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) {
		if (hi->pci_slot == 0xff)
			break;
		if (hi->addr_mapped[0]) {
			iounmap(hi->addr_mapped[0]);
			release_mem_region((long) hi->addr[0], hi->len[0]);
			hi->addr_mapped[0] = NULL;
		}
		if (hi->addr_mapped[1]) {
			iounmap(hi->addr_mapped[1]);
			release_mem_region((long) hi->addr[1], hi->len[1]);
			hi->addr_mapped[1] = NULL;
		}
	}
}


void
cleanup_devs(void)
{
	hdw_info_t *hi;
	int         i;

	for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) {
		if (hi->pci_slot == 0xff || !hi->ndev)
			break;
		c4_stopwd(netdev_priv(hi->ndev));
#ifdef CONFIG_PROC_FS
		sbecom_proc_brd_cleanup(netdev_priv(hi->ndev));
#endif
		unregister_netdev(hi->ndev);
		free_irq(hi->pdev[0]->irq, hi->ndev);
#ifdef CONFIG_SBE_PMCC4_NCOMM
		free_irq(hi->pdev[1]->irq, hi->ndev);
#endif
		kfree(hi->ndev);
	}
}


static int  __init
c4_hdw_init(struct pci_dev *pdev, int found)
{
	hdw_info_t *hi;
	int         i;
	int         fun, slot;
	unsigned char busno = 0xff;

	/* our MUSYCC chip supports two functions, 0 & 1 */
	fun = PCI_FUNC(pdev->devfn);
	if (fun > 1) {
		pr_warning("unexpected devfun: 0x%x\n", pdev->devfn);
		return 0;
	}

	/* obtain bus number */
	if (pdev->bus)
		busno = pdev->bus->number;
	else
		busno = 0; /* default for system PCI inconsistency */
	slot = pdev->devfn & ~0x07;

	/*
	 * Functions 0 & 1 for a given board (identified by same bus(busno) and
	 * slot(slot)) are placed into the same 'hardware' structure.  The first
	 * part of the board's functionality will be placed into an unpopulated
	 * element, identified by "slot==(0xff)".  The second part of a board's
	 * functionality will match the previously loaded slot/busno.
	 */
	for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) {
		/*
		 * match with board's first found interface, otherwise this is
		 * fisrt found
		 */
		if ((hi->pci_slot == 0xff) ||   /* new board */
		    ((hi->pci_slot == slot) && (hi->bus == pdev->bus)))
			break;                  /* found for-loop exit */
	}

	/* no match in above loop means MAX exceeded */
	if (i == MAX_BOARDS) {
		pr_warning("exceeded number of allowed devices (>%d)?\n",
			   MAX_BOARDS);
		return 0;
	}

	if (pdev->bus)
		hi->pci_busno = pdev->bus->number;
	else
		hi->pci_busno = 0; /* default for system PCI inconsistency */

	hi->pci_slot = slot;
	pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &hi->pci_pin[fun]);
	pci_read_config_byte(pdev, PCI_REVISION_ID, &hi->revid[fun]);
	hi->bus = pdev->bus;
	hi->addr[fun] = pci_resource_start(pdev, 0);
	hi->len[fun] = pci_resource_end(pdev, 0) - hi->addr[fun] + 1;
	hi->pdev[fun] = pdev;

	{
		/*
		 * create device name from module name, plus add the appropriate
		 * board number
		 */
		char       *cp = hi->devname;

		strcpy(cp, KBUILD_MODNAME);
		cp += strlen(cp);          /* reposition */
		*cp++ = '-';
		*cp++ = '0' + (found / 2);  /* there are two found interfaces per
		* board */
		*cp = 0;                    /* termination */
	}

	return 1;
}

status_t __init
c4hw_attach_all(void)
{
	hdw_info_t *hi;
	struct pci_dev *pdev = NULL;
	int         found = 0, i, j;

	error_flag = 0;
	prep_hdw_info();
	/*** scan PCI bus for all possible boards */
	while ((pdev = pci_get_device(PCI_VENDOR_ID_CONEXANT,
				      PCI_DEVICE_ID_CN8474,
				      pdev))) {
		if (c4_hdw_init(pdev, found))
			found++;
	}

	if (!found) {
		pr_warning("No boards found\n");
		return -ENODEV;
	}

	/* sanity check for consistent hardware found */
	for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) {
		if (hi->pci_slot != 0xff && (!hi->addr[0] || !hi->addr[1])) {
			pr_warning("%s: something very wrong with pci_get_device\n",
				   hi->devname);
			return -EIO;
		}
	}
	/* bring board's memory regions on/line */
	for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) {
		if (hi->pci_slot == 0xff)
			break;
		for (j = 0; j < 2; j++) {
			if (!request_mem_region(hi->addr[j], hi->len[j], hi->devname)) {
				pr_warning("%s: memory in use, addr=0x%lx, len=0x%lx ?\n",
					   hi->devname, hi->addr[j], hi->len[j]);
				cleanup_ioremap();
				return -ENOMEM;
			}

			hi->addr_mapped[j] = ioremap(hi->addr[j], hi->len[j]);
			if (!hi->addr_mapped[j]) {
				pr_warning("%s: ioremap fails, addr=0x%lx, len=0x%lx ?\n",
					   hi->devname, hi->addr[j], hi->len[j]);
				cleanup_ioremap();
				return -ENOMEM;
			}
#ifdef SBE_MAP_DEBUG
			pr_warning("%s: io remapped from phys %x to virt %x\n",
				   hi->devname, (u_int32_t) hi->addr[j],
				   (u_int32_t) hi->addr_mapped[j]);
#endif
		}
	}

	drvr_state = SBE_DRVR_AVAILABLE;

	/* Have now memory mapped all boards.  Now allow board's access to system */
	for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) {
		if (hi->pci_slot == 0xff)
			break;
		if (pci_enable_device(hi->pdev[0]) ||
		    pci_enable_device(hi->pdev[1])) {
			drvr_state = SBE_DRVR_DOWN;
			pr_warning("%s: failed to enable card %d slot %d\n",
				   hi->devname, i, hi->pci_slot);
			cleanup_devs();
			cleanup_ioremap();
			return -EIO;
		}
		pci_set_master(hi->pdev[0]);
		pci_set_master(hi->pdev[1]);
		hi->ndev = c4_add_dev(hi, i, (long) hi->addr_mapped[0],
				      (long) hi->addr_mapped[1],
				      hi->pdev[0]->irq,
				      hi->pdev[1]->irq);
		if (!hi->ndev) {
			drvr_state = SBE_DRVR_DOWN;
			cleanup_ioremap();
			/* NOTE: c4_add_dev() does its own device cleanup */
#if 0
			cleanup_devs();
#endif
			return error_flag; /* error_flag set w/in add_dev() */
		}
		show_two(hi, i); /* displays found information */
	}
	return 0;
}

/***  End-of-File  ***/