/*
 * pci.c (C) 2002 Minoru Murashima
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * PCIÀ©¸æ
 */


#include"types.h"
#include"lib.h"
#include"pci.h"


enum{
	/* IO port */
	CONFIG_ADDR=0xcf8,
	CONFIG_DATA=0xcfc
};


/*
 * Read pci config register
 * parameters : Bus number,Device number,Function number,Register
 * return : Read value
 */
uint read_pci_config(int bus,int dev,int func,int reg)
{
	uint value;


	/* Read IO */
	outdw(CONFIG_ADDR,(1<<31)|(bus<<16)|(dev<<11)|(func<<8)|(reg&~3));
	value=indw(CONFIG_DATA);
	outdw(CONFIG_ADDR,0);

	return value>>((reg&3)*8);
}


/*
 * Write double word value to pci config register
 * parameters : Bus number,Device number,Function number,Register,Write value
 */
void writedw_pci_config(int bus,int dev,int func,int reg,uint value)
{
	outdw(CONFIG_ADDR,(1<<31)|(bus<<16)|(dev<<11)|(func<<8)|(reg&~3));
	outdw(CONFIG_DATA+(reg&3),value);
	outdw(CONFIG_ADDR,0);
}


/*
 * Write word value to pci config register
 * parameters : Bus number,Device number,Function number,Register,Write value
 */
void writew_pci_config(int bus,int dev,int func,int reg,ushort value)
{
	outdw(CONFIG_ADDR,(1<<31)|(bus<<16)|(dev<<11)|(func<<8)|(reg&~3));
	outw(CONFIG_DATA+(reg&3),value);
	outdw(CONFIG_ADDR,0);
}


/*
 * Write byte value to pci config register
 * parameters : Bus number,Device number,Function number,Register,Write value
 */
void writeb_pci_config(int bus,int dev,int func,int reg,uchar value)
{
	outdw(CONFIG_ADDR,(1<<31)|(bus<<16)|(dev<<11)|(func<<8)|(reg&~3));
	outb(CONFIG_DATA+(reg&3),value);
	outdw(CONFIG_ADDR,0);
}


/*
 * Search PCI device from class coad
 * parameters : Class coad,Buffer for writing
 * return : 0 or failse=-1
 */
int search_pci_class(int class,PCI_INFO *pci_info)
{
	int i,j,k;


	for(i=0;i<255;++i)
	{
		for(j=0;j<32;++j)
		{
			for(k=0;k<8;++k)
			{
				if((ushort)read_pci_config(i,j,k,PCI_CONF_VEND)==0xffff)continue;
				if((read_pci_config(i,j,k,PCI_CONF_CLS)>>8)==class)
				{
					pci_info->vender=read_pci_config(i,j,k,PCI_CONF_VEND);
					pci_info->bus=i;
					pci_info->dev=j;
					pci_info->func=k;

					return 0;
				}
			}
		}
	}

	return -1;
}
