u-boot/arch/powerpc/cpu/mpc83xx/pcie.c
Tom Rini 83d290c56f SPDX: Convert all of our single license tags to Linux Kernel style
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>
2018-05-07 09:34:12 -04:00

334 lines
8.7 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2007-2009 Freescale Semiconductor, Inc.
* Copyright (C) 2008-2009 MontaVista Software, Inc.
*
* Authors: Tony Li <tony.li@freescale.com>
* Anton Vorontsov <avorontsov@ru.mvista.com>
*/
#include <common.h>
#include <pci.h>
#include <mpc83xx.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
#define PCIE_MAX_BUSES 2
static struct {
u32 base;
u32 size;
} mpc83xx_pcie_cfg_space[] = {
{
.base = CONFIG_SYS_PCIE1_CFG_BASE,
.size = CONFIG_SYS_PCIE1_CFG_SIZE,
},
#if defined(CONFIG_SYS_PCIE2_CFG_BASE) && defined(CONFIG_SYS_PCIE2_CFG_SIZE)
{
.base = CONFIG_SYS_PCIE2_CFG_BASE,
.size = CONFIG_SYS_PCIE2_CFG_SIZE,
},
#endif
};
#ifdef CONFIG_83XX_GENERIC_PCIE_REGISTER_HOSES
/* private structure for mpc83xx pcie hose */
static struct mpc83xx_pcie_priv {
u8 index;
} pcie_priv[PCIE_MAX_BUSES] = {
{
/* pcie controller 1 */
.index = 0,
},
{
/* pcie controller 2 */
.index = 1,
},
};
static int mpc83xx_pcie_remap_cfg(struct pci_controller *hose, pci_dev_t dev)
{
int bus = PCI_BUS(dev) - hose->first_busno;
immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
struct mpc83xx_pcie_priv *pcie_priv = hose->priv_data;
pex83xx_t *pex = &immr->pciexp[pcie_priv->index];
struct pex_outbound_window *out_win = &pex->bridge.pex_outbound_win[0];
u8 devfn = PCI_DEV(dev) << 3 | PCI_FUNC(dev);
u32 dev_base = bus << 24 | devfn << 16;
if (hose->indirect_type == INDIRECT_TYPE_NO_PCIE_LINK)
return -1;
/*
* Workaround for the HW bug: for Type 0 configure transactions the
* PCI-E controller does not check the device number bits and just
* assumes that the device number bits are 0.
*/
if (devfn & 0xf8)
return -1;
out_le32(&out_win->tarl, dev_base);
return 0;
}
#define cfg_read(val, addr, type, op) \
do { *val = op((type)(addr)); } while (0)
#define cfg_write(val, addr, type, op) \
do { op((type *)(addr), (val)); } while (0)
#define cfg_read_err(val) do { *val = -1; } while (0)
#define cfg_write_err(val) do { } while (0)
#define PCIE_OP(rw, size, type, op) \
static int pcie_##rw##_config_##size(struct pci_controller *hose, \
pci_dev_t dev, int offset, \
type val) \
{ \
int ret; \
\
ret = mpc83xx_pcie_remap_cfg(hose, dev); \
if (ret) { \
cfg_##rw##_err(val); \
return ret; \
} \
cfg_##rw(val, (void *)hose->cfg_addr + offset, type, op); \
return 0; \
}
PCIE_OP(read, byte, u8 *, in_8)
PCIE_OP(read, word, u16 *, in_le16)
PCIE_OP(read, dword, u32 *, in_le32)
PCIE_OP(write, byte, u8, out_8)
PCIE_OP(write, word, u16, out_le16)
PCIE_OP(write, dword, u32, out_le32)
static void mpc83xx_pcie_register_hose(int bus, struct pci_region *reg,
u8 link)
{
extern void disable_addr_trans(void); /* start.S */
static struct pci_controller pcie_hose[PCIE_MAX_BUSES];
struct pci_controller *hose = &pcie_hose[bus];
int i;
/*
* There are no spare BATs to remap all PCI-E windows for U-Boot, so
* disable translations. In general, this is not great solution, and
* that's why we don't register PCI-E hoses by default.
*/
disable_addr_trans();
for (i = 0; i < 2; i++, reg++) {
if (reg->size == 0)
break;
hose->regions[i] = *reg;
hose->region_count++;
}
i = hose->region_count++;
hose->regions[i].bus_start = 0;
hose->regions[i].phys_start = 0;
hose->regions[i].size = gd->ram_size;
hose->regions[i].flags = PCI_REGION_MEM | PCI_REGION_SYS_MEMORY;
i = hose->region_count++;
hose->regions[i].bus_start = CONFIG_SYS_IMMR;
hose->regions[i].phys_start = CONFIG_SYS_IMMR;
hose->regions[i].size = 0x100000;
hose->regions[i].flags = PCI_REGION_MEM | PCI_REGION_SYS_MEMORY;
hose->first_busno = pci_last_busno() + 1;
hose->last_busno = 0xff;
hose->cfg_addr = (unsigned int *)mpc83xx_pcie_cfg_space[bus].base;
hose->priv_data = &pcie_priv[bus];
pci_set_ops(hose,
pcie_read_config_byte,
pcie_read_config_word,
pcie_read_config_dword,
pcie_write_config_byte,
pcie_write_config_word,
pcie_write_config_dword);
if (!link)
hose->indirect_type = INDIRECT_TYPE_NO_PCIE_LINK;
pci_register_hose(hose);
#ifdef CONFIG_PCI_SCAN_SHOW
printf("PCI: Bus Dev VenId DevId Class Int\n");
#endif
/*
* Hose scan.
*/
hose->last_busno = pci_hose_scan(hose);
}
#else
static void mpc83xx_pcie_register_hose(int bus, struct pci_region *reg,
u8 link) {}
#endif /* CONFIG_83XX_GENERIC_PCIE_REGISTER_HOSES */
static void mpc83xx_pcie_init_bus(int bus, struct pci_region *reg)
{
immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
pex83xx_t *pex = &immr->pciexp[bus];
struct pex_outbound_window *out_win;
struct pex_inbound_window *in_win;
void *hose_cfg_base;
unsigned int ram_sz;
unsigned int barl;
unsigned int tar;
u16 reg16;
int i;
/* Enable pex csb bridge inbound & outbound transactions */
out_le32(&pex->bridge.pex_csb_ctrl,
in_le32(&pex->bridge.pex_csb_ctrl) | PEX_CSB_CTRL_OBPIOE |
PEX_CSB_CTRL_IBPIOE);
/* Enable bridge outbound */
out_le32(&pex->bridge.pex_csb_obctrl, PEX_CSB_OBCTRL_PIOE |
PEX_CSB_OBCTRL_MEMWE | PEX_CSB_OBCTRL_IOWE |
PEX_CSB_OBCTRL_CFGWE);
out_win = &pex->bridge.pex_outbound_win[0];
out_le32(&out_win->ar, PEX_OWAR_EN | PEX_OWAR_TYPE_CFG |
mpc83xx_pcie_cfg_space[bus].size);
out_le32(&out_win->bar, mpc83xx_pcie_cfg_space[bus].base);
out_le32(&out_win->tarl, 0);
out_le32(&out_win->tarh, 0);
for (i = 0; i < 2; i++) {
u32 ar;
if (reg[i].size == 0)
break;
out_win = &pex->bridge.pex_outbound_win[i + 1];
out_le32(&out_win->bar, reg[i].phys_start);
out_le32(&out_win->tarl, reg[i].bus_start);
out_le32(&out_win->tarh, 0);
ar = PEX_OWAR_EN | (reg[i].size & PEX_OWAR_SIZE);
if (reg[i].flags & PCI_REGION_IO)
ar |= PEX_OWAR_TYPE_IO;
else
ar |= PEX_OWAR_TYPE_MEM;
out_le32(&out_win->ar, ar);
}
out_le32(&pex->bridge.pex_csb_ibctrl, PEX_CSB_IBCTRL_PIOE);
ram_sz = gd->ram_size;
barl = 0;
tar = 0;
i = 0;
while (ram_sz > 0) {
in_win = &pex->bridge.pex_inbound_win[i];
out_le32(&in_win->barl, barl);
out_le32(&in_win->barh, 0x0);
out_le32(&in_win->tar, tar);
if (ram_sz >= 0x10000000) {
/* The maxium windows size is 256M */
out_le32(&in_win->ar, PEX_IWAR_EN | PEX_IWAR_NSOV |
PEX_IWAR_TYPE_PF | 0x0FFFF000);
barl += 0x10000000;
tar += 0x10000000;
ram_sz -= 0x10000000;
} else {
/* The UM is not clear here.
* So, round up to even Mb boundary */
ram_sz = ram_sz >> (20 +
((ram_sz & 0xFFFFF) ? 1 : 0));
if (!(ram_sz % 2))
ram_sz -= 1;
out_le32(&in_win->ar, PEX_IWAR_EN | PEX_IWAR_NSOV |
PEX_IWAR_TYPE_PF | (ram_sz << 20) | 0xFF000);
ram_sz = 0;
}
i++;
}
in_win = &pex->bridge.pex_inbound_win[i];
out_le32(&in_win->barl, CONFIG_SYS_IMMR);
out_le32(&in_win->barh, 0);
out_le32(&in_win->tar, CONFIG_SYS_IMMR);
out_le32(&in_win->ar, PEX_IWAR_EN |
PEX_IWAR_TYPE_NO_PF | PEX_IWAR_SIZE_1M);
/* Enable the host virtual INTX interrupts */
out_le32(&pex->bridge.pex_int_axi_misc_enb,
in_le32(&pex->bridge.pex_int_axi_misc_enb) | 0x1E0);
/* Hose configure header is memory-mapped */
hose_cfg_base = (void *)pex;
get_clocks();
/* Configure the PCIE controller core clock ratio */
out_le32(hose_cfg_base + PEX_GCLK_RATIO,
(((bus ? gd->arch.pciexp2_clk : gd->arch.pciexp1_clk)
/ 1000000) * 16) / 333);
udelay(1000000);
/* Do Type 1 bridge configuration */
out_8(hose_cfg_base + PCI_PRIMARY_BUS, 0);
out_8(hose_cfg_base + PCI_SECONDARY_BUS, 1);
out_8(hose_cfg_base + PCI_SUBORDINATE_BUS, 255);
/*
* Write to Command register
*/
reg16 = in_le16(hose_cfg_base + PCI_COMMAND);
reg16 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO |
PCI_COMMAND_SERR | PCI_COMMAND_PARITY;
out_le16(hose_cfg_base + PCI_COMMAND, reg16);
/*
* Clear non-reserved bits in status register.
*/
out_le16(hose_cfg_base + PCI_STATUS, 0xffff);
out_8(hose_cfg_base + PCI_LATENCY_TIMER, 0x80);
out_8(hose_cfg_base + PCI_CACHE_LINE_SIZE, 0x08);
printf("PCIE%d: ", bus);
#define PCI_LTSSM 0x404 /* PCIe Link Training, Status State Machine */
#define PCI_LTSSM_L0 0x16 /* L0 state */
reg16 = in_le16(hose_cfg_base + PCI_LTSSM);
if (reg16 >= PCI_LTSSM_L0)
printf("link\n");
else
printf("No link\n");
mpc83xx_pcie_register_hose(bus, reg, reg16 >= PCI_LTSSM_L0);
}
/*
* The caller must have already set SCCR, SERDES and the PCIE_LAW BARs
* must have been set to cover all of the requested regions.
*/
void mpc83xx_pcie_init(int num_buses, struct pci_region **reg)
{
int i;
/*
* Release PCI RST Output signal.
* Power on to RST high must be at least 100 ms as per PCI spec.
* On warm boots only 1 ms is required, but we play it safe.
*/
udelay(100000);
if (num_buses > ARRAY_SIZE(mpc83xx_pcie_cfg_space)) {
printf("Second PCIE host contoller not configured!\n");
num_buses = ARRAY_SIZE(mpc83xx_pcie_cfg_space);
}
for (i = 0; i < num_buses; i++)
mpc83xx_pcie_init_bus(i, reg[i]);
}