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