2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2002-11-03 00:24:07 +00:00
|
|
|
/*
|
|
|
|
* (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
|
|
|
* Andreas Heppel <aheppel@sysgo.de>
|
|
|
|
*
|
2003-05-28 08:06:31 +00:00
|
|
|
* (C) Copyright 2002, 2003
|
2002-11-03 00:24:07 +00:00
|
|
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2015-11-29 20:17:46 +00:00
|
|
|
* Old PCI routines
|
|
|
|
*
|
|
|
|
* Do not change this file. Instead, convert your board to use CONFIG_DM_PCI
|
|
|
|
* and change pci-uclass.c.
|
2002-11-03 00:24:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
|
|
|
|
#include <command.h>
|
2019-08-01 15:46:52 +00:00
|
|
|
#include <env.h>
|
2015-01-28 05:13:27 +00:00
|
|
|
#include <errno.h>
|
2002-11-03 00:24:07 +00:00
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <pci.h>
|
|
|
|
|
2014-12-30 14:53:21 +00:00
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
|
2003-05-28 08:06:31 +00:00
|
|
|
#define PCI_HOSE_OP(rw, size, type) \
|
2008-05-20 14:00:29 +00:00
|
|
|
int pci_hose_##rw##_config_##size(struct pci_controller *hose, \
|
|
|
|
pci_dev_t dev, \
|
2003-05-28 08:06:31 +00:00
|
|
|
int offset, type value) \
|
|
|
|
{ \
|
|
|
|
return hose->rw##_##size(hose, dev, offset, value); \
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
2003-05-28 08:06:31 +00:00
|
|
|
#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); \
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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, )
|
|
|
|
|
2003-05-28 08:06:31 +00:00
|
|
|
#define PCI_READ_VIA_DWORD_OP(size, type, off_mask) \
|
|
|
|
int pci_hose_read_config_##size##_via_dword(struct pci_controller *hose,\
|
2008-05-20 14:00:29 +00:00
|
|
|
pci_dev_t dev, \
|
2003-05-28 08:06:31 +00:00
|
|
|
int offset, type val) \
|
|
|
|
{ \
|
|
|
|
u32 val32; \
|
|
|
|
\
|
2007-08-17 03:43:44 +00:00
|
|
|
if (pci_hose_read_config_dword(hose, dev, offset & 0xfc, &val32) < 0) { \
|
|
|
|
*val = -1; \
|
2003-05-28 08:06:31 +00:00
|
|
|
return -1; \
|
2007-08-17 03:43:44 +00:00
|
|
|
} \
|
2003-05-28 08:06:31 +00:00
|
|
|
\
|
|
|
|
*val = (val32 >> ((offset & (int)off_mask) * 8)); \
|
|
|
|
\
|
|
|
|
return 0; \
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
|
2003-05-28 08:06:31 +00:00
|
|
|
#define PCI_WRITE_VIA_DWORD_OP(size, type, off_mask, val_mask) \
|
|
|
|
int pci_hose_write_config_##size##_via_dword(struct pci_controller *hose,\
|
2008-05-20 14:00:29 +00:00
|
|
|
pci_dev_t dev, \
|
2003-05-28 08:06:31 +00:00
|
|
|
int offset, type val) \
|
|
|
|
{ \
|
2004-04-18 22:26:17 +00:00
|
|
|
u32 val32, mask, ldata, shift; \
|
2003-05-28 08:06:31 +00:00
|
|
|
\
|
|
|
|
if (pci_hose_read_config_dword(hose, dev, offset & 0xfc, &val32) < 0)\
|
|
|
|
return -1; \
|
|
|
|
\
|
2004-04-18 22:26:17 +00:00
|
|
|
shift = ((offset & (int)off_mask) * 8); \
|
|
|
|
ldata = (((unsigned long)val) & val_mask) << shift; \
|
|
|
|
mask = val_mask << shift; \
|
2003-05-28 08:06:31 +00:00
|
|
|
val32 = (val32 & ~mask) | ldata; \
|
|
|
|
\
|
|
|
|
if (pci_hose_write_config_dword(hose, dev, offset & 0xfc, val32) < 0)\
|
|
|
|
return -1; \
|
|
|
|
\
|
|
|
|
return 0; \
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2010-10-22 05:20:23 +00:00
|
|
|
static struct pci_controller* hose_head;
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2014-12-30 14:53:21 +00:00
|
|
|
struct pci_controller *pci_get_hose_head(void)
|
|
|
|
{
|
|
|
|
if (gd->hose)
|
|
|
|
return gd->hose;
|
|
|
|
|
|
|
|
return hose_head;
|
|
|
|
}
|
|
|
|
|
2002-11-03 00:24:07 +00:00
|
|
|
void pci_register_hose(struct pci_controller* hose)
|
|
|
|
{
|
|
|
|
struct pci_controller **phose = &hose_head;
|
|
|
|
|
|
|
|
while(*phose)
|
|
|
|
phose = &(*phose)->next;
|
|
|
|
|
|
|
|
hose->next = NULL;
|
|
|
|
|
|
|
|
*phose = hose;
|
|
|
|
}
|
|
|
|
|
2012-08-29 14:16:29 +00:00
|
|
|
struct pci_controller *pci_bus_to_hose(int bus)
|
2002-11-03 00:24:07 +00:00
|
|
|
{
|
|
|
|
struct pci_controller *hose;
|
|
|
|
|
2014-12-30 14:53:21 +00:00
|
|
|
for (hose = pci_get_hose_head(); hose; hose = hose->next) {
|
2003-05-28 08:06:31 +00:00
|
|
|
if (bus >= hose->first_busno && bus <= hose->last_busno)
|
2002-11-03 00:24:07 +00:00
|
|
|
return hose;
|
2012-08-29 14:16:29 +00:00
|
|
|
}
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2005-10-17 00:39:53 +00:00
|
|
|
printf("pci_bus_to_hose() failed\n");
|
2002-11-03 00:24:07 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-12-17 11:57:25 +00:00
|
|
|
struct pci_controller *find_hose_by_cfg_addr(void *cfg_addr)
|
|
|
|
{
|
|
|
|
struct pci_controller *hose;
|
|
|
|
|
2014-12-30 14:53:21 +00:00
|
|
|
for (hose = pci_get_hose_head(); hose; hose = hose->next) {
|
2010-12-17 11:57:25 +00:00
|
|
|
if (hose->cfg_addr == cfg_addr)
|
|
|
|
return hose;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-02-19 15:20:41 +00:00
|
|
|
int pci_last_busno(void)
|
|
|
|
{
|
2014-12-30 14:53:21 +00:00
|
|
|
struct pci_controller *hose = pci_get_hose_head();
|
2009-02-19 15:20:41 +00:00
|
|
|
|
|
|
|
if (!hose)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while (hose->next)
|
|
|
|
hose = hose->next;
|
|
|
|
|
|
|
|
return hose->last_busno;
|
|
|
|
}
|
|
|
|
|
2002-11-03 00:24:07 +00:00
|
|
|
pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
|
|
|
|
{
|
|
|
|
struct pci_controller * hose;
|
|
|
|
pci_dev_t bdf;
|
2015-03-05 19:25:24 +00:00
|
|
|
int bus;
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2014-12-30 14:53:21 +00:00
|
|
|
for (hose = pci_get_hose_head(); hose; hose = hose->next) {
|
2015-03-05 19:25:24 +00:00
|
|
|
for (bus = hose->first_busno; bus <= hose->last_busno; bus++) {
|
|
|
|
bdf = pci_hose_find_devices(hose, bus, ids, &index);
|
|
|
|
if (bdf != -1)
|
2015-01-28 05:13:27 +00:00
|
|
|
return bdf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-05 19:25:24 +00:00
|
|
|
return -1;
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
|
2019-02-17 03:24:40 +00:00
|
|
|
static int pci_hose_config_device(struct pci_controller *hose, pci_dev_t dev,
|
|
|
|
ulong io, pci_addr_t mem, ulong command)
|
2002-11-03 00:24:07 +00:00
|
|
|
{
|
2012-09-19 04:47:36 +00:00
|
|
|
u32 bar_response;
|
2012-08-01 12:27:16 +00:00
|
|
|
unsigned int old_command;
|
2008-10-21 13:36:08 +00:00
|
|
|
pci_addr_t bar_value;
|
|
|
|
pci_size_t bar_size;
|
2002-11-03 00:24:07 +00:00
|
|
|
unsigned char pin;
|
|
|
|
int bar, found_mem64;
|
|
|
|
|
2012-08-29 14:16:29 +00:00
|
|
|
debug("PCI Config: I/O=0x%lx, Memory=0x%llx, Command=0x%lx\n", io,
|
|
|
|
(u64)mem, command);
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2012-08-29 14:16:29 +00:00
|
|
|
pci_hose_write_config_dword(hose, dev, PCI_COMMAND, 0);
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2010-03-09 13:27:25 +00:00
|
|
|
for (bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar += 4) {
|
2012-08-29 14:16:29 +00:00
|
|
|
pci_hose_write_config_dword(hose, dev, bar, 0xffffffff);
|
|
|
|
pci_hose_read_config_dword(hose, dev, bar, &bar_response);
|
2002-11-03 00:24:07 +00:00
|
|
|
|
|
|
|
if (!bar_response)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
found_mem64 = 0;
|
|
|
|
|
|
|
|
/* Check the BAR type and set our address mask */
|
2003-05-28 08:06:31 +00:00
|
|
|
if (bar_response & PCI_BASE_ADDRESS_SPACE) {
|
2002-11-03 00:24:07 +00:00
|
|
|
bar_size = ~(bar_response & PCI_BASE_ADDRESS_IO_MASK) + 1;
|
2003-05-28 08:06:31 +00:00
|
|
|
/* round up region base address to a multiple of size */
|
2002-11-03 00:24:07 +00:00
|
|
|
io = ((io - 1) | (bar_size - 1)) + 1;
|
2003-05-28 08:06:31 +00:00
|
|
|
bar_value = io;
|
|
|
|
/* compute new region base address */
|
|
|
|
io = io + bar_size;
|
|
|
|
} else {
|
|
|
|
if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
|
2008-10-21 13:36:08 +00:00
|
|
|
PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
|
|
|
u32 bar_response_upper;
|
|
|
|
u64 bar64;
|
2012-08-29 14:16:29 +00:00
|
|
|
pci_hose_write_config_dword(hose, dev, bar + 4,
|
|
|
|
0xffffffff);
|
|
|
|
pci_hose_read_config_dword(hose, dev, bar + 4,
|
|
|
|
&bar_response_upper);
|
2008-10-21 13:36:08 +00:00
|
|
|
|
|
|
|
bar64 = ((u64)bar_response_upper << 32) | bar_response;
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2008-10-21 13:36:08 +00:00
|
|
|
bar_size = ~(bar64 & PCI_BASE_ADDRESS_MEM_MASK) + 1;
|
|
|
|
found_mem64 = 1;
|
|
|
|
} else {
|
|
|
|
bar_size = (u32)(~(bar_response & PCI_BASE_ADDRESS_MEM_MASK) + 1);
|
|
|
|
}
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2003-05-28 08:06:31 +00:00
|
|
|
/* round up region base address to multiple of size */
|
2002-11-03 00:24:07 +00:00
|
|
|
mem = ((mem - 1) | (bar_size - 1)) + 1;
|
2003-05-28 08:06:31 +00:00
|
|
|
bar_value = mem;
|
|
|
|
/* compute new region base address */
|
|
|
|
mem = mem + bar_size;
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Write it out and update our limit */
|
2008-10-21 13:36:08 +00:00
|
|
|
pci_hose_write_config_dword (hose, dev, bar, (u32)bar_value);
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2003-05-28 08:06:31 +00:00
|
|
|
if (found_mem64) {
|
2002-11-03 00:24:07 +00:00
|
|
|
bar += 4;
|
2008-10-21 13:36:08 +00:00
|
|
|
#ifdef CONFIG_SYS_PCI_64BIT
|
2012-08-29 14:16:29 +00:00
|
|
|
pci_hose_write_config_dword(hose, dev, bar,
|
|
|
|
(u32)(bar_value >> 32));
|
2008-10-21 13:36:08 +00:00
|
|
|
#else
|
2012-08-29 14:16:29 +00:00
|
|
|
pci_hose_write_config_dword(hose, dev, bar, 0x00000000);
|
2008-10-21 13:36:08 +00:00
|
|
|
#endif
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure Cache Line Size Register */
|
2012-08-29 14:16:29 +00:00
|
|
|
pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, 0x08);
|
2002-11-03 00:24:07 +00:00
|
|
|
|
|
|
|
/* Configure Latency Timer */
|
2012-08-29 14:16:29 +00:00
|
|
|
pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80);
|
2002-11-03 00:24:07 +00:00
|
|
|
|
|
|
|
/* Disable interrupt line, if device says it wants to use interrupts */
|
2012-08-29 14:16:29 +00:00
|
|
|
pci_hose_read_config_byte(hose, dev, PCI_INTERRUPT_PIN, &pin);
|
2003-05-28 08:06:31 +00:00
|
|
|
if (pin != 0) {
|
2015-07-27 21:47:17 +00:00
|
|
|
pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE,
|
|
|
|
PCI_INTERRUPT_LINE_DISABLE);
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
|
2012-08-29 14:16:29 +00:00
|
|
|
pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &old_command);
|
|
|
|
pci_hose_write_config_dword(hose, dev, PCI_COMMAND,
|
2003-05-28 08:06:31 +00:00
|
|
|
(old_command & 0xffff0000) | command);
|
2002-11-03 00:24:07 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2003-05-28 08:06:31 +00:00
|
|
|
for (table = hose->config_table; table && table->vendor; table++) {
|
2002-11-03 00:24:07 +00:00
|
|
|
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) &&
|
2003-05-28 08:06:31 +00:00
|
|
|
(table->func == PCI_ANY_ID || table->func == func)) {
|
2002-11-03 00:24:07 +00:00
|
|
|
return table;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pci_cfgfunc_config_device(struct pci_controller *hose,
|
|
|
|
pci_dev_t dev,
|
|
|
|
struct pci_config_table *entry)
|
|
|
|
{
|
2012-08-29 14:16:29 +00:00
|
|
|
pci_hose_config_device(hose, dev, entry->priv[0], entry->priv[1],
|
|
|
|
entry->priv[2]);
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void pci_cfgfunc_do_nothing(struct pci_controller *hose,
|
|
|
|
pci_dev_t dev, struct pci_config_table *entry)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-08-29 14:16:29 +00:00
|
|
|
* HJF: Changed this to return int. I think this is required
|
2002-11-19 11:04:11 +00:00
|
|
|
* to get the correct result when scanning bridges
|
|
|
|
*/
|
|
|
|
extern int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev);
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2008-07-08 10:01:47 +00:00
|
|
|
#ifdef CONFIG_PCI_SCAN_SHOW
|
2014-10-08 20:57:27 +00:00
|
|
|
__weak int pci_print_dev(struct pci_controller *hose, pci_dev_t dev)
|
2008-07-08 10:01:47 +00:00
|
|
|
{
|
|
|
|
if (dev == PCI_BDF(hose->first_busno, 0, 0))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PCI_SCAN_SHOW */
|
|
|
|
|
2002-11-03 00:24:07 +00:00
|
|
|
int pci_hose_scan_bus(struct pci_controller *hose, int bus)
|
|
|
|
{
|
2012-08-29 14:16:29 +00:00
|
|
|
unsigned int sub_bus, found_multi = 0;
|
2002-11-03 00:24:07 +00:00
|
|
|
unsigned short vendor, device, class;
|
|
|
|
unsigned char header_type;
|
2012-08-29 14:16:30 +00:00
|
|
|
#ifndef CONFIG_PCI_PNP
|
2002-11-03 00:24:07 +00:00
|
|
|
struct pci_config_table *cfg;
|
2012-08-29 14:16:30 +00:00
|
|
|
#endif
|
2002-11-03 00:24:07 +00:00
|
|
|
pci_dev_t dev;
|
2010-10-29 22:59:29 +00:00
|
|
|
#ifdef CONFIG_PCI_SCAN_SHOW
|
|
|
|
static int indent = 0;
|
|
|
|
#endif
|
2002-11-03 00:24:07 +00:00
|
|
|
|
|
|
|
sub_bus = bus;
|
|
|
|
|
|
|
|
for (dev = PCI_BDF(bus,0,0);
|
2012-08-29 14:16:29 +00:00
|
|
|
dev < PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1,
|
|
|
|
PCI_MAX_PCI_FUNCTIONS - 1);
|
|
|
|
dev += PCI_BDF(0, 0, 1)) {
|
2008-07-08 10:01:47 +00:00
|
|
|
|
|
|
|
if (pci_skip_dev(hose, dev))
|
|
|
|
continue;
|
2002-11-03 00:24:07 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2010-10-29 22:59:27 +00:00
|
|
|
if (vendor == 0xffff || vendor == 0x0000)
|
|
|
|
continue;
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2010-10-29 22:59:27 +00:00
|
|
|
if (!PCI_FUNC(dev))
|
|
|
|
found_multi = header_type & 0x80;
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2012-08-29 14:16:29 +00:00
|
|
|
debug("PCI Scan: Found Bus %d, Device %d, Function %d\n",
|
|
|
|
PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev));
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2010-10-29 22:59:27 +00:00
|
|
|
pci_hose_read_config_word(hose, dev, PCI_DEVICE_ID, &device);
|
|
|
|
pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class);
|
2002-11-03 00:24:07 +00:00
|
|
|
|
2014-08-08 05:49:56 +00:00
|
|
|
#ifdef CONFIG_PCI_FIXUP_DEV
|
|
|
|
board_pci_fixup_dev(hose, dev, vendor, device, class);
|
|
|
|
#endif
|
|
|
|
|
2010-10-29 22:59:28 +00:00
|
|
|
#ifdef CONFIG_PCI_SCAN_SHOW
|
2010-10-29 22:59:29 +00:00
|
|
|
indent++;
|
|
|
|
|
|
|
|
/* Print leading space, including bus indentation */
|
|
|
|
printf("%*c", indent + 1, ' ');
|
|
|
|
|
2010-10-29 22:59:28 +00:00
|
|
|
if (pci_print_dev(hose, dev)) {
|
2010-10-29 22:59:29 +00:00
|
|
|
printf("%02x:%02x.%-*x - %04x:%04x - %s\n",
|
|
|
|
PCI_BUS(dev), PCI_DEV(dev), 6 - indent, PCI_FUNC(dev),
|
2010-10-29 22:59:28 +00:00
|
|
|
vendor, device, pci_class_str(class >> 8));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-08-29 14:16:30 +00:00
|
|
|
#ifdef CONFIG_PCI_PNP
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
sub_bus = max((unsigned int)pciauto_config_device(hose, dev),
|
|
|
|
sub_bus);
|
2012-08-29 14:16:30 +00:00
|
|
|
#else
|
2010-10-29 22:59:27 +00:00
|
|
|
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);
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
sub_bus = max(sub_bus,
|
|
|
|
(unsigned int)hose->current_busno);
|
2010-10-29 22:59:27 +00:00
|
|
|
}
|
2012-08-29 14:16:30 +00:00
|
|
|
#endif
|
2010-10-29 22:59:28 +00:00
|
|
|
|
2010-10-29 22:59:29 +00:00
|
|
|
#ifdef CONFIG_PCI_SCAN_SHOW
|
|
|
|
indent--;
|
|
|
|
#endif
|
|
|
|
|
2010-10-29 22:59:27 +00:00
|
|
|
if (hose->fixup_irq)
|
|
|
|
hose->fixup_irq(hose, dev);
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return sub_bus;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pci_hose_scan(struct pci_controller *hose)
|
|
|
|
{
|
2011-10-11 22:44:30 +00:00
|
|
|
#if defined(CONFIG_PCI_BOOTDELAY)
|
|
|
|
char *s;
|
|
|
|
int i;
|
|
|
|
|
2014-12-30 14:53:21 +00:00
|
|
|
if (!gd->pcidelay_done) {
|
2011-10-11 22:44:30 +00:00
|
|
|
/* wait "pcidelay" ms (if defined)... */
|
2017-08-03 18:22:12 +00:00
|
|
|
s = env_get("pcidelay");
|
2011-10-11 22:44:30 +00:00
|
|
|
if (s) {
|
|
|
|
int val = simple_strtoul(s, NULL, 10);
|
|
|
|
for (i = 0; i < val; i++)
|
|
|
|
udelay(1000);
|
|
|
|
}
|
2014-12-30 14:53:21 +00:00
|
|
|
gd->pcidelay_done = 1;
|
2011-10-11 22:44:30 +00:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_PCI_BOOTDELAY */
|
|
|
|
|
2015-05-08 22:16:07 +00:00
|
|
|
#ifdef CONFIG_PCI_SCAN_SHOW
|
|
|
|
puts("PCI:\n");
|
|
|
|
#endif
|
|
|
|
|
2012-08-29 14:16:29 +00:00
|
|
|
/*
|
|
|
|
* Start scan at current_busno.
|
2007-07-11 19:51:35 +00:00
|
|
|
* PCIe will start scan at first_busno+1.
|
|
|
|
*/
|
2012-08-29 14:16:29 +00:00
|
|
|
/* For legacy support, ensure current >= first */
|
2007-07-11 19:51:35 +00:00
|
|
|
if (hose->first_busno > hose->current_busno)
|
|
|
|
hose->current_busno = hose->first_busno;
|
2002-11-03 00:24:07 +00:00
|
|
|
#ifdef CONFIG_PCI_PNP
|
|
|
|
pciauto_config_init(hose);
|
|
|
|
#endif
|
2007-07-11 19:51:35 +00:00
|
|
|
return pci_hose_scan_bus(hose, hose->current_busno);
|
2002-11-03 00:24:07 +00:00
|
|
|
}
|
|
|
|
|
2003-02-14 11:21:23 +00:00
|
|
|
void pci_init(void)
|
|
|
|
{
|
2010-10-22 05:20:23 +00:00
|
|
|
hose_head = NULL;
|
|
|
|
|
2016-06-17 13:20:25 +00:00
|
|
|
/* allow env to disable pci init/enum */
|
2017-08-03 18:22:12 +00:00
|
|
|
if (env_get("pcidisable") != NULL)
|
2016-06-17 13:20:25 +00:00
|
|
|
return;
|
|
|
|
|
2003-02-14 11:21:23 +00:00
|
|
|
/* now call board specific pci_init()... */
|
|
|
|
pci_init_board();
|
|
|
|
}
|
2013-10-12 05:46:33 +00:00
|
|
|
|
|
|
|
/* Returns the address of the requested capability structure within the
|
|
|
|
* device's PCI configuration space or 0 in case the device does not
|
|
|
|
* support it.
|
|
|
|
* */
|
|
|
|
int pci_hose_find_capability(struct pci_controller *hose, pci_dev_t dev,
|
|
|
|
int cap)
|
|
|
|
{
|
|
|
|
int pos;
|
|
|
|
u8 hdr_type;
|
|
|
|
|
|
|
|
pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE, &hdr_type);
|
|
|
|
|
|
|
|
pos = pci_hose_find_cap_start(hose, dev, hdr_type & 0x7F);
|
|
|
|
|
|
|
|
if (pos)
|
|
|
|
pos = pci_find_cap(hose, dev, pos, cap);
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the header pointer to the Capabilities*/
|
|
|
|
int pci_hose_find_cap_start(struct pci_controller *hose, pci_dev_t dev,
|
|
|
|
u8 hdr_type)
|
|
|
|
{
|
|
|
|
u16 status;
|
|
|
|
|
|
|
|
pci_hose_read_config_word(hose, dev, PCI_STATUS, &status);
|
|
|
|
|
|
|
|
if (!(status & PCI_STATUS_CAP_LIST))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (hdr_type) {
|
|
|
|
case PCI_HEADER_TYPE_NORMAL:
|
|
|
|
case PCI_HEADER_TYPE_BRIDGE:
|
|
|
|
return PCI_CAPABILITY_LIST;
|
|
|
|
case PCI_HEADER_TYPE_CARDBUS:
|
|
|
|
return PCI_CB_CAPABILITY_LIST;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int pci_find_cap(struct pci_controller *hose, pci_dev_t dev, int pos, int cap)
|
|
|
|
{
|
|
|
|
int ttl = PCI_FIND_CAP_TTL;
|
|
|
|
u8 id;
|
|
|
|
u8 next_pos;
|
|
|
|
|
|
|
|
while (ttl--) {
|
|
|
|
pci_hose_read_config_byte(hose, dev, pos, &next_pos);
|
|
|
|
if (next_pos < CAP_START_POS)
|
|
|
|
break;
|
|
|
|
next_pos &= ~3;
|
|
|
|
pos = (int) next_pos;
|
|
|
|
pci_hose_read_config_byte(hose, dev,
|
|
|
|
pos + PCI_CAP_LIST_ID, &id);
|
|
|
|
if (id == 0xff)
|
|
|
|
break;
|
|
|
|
if (id == cap)
|
|
|
|
return pos;
|
|
|
|
pos += PCI_CAP_LIST_NEXT;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2015-07-10 03:35:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* pci_find_next_ext_capability - Find an extended capability
|
|
|
|
*
|
|
|
|
* Returns the address of the next matching extended capability structure
|
|
|
|
* within the device's PCI configuration space or 0 if the device does
|
|
|
|
* not support it. Some capabilities can occur several times, e.g., the
|
|
|
|
* vendor-specific capability, and this provides a way to find them all.
|
|
|
|
*/
|
|
|
|
int pci_find_next_ext_capability(struct pci_controller *hose, pci_dev_t dev,
|
|
|
|
int start, int cap)
|
|
|
|
{
|
|
|
|
u32 header;
|
|
|
|
int ttl, pos = PCI_CFG_SPACE_SIZE;
|
|
|
|
|
|
|
|
/* minimum 8 bytes per capability */
|
|
|
|
ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
|
|
|
|
|
|
|
|
if (start)
|
|
|
|
pos = start;
|
|
|
|
|
|
|
|
pci_hose_read_config_dword(hose, dev, pos, &header);
|
|
|
|
if (header == 0xffffffff || header == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (ttl-- > 0) {
|
|
|
|
if (PCI_EXT_CAP_ID(header) == cap && pos != start)
|
|
|
|
return pos;
|
|
|
|
|
|
|
|
pos = PCI_EXT_CAP_NEXT(header);
|
|
|
|
if (pos < PCI_CFG_SPACE_SIZE)
|
|
|
|
break;
|
|
|
|
|
|
|
|
pci_hose_read_config_dword(hose, dev, pos, &header);
|
|
|
|
if (header == 0xffffffff || header == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* pci_hose_find_ext_capability - Find an extended capability
|
|
|
|
*
|
|
|
|
* Returns the address of the requested extended capability structure
|
|
|
|
* within the device's PCI configuration space or 0 if the device does
|
|
|
|
* not support it.
|
|
|
|
*/
|
|
|
|
int pci_hose_find_ext_capability(struct pci_controller *hose, pci_dev_t dev,
|
|
|
|
int cap)
|
|
|
|
{
|
|
|
|
return pci_find_next_ext_capability(hose, dev, 0, cap);
|
|
|
|
}
|