mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-30 08:31:03 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
317 lines
8 KiB
C
317 lines
8 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Faraday FTPCI100 PCI Bridge Controller Device Driver Implementation
|
|
*
|
|
* Copyright (C) 2011 Andes Technology Corporation
|
|
* Gavin Guo, Andes Technology Corporation <gavinguo@andestech.com>
|
|
* Macpaul Lin, Andes Technology Corporation <macpaul@andestech.com>
|
|
*/
|
|
#include <common.h>
|
|
#include <malloc.h>
|
|
#include <pci.h>
|
|
|
|
#include <faraday/ftpci100.h>
|
|
|
|
#include <asm/io.h>
|
|
#include <asm/types.h> /* u32, u16.... used by pci.h */
|
|
|
|
struct ftpci100_data {
|
|
unsigned int reg_base;
|
|
unsigned int io_base;
|
|
unsigned int mem_base;
|
|
unsigned int mmio_base;
|
|
unsigned int ndevs;
|
|
};
|
|
|
|
static struct pci_config devs[FTPCI100_MAX_FUNCTIONS];
|
|
static struct pci_controller local_hose;
|
|
|
|
static void setup_pci_bar(unsigned int bus, unsigned int dev, unsigned func,
|
|
unsigned char header, struct ftpci100_data *priv)
|
|
{
|
|
struct pci_controller *hose = (struct pci_controller *)&local_hose;
|
|
unsigned int i, tmp32, bar_no, iovsmem = 1;
|
|
pci_dev_t dev_nu;
|
|
|
|
/* A device is present, add an entry to the array */
|
|
devs[priv->ndevs].bus = bus;
|
|
devs[priv->ndevs].dev = dev;
|
|
devs[priv->ndevs].func = func;
|
|
|
|
dev_nu = PCI_BDF(bus, dev, func);
|
|
|
|
if ((header & 0x7f) == 0x01)
|
|
/* PCI-PCI Bridge */
|
|
bar_no = 2;
|
|
else
|
|
bar_no = 6;
|
|
|
|
/* Allocate address spaces by configuring BARs */
|
|
for (i = 0; i < bar_no; i++) {
|
|
pci_hose_write_config_dword(hose, dev_nu,
|
|
PCI_BASE_ADDRESS_0 + i * 4, 0xffffffff);
|
|
pci_hose_read_config_dword(hose, dev_nu,
|
|
PCI_BASE_ADDRESS_0 + i * 4, &tmp32);
|
|
|
|
if (tmp32 == 0x0)
|
|
continue;
|
|
|
|
/* IO space */
|
|
if (tmp32 & 0x1) {
|
|
iovsmem = 0;
|
|
unsigned int size_mask = ~(tmp32 & 0xfffffffc);
|
|
|
|
if (priv->io_base & size_mask)
|
|
priv->io_base = (priv->io_base & ~size_mask) + \
|
|
size_mask + 1;
|
|
|
|
devs[priv->ndevs].bar[i].addr = priv->io_base;
|
|
devs[priv->ndevs].bar[i].size = size_mask + 1;
|
|
|
|
pci_hose_write_config_dword(hose, dev_nu,
|
|
PCI_BASE_ADDRESS_0 + i * 4,
|
|
priv->io_base);
|
|
|
|
debug("Allocated IO address 0x%X-" \
|
|
"0x%X for Bus %d, Device %d, Function %d\n",
|
|
priv->io_base,
|
|
priv->io_base + size_mask, bus, dev, func);
|
|
|
|
priv->io_base += size_mask + 1;
|
|
} else {
|
|
/* Memory space */
|
|
unsigned int is_64bit = ((tmp32 & 0x6) == 0x4);
|
|
unsigned int is_pref = tmp32 & 0x8;
|
|
unsigned int size_mask = ~(tmp32 & 0xfffffff0);
|
|
unsigned int alloc_base;
|
|
unsigned int *addr_mem_base;
|
|
|
|
if (is_pref)
|
|
addr_mem_base = &priv->mem_base;
|
|
else
|
|
addr_mem_base = &priv->mmio_base;
|
|
|
|
alloc_base = *addr_mem_base;
|
|
|
|
if (alloc_base & size_mask)
|
|
alloc_base = (alloc_base & ~size_mask) \
|
|
+ size_mask + 1;
|
|
|
|
pci_hose_write_config_dword(hose, dev_nu,
|
|
PCI_BASE_ADDRESS_0 + i * 4, alloc_base);
|
|
|
|
debug("Allocated %s address 0x%X-" \
|
|
"0x%X for Bus %d, Device %d, Function %d\n",
|
|
is_pref ? "MEM" : "MMIO", alloc_base,
|
|
alloc_base + size_mask, bus, dev, func);
|
|
|
|
devs[priv->ndevs].bar[i].addr = alloc_base;
|
|
devs[priv->ndevs].bar[i].size = size_mask + 1;
|
|
|
|
debug("BAR address BAR size\n");
|
|
debug("%010x %08d\n",
|
|
devs[priv->ndevs].bar[0].addr,
|
|
devs[priv->ndevs].bar[0].size);
|
|
|
|
alloc_base += size_mask + 1;
|
|
*addr_mem_base = alloc_base;
|
|
|
|
if (is_64bit) {
|
|
i++;
|
|
pci_hose_write_config_dword(hose, dev_nu,
|
|
PCI_BASE_ADDRESS_0 + i * 4, 0x0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Enable Bus Master, Memory Space, and IO Space */
|
|
pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32);
|
|
pci_hose_write_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, 0x08);
|
|
pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32);
|
|
|
|
pci_hose_read_config_dword(hose, dev_nu, PCI_COMMAND, &tmp32);
|
|
|
|
tmp32 &= 0xffff;
|
|
|
|
if (iovsmem == 0)
|
|
tmp32 |= 0x5;
|
|
else
|
|
tmp32 |= 0x6;
|
|
|
|
pci_hose_write_config_dword(hose, dev_nu, PCI_COMMAND, tmp32);
|
|
}
|
|
|
|
static void pci_bus_scan(struct ftpci100_data *priv)
|
|
{
|
|
struct pci_controller *hose = (struct pci_controller *)&local_hose;
|
|
unsigned int bus, dev, func;
|
|
pci_dev_t dev_nu;
|
|
unsigned int data32;
|
|
unsigned int tmp;
|
|
unsigned char header;
|
|
unsigned char int_pin;
|
|
unsigned int niobars;
|
|
unsigned int nmbars;
|
|
|
|
priv->ndevs = 1;
|
|
|
|
nmbars = 0;
|
|
niobars = 0;
|
|
|
|
for (bus = 0; bus < MAX_BUS_NUM; bus++)
|
|
for (dev = 0; dev < MAX_DEV_NUM; dev++)
|
|
for (func = 0; func < MAX_FUN_NUM; func++) {
|
|
dev_nu = PCI_BDF(bus, dev, func);
|
|
pci_hose_read_config_dword(hose, dev_nu,
|
|
PCI_VENDOR_ID, &data32);
|
|
|
|
/*
|
|
* some broken boards return 0 or ~0,
|
|
* if a slot is empty.
|
|
*/
|
|
if (data32 == 0xffffffff ||
|
|
data32 == 0x00000000 ||
|
|
data32 == 0x0000ffff ||
|
|
data32 == 0xffff0000)
|
|
continue;
|
|
|
|
pci_hose_read_config_dword(hose, dev_nu,
|
|
PCI_HEADER_TYPE, &tmp);
|
|
header = (unsigned char)tmp;
|
|
setup_pci_bar(bus, dev, func, header, priv);
|
|
|
|
devs[priv->ndevs].v_id = (u16)(data32 & \
|
|
0x0000ffff);
|
|
|
|
devs[priv->ndevs].d_id = (u16)((data32 & \
|
|
0xffff0000) >> 16);
|
|
|
|
/* Figure out what INTX# line the card uses */
|
|
pci_hose_read_config_byte(hose, dev_nu,
|
|
PCI_INTERRUPT_PIN, &int_pin);
|
|
|
|
/* assign the appropriate irq line */
|
|
if (int_pin > PCI_IRQ_LINES) {
|
|
printf("more irq lines than expect\n");
|
|
} else if (int_pin != 0) {
|
|
/* This device uses an interrupt line */
|
|
devs[priv->ndevs].pin = int_pin;
|
|
}
|
|
|
|
pci_hose_read_config_dword(hose, dev_nu,
|
|
PCI_CLASS_DEVICE, &data32);
|
|
|
|
debug("%06d %03d %03d " \
|
|
"%04d %08x %08x " \
|
|
"%03d %08x %06d %08x\n",
|
|
priv->ndevs, devs[priv->ndevs].bus,
|
|
devs[priv->ndevs].dev,
|
|
devs[priv->ndevs].func,
|
|
devs[priv->ndevs].d_id,
|
|
devs[priv->ndevs].v_id,
|
|
devs[priv->ndevs].pin,
|
|
devs[priv->ndevs].bar[0].addr,
|
|
devs[priv->ndevs].bar[0].size,
|
|
data32 >> 8);
|
|
|
|
priv->ndevs++;
|
|
}
|
|
}
|
|
|
|
static void ftpci_preinit(struct ftpci100_data *priv)
|
|
{
|
|
struct ftpci100_ahbc *ftpci100;
|
|
struct pci_controller *hose = (struct pci_controller *)&local_hose;
|
|
u32 pci_config_addr;
|
|
u32 pci_config_data;
|
|
|
|
priv->reg_base = CONFIG_FTPCI100_BASE;
|
|
priv->io_base = CONFIG_FTPCI100_BASE + CONFIG_FTPCI100_IO_SIZE;
|
|
priv->mmio_base = CONFIG_FTPCI100_MEM_BASE;
|
|
priv->mem_base = CONFIG_FTPCI100_MEM_BASE + CONFIG_FTPCI100_MEM_SIZE;
|
|
|
|
ftpci100 = (struct ftpci100_ahbc *)priv->reg_base;
|
|
|
|
pci_config_addr = (u32) &ftpci100->conf;
|
|
pci_config_data = (u32) &ftpci100->data;
|
|
|
|
/* print device name */
|
|
printf("FTPCI100\n");
|
|
|
|
/* dump basic configuration */
|
|
debug("%s: Config addr is %08X, data port is %08X\n",
|
|
__func__, pci_config_addr, pci_config_data);
|
|
|
|
/* PCI memory space */
|
|
pci_set_region(hose->regions + 0,
|
|
CONFIG_PCI_MEM_BUS,
|
|
CONFIG_PCI_MEM_PHYS,
|
|
CONFIG_PCI_MEM_SIZE,
|
|
PCI_REGION_MEM);
|
|
hose->region_count++;
|
|
|
|
/* PCI IO space */
|
|
pci_set_region(hose->regions + 1,
|
|
CONFIG_PCI_IO_BUS,
|
|
CONFIG_PCI_IO_PHYS,
|
|
CONFIG_PCI_IO_SIZE,
|
|
PCI_REGION_IO);
|
|
hose->region_count++;
|
|
|
|
#if defined(CONFIG_PCI_SYS_BUS)
|
|
/* PCI System Memory space */
|
|
pci_set_region(hose->regions + 2,
|
|
CONFIG_PCI_SYS_BUS,
|
|
CONFIG_PCI_SYS_PHYS,
|
|
CONFIG_PCI_SYS_SIZE,
|
|
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
|
hose->region_count++;
|
|
#endif
|
|
|
|
/* setup indirect read/write function */
|
|
pci_setup_indirect(hose, pci_config_addr, pci_config_data);
|
|
|
|
/* register hose */
|
|
pci_register_hose(hose);
|
|
}
|
|
|
|
void pci_ftpci_init(void)
|
|
{
|
|
struct ftpci100_data *priv = NULL;
|
|
struct pci_controller *hose = (struct pci_controller *)&local_hose;
|
|
pci_dev_t bridge_num;
|
|
|
|
struct pci_device_id bridge_ids[] = {
|
|
{FTPCI100_BRIDGE_VENDORID, FTPCI100_BRIDGE_DEVICEID},
|
|
{0, 0}
|
|
};
|
|
|
|
priv = malloc(sizeof(struct ftpci100_data));
|
|
|
|
if (!priv) {
|
|
printf("%s(): failed to malloc priv\n", __func__);
|
|
return;
|
|
}
|
|
|
|
memset(priv, 0, sizeof(struct ftpci100_data));
|
|
|
|
ftpci_preinit(priv);
|
|
|
|
debug("Device bus dev func deviceID vendorID pin address" \
|
|
" size class\n");
|
|
|
|
pci_bus_scan(priv);
|
|
|
|
/*
|
|
* Setup the PCI Bridge Window to 1GB,
|
|
* it will cause USB OHCI Host controller Unrecoverable Error
|
|
* if it is not set.
|
|
*/
|
|
bridge_num = pci_find_devices(bridge_ids, 0);
|
|
if (bridge_num == -1) {
|
|
printf("PCI Bridge not found\n");
|
|
return;
|
|
}
|
|
pci_hose_write_config_dword(hose, bridge_num, PCI_MEM_BASE_SIZE1,
|
|
FTPCI100_BASE_ADR_SIZE(1024));
|
|
}
|