176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* 276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. 376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is free software; you can redistribute it and/or 576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * modify it under the terms of the GNU General Public License as 676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * published by the Free Software Foundation; either version 2 of the 776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * License, or any later version. 876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is distributed in the hope that it will be useful, but 1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * WITHOUT ANY WARRANTY; without even the implied warranty of 1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * General Public License for more details. 1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * You should have received a copy of the GNU General Public License 1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * along with this program; if not, write to the Free Software 1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 1976d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFILE_LICENCE ( GPL2_OR_LATER ); 2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdint.h> 2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdlib.h> 2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdio.h> 2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <string.h> 2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/pci.h> 2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <undi.h> 2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <undirom.h> 2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <undiload.h> 2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <undinet.h> 3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <undipreload.h> 3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** @file 3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * UNDI PCI driver 3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Find UNDI ROM for PCI device 4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v pci PCI device 4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret undirom UNDI ROM, or NULL 4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Try to find a driver for this device. Try an exact match on the 4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ROM address first, then fall back to a vendor/device ID match only 4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct undi_rom * undipci_find_rom ( struct pci_device *pci ) { 4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct undi_rom *undirom; 4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned long rombase; 5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rombase = pci_bar_start ( pci, PCI_ROM_ADDRESS ); 5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman undirom = undirom_find_pci ( pci->vendor, pci->device, rombase ); 5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! undirom ) 5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman undirom = undirom_find_pci ( pci->vendor, pci->device, 0 ); 5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return undirom; 5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Probe PCI device 6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v pci PCI device 6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v id PCI ID 6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int undipci_probe ( struct pci_device *pci, 6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const struct pci_device_id *id __unused ) { 6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct undi_device *undi; 6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct undi_rom *undirom; 6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int busdevfn = PCI_BUSDEVFN ( pci->bus, pci->devfn ); 7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int rc; 7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Ignore non-network devices */ 7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( PCI_BASE_CLASS ( pci->class ) != PCI_BASE_CLASS_NETWORK ) 7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -ENOTTY; 7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Allocate UNDI device structure */ 7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman undi = zalloc ( sizeof ( *undi ) ); 7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! undi ) 7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -ENOMEM; 8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman pci_set_drvdata ( pci, undi ); 8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Find/create our pixie */ 8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( preloaded_undi.pci_busdevfn == busdevfn ) { 8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Claim preloaded UNDI device */ 8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( undi, "UNDI %p using preloaded UNDI device\n", undi ); 8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memcpy ( undi, &preloaded_undi, sizeof ( *undi ) ); 8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memset ( &preloaded_undi, 0, sizeof ( preloaded_undi ) ); 8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else { 8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Find UNDI ROM for PCI device */ 9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! ( undirom = undipci_find_rom ( pci ) ) ) { 9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -ENODEV; 9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err_find_rom; 9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Call UNDI ROM loader to create pixie */ 9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = undi_load_pci ( undi, undirom, busdevfn ) ) != 0 ) 9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err_load_pci; 9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Add to device hierarchy */ 10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman snprintf ( undi->dev.name, sizeof ( undi->dev.name ), 10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "UNDI-%s", pci->dev.name ); 10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memcpy ( &undi->dev.desc, &pci->dev.desc, sizeof ( undi->dev.desc ) ); 10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman undi->dev.parent = &pci->dev; 10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman INIT_LIST_HEAD ( &undi->dev.children ); 10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_add ( &undi->dev.siblings, &pci->dev.children ); 10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Create network device */ 10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = undinet_probe ( undi ) ) != 0 ) 11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err_undinet_probe; 11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err_undinet_probe: 11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman undi_unload ( undi ); 11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_del ( &undi->dev.siblings ); 11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err_find_rom: 11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err_load_pci: 11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free ( undi ); 12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman pci_set_drvdata ( pci, NULL ); 12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Remove PCI device 12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v pci PCI device 12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void undipci_remove ( struct pci_device *pci ) { 13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct undi_device *undi = pci_get_drvdata ( pci ); 13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman undinet_remove ( undi ); 13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman undi_unload ( undi ); 13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_del ( &undi->dev.siblings ); 13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free ( undi ); 13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman pci_set_drvdata ( pci, NULL ); 13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct pci_device_id undipci_nics[] = { 14076d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)", 0 ), 14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct pci_driver undipci_driver __pci_driver = { 14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .ids = undipci_nics, 14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .id_count = ( sizeof ( undipci_nics ) / sizeof ( undipci_nics[0] ) ), 14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .probe = undipci_probe, 14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .remove = undipci_remove, 14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 149