| /* 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 |
| |
| #ifdef SBE_INCLUDE_SYMBOLS |
| #define STATIC |
| #else |
| #define STATIC static |
| #endif |
| |
| extern int log_level; |
| 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 *bp, banner[80]; |
| char sn[6]; |
| |
| bp = banner; |
| memset (banner, 0, 80); /* clear print buffer */ |
| |
| ci = (ci_t *)(netdev_priv(hi->ndev)); |
| bid = sbeid_get_bdname (ci); |
| switch (hi->promfmt) |
| { |
| case PROM_FORMAT_TYPE1: |
| memcpy (sn, (FLD_TYPE1 *) (hi->mfg_info.pft1.Serial), 6); |
| break; |
| case PROM_FORMAT_TYPE2: |
| memcpy (sn, (FLD_TYPE2 *) (hi->mfg_info.pft2.Serial), 6); |
| break; |
| default: |
| memset (sn, 0, 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 |
| |
| if ((hi->promfmt = pmc_verify_cksum (&hi->mfg_info.data)) == PROM_FORMAT_Unk) |
| { |
| /* bad crc, data is suspect */ |
| if (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 = 0; |
| hi->addr[0] = 0L; |
| hi->addr[1] = 0L; |
| hi->addr_mapped[0] = 0L; |
| hi->addr_mapped[1] = 0L; |
| } |
| } |
| |
| 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 ((void *) (hi->addr_mapped[0])); |
| release_mem_region ((long) hi->addr[0], hi->len[0]); |
| hi->addr_mapped[0] = 0; |
| } |
| if (hi->addr_mapped[1]) |
| { |
| iounmap ((void *) (hi->addr_mapped[1])); |
| release_mem_region ((long) hi->addr[1], hi->len[1]); |
| hi->addr_mapped[1] = 0; |
| } |
| } |
| } |
| |
| |
| 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 |
| OS_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 */ |
| if ((fun = PCI_FUNC (pdev->devfn)) > 1) |
| { |
| pr_warning("unexpected devfun: 0x%x\n", pdev->devfn); |
| return 0; |
| } |
| if (pdev->bus) /* obtain bus number */ |
| 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 first |
| * found |
| */ |
| if ((hi->pci_slot == 0xff) || /* new board */ |
| ((hi->pci_slot == slot) && (hi->bus == pdev->bus))) |
| break; /* found for-loop exit */ |
| } |
| if (i == MAX_BOARDS) /* no match in above loop means MAX |
| * exceeded */ |
| { |
| 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 */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) |
| while ((pdev = pci_get_device (PCI_VENDOR_ID_CONEXANT, |
| PCI_DEVICE_ID_CN8474, |
| pdev))) |
| #else |
| while ((pdev = pci_find_device (PCI_VENDOR_ID_CONEXANT, |
| PCI_DEVICE_ID_CN8474, |
| pdev))) |
| #endif |
| { |
| if (c4_hdw_init (pdev, found)) |
| found++; |
| } |
| if (!found) |
| { |
| pr_warning("No boards found\n"); |
| return ENODEV; |
| } |
| /* sanity check for consistant 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) == 0) |
| { |
| 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] = (unsigned long) 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]); |
| if (!(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))) |
| { |
| 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 ***/ |