1
1
Files
zyxel-vmg8825_b50b-cfw/package/boot/uboot-zyxel/files/board/ruby/pcie.c
T
2026-04-17 18:33:03 +02:00

592 lines
16 KiB
C

/*
* (C) Copyright 2011 Quantenna Communications Inc.
*
* 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
*/
/*
* Header file which describes Ruby PCI Express specific functions.
*/
#include <common.h>
#include <command.h>
#include <asm/arch/platform.h>
#include <environment.h>
#include "ruby.h"
#include "ruby_board_cfg.h"
#include "ruby_pcie_bda.h"
#include "pcie.h"
#include "board_cfg.h"
static inline void arc_write_uncached_32(volatile void *ptr, uint32_t data)
{
uint32_t start = (unsigned long)ptr;
invalidate_dcache_range(start, start + sizeof(uint32_t));
_arc_write_uncached_32(ptr, data);
}
/*
* for End Point mode
* Allocate and setup BAR mapping for shared memory
*/
static int32_t setup_atu_shmem(void)
{
uint32_t val = 0x0;
/* Select shared mem region */
writel(PCIE_SHMEM_REGION, RUBY_PCIE_ATU_VIEW);
/* Bar mapped area in EP */
writel(PCIE_BAR_SHMEM_LO, RUBY_PCIE_ATU_TARGET_LO);
writel(PCIE_BAR_SHMEM_HI, RUBY_PCIE_ATU_TARGET_HI);
/* Set BAR size to EP memory */
writel(PCIE_BAR_SHMEM_LEN, RUBY_PCIE_ATU_BASE_LIMIT);
/* Define region of type memory */
writel(PCIE_ATU_MEMREGION, RUBY_PCIE_ATU_CTL1);
/* Enable BAR mapped region */
writel(PCIE_SHMEM_ENABLE, RUBY_PCIE_ATU_CTL2);
val = readl(RUBY_PCIE_ATU_CTL2);
printf("PCIe Shmem BAR%u=0x%x Len:%uk\n", PCIE_BAR_SHMEM,
PCIE_BAR_SHMEM_LO, (PCIE_BAR_SHMEM_LEN >> 10) + 1);
return 0;
}
/*
* for End Point mode
* Allocate and setup BAR mapping for syscontrol
*/
static int32_t setup_atu_sysctl(void)
{
uint32_t val = 0x0;
/* Select shared mem region */
writel(PCIE_SYSCTL_REGION, RUBY_PCIE_ATU_VIEW);
/* Bar mapped area in EP */
writel(PCIE_BAR_SYSCTL_LO, RUBY_PCIE_ATU_TARGET_LO);
writel(PCIE_BAR_SYSCTL_HI, RUBY_PCIE_ATU_TARGET_HI);
/* Set size */
writel(PCIE_BAR_SYSCTL_LEN, RUBY_PCIE_ATU_BASE_LIMIT);
/* Define region of type memory */
writel(PCIE_ATU_MEMREGION, RUBY_PCIE_ATU_CTL1);
/* Enable BAR mapped region */
writel(PCIE_SYSCTL_ENABLE, RUBY_PCIE_ATU_CTL2);
val = readl(RUBY_PCIE_ATU_CTL2);
printf("PCIe Sysctl BAR%u=0x%x Len:%uk\n", PCIE_BAR_SYSCTL,
PCIE_BAR_SYSCTL_LO, ( PCIE_BAR_SYSCTL_LEN >> 10) + 1);
return 0;
}
/*
* for End Point mode *
* map the host memory to target
*/
static int32_t setup_atu_host(uint32_t addr_mask)
{
uint32_t val = 0x0;
/* Select shared mem region */
writel(PCIE_HOSTMEM_REGION, RUBY_PCIE_ATU_VIEW);
/* Memory mapped area in EP )*/
writel(PCIE_HOSTMEM_EP_START_LO, RUBY_PCIE_ATU_BASE_LO);
writel(PCIE_HOSTMEM_EP_START_HI, RUBY_PCIE_ATU_BASE_HI);
/* Memory mapped area in Host*/
writel(PCIE_HOSTMEM_START_LO, RUBY_PCIE_ATU_TARGET_LO);
writel(PCIE_HOSTMEM_START_HI, RUBY_PCIE_ATU_TARGET_HI);
/* Set size */
writel(PCIE_HOSTMEM_EP_START_LO + addr_mask, RUBY_PCIE_ATU_BASE_LIMIT);
/* Define region of type memory */
writel(PCIE_ATU_MEMREGION, RUBY_PCIE_ATU_CTL1);
/* Enable BAR mapped region */
writel(PCIE_HOSTMEM_REGION_ENABLE, RUBY_PCIE_ATU_CTL2);
val = readl(RUBY_PCIE_ATU_CTL2);
printf("%u:Mem: EP(0x%x->0x%x) Host(0x%x->0x%x)\n", PCIE_HOSTMEM_REGION,
PCIE_HOSTMEM_EP_START_LO,PCIE_HOSTMEM_EP_START_LO + addr_mask,
PCIE_HOSTMEM_START_LO,PCIE_HOSTMEM_START_LO + addr_mask);
return 0;
}
/*
* for End Point mode
* Setup 64KB region ATU for target to access host msi register
*/
static int setup_atu_msi(volatile ruby_pcie_bda_t *bda, uint32_t end_addr)
{
uint16_t flag = 0;
uint32_t msi_addr = 0x0;
uint32_t msi_addr_up = 0x0;
uint32_t val = 0x0;
uint32_t msi64;
flag = readl(PCIE_MSI_CAP) >> 16;
msi_addr = readl(PCIE_MSI_LOW_ADDR);
msi64 = (flag & MSI_64_EN);
/* Exit if MSI is not enabled */
if (!(flag & MSI_EN)) {
return 1;
}
/* If address range of of MSI data area is within primary ATU region, we can use one ATU for both */
if ((msi_addr + 2) <= end_addr)
{
/* Setup EP MSI address */
arc_write_uncached_32(&bda->bda_msi_addr,PCIE_MSI_EP_START_LO + PCIE_MSI_ADDR_OFFSET(msi_addr));
return 1;
}
/* Enable ATU viewport */
writel(PCIE_MSI_REGION, RUBY_PCIE_ATU_VIEW);
/* mapped region area in EP */
writel(PCIE_MSI_EP_START_LO, RUBY_PCIE_ATU_BASE_LO);
writel(PCIE_MSI_EP_START_HI, RUBY_PCIE_ATU_BASE_HI);
writel(PCIE_MSI_EP_END, RUBY_PCIE_ATU_BASE_LIMIT);
/* Set host side msi addr */
writel(PCIE_MSI_ADDR_ALIGN(msi_addr), RUBY_PCIE_ATU_TARGET_LO);
arc_write_uncached_32(&bda->bda_flags,PCIE_BDA_MSI| arc_read_uncached_32(&bda->bda_flags));
if (msi64) {
msi_addr_up = readl(PCIE_MSI_HIG_ADDR);
writel(msi_addr_up, RUBY_PCIE_ATU_TARGET_HI);
} else {
writel(0x00000000, RUBY_PCIE_ATU_TARGET_HI);
}
/* Setup EP MSI address */
arc_write_uncached_32(&bda->bda_msi_addr,PCIE_MSI_EP_START_LO + PCIE_MSI_ADDR_OFFSET(msi_addr));
/* Define region of type memory */
writel(PCIE_ATU_MEMREGION, RUBY_PCIE_ATU_CTL1);
/* Enable region */
writel(PCIE_MSI_REGION_ENABLE, RUBY_PCIE_ATU_CTL2);
val = readl(RUBY_PCIE_ATU_CTL2);
printf("%u:MSI%s: Host:0x%x%x EP:0x%x\n",PCIE_MSI_REGION, (msi64) ? "64" : "",
msi_addr_up, msi_addr, bda->bda_msi_addr);
return 0;
}
/*
* for End Point mode
*/
static void setup_atu_outbound(volatile ruby_pcie_bda_t *bda)
{
uint32_t dma_mask = 0;
uint32_t region_size_mask = (PCIE_REGION_END - PCIE_REGION_BASE);
if (setup_atu_msi(bda, region_size_mask)){
dma_mask = region_size_mask ;
} else {
dma_mask = region_size_mask - PCIE_MSIMEM_SIZE;
}
setup_atu_host(dma_mask);
arc_write_uncached_32(&bda->bda_dma_mask, dma_mask);
}
/*
* for End Point mode
*/
static void setup_atu_inbound(void)
{
setup_atu_shmem();
setup_atu_sysctl();
}
static int bootpoll(volatile ruby_pcie_bda_t *bda, uint32_t state)
{
while (arc_read_uncached_32(&bda->bda_bootstate) != state)
{
if (arc_read_uncached_32(&bda->bda_flags) & PCIE_BDA_ERROR_MASK)
return -1;
udelay(1000);
}
return 0;
}
static void set_bootstate(volatile ruby_pcie_bda_t *bda, uint32_t state)
{
arc_write_uncached_32(&bda->bda_bootstate, state);
}
static void booterror(volatile ruby_pcie_bda_t *bda)
{
if (PCIE_BDA_HOST_NOFW_ERR & arc_read_uncached_32(&bda->bda_flags))
printf("There is no firmware in host file system!\n");
else if (PCIE_BDA_HOST_MEMALLOC_ERR & arc_read_uncached_32(&bda->bda_flags))
printf("Host alloc memory block for firmware download failed!\n");
else if (PCIE_BDA_HOST_MEMMAP_ERR & arc_read_uncached_32(&bda->bda_flags))
printf("Host do dma map for share memory block failed!\n");
else
printf("Other error found in host side , bda flag: 0x%x!\n", bda->bda_flags);
}
#define RUN(args...) _run(__FUNCTION__, args)
static int _run(const char* function_name, ...)
{
va_list args;
char cmdbuf[64];
sprintf(cmdbuf, args);
printf("%s: %s\n", function_name, cmdbuf);
return run_command(cmdbuf, 0);
}
/*
* for End Point mode
*/
int do_flash_boot (volatile ruby_pcie_bda_t *bda)
{
unsigned long live_addr = 0;
unsigned long live_size = 0;
const unsigned long mem_addr = QTNBOOT_COPY_DRAM_ADDR;
char *live_addr_str = getenv (LIVE_IMG_ADDR_ARG);
char *live_size_str = getenv (LIVE_IMG_SIZE_ARG);
printf("do flash boot\n");
set_bootstate(bda,RUBY_BDA_FW_FLASH_BOOT);
if (live_addr_str && live_size_str) {
live_addr = simple_strtoul(live_addr_str, NULL, 0);
live_size = simple_strtoul(live_size_str, NULL, 0);
} else {
printf("Variables: %s %s must be set\n",
LIVE_IMG_ADDR_ARG,
LIVE_IMG_SIZE_ARG);
arc_write_uncached_32(&bda->bda_flags, PCIE_BDA_TARGET_FBOOT_ERR | arc_read_uncached_32(&bda->bda_flags));
return 1;
}
/* attempt to load the live image into memory and boot it. */
RUN("spi_flash read 0x%08lx 0x%08lx 0x%08lx", live_addr, mem_addr, live_size);
RUN("bootm 0x%08lx", mem_addr);
/* never gets to here */
arc_write_uncached_32(&bda->bda_flags, PCIE_BDA_TARGET_FBOOT_ERR | arc_read_uncached_32(&bda->bda_flags));
printf("flash boot error!\n");
return 0;
}
/*
* for End Point mode
*/
static int do_pcieboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int rc = 0;
volatile ruby_pcie_bda_t *bda = (ruby_pcie_bda_t *)(PCIE_BDA);
void *start = (void *)PCIE_FW_LZMA_LOAD;
void *dstaddr = start;
uint32_t size, i=0;
volatile void *srcaddr;
char *local_args[2];
char load_addr[16];
char *s;
extern int do_bootm (cmd_tbl_t *, int, int, char *[]);
/* set the flash_present flag if env indicate we have firmware in flash */
s = getenv("flash_img");
if (s && (*s == '1')) {
arc_write_uncached_32(&bda->bda_flags,PCIE_BDA_FLASH_PRESENT| arc_read_uncached_32(&bda->bda_flags));
}
/* Wait for host ready */
bootpoll(bda, RUBY_BDA_FW_HOST_RDY);
setup_atu_outbound(bda);
set_bootstate(bda,RUBY_BDA_FW_TARGET_RDY);
bootpoll(bda, RUBY_BDA_FW_TARGET_BOOT);
/* boot from flash */
if (PCIE_BDA_FLASH_BOOT & arc_read_uncached_32((void *)&bda->bda_flags)) {
do_flash_boot(bda);
return rc;
}
set_bootstate(bda,RUBY_BDA_FW_LOAD_RDY);
printf("Ready to load firmware....\n");
if (bootpoll(bda, RUBY_BDA_FW_HOST_LOAD)) {
booterror(bda);
return -1;
}
set_bootstate(bda,RUBY_BDA_FW_EP_RDY);
bootpoll(bda, RUBY_BDA_FW_BLOCK_RDY);
srcaddr = (void *)arc_read_uncached_32(&bda->bda_img);
size = arc_read_uncached_32(&bda->bda_img_size);
dcache_disable();
/* Keep loading until we see a zero sized block */
while(srcaddr && size){
printf("PCIe Load FW[%u] 0x%x->0x%x Sz:%u...\n", i++, (uint32_t)srcaddr, (uint32_t)dstaddr, size);
memcpy_fromio(dstaddr, srcaddr, size);
/* No error checking done, do_bootm() performs the CRC32 check */
/* Block done, inform host */
set_bootstate(bda, RUBY_BDA_FW_BLOCK_DONE);
/* Wait for next block */
bootpoll(bda, RUBY_BDA_FW_BLOCK_RDY);
srcaddr = (void *)arc_read_uncached_32(&bda->bda_img);
dstaddr += size;
size = arc_read_uncached_32(&bda->bda_img_size);
}
/* Invalidate i-cache */
invalidate_icache_range((int)start, (int)(dstaddr - 1));
/* Acknowledge the last zero sized block */
set_bootstate(bda, RUBY_BDA_FW_BLOCK_DONE);
/* Wait for bootload end message */
bootpoll(bda, RUBY_BDA_FW_BLOCK_END);
/* Tell host we are done */
set_bootstate(bda, RUBY_BDA_FW_LOAD_DONE);
dcache_enable();
sprintf(load_addr,"0x%08lx", (unsigned long)PCIE_FW_LZMA_LOAD);
local_args[0] = argv[0];
local_args[1] = load_addr;
printf("PCIe Loadaddr:%s\n",load_addr);
rc = do_bootm(cmdtp, 0 , 2 ,local_args);
if (rc) {
set_bootstate(bda, RUBY_BDA_FW_LOAD_FAIL);
arc_write_uncached_32(&bda->bda_flags, PCIE_BDA_TARGET_FWLOAD_ERR | arc_read_uncached_32(&bda->bda_flags));
}
return rc;
}
static int on_off (const char *s)
{
if (strcmp(s, "on") == 0) {
return (1);
} else if (strcmp(s, "off") == 0) {
return (0);
}
return (-1);
};
static void msi_enable(void)
{
ulong var=0;
var = readl(PCIE_MSI_CAP);
writel(var|RUBY_PCIE_MSI_ENABLE, PCIE_MSI_CAP);
printf("msi enabled\n");
}
static void msi_disable(void)
{
ulong var=0;
var = readl(PCIE_MSI_CAP);
writel(var&~RUBY_PCIE_MSI_ENABLE, PCIE_MSI_CAP);
printf("msi disabled\n");
}
/*
* for End Point mode
*/
static int msi_config (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
switch (argc) {
case 2: /* on / off */
switch (on_off(argv[1])) {
case 1:
msi_enable();
break;
case 0:
msi_disable();
break;
default: cmd_usage(cmdtp);
return 1;
}
break;
case 1: /* default on */
msi_enable();
break;
default: cmd_usage(cmdtp);
return 1;
}
return 0;
}
/*
* Exported functions - visible outside of this module
*/
/* enable or disable MSI */
U_BOOT_CMD(
msi_cfg, 2, 1, msi_config,
"enable or disable msi",
"[on, off]\n"
" - enable or disable msi with cmd msi_cfg [on, off]\n"
);
/* pcieboot */
U_BOOT_CMD(pcieboot,CONFIG_SYS_MAXARGS, 0, do_pcieboot,
"boot from pcie. Waits for host to load memory and then calls bootm",
NULL);
/*
* maybe move this later, for now we just need to remove pcie reset and set link
* flags will be used to do any back door init we might require
*/
void pcie_ep_init(size_t memsz, uint32_t flags )
{
uint32_t i = 0;
uint32_t bar64 = PCIE_CFG_BAR64;
uint32_t msi = PCIE_CFG_MSI;
volatile ruby_pcie_bda_t *bda = (ruby_pcie_bda_t *)(PCIE_BDA);
char *msi_en;
/* PCIe init */
writel(RUBY_SYS_CTL_RESET_IOSS|RUBY_SYS_CTL_RESET_PCIE,RUBY_SYS_CTL_CPU_VEC_MASK);
writel(0,RUBY_SYS_CTL_CPU_VEC);
udelay(10);
writel(RUBY_SYS_CTL_RESET_IOSS|RUBY_SYS_CTL_RESET_PCIE,RUBY_SYS_CTL_CPU_VEC);
/* Zero out boot data area */
memset((void *)bda, 0, PCIE_BDA_LEN);
arc_write_uncached_32(&bda->bda_flags, (RUBY_PCIE_BDA_VERSION << 4));
arc_write_uncached_32(&bda->bda_dma_offset, PCIE_HOSTMEM_EP_START_LO);
set_bootstate(bda, RUBY_BDA_PCIE_INIT);
/* Disable all BARs */
for (i = 0 ; i < RUBY_PCIE_BAR_NUM; i++)
{
writel(1, RUBY_PCIE_BAR_MASK(i));
writel(0x0, RUBY_PCIE_BAR_MASK(i));
}
/* Disable expansion ROM */
writel(1, PCIE_ROM_MASK_ADDR);
writel(0x0, PCIE_ROM_MASK_ADDR);
/* Setup Sysctl BAR */
writel(1, RUBY_PCIE_BAR_MASK(PCIE_BAR_SYSCTL));
writel(PCIE_BAR_SYSCTL_LEN, RUBY_PCIE_BAR_MASK(PCIE_BAR_SYSCTL));
writel(PCIE_BAR_CFG(bar64), RUBY_PCIE_BAR(PCIE_BAR_SYSCTL));
/* Setup Shared memory BAR */
writel(1, RUBY_PCIE_BAR_MASK(PCIE_BAR_SHMEM));
writel(PCIE_BAR_SHMEM_LEN, RUBY_PCIE_BAR_MASK(PCIE_BAR_SHMEM));
writel(PCIE_BAR_CFG(bar64), RUBY_PCIE_BAR(PCIE_BAR_SHMEM));
/* Check to see if MSI override has been added to uboot env */
msi_en = getenv(PCIE_MSI_ENV);
if (msi_en) {
msi = simple_strtoul(msi_en, NULL, 0);
}
/* Enable MSI */
if (msi) {
uint32_t var = readl(PCIE_MSI_CAP);
writel(var|RUBY_PCIE_MSI_ENABLE, PCIE_MSI_CAP);
}
/* Setup ATU Inbound BAR mappings*/
setup_atu_inbound();
/* Enable and wait for link up */
writel(PCIE_DEFAULT_CFG0,RUBY_SYS_CTL_PCIE_CFG0);
printf("polling for PCIe Link up\n");
while (1) {
if (readl(RUBY_SYS_CTL_CSR) & PCIE_LINKUP)
break;
udelay(10); /* Delay. */
}
set_bootstate(bda, RUBY_BDA_PCIE_RDY);
}
/*
* init for root complex mode
*/
void pcie_rc_init(void)
{
/* set as RC mode */
writel(SYS_RST_PCIE|SYS_RST_IOSS, RUBY_SYS_CTL_CPU_VEC_MASK);
writel(SYS_RST_PCIE|SYS_RST_IOSS, RUBY_SYS_CTL_CPU_VEC);
writel(PCIE_CFG0_DEFAULT_VALUE, RUBY_SYS_CTL_PCIE_CFG0); /* reset */
writel(PCIE_CFG0_DEFAULT_VALUE|PCIE_CFG_RC_MODE, RUBY_SYS_CTL_PCIE_CFG0); /* RC-mode */
/* pci config space map: Define outbound region-0 that maps PCIE slave region to PCI config space */
writel(RUBY_PCIE_ATU_OB_REGION(0), RUBY_PCIE_ATU_VIEW);
writel(RUBY_PCIE_CONFIG_REGION, RUBY_PCIE_ATU_BASE_LO);
writel(0x00000000, RUBY_PCIE_ATU_BASE_HI);
writel(RUBY_PCIE_CONFIG_REGION + (RUBY_PCI_RC_CFG_SIZE - 1), RUBY_PCIE_ATU_BASE_LIMIT );
writel(0x00000000, RUBY_PCIE_ATU_TARGET_LO);
writel(0, RUBY_PCIE_ATU_TARGET_HI);
writel(4, RUBY_PCIE_ATU_CTL1);
writel(RUBY_PCIE_ATU_OB_ENABLE|RUBY_PCIE_ATU_CFG_SHIFT, RUBY_PCIE_ATU_CTL2);
/* pci memory space map: Define outbound region-1 that maps PCIE slave region to PCI mem space */
writel(RUBY_PCIE_ATU_OB_REGION(1), RUBY_PCIE_ATU_VIEW);
writel(RUBY_PCI_RC_MEM_START, RUBY_PCIE_ATU_BASE_LO);
writel(0x00000000, RUBY_PCIE_ATU_BASE_HI);
writel(RUBY_PCI_RC_MEM_START + (RUBY_PCI_RC_MEM_WINDOW - 1), RUBY_PCIE_ATU_BASE_LIMIT );
writel(0xc0000000, RUBY_PCIE_ATU_TARGET_LO);
writel(0, RUBY_PCIE_ATU_TARGET_HI);
writel(0, RUBY_PCIE_ATU_CTL1);
writel(RUBY_PCIE_ATU_OB_ENABLE, RUBY_PCIE_ATU_CTL2);
/* pci access enable */
//writel(RUBY_PCI_RC_MEM_START, RUBY_PCIE_BAR(0));
writel(PCIE_MEM_EN | PCIE_IO_EN | PCIE_BUS_MASTER_EN, RUBY_PCIE_CMD_REG);
/* Set up msi generation info */
writel(RUBY_PCIE_MSI_REGION, RUBY_MSI_ADDR_LOWER);
writel(0, RUBY_MSI_ADDR_UPPER);
writel(BIT(0), RUBY_MSI_INT_ENABLE);
writel(0, RUBY_PCIE_MSI_MASK);
}
void board_pcie_init(size_t memsz, uint32_t flags )
{
if (flags & PCIE_RC_MODE) {
printf("init board as PCIe Root Complex mode\n");
pcie_rc_init();
} else {
printf("init board as PCIe End Point mode\n");
pcie_ep_init(memsz, 0);
}
}