blob: 351173f8378ec306a4f3c894b3d8e134dd8b35d3 [file] [log] [blame]
/*
* (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_pcie_bda.h"
#include "pcie.h"
#include "ddr.h"
#include "malloc.h"
#include "spi_flash.h"
#define BUFSIZE 256
#define ENV_STAGE2 "stage2"
/*
* for End Point mode
* Allocate and setup BAR mapping for shared memory
*/
static int32_t setup_atu_shmem(void)
{
uint32_t __attribute__((unused)) 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,
(unsigned int)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 __attribute__((unused)) 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
* Allocate and setup BAR mapping for PCIe DMA registers
*/
static int32_t setup_atu_dma(void)
{
uint32_t __attribute__((unused)) val = 0x0;
/* Select dma register region */
writel(PCIE_DMAREG_REGION, RUBY_PCIE_ATU_VIEW);
/* Bar mapped area in EP */
writel(PCIE_BAR_DMAREG_LO, RUBY_PCIE_ATU_TARGET_LO);
writel(PCIE_BAR_DMAREG_HI, RUBY_PCIE_ATU_TARGET_HI);
/* Set size */
writel(PCIE_BAR_DMAREG_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_DMAREG_ENABLE, RUBY_PCIE_ATU_CTL2);
val = readl(RUBY_PCIE_ATU_CTL2);
printf("PCIe DMA BAR%u=0x%x Len:%uk\n", PCIE_BAR_DMAREG,
PCIE_BAR_DMAREG_LO, ( PCIE_BAR_DMAREG_LEN >> 10) + 1);
return 0;
}
/*
* for End Point mode *
* map the host memory to target
*/
static int32_t setup_atu_host(volatile qdpc_pcie_bda_t *bda)
{
uint32_t host_mem_start = PCIE_HOSTMEM_EP_START_LO - readl(&bda->bda_dma_offset);
uint32_t __attribute__((unused)) 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(host_mem_start, RUBY_PCIE_ATU_TARGET_LO);
writel(PCIE_HOSTMEM_START_HI, RUBY_PCIE_ATU_TARGET_HI);
/* Set size */
writel(PCIE_HOSTMEM_EP_END, 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, PCIE_HOSTMEM_EP_END,
host_mem_start, host_mem_start + PCIE_HOSTMEM_DMA_MASK);
return 0;
}
/*
* for End Point mode *
* map the host buffer descriptor to target
* need to finish the mapping after linux bootup
*/
static int32_t setup_atu_hostbd_early(void)
{
/* Select shared mem region */
writel(PCIE_HOSTBD_REGION, RUBY_PCIE_ATU_VIEW);
/* Memory mapped area in EP )*/
writel(PCIE_HOSTBD_EP_START_LO, RUBY_PCIE_ATU_BASE_LO);
writel(PCIE_HOSTBD_EP_START_HI, RUBY_PCIE_ATU_BASE_HI);
/* Set size */
writel(PCIE_HOSTBD_EP_END, RUBY_PCIE_ATU_BASE_LIMIT);
/* Define region of type memory */
writel(PCIE_ATU_MEMREGION, RUBY_PCIE_ATU_CTL1);
printf("%u:BD: EP(0x%x->0x%x) Host(dynamic alloc in RC)\n", PCIE_HOSTBD_REGION,
PCIE_HOSTBD_EP_START_LO,PCIE_HOSTBD_EP_END);
return 0;
}
/*
* for End Point mode
* Setup 64KB region ATU for target to access host msi register
*/
static int setup_atu_msi(volatile qdpc_pcie_bda_t *bda)
{
uint16_t flag = 0;
uint32_t msi_addr = 0x0;
uint32_t msi_addr_up = 0x0;
uint32_t __attribute__((unused)) val = 0x0;
uint32_t msi64 = 0;
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)) {
printf("PCIe Legacy Interrupt Support\n");
return 1;
}
printf("PCIe MSI Interrupt Support\n");
printf("%s: msi_addr=%08x\n", __func__, msi_addr);
arc_write_uncached_32(&bda->bda_flags,PCIE_BDA_MSI| arc_read_uncached_32(&bda->bda_flags));
/* 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);
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
*/
void setup_atu_outbound(volatile qdpc_pcie_bda_t *bda)
{
setup_atu_msi(bda);
setup_atu_host(bda);
setup_atu_hostbd_early();
arc_write_uncached_32(&bda->bda_dma_mask, PCIE_HOSTMEM_DMA_MASK);
}
/*
* for End Point mode
*/
static void setup_atu_inbound(void)
{
setup_atu_shmem();
setup_atu_sysctl();
setup_atu_dma();
}
static void setup_pcie_capability(void)
{
uint32_t *prev_cap = (uint32_t *)PCIE_CAP_PTR;
uint32_t *curr_cap = NULL;
uint32_t prev_val, curr_val;
uint8_t prev_shift = 0, curr_shift;
uint8_t prev_cap_offset;
uint8_t curr_cap_offset;
uint8_t curr_cap_id;
const char *val;
val = getenv("msi");
if ( val == NULL || val[0] != '0')
return;
do {
prev_val = readl(prev_cap);
prev_cap_offset = ((prev_val >> prev_shift) & 0xff);
curr_shift = 8;
curr_cap = (uint32_t *)(PCIE_BASE_ADDRESS + prev_cap_offset);
curr_val = readl(curr_cap);
curr_cap_id = (uint8_t)(curr_val & 0xff);
curr_cap_offset = (uint8_t)((curr_val >> curr_shift) & 0xff);
if (curr_cap_id == 0x05) {
writel((curr_cap_offset << prev_shift), prev_cap);
printf("PCIe disabling MSI capability\n");
break;
}
prev_cap = curr_cap;
prev_shift = curr_shift;
} while (curr_cap_offset != 0);
}
static int bootpoll(volatile qdpc_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 qdpc_pcie_bda_t *bda, uint32_t state)
{
arc_write_uncached_32(&bda->bda_bootstate, state);
}
static void booterror(volatile qdpc_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);
}
#ifndef TOPAZ_EP_MINI_UBOOT
#ifdef GFRG240
static void set_tiny_mtdparts(void)
{
char mtdparts[BUFSIZE];
sprintf(mtdparts, "spi_flash:"
"%dk(" MTD_PARTNAME_UBOOT_BIN "),"
"%dk(" MTD_PARTNAME_UBOOT_ENV "),"
"%dk(" MTD_PARTNAME_RESERVED_A "),"
"%dk(" MTD_PARTNAME_VENDOR "),"
"%dk(" MTD_PARTNAME_RESERVED_B "),"
"%dk(" MTD_PARTNAME_HNVRAM "),"
"%dk(" MTD_PARTNAME_KERNEL0 "),"
"%dk(" MTD_PARTNAME_KERNEL1 "),"
"-(" MTD_PARTNAME_DATA ")",
UBOOT_TEXT_PARTITION_SIZE / 1024,
UBOOT_ENV_PARTITION_SIZE / 1024,
UBOOT_RESERVED_A_PARTITION_SIZE / 1024,
UBOOT_VENDOR_PARTITION_SIZE / 1024,
UBOOT_RESERVED_B_PARTITION_SIZE / 1024,
UBOOT_HNVRAM_PARTITION_SIZE / 1024,
UBOOT_KERNEL_PARTITION_SIZE / 1024,
UBOOT_KERNEL_PARTITION_SIZE / 1024
);
setenv("mtdparts", mtdparts);
}
static int prepare_tiny_bootargs(void)
{
set_tiny_mtdparts();
return 0;
}
#else // #ifdef GFRG240
static void set_tiny_mtdparts(unsigned long linux_safety_size, unsigned long linux_live_size)
{
char mtdparts[BUFSIZE];
sprintf(mtdparts, "spi_flash:"
"%dk(" MTD_PARTNAME_UBOOT_TINY_BIN "),"
"%dk(" MTD_PARTNAME_UBOOT_ENV "),"
"%dk(" MTD_PARTNAME_UBOOT_ENV_BAK "),"
"%luk(" MTD_PARTNAME_LINUX_SAFETY "),"
"%luk(" MTD_PARTNAME_LINUX_LIVE "),"
"%dk(" MTD_PARTNAME_UBOOT_SAFETY "),"
"%dk(" MTD_PARTNAME_UBOOT_LIVE "),"
"-(" MTD_PARTNAME_DATA ")",
UBOOT_TINY_TEXT_PARTITION_SIZE / 1024,
UBOOT_ENV_PARTITION_SIZE / 1024,
UBOOT_ENV_PARTITION_SIZE / 1024,
linux_safety_size / 1024,
linux_live_size / 1024,
UBOOT_TEXT_PARTITION_SIZE / 1024,
UBOOT_TEXT_PARTITION_SIZE / 1024
);
setenv("mtdparts", mtdparts);
}
static int prepare_tiny_bootargs(void)
{
set_tiny_mtdparts(TINY_CFG_SIZE_16M_FLASH_1_IMG / 2, TINY_CFG_SIZE_16M_FLASH_1_IMG / 2);
return 0;
}
#endif // #ifdef GFRG240
static void set_tiny_bootargs(void)
{
RUN("setenv bootargs ${bootargs} mtdparts=${mtdparts}");
}
/*
* for End Point mode
*/
int do_flash_boot (volatile qdpc_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);
char *stage2_str = getenv(ENV_STAGE2);
unsigned long is_stage2 = stage2_str ? simple_strtoul(stage2_str, NULL, 10) : 0;
if (is_stage2 && prepare_tiny_bootargs())
return -1;
printf("do flash boot\n");
set_bootstate(bda, QDPC_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;
}
if (is_stage2)
set_tiny_bootargs();
/* 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;
}
#endif
/*
* for End Point mode
*/
static int pci_endian_detect(volatile qdpc_pcie_bda_t *bda)
{
uint32_t pci_endian;
while (readl(&bda->bda_pci_pre_status) != QDPC_PCI_ENDIAN_VALID_STATUS)
udelay(1000);
pci_endian = readl(&bda->bda_pci_endian);
if (pci_endian == QDPC_PCI_ENDIAN_DETECT_DATA) {
printf("PCI memory region is little endian\n");
writel(QDPC_PCI_LITTLE_ENDIAN, &bda->bda_pci_endian);
} else if (pci_endian == QDPC_PCI_ENDIAN_REVERSE_DATA) {
printf("PCI memory region is big endian\n");
writel(QDPC_PCI_BIG_ENDIAN, &bda->bda_pci_endian);
} else {
printf("PCI memory endian value:%08x is invalid - using little endian\n", pci_endian);
writel(QDPC_PCI_LITTLE_ENDIAN, &bda->bda_pci_endian);
}
writel(QDPC_PCI_ENDIAN_VALID_STATUS, &bda->bda_pci_post_status);
return 0;
}
static int host_mem_start_address_detect(volatile qdpc_pcie_bda_t *bda)
{
while ((readl(&bda->bda_dma_offset) & PCIE_DMA_OFFSET_ERROR_MASK) == PCIE_DMA_OFFSET_ERROR)
udelay(1000);
printf("Host memory start address is 0x%08x\n",
PCIE_HOSTMEM_EP_START_LO - readl(&bda->bda_dma_offset));
return 0;
}
static void pcie_dma_read(void *dar, void *sar, u32 size, u8 ch)
{
#define EDMA_TIMEOUT 100000
uint32_t tmp = 0;
int i;
writel(0x00000001, PCIE_DMA_RD_ENABLE);
writel(0x00000000, PCIE_DMA_RD_INTMASK);
writel(0x80000000, PCIE_DMA_CHNL_CONTEXT);
writel(0x04000008, PCIE_DMA_CHNL_CNTRL);
writel(size, PCIE_DMA_XFR_SIZE);
writel(sar, PCIE_DMA_SAR_LOW);
writel(0x00000000, PCIE_DMA_SAR_HIGH);
writel(dar, PCIE_DMA_DAR_LOW);
writel(0x00000000, PCIE_DMA_DAR_HIGH);
writel(0x00000000, PCIE_DMA_RD_DOORBELL);
for (i = 0; i < EDMA_TIMEOUT; i++) {
udelay(1);
tmp = readl(PCIE_DMA_RD_INTSTS);
if (tmp & PCIE_DMA_RD_DONE_STS(ch)) {
printf("done\n");
break;
}
if (tmp & PCIE_DMA_RD_ABORT_STS(ch)) {
printf("Error: eDMA abort\n");
break;
}
}
if (i == EDMA_TIMEOUT)
printf("Error: eDMA timeout\n");
writel(PCIE_DMA_RD_DONE_STS_CLR(ch), PCIE_DMA_RD_INTCLER);
}
int do_pcieboot(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
volatile qdpc_pcie_bda_t *bda = (qdpc_pcie_bda_t *)(RUBY_PCIE_BDA_ADDR);
uint32_t size, i=0;
void *srcaddr;
#ifdef TOPAZ_EP_MINI_UBOOT
extern unsigned long load_addr;
void *start = (void *)load_addr;
#else
void *start = (void *)PCIE_FW_LZMA_LOAD;
char *s = NULL;
char *local_args[2];
char load_addr[16];
char *stage2_str = getenv(ENV_STAGE2);
unsigned long is_stage2 = stage2_str ? simple_strtoul(stage2_str, NULL, 10) : 0;
#endif
void *dstaddr = start;
unsigned int tmp;
uint8_t ch = 0;
extern int do_bootm (cmd_tbl_t *, int, int, char *[]);
#ifndef TOPAZ_EP_MINI_UBOOT
if (is_stage2 && prepare_tiny_bootargs())
return -1;
#endif
set_bootstate(bda, QDPC_BDA_PCIE_RDY);
printf("Waiting for handshake start\n");
if (pci_endian_detect(bda)) {
printf("PCI memory endian detect failed\n");
}
/*
* workaround to fix tag clock issue by switching from Gen1 to Gen2
* two dummy read
*/
tmp = readl(0xcfff0000);
tmp = readl(0xcfff0000);
if (host_mem_start_address_detect(bda)) {
printf("Host memory start address detect failed\n");
}
#ifdef TOPAZ_EP_MINI_UBOOT
tmp = arc_read_uncached_32(&bda->bda_flags);
arc_write_uncached_32(&bda->bda_flags, PCIE_BDA_XMIT_UBOOT | tmp);
#else
/* set the flash_present flag if env indicate we have firmware in flash */
s = getenv("flash_img");
if (s && (*s == '1')) {
tmp = arc_read_uncached_32(&bda->bda_flags);
arc_write_uncached_32(&bda->bda_flags, PCIE_BDA_FLASH_PRESENT | tmp);
}
/*
* workaround to fix tag clock issue by switching from Gen1 to Gen2
* switch Gen2 from Gen1 here in full u-boot.bin for revB
*/
writel(PCIE_LINK_GEN2, PCIE_LINK_CTL2);
#endif
/* Wait for host ready */
bootpoll(bda, QDPC_BDA_FW_HOST_RDY);
setup_atu_outbound(bda);
set_bootstate(bda,QDPC_BDA_FW_TARGET_RDY);
bootpoll(bda, QDPC_BDA_FW_TARGET_BOOT);
#ifndef TOPAZ_EP_MINI_UBOOT
/* boot from flash */
if (PCIE_BDA_FLASH_BOOT & arc_read_uncached_32((void *)&bda->bda_flags)) {
do_flash_boot(bda);
return 0;
}
#endif
set_bootstate(bda,QDPC_BDA_FW_LOAD_RDY);
printf("Ready to load firmware....\n");
if (bootpoll(bda, QDPC_BDA_FW_HOST_LOAD)) {
booterror(bda);
return -1;
}
set_bootstate(bda, QDPC_BDA_FW_EP_RDY);
bootpoll(bda, QDPC_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 */
i = 0;
while (srcaddr && size) {
printf("PCIe Load FW[%u] 0x%x->0x%x Sz:%u...\n", i++, (uint32_t)srcaddr, (uint32_t)dstaddr, size);
pcie_dma_read((void *)virt_to_bus(dstaddr), srcaddr, size, ch);
/* Block done, inform host */
set_bootstate(bda, QDPC_BDA_FW_BLOCK_DONE);
/* Wait for next block */
bootpoll(bda, QDPC_BDA_FW_BLOCK_RDY);
srcaddr = (void *)arc_read_uncached_32(&bda->bda_img);
dstaddr += size;
size = arc_read_uncached_32(&bda->bda_img_size);
}
printf("PCIe Gen: %x\n", PCIE_LINK_MODE(readl(PCIE_LINK_STAT)));
/* Invalidate i-cache */
invalidate_icache_range((int)start, (int)(dstaddr - 1));
/* Acknowledge the last zero sized block */
set_bootstate(bda, QDPC_BDA_FW_BLOCK_DONE);
/* Wait for bootload end message */
bootpoll(bda, QDPC_BDA_FW_BLOCK_END);
/* Tell host we are done */
set_bootstate(bda, QDPC_BDA_FW_LOAD_DONE);
#ifdef TOPAZ_EP_MINI_UBOOT
extern char warm_boot;
extern char _start;
tmp = arc_read_uncached_32(&bda->bda_flags);
tmp &= ~(PCIE_BDA_XMIT_UBOOT);
arc_write_uncached_32(&bda->bda_flags, tmp);
/* warm boot up the full u-boot */
start += (&warm_boot - &_start);
printf("Go to address %p\n", start);
((void (*)(void))start)();
#else
dcache_enable();
sprintf(load_addr,"0x%08lx", (unsigned long)start);
local_args[0] = argv[0];
local_args[1] = load_addr;
if (is_stage2)
set_tiny_bootargs();
printf("PCIe Loadaddr:%s\n",load_addr);
do_bootm(cmdtp, 0 , 2 ,local_args);
#endif
/*
* it never reaches this pointer if boot successfully, otherwise it fails to run img
*/
set_bootstate(bda, QDPC_BDA_FW_LOAD_FAIL);
tmp = arc_read_uncached_32(&bda->bda_flags);
arc_write_uncached_32(&bda->bda_flags, PCIE_BDA_TARGET_FWLOAD_ERR | tmp);
return 1;
}
#ifndef TOPAZ_EP_MINI_UBOOT
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);
#endif
/*
* 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)
{
volatile qdpc_pcie_bda_t *bda = (qdpc_pcie_bda_t *)(RUBY_PCIE_BDA_ADDR);
/* Zero out boot data area */
memset((void *)bda, 0, RUBY_PCIE_BDA_SIZE);
/*
* Flush BDA zero to concret memory before any
* update to it
*/
flush_and_inv_dcache_range((unsigned long)bda,
(unsigned long)bda + RUBY_PCIE_BDA_SIZE);
arc_write_uncached_32(&bda->bda_flags, QDPC_PCIE_BDA_VERSION << 4);
/*
* Set dma offset to PCIE_HOSTMEM_EP_START_LO, and mask to a invalid value.
* The dma offset will be reset by root complex.
*/
arc_write_uncached_32(&bda->bda_dma_offset, PCIE_HOSTMEM_EP_START_LO | PCIE_DMA_OFFSET_ERROR);
set_bootstate(bda, QDPC_BDA_PCIE_INIT);
/* Setup ATU Inbound BAR mappings*/
setup_atu_inbound();
}
void pcie_ep_early_config(void)
{
uint32_t i = 0;
uint32_t bar64 = PCIE_CFG_BAR64;
/* Enable DMA Read Channel */
REG_WRITE(PCIE_DMA_RD_ENABLE, 0x00000001);
REG_WRITE(PCIE_DMA_RD_CHWTLOW,0x000001FF);
REG_WRITE(PCIE_DMA_RD_CHWTHIG,0x00000000);
/* Enable DMA write channel */
REG_WRITE(PCIE_DMA_WR_ENABLE, 0x00000001);
REG_WRITE(PCIE_DMA_WR_CHWTLOW,0x000001FF);
REG_WRITE(PCIE_DMA_WR_CHWTHIG,0x00000000);
/* 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));
/* Setup PCIE DMA register BAR */
writel(1, RUBY_PCIE_BAR_MASK(PCIE_BAR_DMAREG));
writel(PCIE_BAR_DMAREG_LEN, RUBY_PCIE_BAR_MASK(PCIE_BAR_DMAREG));
writel(PCIE_BAR_CFG(bar64), RUBY_PCIE_BAR(PCIE_BAR_DMAREG));
/* Setup PCIe capability structures */
setup_pcie_capability();
/*
* workaround to fix tag clock issue by switching from Gen1 to Gen2
* Init PCIe link as Gen1 mode
*/
if ((readl(RUBY_SYS_CTL_CSR) & 0xff) == TOPAZ_BOARD_REVB)
writel(PCIE_LINK_GEN1, PCIE_LINK_CTL2);
writel(0x00010020, PCIE_PORT_LINK_CTL);
writel(PCIE_CFG0_DEFAULT_VALUE, RUBY_SYS_CTL_PCIE_CFG0);
writel(0x00000001, RUBY_SYS_CTL_PCIE_CFG1);
writel(0x00000000, RUBY_SYS_CTL_PCIE_CFG2);
writel(0x45220000, RUBY_SYS_CTL_PCIE_CFG3);
writel(0x00100007, PCIE_CMDSTS);
writel(0xf << 22, RUBY_SYS_CTL_PCIE_SLV_REQ_MISC_INFO);
}
int pcie_linkup_check(void)
{
uint32_t loop_timeout = 0;
char *loop_timeout_str = NULL;
int i;
#define PCIE_LINK_TIMEOUT (65536)
printf("Polling for PCIe link up: ");
/*
* Set uboot environment variable max_link_poll to a large value
* if you want to skip timeout mechanism
*/
loop_timeout_str = getenv("max_link_poll");
if (loop_timeout_str)
loop_timeout = simple_strtoul(loop_timeout_str, NULL, 16);
else
loop_timeout = PCIE_LINK_TIMEOUT;
for (i = 0; i < loop_timeout; i++) {
if ((readl(TOPAZ_PCIE_STAT) & TOPAZ_PCIE_LINKUP) == TOPAZ_PCIE_LINKUP)
break;
udelay(10); /* Delay. */
}
if (i >= loop_timeout) {
printf("Timeout. Polling threshold: %d\n", loop_timeout);
printf("Fails to establish PCIe link: link stat: addr: 0x%08x val: 0x%08x\n",\
TOPAZ_PCIE_STAT, readl(TOPAZ_PCIE_STAT));
return -1;
} else {
printf("Established\n");
return 0;
}
}
void pcie_reset(void)
{
printf("Reset PCIe link\n");
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);
}
void pcie_ep_early_init(uint32_t flags)
{
char *val;
int force_pcie_reset = 1;
int i = 0;
#define PCIE_RELINK_TIMES (5)
val = getenv("disable_pcie");
if ((val != NULL) && (val[0] != '0')) {
return;
}
if (flags & PCIE_RC_MODE) {
return;
}
val = getenv("bypass_first_pcie_reset");
if ((val != NULL) && (val[0] == '1'))
force_pcie_reset = 0;
do {
if (i != 0 || (i == 0 && force_pcie_reset == 1))
pcie_reset();
pcie_ep_early_config();
if (pcie_linkup_check() == 0)
break;
i++;
} while (i < PCIE_RELINK_TIMES);
if (i == PCIE_RELINK_TIMES)
printf("Error: Failed to establish PCIE link after %d retries\n", i - 1);
}
#ifndef TOPAZ_EP_MINI_UBOOT
void pci_rc_reset_ep(void)
{
/* Set GPIO 13 to output mode */
gpio_config(13, GPIO_MODE_OUTPUT);
gpio_output(13, 1);
udelay(10);
/* Reset EP by write 0 to data register */
gpio_output(13, 0);
/* Keep Reset signal 10 ms */
udelay(10000);
gpio_output(13, 1);
}
#endif
#ifndef TOPAZ_EP_MINI_UBOOT
/*
* init for root complex mode
*/
void pcie_rc_init(void)
{
pci_rc_reset_ep();
/* 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(0x00010020, PCIE_PORT_LINK_CTL);
writel(PCIE_CFG0_DEFAULT_VALUE | PCIE_CFG_RC_MODE, RUBY_SYS_CTL_PCIE_CFG0);
writel(0x00000001, RUBY_SYS_CTL_PCIE_CFG1);
writel(0x00000000, RUBY_SYS_CTL_PCIE_CFG2);
writel(0x45220000, RUBY_SYS_CTL_PCIE_CFG3);
writel(0x00100007, PCIE_CMDSTS);
writel(0xf << 22, RUBY_SYS_CTL_PCIE_SLV_REQ_MISC_INFO);
/* Enable DMA Read Channel */
REG_WRITE(PCIE_DMA_RD_ENABLE, 0x00000001);
REG_WRITE(PCIE_DMA_RD_CHWTLOW, 0x000001FF);
REG_WRITE(PCIE_DMA_RD_CHWTHIG, 0x00000000);
/* Enable DMA write channel */
REG_WRITE(PCIE_DMA_WR_ENABLE, 0x00000001);
REG_WRITE(PCIE_DMA_WR_CHWTLOW, 0x000001FF);
REG_WRITE(PCIE_DMA_WR_CHWTHIG, 0x00000000);
/* 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);
}
#endif /* TOPAZ_EP_MINI_UBOOT */
void board_pcie_init(size_t memsz, uint32_t flags )
{
char *val;
if (flags & PCIE_RC_MODE) {
#ifdef TOPAZ_EP_MINI_UBOOT
printf("Doesn't support RC mode in mini u-boot\n");
#else
printf("init board as PCIe Root Complex mode\n");
pcie_rc_init();
#endif
} else {
val = getenv("disable_pcie");
if (val == NULL || val[0] == '0') {
printf("init board as PCIe End Point mode\n");
pcie_ep_init(memsz, 0);
}
}
}