diff options
| author | Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> | 2007-11-20 20:28:09 +0100 | 
|---|---|---|
| committer | Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> | 2007-11-24 20:35:55 +0100 | 
| commit | 93a686ee9c5ddc6fa368c32cfbfde6f6724599fc (patch) | |
| tree | 19a64b773315470460b01776ad434715ceec9f7f /drivers/pci | |
| parent | 080c646dbf474a109c3f85718fb01ce042a38c45 (diff) | |
| download | olio-uboot-2014.01-93a686ee9c5ddc6fa368c32cfbfde6f6724599fc.tar.xz olio-uboot-2014.01-93a686ee9c5ddc6fa368c32cfbfde6f6724599fc.zip | |
drivers/pci : move pci drivers to drivers/pci
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Diffstat (limited to 'drivers/pci')
| -rw-r--r-- | drivers/pci/Makefile | 51 | ||||
| -rw-r--r-- | drivers/pci/fsl_pci_init.c | 177 | ||||
| -rw-r--r-- | drivers/pci/pci.c | 526 | ||||
| -rw-r--r-- | drivers/pci/pci_auto.c | 412 | ||||
| -rw-r--r-- | drivers/pci/pci_indirect.c | 138 | ||||
| -rw-r--r-- | drivers/pci/tsi108_pci.c | 181 | ||||
| -rw-r--r-- | drivers/pci/w83c553f.c | 226 | 
7 files changed, 1711 insertions, 0 deletions
| diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile new file mode 100644 index 000000000..fe4583946 --- /dev/null +++ b/drivers/pci/Makefile @@ -0,0 +1,51 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB 	:= $(obj)libpci.a + +COBJS-y += fsl_pci_init.o +COBJS-y += pci.o +COBJS-y += pci_auto.o +COBJS-y += pci_indirect.o +COBJS-y += tsi108_pci.o +COBJS-y += w83c553f.o + +COBJS	:= $(COBJS-y) +SRCS 	:= $(COBJS:.o=.c) +OBJS 	:= $(addprefix $(obj),$(COBJS)) + +all:	$(LIB) + +$(LIB):	$(obj).depend $(OBJS) +	$(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/pci/fsl_pci_init.c b/drivers/pci/fsl_pci_init.c new file mode 100644 index 000000000..1e778844a --- /dev/null +++ b/drivers/pci/fsl_pci_init.c @@ -0,0 +1,177 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> + +#ifdef CONFIG_FSL_PCI_INIT + +/* + * PCI/PCIE Controller initialization for mpc85xx/mpc86xx soc's + * + * Initialize controller and call the common driver/pci pci_hose_scan to + * scan for bridges and devices. + * + * Hose fields which need to be pre-initialized by board specific code: + *   regions[] + *   first_busno + * + * Fields updated: + *   last_busno + */ + +#include <pci.h> +#include <asm/immap_fsl_pci.h> + +void pciauto_prescan_setup_bridge(struct pci_controller *hose, +				pci_dev_t dev, int sub_bus); +void pciauto_postscan_setup_bridge(struct pci_controller *hose, +				pci_dev_t dev, int sub_bus); + +void pciauto_config_init(struct pci_controller *hose); +void +fsl_pci_init(struct pci_controller *hose) +{ +	u16 temp16; +	u32 temp32; +	int busno = hose->first_busno; +	int enabled; +	u16 ltssm; +	u8 temp8; +	int r; +	int bridge; +	int inbound = 0; +	volatile ccsr_fsl_pci_t *pci = (ccsr_fsl_pci_t *) hose->cfg_addr; +	pci_dev_t dev = PCI_BDF(busno,0,0); + +	/* Initialize ATMU registers based on hose regions and flags */ +	volatile pot_t *po=&pci->pot[1];	/* skip 0 */ +	volatile pit_t *pi=&pci->pit[0];	/* ranges from: 3 to 1 */ + +#ifdef DEBUG +	int neg_link_w; +#endif + +	for (r=0; r<hose->region_count; r++) { +		if (hose->regions[r].flags & PCI_REGION_MEMORY) { /* inbound */ +			pi->pitar = (hose->regions[r].bus_start >> 12) & 0x000fffff; +			pi->piwbar = (hose->regions[r].phys_start >> 12) & 0x000fffff; +			pi->piwbear = 0; +			pi->piwar = PIWAR_EN | PIWAR_PF | PIWAR_LOCAL | +				PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP | +				(__ilog2(hose->regions[r].size) - 1); +			pi++; +			inbound = hose->regions[r].size > 0; +		} else { /* Outbound */ +			po->powbar = (hose->regions[r].phys_start >> 12) & 0x000fffff; +			po->potar = (hose->regions[r].bus_start >> 12) & 0x000fffff; +			po->potear = 0; +			if (hose->regions[r].flags & PCI_REGION_IO) +				po->powar = POWAR_EN | POWAR_IO_READ | POWAR_IO_WRITE | +					(__ilog2(hose->regions[r].size) - 1); +			else +				po->powar = POWAR_EN | POWAR_MEM_READ | POWAR_MEM_WRITE | +					(__ilog2(hose->regions[r].size) - 1); +			po++; +		} +	} + +	pci_register_hose(hose); +	pciauto_config_init(hose);	/* grab pci_{mem,prefetch,io} */ +	hose->current_busno = hose->first_busno; + +	pci->pedr = 0xffffffff;		/* Clear any errors */ +	pci->peer = ~0x20140;		/* Enable All Error Interupts except +					 * - Master abort (pci) +					 * - Master PERR (pci) +					 * - ICCA (PCIe) +					 */ +	pci_hose_read_config_dword (hose, dev, PCI_DCR, &temp32); +	temp32 |= 0xf000e;		/* set URR, FER, NFER (but not CER) */ +	pci_hose_write_config_dword(hose, dev, PCI_DCR, temp32); + +	pci_hose_read_config_byte (hose, dev, PCI_HEADER_TYPE, &temp8); +	bridge = temp8 & PCI_HEADER_TYPE_BRIDGE; /* Bridge, such as pcie */ + +	if ( bridge ) { + +		pci_hose_read_config_word(hose, dev, PCI_LTSSM, <ssm); +		enabled = ltssm >= PCI_LTSSM_L0; + +		if (!enabled) { +			debug("....PCIE link error.  Skipping scan." +			      "LTSSM=0x%02x\n", ltssm); +			hose->last_busno = hose->first_busno; +			return; +		} + +		pci->pme_msg_det = 0xffffffff; +		pci->pme_msg_int_en = 0xffffffff; +#ifdef DEBUG +		pci_hose_read_config_word(hose, dev, PCI_LSR, &temp16); +		neg_link_w = (temp16 & 0x3f0 ) >> 4; +		printf("...PCIE LTSSM=0x%x, Negotiated link width=%d\n", +		      ltssm, neg_link_w); +#endif +		hose->current_busno++; /* Start scan with secondary */ +		pciauto_prescan_setup_bridge(hose, dev, hose->current_busno); + +	} + +	/* Use generic setup_device to initialize standard pci regs, +	 * but do not allocate any windows since any BAR found (such +	 * as PCSRBAR) is not in this cpu's memory space. +	 */ + +	pciauto_setup_device(hose, dev, 0, hose->pci_mem, +			     hose->pci_prefetch, hose->pci_io); + +	if (inbound) { +		pci_hose_read_config_word(hose, dev, PCI_COMMAND, &temp16); +		pci_hose_write_config_word(hose, dev, PCI_COMMAND, +					   temp16 | PCI_COMMAND_MEMORY); +	} + +#ifndef CONFIG_PCI_NOSCAN +	printf ("               Scanning PCI bus %02x\n", hose->current_busno); +	hose->last_busno = pci_hose_scan_bus(hose,hose->current_busno); + +	if ( bridge ) { /* update limit regs and subordinate busno */ +		pciauto_postscan_setup_bridge(hose, dev, hose->last_busno); +	} +#else +	hose->last_busno = hose->current_busno; +#endif + +	/* Clear all error indications */ + +	pci->pme_msg_det = 0xffffffff; +	pci->pedr = 0xffffffff; + +	pci_hose_read_config_word (hose, dev, PCI_DSR, &temp16); +	if (temp16) { +		pci_hose_write_config_word(hose, dev, +					PCI_DSR, 0xffff); +	} + +	pci_hose_read_config_word (hose, dev, PCI_SEC_STATUS, &temp16); +	if (temp16) { +		pci_hose_write_config_word(hose, dev, PCI_SEC_STATUS, 0xffff); +	} +} + +#endif /* CONFIG_FSL_PCI */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c new file mode 100644 index 000000000..50ca6b0ba --- /dev/null +++ b/drivers/pci/pci.c @@ -0,0 +1,526 @@ +/* + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * (C) Copyright 2002, 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * PCI routines + */ + +#include <common.h> + +#ifdef CONFIG_PCI + +#include <command.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> + +#define PCI_HOSE_OP(rw, size, type)					\ +int pci_hose_##rw##_config_##size(struct pci_controller *hose, 		\ +				  pci_dev_t dev, 			\ +				  int offset, type value)		\ +{									\ +	return hose->rw##_##size(hose, dev, offset, value);		\ +} + +PCI_HOSE_OP(read, byte, u8 *) +PCI_HOSE_OP(read, word, u16 *) +PCI_HOSE_OP(read, dword, u32 *) +PCI_HOSE_OP(write, byte, u8) +PCI_HOSE_OP(write, word, u16) +PCI_HOSE_OP(write, dword, u32) + +#ifndef CONFIG_IXP425 +#define PCI_OP(rw, size, type, error_code)				\ +int pci_##rw##_config_##size(pci_dev_t dev, int offset, type value)	\ +{									\ +	struct pci_controller *hose = pci_bus_to_hose(PCI_BUS(dev));	\ +									\ +	if (!hose)							\ +	{								\ +		error_code;						\ +		return -1;						\ +	}								\ +									\ +	return pci_hose_##rw##_config_##size(hose, dev, offset, value);	\ +} + +PCI_OP(read, byte, u8 *, *value = 0xff) +PCI_OP(read, word, u16 *, *value = 0xffff) +PCI_OP(read, dword, u32 *, *value = 0xffffffff) +PCI_OP(write, byte, u8, ) +PCI_OP(write, word, u16, ) +PCI_OP(write, dword, u32, ) +#endif	/* CONFIG_IXP425 */ + +#define PCI_READ_VIA_DWORD_OP(size, type, off_mask)			\ +int pci_hose_read_config_##size##_via_dword(struct pci_controller *hose,\ +					pci_dev_t dev, 			\ +					int offset, type val)		\ +{									\ +	u32 val32;							\ +									\ +	if (pci_hose_read_config_dword(hose, dev, offset & 0xfc, &val32) < 0) {	\ +		*val = -1;						\ +		return -1;						\ +	}								\ +									\ +	*val = (val32 >> ((offset & (int)off_mask) * 8));		\ +									\ +	return 0;							\ +} + +#define PCI_WRITE_VIA_DWORD_OP(size, type, off_mask, val_mask)		\ +int pci_hose_write_config_##size##_via_dword(struct pci_controller *hose,\ +					     pci_dev_t dev, 		\ +					     int offset, type val)	\ +{									\ +	u32 val32, mask, ldata, shift;					\ +									\ +	if (pci_hose_read_config_dword(hose, dev, offset & 0xfc, &val32) < 0)\ +		return -1;						\ +									\ +	shift = ((offset & (int)off_mask) * 8);				\ +	ldata = (((unsigned long)val) & val_mask) << shift;		\ +	mask = val_mask << shift;					\ +	val32 = (val32 & ~mask) | ldata;				\ +									\ +	if (pci_hose_write_config_dword(hose, dev, offset & 0xfc, val32) < 0)\ +		return -1;						\ +									\ +	return 0;							\ +} + +PCI_READ_VIA_DWORD_OP(byte, u8 *, 0x03) +PCI_READ_VIA_DWORD_OP(word, u16 *, 0x02) +PCI_WRITE_VIA_DWORD_OP(byte, u8, 0x03, 0x000000ff) +PCI_WRITE_VIA_DWORD_OP(word, u16, 0x02, 0x0000ffff) + +/* + * + */ + +static struct pci_controller* hose_head = NULL; + +void pci_register_hose(struct pci_controller* hose) +{ +	struct pci_controller **phose = &hose_head; + +	while(*phose) +		phose = &(*phose)->next; + +	hose->next = NULL; + +	*phose = hose; +} + +struct pci_controller *pci_bus_to_hose (int bus) +{ +	struct pci_controller *hose; + +	for (hose = hose_head; hose; hose = hose->next) +		if (bus >= hose->first_busno && bus <= hose->last_busno) +			return hose; + +	printf("pci_bus_to_hose() failed\n"); +	return NULL; +} + +#ifndef CONFIG_IXP425 +pci_dev_t pci_find_devices(struct pci_device_id *ids, int index) +{ +	struct pci_controller * hose; +	u16 vendor, device; +	u8 header_type; +	pci_dev_t bdf; +	int i, bus, found_multi = 0; + +	for (hose = hose_head; hose; hose = hose->next) +	{ +#ifdef CFG_SCSI_SCAN_BUS_REVERSE +		for (bus = hose->last_busno; bus >= hose->first_busno; bus--) +#else +		for (bus = hose->first_busno; bus <= hose->last_busno; bus++) +#endif +			for (bdf = PCI_BDF(bus,0,0); +#if defined(CONFIG_ELPPC) || defined(CONFIG_PPMC7XX) +			     bdf < PCI_BDF(bus,PCI_MAX_PCI_DEVICES-1,PCI_MAX_PCI_FUNCTIONS-1); +#else +			     bdf < PCI_BDF(bus+1,0,0); +#endif +			     bdf += PCI_BDF(0,0,1)) +			{ +				if (!PCI_FUNC(bdf)) { +					pci_read_config_byte(bdf, +							     PCI_HEADER_TYPE, +							     &header_type); + +					found_multi = header_type & 0x80; +				} else { +					if (!found_multi) +						continue; +				} + +				pci_read_config_word(bdf, +						     PCI_VENDOR_ID, +						     &vendor); +				pci_read_config_word(bdf, +						     PCI_DEVICE_ID, +						     &device); + +				for (i=0; ids[i].vendor != 0; i++) +					if (vendor == ids[i].vendor && +					    device == ids[i].device) +					{ +						if (index <= 0) +							return bdf; + +						index--; +					} +			} +	} + +	return (-1); +} +#endif	/* CONFIG_IXP425 */ + +pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index) +{ +	static struct pci_device_id ids[2] = {{}, {0, 0}}; + +	ids[0].vendor = vendor; +	ids[0].device = device; + +	return pci_find_devices(ids, index); +} + +/* + * + */ + +unsigned long pci_hose_phys_to_bus (struct pci_controller *hose, +				    unsigned long phys_addr, +				    unsigned long flags) +{ +	struct pci_region *res; +	unsigned long bus_addr; +	int i; + +	if (!hose) { +		printf ("pci_hose_phys_to_bus: %s\n", "invalid hose"); +		goto Done; +	} + +	for (i = 0; i < hose->region_count; i++) { +		res = &hose->regions[i]; + +		if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0) +			continue; + +		bus_addr = phys_addr - res->phys_start + res->bus_start; + +		if (bus_addr >= res->bus_start && +			bus_addr < res->bus_start + res->size) { +			return bus_addr; +		} +	} + +	printf ("pci_hose_phys_to_bus: %s\n", "invalid physical address"); + +Done: +	return 0; +} + +unsigned long pci_hose_bus_to_phys(struct pci_controller* hose, +				   unsigned long bus_addr, +				   unsigned long flags) +{ +	struct pci_region *res; +	int i; + +	if (!hose) { +		printf ("pci_hose_bus_to_phys: %s\n", "invalid hose"); +		goto Done; +	} + +	for (i = 0; i < hose->region_count; i++) { +		res = &hose->regions[i]; + +		if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0) +			continue; + +		if (bus_addr >= res->bus_start && +			bus_addr < res->bus_start + res->size) { +			return bus_addr - res->bus_start + res->phys_start; +		} +	} + +	printf ("pci_hose_bus_to_phys: %s\n", "invalid physical address"); + +Done: +	return 0; +} + +/* + * + */ + +int pci_hose_config_device(struct pci_controller *hose, +			   pci_dev_t dev, +			   unsigned long io, +			   unsigned long mem, +			   unsigned long command) +{ +	unsigned int bar_response, bar_size, bar_value, old_command; +	unsigned char pin; +	int bar, found_mem64; + +	debug ("PCI Config: I/O=0x%lx, Memory=0x%lx, Command=0x%lx\n", +		io, mem, command); + +	pci_hose_write_config_dword (hose, dev, PCI_COMMAND, 0); + +	for (bar = PCI_BASE_ADDRESS_0; bar < PCI_BASE_ADDRESS_5; bar += 4) { +		pci_hose_write_config_dword (hose, dev, bar, 0xffffffff); +		pci_hose_read_config_dword (hose, dev, bar, &bar_response); + +		if (!bar_response) +			continue; + +		found_mem64 = 0; + +		/* Check the BAR type and set our address mask */ +		if (bar_response & PCI_BASE_ADDRESS_SPACE) { +			bar_size = ~(bar_response & PCI_BASE_ADDRESS_IO_MASK) + 1; +			/* round up region base address to a multiple of size */ +			io = ((io - 1) | (bar_size - 1)) + 1; +			bar_value = io; +			/* compute new region base address */ +			io = io + bar_size; +		} else { +			if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == +				PCI_BASE_ADDRESS_MEM_TYPE_64) +				found_mem64 = 1; + +			bar_size = ~(bar_response & PCI_BASE_ADDRESS_MEM_MASK) + 1; + +			/* round up region base address to multiple of size */ +			mem = ((mem - 1) | (bar_size - 1)) + 1; +			bar_value = mem; +			/* compute new region base address */ +			mem = mem + bar_size; +		} + +		/* Write it out and update our limit */ +		pci_hose_write_config_dword (hose, dev, bar, bar_value); + +		if (found_mem64) { +			bar += 4; +			pci_hose_write_config_dword (hose, dev, bar, 0x00000000); +		} +	} + +	/* Configure Cache Line Size Register */ +	pci_hose_write_config_byte (hose, dev, PCI_CACHE_LINE_SIZE, 0x08); + +	/* Configure Latency Timer */ +	pci_hose_write_config_byte (hose, dev, PCI_LATENCY_TIMER, 0x80); + +	/* Disable interrupt line, if device says it wants to use interrupts */ +	pci_hose_read_config_byte (hose, dev, PCI_INTERRUPT_PIN, &pin); +	if (pin != 0) { +		pci_hose_write_config_byte (hose, dev, PCI_INTERRUPT_LINE, 0xff); +	} + +	pci_hose_read_config_dword (hose, dev, PCI_COMMAND, &old_command); +	pci_hose_write_config_dword (hose, dev, PCI_COMMAND, +				     (old_command & 0xffff0000) | command); + +	return 0; +} + +/* + * + */ + +struct pci_config_table *pci_find_config(struct pci_controller *hose, +					 unsigned short class, +					 unsigned int vendor, +					 unsigned int device, +					 unsigned int bus, +					 unsigned int dev, +					 unsigned int func) +{ +	struct pci_config_table *table; + +	for (table = hose->config_table; table && table->vendor; table++) { +		if ((table->vendor == PCI_ANY_ID || table->vendor == vendor) && +		    (table->device == PCI_ANY_ID || table->device == device) && +		    (table->class  == PCI_ANY_ID || table->class  == class)  && +		    (table->bus    == PCI_ANY_ID || table->bus    == bus)    && +		    (table->dev    == PCI_ANY_ID || table->dev    == dev)    && +		    (table->func   == PCI_ANY_ID || table->func   == func)) { +			return table; +		} +	} + +	return NULL; +} + +void pci_cfgfunc_config_device(struct pci_controller *hose, +			       pci_dev_t dev, +			       struct pci_config_table *entry) +{ +	pci_hose_config_device(hose, dev, entry->priv[0], entry->priv[1], entry->priv[2]); +} + +void pci_cfgfunc_do_nothing(struct pci_controller *hose, +			    pci_dev_t dev, struct pci_config_table *entry) +{ +} + +/* + * + */ + +/* HJF: Changed this to return int. I think this is required + * to get the correct result when scanning bridges + */ +extern int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev); +extern void pciauto_config_init(struct pci_controller *hose); + +int pci_hose_scan_bus(struct pci_controller *hose, int bus) +{ +	unsigned int sub_bus, found_multi=0; +	unsigned short vendor, device, class; +	unsigned char header_type; +	struct pci_config_table *cfg; +	pci_dev_t dev; + +	sub_bus = bus; + +	for (dev =  PCI_BDF(bus,0,0); +	     dev <  PCI_BDF(bus,PCI_MAX_PCI_DEVICES-1,PCI_MAX_PCI_FUNCTIONS-1); +	     dev += PCI_BDF(0,0,1)) +	{ +		/* Skip our host bridge */ +		if ( dev == PCI_BDF(hose->first_busno,0,0) ) { +#if defined(CONFIG_PCI_CONFIG_HOST_BRIDGE)              /* don't skip host bridge */ +			/* +			 * Only skip hostbridge configuration if "pciconfighost" is not set +			 */ +			if (getenv("pciconfighost") == NULL) { +				continue; /* Skip our host bridge */ +			} +#else +			continue; /* Skip our host bridge */ +#endif +		} + +		if (PCI_FUNC(dev) && !found_multi) +			continue; + +		pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE, &header_type); + +		pci_hose_read_config_word(hose, dev, PCI_VENDOR_ID, &vendor); + +		if (vendor != 0xffff && vendor != 0x0000) { + +			if (!PCI_FUNC(dev)) +				found_multi = header_type & 0x80; + +			debug ("PCI Scan: Found Bus %d, Device %d, Function %d\n", +				PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev) ); + +			pci_hose_read_config_word(hose, dev, PCI_DEVICE_ID, &device); +			pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class); + +			cfg = pci_find_config(hose, class, vendor, device, +					      PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev)); +			if (cfg) { +				cfg->config_device(hose, dev, cfg); +				sub_bus = max(sub_bus, hose->current_busno); +#ifdef CONFIG_PCI_PNP +			} else { +				int n = pciauto_config_device(hose, dev); + +				sub_bus = max(sub_bus, n); +#endif +			} +			if (hose->fixup_irq) +				hose->fixup_irq(hose, dev); + +#ifdef CONFIG_PCI_SCAN_SHOW +			/* Skip our host bridge */ +			if ( dev != PCI_BDF(hose->first_busno,0,0) ) { +			    unsigned char int_line; + +			    pci_hose_read_config_byte(hose, dev, PCI_INTERRUPT_LINE, +						      &int_line); +			    printf("        %02x  %02x  %04x  %04x  %04x  %02x\n", +				   PCI_BUS(dev), PCI_DEV(dev), vendor, device, class, +				   int_line); +			} +#endif +		} +	} + +	return sub_bus; +} + +int pci_hose_scan(struct pci_controller *hose) +{ +	/* Start scan at current_busno. +	 * PCIe will start scan at first_busno+1. +	 */ +	/* For legacy support, ensure current>=first */ +	if (hose->first_busno > hose->current_busno) +		hose->current_busno = hose->first_busno; +#ifdef CONFIG_PCI_PNP +	pciauto_config_init(hose); +#endif +	return pci_hose_scan_bus(hose, hose->current_busno); +} + +void pci_init(void) +{ +#if defined(CONFIG_PCI_BOOTDELAY) +	char *s; +	int i; + +	/* wait "pcidelay" ms (if defined)... */ +	s = getenv ("pcidelay"); +	if (s) { +		int val = simple_strtoul (s, NULL, 10); +		for (i=0; i<val; i++) +			udelay (1000); +	} +#endif /* CONFIG_PCI_BOOTDELAY */ + +	/* now call board specific pci_init()... */ +	pci_init_board(); +} + +#endif /* CONFIG_PCI */ diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c new file mode 100644 index 000000000..acfda83ba --- /dev/null +++ b/drivers/pci/pci_auto.c @@ -0,0 +1,412 @@ +/* + * arch/ppc/kernel/pci_auto.c + * + * PCI autoconfiguration library + * + * Author: Matt Porter <mporter@mvista.com> + * + * Copyright 2000 MontaVista Software 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. + */ + +#include <common.h> + +#ifdef CONFIG_PCI + +#include <pci.h> + +#undef DEBUG +#ifdef DEBUG +#define DEBUGF(x...) printf(x) +#else +#define DEBUGF(x...) +#endif /* DEBUG */ + +#define	PCIAUTO_IDE_MODE_MASK		0x05 + +/* the user can define CFG_PCI_CACHE_LINE_SIZE to avoid problems */ +#ifndef CFG_PCI_CACHE_LINE_SIZE +#define CFG_PCI_CACHE_LINE_SIZE	8 +#endif + +/* + * + */ + +void pciauto_region_init(struct pci_region* res) +{ +	/* +	 * Avoid allocating PCI resources from address 0 -- this is illegal +	 * according to PCI 2.1 and moreover, this is known to cause Linux IDE +	 * drivers to fail. Use a reasonable starting value of 0x1000 instead. +	 */ +	res->bus_lower = res->bus_start ? res->bus_start : 0x1000; +} + +void pciauto_region_align(struct pci_region *res, unsigned long size) +{ +	res->bus_lower = ((res->bus_lower - 1) | (size - 1)) + 1; +} + +int pciauto_region_allocate(struct pci_region* res, unsigned int size, unsigned int *bar) +{ +	unsigned long addr; + +	if (!res) { +		DEBUGF("No resource"); +		goto error; +	} + +	addr = ((res->bus_lower - 1) | (size - 1)) + 1; + +	if (addr - res->bus_start + size > res->size) { +		DEBUGF("No room in resource"); +		goto error; +	} + +	res->bus_lower = addr + size; + +	DEBUGF("address=0x%lx bus_lower=%x", addr, res->bus_lower); + +	*bar = addr; +	return 0; + + error: +	*bar = 0xffffffff; +	return -1; +} + +/* + * + */ + +void pciauto_setup_device(struct pci_controller *hose, +			  pci_dev_t dev, int bars_num, +			  struct pci_region *mem, +			  struct pci_region *prefetch, +			  struct pci_region *io) +{ +	unsigned int bar_value, bar_response, bar_size; +	unsigned int cmdstat = 0; +	struct pci_region *bar_res; +	int bar, bar_nr = 0; +	int found_mem64 = 0; + +	pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &cmdstat); +	cmdstat = (cmdstat & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) | PCI_COMMAND_MASTER; + +	for (bar = PCI_BASE_ADDRESS_0; bar < PCI_BASE_ADDRESS_0 + (bars_num*4); bar += 4) { +		/* Tickle the BAR and get the response */ +		pci_hose_write_config_dword(hose, dev, bar, 0xffffffff); +		pci_hose_read_config_dword(hose, dev, bar, &bar_response); + +		/* If BAR is not implemented go to the next BAR */ +		if (!bar_response) +			continue; + +		found_mem64 = 0; + +		/* Check the BAR type and set our address mask */ +		if (bar_response & PCI_BASE_ADDRESS_SPACE) { +			bar_size = ((~(bar_response & PCI_BASE_ADDRESS_IO_MASK)) +				   & 0xffff) + 1; +			bar_res = io; + +			DEBUGF("PCI Autoconfig: BAR %d, I/O, size=0x%x, ", bar_nr, bar_size); +		} else { +			if ( (bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == +			     PCI_BASE_ADDRESS_MEM_TYPE_64) +				found_mem64 = 1; + +			bar_size = ~(bar_response & PCI_BASE_ADDRESS_MEM_MASK) + 1; +			if (prefetch && (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH)) +				bar_res = prefetch; +			else +				bar_res = mem; + +			DEBUGF("PCI Autoconfig: BAR %d, Mem, size=0x%x, ", bar_nr, bar_size); +		} + +		if (pciauto_region_allocate(bar_res, bar_size, &bar_value) == 0) { +			/* Write it out and update our limit */ +			pci_hose_write_config_dword(hose, dev, bar, bar_value); + +			/* +			 * If we are a 64-bit decoder then increment to the +			 * upper 32 bits of the bar and force it to locate +			 * in the lower 4GB of memory. +			 */ +			if (found_mem64) { +				bar += 4; +				pci_hose_write_config_dword(hose, dev, bar, 0x00000000); +			} + +			cmdstat |= (bar_response & PCI_BASE_ADDRESS_SPACE) ? +				PCI_COMMAND_IO : PCI_COMMAND_MEMORY; +		} + +		DEBUGF("\n"); + +		bar_nr++; +	} + +	pci_hose_write_config_dword(hose, dev, PCI_COMMAND, cmdstat); +	pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, +		CFG_PCI_CACHE_LINE_SIZE); +	pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80); +} + +void pciauto_prescan_setup_bridge(struct pci_controller *hose, +					 pci_dev_t dev, int sub_bus) +{ +	struct pci_region *pci_mem = hose->pci_mem; +	struct pci_region *pci_prefetch = hose->pci_prefetch; +	struct pci_region *pci_io = hose->pci_io; +	unsigned int cmdstat; + +	pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &cmdstat); + +	/* Configure bus number registers */ +	pci_hose_write_config_byte(hose, dev, PCI_PRIMARY_BUS, +				   PCI_BUS(dev) - hose->first_busno); +	pci_hose_write_config_byte(hose, dev, PCI_SECONDARY_BUS, +				   sub_bus - hose->first_busno); +	pci_hose_write_config_byte(hose, dev, PCI_SUBORDINATE_BUS, 0xff); + +	if (pci_mem) { +		/* Round memory allocator to 1MB boundary */ +		pciauto_region_align(pci_mem, 0x100000); + +		/* Set up memory and I/O filter limits, assume 32-bit I/O space */ +		pci_hose_write_config_word(hose, dev, PCI_MEMORY_BASE, +					(pci_mem->bus_lower & 0xfff00000) >> 16); + +		cmdstat |= PCI_COMMAND_MEMORY; +	} + +	if (pci_prefetch) { +		/* Round memory allocator to 1MB boundary */ +		pciauto_region_align(pci_prefetch, 0x100000); + +		/* Set up memory and I/O filter limits, assume 32-bit I/O space */ +		pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_BASE, +					(pci_prefetch->bus_lower & 0xfff00000) >> 16); + +		cmdstat |= PCI_COMMAND_MEMORY; +	} else { +		/* We don't support prefetchable memory for now, so disable */ +		pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_BASE, 0x1000); +		pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_LIMIT, 0x0); +	} + +	if (pci_io) { +		/* Round I/O allocator to 4KB boundary */ +		pciauto_region_align(pci_io, 0x1000); + +		pci_hose_write_config_byte(hose, dev, PCI_IO_BASE, +					(pci_io->bus_lower & 0x0000f000) >> 8); +		pci_hose_write_config_word(hose, dev, PCI_IO_BASE_UPPER16, +					(pci_io->bus_lower & 0xffff0000) >> 16); + +		cmdstat |= PCI_COMMAND_IO; +	} + +	/* Enable memory and I/O accesses, enable bus master */ +	pci_hose_write_config_dword(hose, dev, PCI_COMMAND, cmdstat | PCI_COMMAND_MASTER); +} + +void pciauto_postscan_setup_bridge(struct pci_controller *hose, +					  pci_dev_t dev, int sub_bus) +{ +	struct pci_region *pci_mem = hose->pci_mem; +	struct pci_region *pci_prefetch = hose->pci_prefetch; +	struct pci_region *pci_io = hose->pci_io; + +	/* Configure bus number registers */ +	pci_hose_write_config_byte(hose, dev, PCI_SUBORDINATE_BUS, +				   sub_bus - hose->first_busno); + +	if (pci_mem) { +		/* Round memory allocator to 1MB boundary */ +		pciauto_region_align(pci_mem, 0x100000); + +		pci_hose_write_config_word(hose, dev, PCI_MEMORY_LIMIT, +					(pci_mem->bus_lower-1) >> 16); +	} + +	if (pci_prefetch) { +		/* Round memory allocator to 1MB boundary */ +		pciauto_region_align(pci_prefetch, 0x100000); + +		pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_LIMIT, +					(pci_prefetch->bus_lower-1) >> 16); +	} + +	if (pci_io) { +		/* Round I/O allocator to 4KB boundary */ +		pciauto_region_align(pci_io, 0x1000); + +		pci_hose_write_config_byte(hose, dev, PCI_IO_LIMIT, +					((pci_io->bus_lower-1) & 0x0000f000) >> 8); +		pci_hose_write_config_word(hose, dev, PCI_IO_LIMIT_UPPER16, +					((pci_io->bus_lower-1) & 0xffff0000) >> 16); +	} +} + +/* + * + */ + +void pciauto_config_init(struct pci_controller *hose) +{ +	int i; + +	hose->pci_io = hose->pci_mem = NULL; + +	for (i=0; i<hose->region_count; i++) { +		switch(hose->regions[i].flags) { +		case PCI_REGION_IO: +			if (!hose->pci_io || +			    hose->pci_io->size < hose->regions[i].size) +				hose->pci_io = hose->regions + i; +			break; +		case PCI_REGION_MEM: +			if (!hose->pci_mem || +			    hose->pci_mem->size < hose->regions[i].size) +				hose->pci_mem = hose->regions + i; +			break; +		case (PCI_REGION_MEM | PCI_REGION_PREFETCH): +			if (!hose->pci_prefetch || +			    hose->pci_prefetch->size < hose->regions[i].size) +				hose->pci_prefetch = hose->regions + i; +			break; +		} +	} + + +	if (hose->pci_mem) { +		pciauto_region_init(hose->pci_mem); + +		DEBUGF("PCI Autoconfig: Bus Memory region: [%lx-%lx],\n" +		       "\t\tPhysical Memory [%x-%x]\n", +		    hose->pci_mem->bus_start, +		    hose->pci_mem->bus_start + hose->pci_mem->size - 1, +		    hose->pci_mem->phys_start, +		    hose->pci_mem->phys_start + hose->pci_mem->size - 1); +	} + +	if (hose->pci_prefetch) { +		pciauto_region_init(hose->pci_prefetch); + +		DEBUGF("PCI Autoconfig: Bus Prefetchable Mem: [%lx-%lx],\n" +		       "\t\tPhysical Memory [%x-%x]\n", +		    hose->pci_prefetch->bus_start, +		    hose->pci_prefetch->bus_start + hose->pci_prefetch->size - 1, +		    hose->pci_prefetch->phys_start, +		    hose->pci_prefetch->phys_start + +				hose->pci_prefetch->size - 1); +	} + +	if (hose->pci_io) { +		pciauto_region_init(hose->pci_io); + +		DEBUGF("PCI Autoconfig: Bus I/O region: [%lx-%lx],\n" +		       "\t\tPhysical Memory: [%x-%x]\n", +		    hose->pci_io->bus_start, +		    hose->pci_io->bus_start + hose->pci_io->size - 1, +		    hose->pci_io->phys_start, +		    hose->pci_io->phys_start + hose->pci_io->size - 1); + +	} +} + +/* HJF: Changed this to return int. I think this is required + * to get the correct result when scanning bridges + */ +int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev) +{ +	unsigned int sub_bus = PCI_BUS(dev); +	unsigned short class; +	unsigned char prg_iface; +	int n; + +	pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class); + +	switch(class) { +	case PCI_CLASS_PROCESSOR_POWERPC: /* an agent or end-point */ +		DEBUGF("PCI AutoConfig: Found PowerPC device\n"); +		pciauto_setup_device(hose, dev, 6, hose->pci_mem, +				     hose->pci_prefetch, hose->pci_io); +		break; + +	case PCI_CLASS_BRIDGE_PCI: +		hose->current_busno++; +		pciauto_setup_device(hose, dev, 2, hose->pci_mem, hose->pci_prefetch, hose->pci_io); + +		DEBUGF("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_DEV(dev)); + +		/* Passing in current_busno allows for sibling P2P bridges */ +		pciauto_prescan_setup_bridge(hose, dev, hose->current_busno); +		/* +		 * need to figure out if this is a subordinate bridge on the bus +		 * to be able to properly set the pri/sec/sub bridge registers. +		 */ +		n = pci_hose_scan_bus(hose, hose->current_busno); + +		/* figure out the deepest we've gone for this leg */ +		sub_bus = max(n, sub_bus); +		pciauto_postscan_setup_bridge(hose, dev, sub_bus); + +		sub_bus = hose->current_busno; +		break; + +	case PCI_CLASS_STORAGE_IDE: +		pci_hose_read_config_byte(hose, dev, PCI_CLASS_PROG, &prg_iface); +		if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) { +			DEBUGF("PCI Autoconfig: Skipping legacy mode IDE controller\n"); +			return sub_bus; +		} + +		pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_prefetch, hose->pci_io); +		break; + +	case PCI_CLASS_BRIDGE_CARDBUS: +		/* just do a minimal setup of the bridge, let the OS take care of the rest */ +		pciauto_setup_device(hose, dev, 0, hose->pci_mem, hose->pci_prefetch, hose->pci_io); + +		DEBUGF("PCI Autoconfig: Found P2CardBus bridge, device %d\n", PCI_DEV(dev)); + +		hose->current_busno++; +		break; + +#ifdef CONFIG_MPC5200 +	case PCI_CLASS_BRIDGE_OTHER: +		DEBUGF("PCI Autoconfig: Skipping bridge device %d\n", +		       PCI_DEV(dev)); +		break; +#endif +#ifdef CONFIG_MPC834X +	case PCI_CLASS_BRIDGE_OTHER: +		/* +		 * The host/PCI bridge 1 seems broken in 8349 - it presents +		 * itself as 'PCI_CLASS_BRIDGE_OTHER' and appears as an _agent_ +		 * device claiming resources io/mem/irq.. we only allow for +		 * the PIMMR window to be allocated (BAR0 - 1MB size) +		 */ +		DEBUGF("PCI Autoconfig: Broken bridge found, only minimal config\n"); +		pciauto_setup_device(hose, dev, 0, hose->pci_mem, hose->pci_prefetch, hose->pci_io); +		break; +#endif +	default: +		pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_prefetch, hose->pci_io); +		break; +	} + +	return sub_bus; +} + +#endif /* CONFIG_PCI */ diff --git a/drivers/pci/pci_indirect.c b/drivers/pci/pci_indirect.c new file mode 100644 index 000000000..a8220fb41 --- /dev/null +++ b/drivers/pci/pci_indirect.c @@ -0,0 +1,138 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (C) 1998 Gabriel Paubert. + * + * 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. + */ + +#include <common.h> + +#ifdef CONFIG_PCI +#if (!defined(__I386__) && !defined(CONFIG_IXDP425)) + +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> + +#define cfg_read(val, addr, type, op)	*val = op((type)(addr)) +#define cfg_write(val, addr, type, op)	op((type *)(addr), (val)) + +#ifdef CONFIG_IXP425 +extern unsigned char	in_8 (volatile unsigned *addr); +extern unsigned short	in_le16 (volatile unsigned *addr); +extern unsigned		in_le32 (volatile unsigned *addr); +extern void		out_8 (volatile unsigned *addr, char val); +extern void		out_le16 (volatile unsigned *addr, unsigned short val); +extern void		out_le32 (volatile unsigned *addr, unsigned int val); +#endif	/* CONFIG_IXP425 */ + +#if defined(CONFIG_MPC8260) +#define INDIRECT_PCI_OP(rw, size, type, op, mask)			 \ +static int								 \ +indirect_##rw##_config_##size(struct pci_controller *hose, 		 \ +			      pci_dev_t dev, int offset, type val)	 \ +{									 \ +	u32 b, d,f;							 \ +	b = PCI_BUS(dev); d = PCI_DEV(dev); f = PCI_FUNC(dev);		 \ +	b = b - hose->first_busno;					 \ +	dev = PCI_BDF(b, d, f);						 \ +	out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); 	 \ +	sync();								 \ +	cfg_##rw(val, hose->cfg_data + (offset & mask), type, op);	 \ +	return 0;    					 		 \ +} +#elif defined(CONFIG_E500) || defined(CONFIG_MPC86xx) +#define INDIRECT_PCI_OP(rw, size, type, op, mask)                        \ +static int                                                               \ +indirect_##rw##_config_##size(struct pci_controller *hose,               \ +			      pci_dev_t dev, int offset, type val)       \ +{                                                                        \ +	u32 b, d,f;							 \ +	b = PCI_BUS(dev); d = PCI_DEV(dev); f = PCI_FUNC(dev);		 \ +	b = b - hose->first_busno;					 \ +	dev = PCI_BDF(b, d, f);						 \ +	*(hose->cfg_addr) = dev | (offset & 0xfc) | ((offset & 0xf00) << 16) | 0x80000000; \ +	sync();                                                          \ +	cfg_##rw(val, hose->cfg_data + (offset & mask), type, op);       \ +	return 0;                                                        \ +} +#elif defined(CONFIG_440GX) || defined(CONFIG_440EP) || defined(CONFIG_440GR) || defined(CONFIG_440SPE) +#define INDIRECT_PCI_OP(rw, size, type, op, mask)			 \ +static int								 \ +indirect_##rw##_config_##size(struct pci_controller *hose, 		 \ +			      pci_dev_t dev, int offset, type val)	 \ +{									 \ +	u32 b, d,f;							 \ +	b = PCI_BUS(dev); d = PCI_DEV(dev); f = PCI_FUNC(dev);		 \ +	b = b - hose->first_busno;					 \ +	dev = PCI_BDF(b, d, f);						 \ +	if (PCI_BUS(dev) > 0)                                            \ +		out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000001); \ +	else                                                             \ +		out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); \ +	cfg_##rw(val, hose->cfg_data + (offset & mask), type, op);	 \ +	return 0;    					 		 \ +} +#else +#define INDIRECT_PCI_OP(rw, size, type, op, mask)			 \ +static int								 \ +indirect_##rw##_config_##size(struct pci_controller *hose, 		 \ +			      pci_dev_t dev, int offset, type val)	 \ +{									 \ +	u32 b, d,f;							 \ +	b = PCI_BUS(dev); d = PCI_DEV(dev); f = PCI_FUNC(dev);		 \ +	b = b - hose->first_busno;					 \ +	dev = PCI_BDF(b, d, f);						 \ +	out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); 	 \ +	cfg_##rw(val, hose->cfg_data + (offset & mask), type, op);	 \ +	return 0;    					 		 \ +} +#endif + +#define INDIRECT_PCI_OP_ERRATA6(rw, size, type, op, mask)		 \ +static int								 \ +indirect_##rw##_config_##size(struct pci_controller *hose, 		 \ +			      pci_dev_t dev, int offset, type val)	 \ +{									 \ +	unsigned int msr = mfmsr();					 \ +	mtmsr(msr & ~(MSR_EE | MSR_CE));				 \ +	out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); 	 \ +	cfg_##rw(val, hose->cfg_data + (offset & mask), type, op);	 \ +	out_le32(hose->cfg_addr, 0x00000000); 				 \ +	mtmsr(msr);							 \ +	return 0;    					 		 \ +} + +INDIRECT_PCI_OP(read, byte, u8 *, in_8, 3) +INDIRECT_PCI_OP(read, word, u16 *, in_le16, 2) +INDIRECT_PCI_OP(read, dword, u32 *, in_le32, 0) +#ifdef CONFIG_405GP +INDIRECT_PCI_OP_ERRATA6(write, byte, u8, out_8, 3) +INDIRECT_PCI_OP_ERRATA6(write, word, u16, out_le16, 2) +INDIRECT_PCI_OP_ERRATA6(write, dword, u32, out_le32, 0) +#else +INDIRECT_PCI_OP(write, byte, u8, out_8, 3) +INDIRECT_PCI_OP(write, word, u16, out_le16, 2) +INDIRECT_PCI_OP(write, dword, u32, out_le32, 0) +#endif + +void pci_setup_indirect(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) +{ +	pci_set_ops(hose, +		    indirect_read_config_byte, +		    indirect_read_config_word, +		    indirect_read_config_dword, +		    indirect_write_config_byte, +		    indirect_write_config_word, +		    indirect_write_config_dword); + +	hose->cfg_addr = (unsigned int *) cfg_addr; +	hose->cfg_data = (unsigned char *) cfg_data; +} + +#endif	/* !__I386__ && !CONFIG_IXDP425 */ +#endif	/* CONFIG_PCI */ diff --git a/drivers/pci/tsi108_pci.c b/drivers/pci/tsi108_pci.c new file mode 100644 index 000000000..d5f11e42f --- /dev/null +++ b/drivers/pci/tsi108_pci.c @@ -0,0 +1,181 @@ +/* + * (C) Copyright 2004 Tundra Semiconductor Corp. + * Alex Bounine <alexandreb@tundra.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * PCI initialisation for the Tsi108 EMU board. + */ + +#include <config.h> + +#ifdef CONFIG_TSI108_PCI + +#include <common.h> +#include <pci.h> +#include <asm/io.h> +#include <tsi108.h> +#ifdef CONFIG_OF_FLAT_TREE +#include <ft_build.h> +#endif + +struct pci_controller local_hose; + +void tsi108_clear_pci_error (void) +{ +	u32 err_stat, err_addr, pci_stat; + +	/* +	 * Quietly clear errors signalled as result of PCI/X configuration read +	 * requests. +	 */ +	/* Read PB Error Log Registers */ +	err_stat = *(volatile u32 *)(CFG_TSI108_CSR_BASE + +				     TSI108_PB_REG_OFFSET + PB_ERRCS); +	err_addr = *(volatile u32 *)(CFG_TSI108_CSR_BASE + +				     TSI108_PB_REG_OFFSET + PB_AERR); +	if (err_stat & PB_ERRCS_ES) { +		/* Clear PCI/X bus errors if applicable */ +		if ((err_addr & 0xFF000000) == CFG_PCI_CFG_BASE) { +			/* Clear error flag */ +			*(u32 *) (CFG_TSI108_CSR_BASE + +				  TSI108_PB_REG_OFFSET + PB_ERRCS) = +			    PB_ERRCS_ES; + +			/* Clear read error reported in PB_ISR */ +			*(u32 *) (CFG_TSI108_CSR_BASE + +				  TSI108_PB_REG_OFFSET + PB_ISR) = +			    PB_ISR_PBS_RD_ERR; + +		/* Clear errors reported by PCI CSR (Normally Master Abort) */ +			pci_stat = *(volatile u32 *)(CFG_TSI108_CSR_BASE + +						     TSI108_PCI_REG_OFFSET + +						     PCI_CSR); +			*(volatile u32 *)(CFG_TSI108_CSR_BASE + +					  TSI108_PCI_REG_OFFSET + PCI_CSR) = +			    pci_stat; + +			*(volatile u32 *)(CFG_TSI108_CSR_BASE + +					  TSI108_PCI_REG_OFFSET + +					  PCI_IRP_STAT) = PCI_IRP_STAT_P_CSR; +		} +	} + +	return; +} + +unsigned int __get_pci_config_dword (u32 addr) +{ +	unsigned int retval; + +	__asm__ __volatile__ ("       lwbrx %0,0,%1\n" +			     "1:     eieio\n" +			     "2:\n" +			     ".section .fixup,\"ax\"\n" +			     "3:     li %0,-1\n" +			     "       b 2b\n" +			     ".section __ex_table,\"a\"\n" +			     "       .align 2\n" +			     "       .long 1b,3b\n" +			     ".text":"=r"(retval):"r"(addr)); + +	return (retval); +} + +static int tsi108_read_config_dword (struct pci_controller *hose, +				    pci_dev_t dev, int offset, u32 * value) +{ +	dev &= (CFG_PCI_CFG_SIZE - 1); +	dev |= (CFG_PCI_CFG_BASE | (offset & 0xfc)); +	*value = __get_pci_config_dword(dev); +	if (0xFFFFFFFF == *value) +		tsi108_clear_pci_error (); +	return 0; +} + +static int tsi108_write_config_dword (struct pci_controller *hose, +				     pci_dev_t dev, int offset, u32 value) +{ +	dev &= (CFG_PCI_CFG_SIZE - 1); +	dev |= (CFG_PCI_CFG_BASE | (offset & 0xfc)); + +	out_le32 ((volatile unsigned *)dev, value); + +	return 0; +} + +void pci_init_board (void) +{ +	struct pci_controller *hose = (struct pci_controller *)&local_hose; + +	hose->first_busno = 0; +	hose->last_busno = 0xff; + +	pci_set_region (hose->regions + 0, +		       CFG_PCI_MEMORY_BUS, +		       CFG_PCI_MEMORY_PHYS, +		       CFG_PCI_MEMORY_SIZE, PCI_REGION_MEM | PCI_REGION_MEMORY); + +	/* PCI memory space */ +	pci_set_region (hose->regions + 1, +		       CFG_PCI_MEM_BUS, +		       CFG_PCI_MEM_PHYS, CFG_PCI_MEM_SIZE, PCI_REGION_MEM); + +	/* PCI I/O space */ +	pci_set_region (hose->regions + 2, +		       CFG_PCI_IO_BUS, +		       CFG_PCI_IO_PHYS, CFG_PCI_IO_SIZE, PCI_REGION_IO); + +	hose->region_count = 3; + +	pci_set_ops (hose, +		    pci_hose_read_config_byte_via_dword, +		    pci_hose_read_config_word_via_dword, +		    tsi108_read_config_dword, +		    pci_hose_write_config_byte_via_dword, +		    pci_hose_write_config_word_via_dword, +		    tsi108_write_config_dword); + +	pci_register_hose (hose); + +	hose->last_busno = pci_hose_scan (hose); + +	debug ("Done PCI initialization\n"); +	return; +} + +#ifdef CONFIG_OF_FLAT_TREE +void +ft_pci_setup (void *blob, bd_t *bd) +{ +	u32 *p; +	int len; + +	p = (u32 *)ft_get_prop (blob, "/" OF_TSI "/pci@1000/bus-range", &len); +	if (p != NULL) { +		p[0] = local_hose.first_busno; +		p[1] = local_hose.last_busno; +	} + +} +#endif + +#endif	/* CONFIG_TSI108_PCI */ diff --git a/drivers/pci/w83c553f.c b/drivers/pci/w83c553f.c new file mode 100644 index 000000000..5d82ed4dd --- /dev/null +++ b/drivers/pci/w83c553f.c @@ -0,0 +1,226 @@ +/* + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * Initialisation of the PCI-to-ISA bridge and disabling the BIOS + * write protection (for flash) in function 0 of the chip. + * Enabling function 1 (IDE controller of the chip. + */ + +#include <common.h> +#include <config.h> + +#ifdef CFG_WINBOND_83C553 + +#include <asm/io.h> +#include <pci.h> + +#include <w83c553f.h> + +#define out8(addr,val)	do { \ +			out_8((u8*) (addr),(val)); udelay(1); \ +			} while (0) +#define out16(addr,val)	do { \ +			out_be16((u16*) (addr),(val)); udelay(1); \ +			} while (0) + +extern uint ide_bus_offset[CFG_IDE_MAXBUS]; + +void initialise_pic(void); +void initialise_dma(void); + +void initialise_w83c553f(void) +{ +	pci_dev_t devbusfn; +	unsigned char reg8; +	unsigned short reg16; +	unsigned int reg32; + +	devbusfn = pci_find_device(W83C553F_VID, W83C553F_DID, 0); +	if (devbusfn == -1) +	{ +		printf("Error: Cannot find W83C553F controller on any PCI bus."); +		return; +	} + +	pci_read_config_word(devbusfn, PCI_COMMAND, ®16); +	reg16 |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; +	pci_write_config_word(devbusfn, PCI_COMMAND, reg16); + +	pci_read_config_byte(devbusfn, WINBOND_IPADCR, ®8); +	/* 16 MB ISA memory space */ +	reg8 |= (IPADCR_IPATOM4 | IPADCR_IPATOM5 | IPADCR_IPATOM6 | IPADCR_IPATOM7); +	reg8 &= ~IPADCR_MBE512; +	pci_write_config_byte(devbusfn, WINBOND_IPADCR, reg8); + +	pci_read_config_byte(devbusfn, WINBOND_CSCR, ®8); +	/* switch off BIOS write protection */ +	reg8 |= CSCR_UBIOSCSE; +	reg8 &= ~CSCR_BIOSWP; +	pci_write_config_byte(devbusfn, WINBOND_CSCR, reg8); + +	/* +	 * Interrupt routing: +	 *  - IDE  -> IRQ 9/0 +	 *  - INTA -> IRQ 10 +	 *  - INTB -> IRQ 11 +	 *  - INTC -> IRQ 14 +	 *  - INTD -> IRQ 15 +	 */ +	pci_write_config_byte(devbusfn, WINBOND_IDEIRCR, 0x90); +	pci_write_config_word(devbusfn, WINBOND_PCIIRCR, 0xABEF); + +	/* +	 * Read IDE bus offsets from function 1 device. +	 * We must unmask the LSB indicating that ist is an IO address. +	 */ +	devbusfn |= PCI_BDF(0,0,1); + +	/* +	 * Switch off legacy IRQ for IDE and IDE port 1. +	 */ +	pci_write_config_byte(devbusfn, 0x09, 0x8F); + +	pci_read_config_dword(devbusfn, WINDOND_IDECSR, ®32); +	reg32 &= ~(IDECSR_LEGIRQ | IDECSR_P1EN | IDECSR_P1F16); +	pci_write_config_dword(devbusfn, WINDOND_IDECSR, reg32); + +	pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &ide_bus_offset[0]); +	ide_bus_offset[0] &= ~1; +#if CFG_IDE_MAXBUS > 1 +	pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_2, &ide_bus_offset[1]); +	ide_bus_offset[1] &= ~1; +#endif + +	/* +	 * Enable function 1, IDE -> busmastering and IO space access +	 */ +	pci_read_config_word(devbusfn, PCI_COMMAND, ®16); +	reg16 |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; +	pci_write_config_word(devbusfn, PCI_COMMAND, reg16); + +	/* +	 * Initialise ISA interrupt controller +	 */ +	initialise_pic(); + +	/* +	 * Initialise DMA controller +	 */ +	initialise_dma(); +} + +void initialise_pic(void) +{ +	out8(W83C553F_PIC1_ICW1, 0x11); +	out8(W83C553F_PIC1_ICW2, 0x08); +	out8(W83C553F_PIC1_ICW3, 0x04); +	out8(W83C553F_PIC1_ICW4, 0x01); +	out8(W83C553F_PIC1_OCW1, 0xfb); +	out8(W83C553F_PIC1_ELC, 0x20); + +	out8(W83C553F_PIC2_ICW1, 0x11); +	out8(W83C553F_PIC2_ICW2, 0x08); +	out8(W83C553F_PIC2_ICW3, 0x02); +	out8(W83C553F_PIC2_ICW4, 0x01); +	out8(W83C553F_PIC2_OCW1, 0xff); +	out8(W83C553F_PIC2_ELC, 0xce); + +	out8(W83C553F_TMR1_CMOD, 0x74); + +	out8(W83C553F_PIC2_OCW1, 0x20); +	out8(W83C553F_PIC1_OCW1, 0x20); + +	out8(W83C553F_PIC2_OCW1, 0x2b); +	out8(W83C553F_PIC1_OCW1, 0x2b); +} + +void initialise_dma(void) +{ +	unsigned int channel; +	unsigned int rvalue1, rvalue2; + +	/* perform a H/W reset of the devices */ + +	out8(W83C553F_DMA1 + W83C553F_DMA1_MC, 0x00); +	out16(W83C553F_DMA2 + W83C553F_DMA2_MC, 0x0000); + +	/* initialise all channels to a sane state */ + +	for (channel = 0; channel < 4; channel++) { +		/* +		 * dependent upon the channel, setup the specifics: +		 * +		 * demand +		 * address-increment +		 * autoinitialize-disable +		 * verify-transfer +		 */ + +		switch (channel) { +		case 0: +			rvalue1 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH0SEL|W83C553F_MODE_TT_VERIFY); +			rvalue2 = (W83C553F_MODE_TM_CASCADE|W83C553F_MODE_CH0SEL); +			break; +	    	case 1: +			rvalue1 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH1SEL|W83C553F_MODE_TT_VERIFY); +			rvalue2 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH1SEL|W83C553F_MODE_TT_VERIFY); +			break; +		case 2: +			rvalue1 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH2SEL|W83C553F_MODE_TT_VERIFY); +			rvalue2 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH2SEL|W83C553F_MODE_TT_VERIFY); +			break; +		case 3: +			rvalue1 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH3SEL|W83C553F_MODE_TT_VERIFY); +			rvalue2 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH3SEL|W83C553F_MODE_TT_VERIFY); +			break; +		default: +			rvalue1 = 0x00; +			rvalue2 = 0x00; +			break; +		} + +		/* write to write mode registers */ + +		out8(W83C553F_DMA1 + W83C553F_DMA1_WM, rvalue1 & 0xFF); +		out16(W83C553F_DMA2 + W83C553F_DMA2_WM, rvalue2 & 0x00FF); +	} + +	/* enable all channels */ + +	out8(W83C553F_DMA1 + W83C553F_DMA1_CM, 0x00); +	out16(W83C553F_DMA2 + W83C553F_DMA2_CM, 0x0000); +	/* +	 * initialize the global DMA configuration +	 * +	 * DACK# active low +	 * DREQ active high +	 * fixed priority +	 * channel group enable +	 */ + +	out8(W83C553F_DMA1 + W83C553F_DMA1_CS, 0x00); +	out16(W83C553F_DMA2 + W83C553F_DMA2_CS, 0x0000); +} + +#endif /* CFG_WINBOND_83C553 */ |